Merge bitcoindevkit/bdk-ffi#296: Add Transaction struct, update PSBT and Blockchain to use it

8e54ada436f84fb5bbcd5f448b7fccc186387bb4 Add Transaction struct, update PSBT and Blockchain to use it (Steve Myers)

Pull request description:

  ### Description

  Add new `Transaction` structure that can be created from transaction consensus encoded bytes. Update `PartiallySignedTransaction.extract_tx()` to return a `Transaction` instead of a the transaction bytes. Update `Blockchain.broadcast()` to take a `Transaction` parameter.

  ### Notes to the reviewers

  Fixes #157.

  ### Changelog notice

  #### Added

  - New `Transaction` structure that can be created from or serialized to consensus encoded bytes.

  #### Changed

  - `PartiallySignedTransaction.extract_tx()` returns a `Transaction` instead of a the transaction bytes.
  - `Blockchain.broadcast()` takes a `Transaction` instead of a `PartiallySignedTransaction`.

  ### Checklists

  #### All Submissions:

  * [x] I've signed all my commits
  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  * [x] I ran `cargo fmt` and `cargo clippy` before committing

  #### New Features:

  * [ ] I've added tests for the new feature
  * [x] I've added docs for the new feature

  #### Bugfixes:

  * [ ] This pull request breaks the existing API
  * [ ] I've added tests to reproduce the issue which are now passing
  * [ ] I'm linking the issue being fixed by this PR

ACKs for top commit:
  thunderbiscuit:
    Tested ACK 8e54ada436f84fb5bbcd5f448b7fccc186387bb4. I ran a script using bdk-jvm and your test vector. Looks good! Another win for the LDK/BDK integration.

Tree-SHA512: f5bfdafad93f0b4ada0756a8e68bdb12f2de680d8ae8a5083dbbeded6fc314bd79c8913f0b7f01e2eb807676092777ad8bf9466996cd51fa9587b8bb53170ca8
This commit is contained in:
Steve Myers 2023-01-23 15:46:19 -06:00
commit f75ead02ff
No known key found for this signature in database
GPG Key ID: 8105A46B22C2D051
5 changed files with 75 additions and 20 deletions

View File

@ -248,7 +248,7 @@ class Blockchain(
config: BlockchainConfig
) {
/** Broadcast a transaction. */
fun broadcast(psbt: PartiallySignedBitcoinTransaction) {}
fun broadcast(transaction: Transaction) {}
/** Estimate the fee rate required to confirm a transaction in a given target of blocks. */
fun estimateFee(target: ULong): FeeRate {}
@ -260,6 +260,18 @@ class Blockchain(
fun getBlockHash(height: UInt): String {}
}
/**
* A bitcoin transaction.
*
* @constructor Build a new Bitcoin Transaction.
*
* @param transactionBytes The transaction bytes, bitcoin consensus encoded.
*/
class Transaction(transactionBytes: List<UByte>) {
/** Return the transaction bytes, bitcoin consensus encoded. */
fun serialize(): List<UByte> {}
}
/**
* A partially signed bitcoin transaction.
*
@ -274,8 +286,8 @@ class PartiallySignedBitcoinTransaction(psbtBase64: String) {
/** Get the txid of the PSBT. */
fun txid(): String {}
/** Return the transaction as bytes. */
fun extractTx(): List<UByte>
/** Extract the transaction. */
fun extractTx(): Transaction {}
/**
* Combines this PartiallySignedTransaction with another PSBT as described by BIP 174.

View File

@ -162,7 +162,7 @@ interface Blockchain {
constructor(BlockchainConfig config);
[Throws=BdkError]
void broadcast([ByRef] PartiallySignedTransaction psbt);
void broadcast([ByRef] Transaction transaction);
[Throws=BdkError]
FeeRate estimate_fee(u64 target);
@ -237,6 +237,13 @@ interface FeeRate {
float as_sat_per_vb();
};
interface Transaction {
[Throws=BdkError]
constructor(sequence<u8> transaction_bytes);
sequence<u8> serialize();
};
interface PartiallySignedTransaction {
[Throws=BdkError]
constructor(string psbt_base64);
@ -245,7 +252,7 @@ interface PartiallySignedTransaction {
string txid();
sequence<u8> extract_tx();
Transaction extract_tx();
[Throws=BdkError]
PartiallySignedTransaction combine(PartiallySignedTransaction other);

View File

@ -1,5 +1,5 @@
// use crate::BlockchainConfig;
use crate::{BdkError, PartiallySignedTransaction};
use crate::{BdkError, Transaction};
use bdk::bitcoin::Network;
use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig};
use bdk::blockchain::rpc::Auth as BdkAuth;
@ -60,9 +60,9 @@ impl Blockchain {
self.blockchain_mutex.lock().expect("blockchain")
}
pub(crate) fn broadcast(&self, psbt: &PartiallySignedTransaction) -> Result<(), BdkError> {
let tx = psbt.internal.lock().unwrap().clone().extract_tx();
self.get_blockchain().broadcast(&tx)
pub(crate) fn broadcast(&self, transaction: &Transaction) -> Result<(), BdkError> {
let tx = &transaction.internal;
self.get_blockchain().broadcast(tx)
}
pub(crate) fn estimate_fee(&self, target: u64) -> Result<Arc<FeeRate>, BdkError> {

View File

@ -15,7 +15,11 @@ use crate::keys::{DescriptorPublicKey, DescriptorSecretKey, Mnemonic};
use crate::psbt::PartiallySignedTransaction;
use crate::wallet::{BumpFeeTxBuilder, TxBuilder, Wallet};
use bdk::bitcoin::blockdata::script::Script as BdkScript;
use bdk::bitcoin::{Address as BdkAddress, Network, OutPoint as BdkOutPoint, Txid};
use bdk::bitcoin::consensus::Decodable;
use bdk::bitcoin::psbt::serialize::Serialize;
use bdk::bitcoin::{
Address as BdkAddress, Network, OutPoint as BdkOutPoint, Transaction as BdkTransaction, Txid,
};
use bdk::blockchain::Progress as BdkProgress;
use bdk::database::any::{SledDbConfiguration, SqliteDbConfiguration};
use bdk::keys::bip39::WordCount;
@ -24,6 +28,7 @@ use bdk::wallet::AddressInfo as BdkAddressInfo;
use bdk::{Balance as BdkBalance, BlockTime, Error as BdkError, FeeRate, KeychainKind};
use std::convert::From;
use std::fmt;
use std::io::Cursor;
use std::str::FromStr;
use std::sync::Arc;
@ -218,6 +223,24 @@ impl fmt::Debug for ProgressHolder {
}
}
/// A Bitcoin transaction.
#[derive(Debug)]
pub struct Transaction {
internal: BdkTransaction,
}
impl Transaction {
fn new(transaction_bytes: Vec<u8>) -> Result<Self, BdkError> {
let mut decoder = Cursor::new(transaction_bytes);
let tx: BdkTransaction = BdkTransaction::consensus_decode(&mut decoder)?;
Ok(Transaction { internal: tx })
}
fn serialize(&self) -> Vec<u8> {
self.internal.serialize()
}
}
/// A Bitcoin address.
struct Address {
address: BdkAddress,
@ -264,3 +287,21 @@ pub struct TxBuilderResult {
}
uniffi::deps::static_assertions::assert_impl_all!(Wallet: Sync, Send);
// The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
// These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
// crate.
#[cfg(test)]
mod test {
use super::Transaction;
use bdk::bitcoin::hashes::hex::FromHex;
// Verify that bdk-ffi Transaction can be created from valid bytes and serialized back into the same bytes.
#[test]
fn test_transaction_serde() {
let test_tx_bytes = Vec::from_hex("020000000001031cfbc8f54fbfa4a33a30068841371f80dbfe166211242213188428f437445c91000000006a47304402206fbcec8d2d2e740d824d3d36cc345b37d9f65d665a99f5bd5c9e8d42270a03a8022013959632492332200c2908459547bf8dbf97c65ab1a28dec377d6f1d41d3d63e012103d7279dfb90ce17fe139ba60a7c41ddf605b25e1c07a4ddcb9dfef4e7d6710f48feffffff476222484f5e35b3f0e43f65fc76e21d8be7818dd6a989c160b1e5039b7835fc00000000171600140914414d3c94af70ac7e25407b0689e0baa10c77feffffffa83d954a62568bbc99cc644c62eb7383d7c2a2563041a0aeb891a6a4055895570000000017160014795d04cc2d4f31480d9a3710993fbd80d04301dffeffffff06fef72f000000000017a91476fd7035cd26f1a32a5ab979e056713aac25796887a5000f00000000001976a914b8332d502a529571c6af4be66399cd33379071c588ac3fda0500000000001976a914fc1d692f8de10ae33295f090bea5fe49527d975c88ac522e1b00000000001976a914808406b54d1044c429ac54c0e189b0d8061667e088ac6eb68501000000001976a914dfab6085f3a8fb3e6710206a5a959313c5618f4d88acbba20000000000001976a914eb3026552d7e3f3073457d0bee5d4757de48160d88ac0002483045022100bee24b63212939d33d513e767bc79300051f7a0d433c3fcf1e0e3bf03b9eb1d70220588dc45a9ce3a939103b4459ce47500b64e23ab118dfc03c9caa7d6bfc32b9c601210354fd80328da0f9ae6eef2b3a81f74f9a6f66761fadf96f1d1d22b1fd6845876402483045022100e29c7e3a5efc10da6269e5fc20b6a1cb8beb92130cc52c67e46ef40aaa5cac5f0220644dd1b049727d991aece98a105563416e10a5ac4221abac7d16931842d5c322012103960b87412d6e169f30e12106bdf70122aabb9eb61f455518322a18b920a4dfa887d30700").unwrap();
let new_tx_from_bytes = Transaction::new(test_tx_bytes.clone()).unwrap();
let serialized_tx_to_bytes = new_tx_from_bytes.serialize();
assert_eq!(test_tx_bytes, serialized_tx_to_bytes);
}
}

View File

@ -1,11 +1,10 @@
use bdk::bitcoin::hashes::hex::ToHex;
use bdk::bitcoin::psbt::serialize::Serialize;
use bdk::bitcoin::util::psbt::PartiallySignedTransaction as BdkPartiallySignedTransaction;
use bdk::psbt::PsbtUtils;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use crate::{BdkError, FeeRate};
use crate::{BdkError, FeeRate, Transaction};
#[derive(Debug)]
pub(crate) struct PartiallySignedTransaction {
@ -32,14 +31,10 @@ impl PartiallySignedTransaction {
txid.to_hex()
}
/// Return the transaction as bytes.
pub(crate) fn extract_tx(&self) -> Vec<u8> {
self.internal
.lock()
.unwrap()
.clone()
.extract_tx()
.serialize()
/// Return the transaction.
pub(crate) fn extract_tx(&self) -> Arc<Transaction> {
let tx = self.internal.lock().unwrap().clone().extract_tx();
Arc::new(Transaction { internal: tx })
}
/// Combines this PartiallySignedTransaction with other PSBT as described by BIP 174.