Allow TxBuilder.add_recipient() to take Script

This commit is contained in:
thunderbiscuit 2022-09-01 13:05:35 -04:00
parent 2cbb314d0b
commit 3fefd3c1fb
No known key found for this signature in database
GPG Key ID: 88253696EB836462
2 changed files with 77 additions and 21 deletions

View File

@ -173,8 +173,8 @@ dictionary LocalUtxo {
boolean is_spent; boolean is_spent;
}; };
dictionary AddressAmount { dictionary ScriptAmount {
string address; Script script;
u64 amount; u64 amount;
}; };
@ -211,6 +211,8 @@ interface PartiallySignedBitcoinTransaction {
string txid(); string txid();
sequence<u8> extract_tx();
[Throws=Error] [Throws=Error]
PartiallySignedBitcoinTransaction combine(PartiallySignedBitcoinTransaction other); PartiallySignedBitcoinTransaction combine(PartiallySignedBitcoinTransaction other);
}; };
@ -218,7 +220,7 @@ interface PartiallySignedBitcoinTransaction {
interface TxBuilder { interface TxBuilder {
constructor(); constructor();
TxBuilder add_recipient(string address, u64 amount); TxBuilder add_recipient(Script script, u64 amount);
TxBuilder add_unspendable(OutPoint unspendable); TxBuilder add_unspendable(OutPoint unspendable);
@ -248,7 +250,7 @@ interface TxBuilder {
TxBuilder add_data(sequence<u8> data); TxBuilder add_data(sequence<u8> data);
TxBuilder set_recipients(sequence<AddressAmount> recipients); TxBuilder set_recipients(sequence<ScriptAmount> recipients);
[Throws=Error] [Throws=Error]
PartiallySignedBitcoinTransaction finish([ByRef] Wallet wallet); PartiallySignedBitcoinTransaction finish([ByRef] Wallet wallet);
@ -296,3 +298,14 @@ interface DescriptorPublicKey {
string as_string(); string as_string();
}; };
interface Address {
[Throws=Error]
constructor(string address);
Script script_pubkey();
};
interface Script {
constructor(sequence<u8> raw_output_script);
};

View File

@ -1,8 +1,10 @@
use bdk::bitcoin::blockdata::script::Script as BdkScript;
use bdk::bitcoin::hashes::hex::ToHex; use bdk::bitcoin::hashes::hex::ToHex;
use bdk::bitcoin::secp256k1::Secp256k1; use bdk::bitcoin::secp256k1::Secp256k1;
use bdk::bitcoin::util::bip32::DerivationPath as BdkDerivationPath; use bdk::bitcoin::util::bip32::DerivationPath as BdkDerivationPath;
use bdk::bitcoin::util::psbt::serialize::Serialize;
use bdk::bitcoin::util::psbt::PartiallySignedTransaction; use bdk::bitcoin::util::psbt::PartiallySignedTransaction;
use bdk::bitcoin::{Address, Network, OutPoint as BdkOutPoint, Script, Txid}; use bdk::bitcoin::{Address as BdkAddress, Network, OutPoint as BdkOutPoint, Txid};
use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig}; use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig};
use bdk::blockchain::GetBlockHash; use bdk::blockchain::GetBlockHash;
use bdk::blockchain::GetHeight; use bdk::blockchain::GetHeight;
@ -35,8 +37,9 @@ use std::sync::{Arc, Mutex, MutexGuard};
uniffi_macros::include_scaffolding!("bdk"); uniffi_macros::include_scaffolding!("bdk");
pub struct AddressAmount { /// A output script and an amount of satoshis.
pub address: String, pub struct ScriptAmount {
pub script: Arc<Script>,
pub amount: u64, pub amount: u64,
} }
@ -302,7 +305,7 @@ impl NetworkLocalUtxo for LocalUtxo {
}, },
txout: TxOut { txout: TxOut {
value: x.txout.value, value: x.txout.value,
address: Address::from_script(&x.txout.script_pubkey, network) address: BdkAddress::from_script(&x.txout.script_pubkey, network)
.unwrap() .unwrap()
.to_string(), .to_string(),
}, },
@ -360,6 +363,16 @@ impl PartiallySignedBitcoinTransaction {
txid.to_hex() txid.to_hex()
} }
/// Return the transaction as bytes.
fn extract_tx(&self) -> Vec<u8> {
self.internal
.lock()
.unwrap()
.clone()
.extract_tx()
.serialize()
}
/// Combines this PartiallySignedTransaction with other PSBT as described by BIP 174. /// Combines this PartiallySignedTransaction with other PSBT as described by BIP 174.
/// ///
/// In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)` /// In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)`
@ -471,12 +484,44 @@ impl Wallet {
} }
} }
fn to_script_pubkey(address: &str) -> Result<Script, Error> { fn to_script_pubkey(address: &str) -> Result<BdkScript, Error> {
Address::from_str(address) BdkAddress::from_str(address)
.map(|x| x.script_pubkey()) .map(|x| x.script_pubkey())
.map_err(|e| Error::Generic(e.to_string())) .map_err(|e| Error::Generic(e.to_string()))
} }
/// A Bitcoin address.
struct Address {
address: BdkAddress,
}
impl Address {
fn new(address: String) -> Result<Self, Error> {
BdkAddress::from_str(address.as_str())
.map(|a| Address { address: a })
.map_err(|e| Error::Generic(e.to_string()))
}
fn script_pubkey(&self) -> Arc<Script> {
Arc::new(Script {
script: self.address.script_pubkey(),
})
}
}
/// A Bitcoin script.
#[derive(Clone)]
pub struct Script {
script: BdkScript,
}
impl Script {
fn new(raw_output_script: Vec<u8>) -> Self {
let script: BdkScript = BdkScript::from(raw_output_script);
Script { script }
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum RbfValue { enum RbfValue {
Default, Default,
@ -488,7 +533,7 @@ enum RbfValue {
/// Each method on the TxBuilder returns an instance of a new TxBuilder with the option set/added. /// Each method on the TxBuilder returns an instance of a new TxBuilder with the option set/added.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct TxBuilder { struct TxBuilder {
recipients: Vec<(String, u64)>, recipients: Vec<(BdkScript, u64)>,
utxos: Vec<OutPoint>, utxos: Vec<OutPoint>,
unspendable: HashSet<OutPoint>, unspendable: HashSet<OutPoint>,
change_policy: ChangeSpendPolicy, change_policy: ChangeSpendPolicy,
@ -519,19 +564,19 @@ impl TxBuilder {
} }
/// Add a recipient to the internal list. /// Add a recipient to the internal list.
fn add_recipient(&self, recipient: String, amount: u64) -> Arc<Self> { fn add_recipient(&self, script: Arc<Script>, amount: u64) -> Arc<Self> {
let mut recipients = self.recipients.to_vec(); let mut recipients: Vec<(BdkScript, u64)> = self.recipients.clone();
recipients.append(&mut vec![(recipient, amount)]); recipients.append(&mut vec![(script.script.clone(), amount)]);
Arc::new(TxBuilder { Arc::new(TxBuilder {
recipients, recipients,
..self.clone() ..self.clone()
}) })
} }
fn set_recipients(&self, recipients: Vec<AddressAmount>) -> Arc<Self> { fn set_recipients(&self, recipients: Vec<ScriptAmount>) -> Arc<Self> {
let recipients = recipients let recipients = recipients
.iter() .iter()
.map(|address_amount| (address_amount.address.clone(), address_amount.amount)) .map(|script_amount| (script_amount.script.script.clone(), script_amount.amount))
.collect(); .collect();
Arc::new(TxBuilder { Arc::new(TxBuilder {
recipients, recipients,
@ -671,8 +716,8 @@ impl TxBuilder {
fn finish(&self, wallet: &Wallet) -> Result<Arc<PartiallySignedBitcoinTransaction>, Error> { fn finish(&self, wallet: &Wallet) -> Result<Arc<PartiallySignedBitcoinTransaction>, Error> {
let wallet = wallet.get_wallet(); let wallet = wallet.get_wallet();
let mut tx_builder = wallet.build_tx(); let mut tx_builder = wallet.build_tx();
for (address, amount) in &self.recipients { for (script, amount) in &self.recipients {
tx_builder.add_recipient(to_script_pubkey(address)?, *amount); tx_builder.add_recipient(script.clone(), *amount);
} }
tx_builder.change_policy(self.change_policy); tx_builder.change_policy(self.change_policy);
if !self.utxos.is_empty() { if !self.utxos.is_empty() {
@ -780,7 +825,7 @@ impl BumpFeeTxBuilder {
tx_builder.fee_rate(FeeRate::from_sat_per_vb(self.fee_rate)); tx_builder.fee_rate(FeeRate::from_sat_per_vb(self.fee_rate));
if let Some(allow_shrinking) = &self.allow_shrinking { if let Some(allow_shrinking) = &self.allow_shrinking {
let address = let address =
Address::from_str(allow_shrinking).map_err(|e| Error::Generic(e.to_string()))?; BdkAddress::from_str(allow_shrinking).map_err(|e| Error::Generic(e.to_string()))?;
let script = address.script_pubkey(); let script = address.script_pubkey();
tx_builder.allow_shrinking(script)?; tx_builder.allow_shrinking(script)?;
} }
@ -1010,7 +1055,6 @@ mod test {
let tx_builder = TxBuilder::new() let tx_builder = TxBuilder::new()
.drain_wallet() .drain_wallet()
.drain_to(drain_to_address.clone()); .drain_to(drain_to_address.clone());
//dbg!(&tx_builder);
assert!(tx_builder.drain_wallet); assert!(tx_builder.drain_wallet);
assert_eq!(tx_builder.drain_to, Some(drain_to_address)); assert_eq!(tx_builder.drain_to, Some(drain_to_address));
@ -1128,7 +1172,6 @@ mod test {
#[test] #[test]
fn test_derive_and_extend_descriptor_secret_key() { fn test_derive_and_extend_descriptor_secret_key() {
let master_dsk = get_descriptor_secret_key(); let master_dsk = get_descriptor_secret_key();
// derive DescriptorSecretKey with path "m/0" from master // derive DescriptorSecretKey with path "m/0" from master
let derived_dsk: &DescriptorSecretKey = &derive_dsk(&master_dsk, "m/0").unwrap(); let derived_dsk: &DescriptorSecretKey = &derive_dsk(&master_dsk, "m/0").unwrap();
assert_eq!(derived_dsk.as_string(), "[d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/*"); assert_eq!(derived_dsk.as_string(), "[d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/*");