From c6cbfb98153c7d2995dbb557837abb04307cca2d Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Tue, 22 Oct 2024 12:47:05 -0400 Subject: [PATCH 1/7] feat: expose TxBuilder::add_data method --- bdk-ffi/src/bdk.udl | 3 +++ bdk-ffi/src/error.rs | 10 ++++++++++ bdk-ffi/src/tx_builder.rs | 15 +++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index 3d001e41..1aff1d73 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -75,6 +75,7 @@ interface CreateTxError { UnknownUtxo(string outpoint); MissingNonWitnessUtxo(string outpoint); MiniscriptPsbt(string error_message); + PushBytesError(); }; [Error] @@ -709,6 +710,8 @@ interface TxBuilder { TxBuilder set_exact_sequence(u32 nsequence); + TxBuilder add_data(sequence data); + [Throws=CreateTxError] Psbt finish([ByRef] Wallet wallet); }; diff --git a/bdk-ffi/src/error.rs b/bdk-ffi/src/error.rs index c7b21489..fbe9ba49 100644 --- a/bdk-ffi/src/error.rs +++ b/bdk-ffi/src/error.rs @@ -1,5 +1,6 @@ use bitcoin_ffi::OutPoint; +use bdk_core::bitcoin::script::PushBytesError; use bdk_electrum::electrum_client::Error as BdkElectrumError; use bdk_esplora::esplora_client::{Error as BdkEsploraError, Error}; use bdk_wallet::bitcoin::address::ParseError as BdkParseError; @@ -195,6 +196,9 @@ pub enum CreateTxError { #[error("miniscript psbt error: {error_message}")] MiniscriptPsbt { error_message: String }, + + #[error("attempt to prepare too many bytes to be pushed into script")] + PushBytesError, } #[derive(Debug, thiserror::Error)] @@ -951,6 +955,12 @@ impl From for CreateTxError { } } +impl From for CreateTxError { + fn from(_: PushBytesError) -> Self { + CreateTxError::PushBytesError + } +} + impl From> for CreateWithPersistError { fn from(error: BdkCreateWithPersistError) -> Self { match error { diff --git a/bdk-ffi/src/tx_builder.rs b/bdk-ffi/src/tx_builder.rs index c11e6b0b..dde2a6fe 100644 --- a/bdk-ffi/src/tx_builder.rs +++ b/bdk-ffi/src/tx_builder.rs @@ -14,7 +14,9 @@ use bdk_wallet::ChangeSpendPolicy; use std::collections::BTreeMap; use std::collections::HashMap; +use bdk_wallet::bitcoin::script::PushBytesBuf; use std::collections::HashSet; +use std::convert::TryFrom; use std::str::FromStr; use std::sync::Arc; @@ -33,6 +35,7 @@ pub struct TxBuilder { pub(crate) drain_wallet: bool, pub(crate) drain_to: Option, pub(crate) sequence: Option, + pub(crate) data: Vec, } impl TxBuilder { @@ -51,6 +54,7 @@ impl TxBuilder { drain_wallet: false, drain_to: None, sequence: None, + data: Vec::new(), } } @@ -193,6 +197,13 @@ impl TxBuilder { }) } + pub(crate) fn add_data(&self, data: Vec) -> Arc { + Arc::new(TxBuilder { + data, + ..self.clone() + }) + } + pub(crate) fn finish(&self, wallet: &Arc) -> Result, CreateTxError> { // TODO: I had to change the wallet here to be mutable. Why is that now required with the 1.0 API? let mut wallet = wallet.get_wallet(); @@ -237,6 +248,10 @@ impl TxBuilder { if let Some(sequence) = self.sequence { tx_builder.set_exact_sequence(Sequence(sequence)); } + if !&self.data.is_empty() { + let push_bytes = PushBytesBuf::try_from(self.data.clone())?; + tx_builder.add_data(&push_bytes); + } let psbt = tx_builder.finish().map_err(CreateTxError::from)?; From cfc03086d1e08ab66ca88d52e2fb087bf75c1d15 Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Fri, 15 Nov 2024 11:31:25 -0500 Subject: [PATCH 2/7] feat: expose TxBuilder::current_height --- bdk-ffi/src/bdk.udl | 4 ++++ bdk-ffi/src/tx_builder.rs | 28 ++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index 1aff1d73..6a653e98 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -712,6 +712,8 @@ interface TxBuilder { TxBuilder add_data(sequence data); + TxBuilder current_height(u32 height); + [Throws=CreateTxError] Psbt finish([ByRef] Wallet wallet); }; @@ -721,6 +723,8 @@ interface BumpFeeTxBuilder { BumpFeeTxBuilder set_exact_sequence(u32 nsequence); + BumpFeeTxBuilder current_height(u32 height); + [Throws=CreateTxError] Psbt finish([ByRef] Wallet wallet); }; diff --git a/bdk-ffi/src/tx_builder.rs b/bdk-ffi/src/tx_builder.rs index dde2a6fe..6d4d7e94 100644 --- a/bdk-ffi/src/tx_builder.rs +++ b/bdk-ffi/src/tx_builder.rs @@ -3,18 +3,18 @@ use crate::error::CreateTxError; use crate::types::ScriptAmount; use crate::wallet::Wallet; -use bdk_wallet::KeychainKind; use bitcoin_ffi::{Amount, FeeRate, Script}; use bdk_wallet::bitcoin::amount::Amount as BdkAmount; +use bdk_wallet::bitcoin::script::PushBytesBuf; use bdk_wallet::bitcoin::Psbt as BdkPsbt; use bdk_wallet::bitcoin::ScriptBuf as BdkScriptBuf; use bdk_wallet::bitcoin::{OutPoint, Sequence, Txid}; use bdk_wallet::ChangeSpendPolicy; +use bdk_wallet::KeychainKind; use std::collections::BTreeMap; use std::collections::HashMap; -use bdk_wallet::bitcoin::script::PushBytesBuf; use std::collections::HashSet; use std::convert::TryFrom; use std::str::FromStr; @@ -36,6 +36,7 @@ pub struct TxBuilder { pub(crate) drain_to: Option, pub(crate) sequence: Option, pub(crate) data: Vec, + pub(crate) current_height: Option, } impl TxBuilder { @@ -55,6 +56,7 @@ impl TxBuilder { drain_to: None, sequence: None, data: Vec::new(), + current_height: None, } } @@ -204,6 +206,13 @@ impl TxBuilder { }) } + pub(crate) fn current_height(&self, height: u32) -> Arc { + Arc::new(TxBuilder { + current_height: Some(height), + ..self.clone() + }) + } + pub(crate) fn finish(&self, wallet: &Arc) -> Result, CreateTxError> { // TODO: I had to change the wallet here to be mutable. Why is that now required with the 1.0 API? let mut wallet = wallet.get_wallet(); @@ -252,6 +261,9 @@ impl TxBuilder { let push_bytes = PushBytesBuf::try_from(self.data.clone())?; tx_builder.add_data(&push_bytes); } + if let Some(height) = self.current_height { + tx_builder.current_height(height); + } let psbt = tx_builder.finish().map_err(CreateTxError::from)?; @@ -264,6 +276,7 @@ pub(crate) struct BumpFeeTxBuilder { pub(crate) txid: String, pub(crate) fee_rate: Arc, pub(crate) sequence: Option, + pub(crate) current_height: Option, } impl BumpFeeTxBuilder { @@ -272,6 +285,7 @@ impl BumpFeeTxBuilder { txid, fee_rate, sequence: None, + current_height: None, } } @@ -282,6 +296,13 @@ impl BumpFeeTxBuilder { }) } + pub(crate) fn current_height(&self, height: u32) -> Arc { + Arc::new(BumpFeeTxBuilder { + current_height: Some(height), + ..self.clone() + }) + } + pub(crate) fn finish(&self, wallet: &Arc) -> Result, CreateTxError> { let txid = Txid::from_str(self.txid.as_str()).map_err(|_| CreateTxError::UnknownUtxo { outpoint: self.txid.clone(), @@ -292,6 +313,9 @@ impl BumpFeeTxBuilder { if let Some(sequence) = self.sequence { tx_builder.set_exact_sequence(Sequence(sequence)); } + if let Some(height) = self.current_height { + tx_builder.current_height(height); + } let psbt: BdkPsbt = tx_builder.finish()?; From 99adbf745b9758a8cd23ef770c92a3c7497c69f4 Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Thu, 21 Nov 2024 14:27:15 -0500 Subject: [PATCH 3/7] feat: expose TxBuilder::nlocktime --- bdk-ffi/src/bdk.udl | 5 +++++ bdk-ffi/src/error.rs | 3 +++ bdk-ffi/src/tx_builder.rs | 34 +++++++++++++++++++++++++++++++--- bdk-ffi/src/types.rs | 16 +++++++++++++++- 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index 6a653e98..9f14aa3c 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -76,6 +76,7 @@ interface CreateTxError { MissingNonWitnessUtxo(string outpoint); MiniscriptPsbt(string error_message); PushBytesError(); + LockTimeConversionError(); }; [Error] @@ -714,6 +715,8 @@ interface TxBuilder { TxBuilder current_height(u32 height); + TxBuilder nlocktime(LockTime locktime); + [Throws=CreateTxError] Psbt finish([ByRef] Wallet wallet); }; @@ -725,6 +728,8 @@ interface BumpFeeTxBuilder { BumpFeeTxBuilder current_height(u32 height); + BumpFeeTxBuilder nlocktime(LockTime locktime); + [Throws=CreateTxError] Psbt finish([ByRef] Wallet wallet); }; diff --git a/bdk-ffi/src/error.rs b/bdk-ffi/src/error.rs index fbe9ba49..88805eb2 100644 --- a/bdk-ffi/src/error.rs +++ b/bdk-ffi/src/error.rs @@ -199,6 +199,9 @@ pub enum CreateTxError { #[error("attempt to prepare too many bytes to be pushed into script")] PushBytesError, + + #[error("invalid lock time value")] + LockTimeConversionError, } #[derive(Debug, thiserror::Error)] diff --git a/bdk-ffi/src/tx_builder.rs b/bdk-ffi/src/tx_builder.rs index 6d4d7e94..a19b133a 100644 --- a/bdk-ffi/src/tx_builder.rs +++ b/bdk-ffi/src/tx_builder.rs @@ -1,10 +1,11 @@ use crate::bitcoin::Psbt; use crate::error::CreateTxError; -use crate::types::ScriptAmount; +use crate::types::{LockTime, ScriptAmount}; use crate::wallet::Wallet; use bitcoin_ffi::{Amount, FeeRate, Script}; +use bdk_wallet::bitcoin::absolute::LockTime as BdkLockTime; use bdk_wallet::bitcoin::amount::Amount as BdkAmount; use bdk_wallet::bitcoin::script::PushBytesBuf; use bdk_wallet::bitcoin::Psbt as BdkPsbt; @@ -16,7 +17,7 @@ use bdk_wallet::KeychainKind; use std::collections::BTreeMap; use std::collections::HashMap; use std::collections::HashSet; -use std::convert::TryFrom; +use std::convert::{TryFrom, TryInto}; use std::str::FromStr; use std::sync::Arc; @@ -37,6 +38,7 @@ pub struct TxBuilder { pub(crate) sequence: Option, pub(crate) data: Vec, pub(crate) current_height: Option, + pub(crate) locktime: Option, } impl TxBuilder { @@ -57,6 +59,7 @@ impl TxBuilder { sequence: None, data: Vec::new(), current_height: None, + locktime: None, } } @@ -213,6 +216,13 @@ impl TxBuilder { }) } + pub(crate) fn nlocktime(&self, locktime: LockTime) -> Arc { + Arc::new(TxBuilder { + locktime: Some(locktime), + ..self.clone() + }) + } + pub(crate) fn finish(&self, wallet: &Arc) -> Result, CreateTxError> { // TODO: I had to change the wallet here to be mutable. Why is that now required with the 1.0 API? let mut wallet = wallet.get_wallet(); @@ -264,6 +274,11 @@ impl TxBuilder { if let Some(height) = self.current_height { tx_builder.current_height(height); } + // let bdk_locktime = locktime.try_into().map_err(CreateTxError::LockTimeConversionError)?; + if let Some(locktime) = &self.locktime { + let bdk_locktime: BdkLockTime = locktime.try_into()?; + tx_builder.nlocktime(bdk_locktime); + } let psbt = tx_builder.finish().map_err(CreateTxError::from)?; @@ -277,15 +292,17 @@ pub(crate) struct BumpFeeTxBuilder { pub(crate) fee_rate: Arc, pub(crate) sequence: Option, pub(crate) current_height: Option, + pub(crate) locktime: Option, } impl BumpFeeTxBuilder { pub(crate) fn new(txid: String, fee_rate: Arc) -> Self { - Self { + BumpFeeTxBuilder { txid, fee_rate, sequence: None, current_height: None, + locktime: None, } } @@ -303,6 +320,13 @@ impl BumpFeeTxBuilder { }) } + pub(crate) fn nlocktime(&self, locktime: LockTime) -> Arc { + Arc::new(BumpFeeTxBuilder { + locktime: Some(locktime), + ..self.clone() + }) + } + pub(crate) fn finish(&self, wallet: &Arc) -> Result, CreateTxError> { let txid = Txid::from_str(self.txid.as_str()).map_err(|_| CreateTxError::UnknownUtxo { outpoint: self.txid.clone(), @@ -316,6 +340,10 @@ impl BumpFeeTxBuilder { if let Some(height) = self.current_height { tx_builder.current_height(height); } + if let Some(locktime) = &self.locktime { + let bdk_locktime: BdkLockTime = locktime.try_into()?; + tx_builder.nlocktime(bdk_locktime); + } let psbt: BdkPsbt = tx_builder.finish()?; diff --git a/bdk-ffi/src/types.rs b/bdk-ffi/src/types.rs index 91bea8ed..12c27065 100644 --- a/bdk-ffi/src/types.rs +++ b/bdk-ffi/src/types.rs @@ -1,5 +1,5 @@ use crate::bitcoin::{Address, Transaction, TxOut}; -use crate::error::RequestBuilderError; +use crate::error::{CreateTxError, RequestBuilderError}; use bitcoin_ffi::Amount; use bitcoin_ffi::OutPoint; @@ -29,6 +29,7 @@ use bdk_wallet::LocalOutput as BdkLocalOutput; use bdk_wallet::Update as BdkUpdate; use std::collections::HashMap; +use std::convert::TryFrom; use std::sync::{Arc, Mutex}; #[derive(Debug)] @@ -405,6 +406,19 @@ impl From for LockTime { } } +impl TryFrom<&LockTime> for BdkLockTime { + type Error = CreateTxError; + + fn try_from(value: &LockTime) -> Result { + match value { + LockTime::Blocks { height } => BdkLockTime::from_height(*height) + .map_err(|_| CreateTxError::LockTimeConversionError), + LockTime::Seconds { consensus_time } => BdkLockTime::from_time(*consensus_time) + .map_err(|_| CreateTxError::LockTimeConversionError), + } + } +} + #[derive(Debug, Clone)] pub enum Satisfaction { Partial { From 465dbebe0f926bd7361765d13715a356080b3c1f Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Fri, 22 Nov 2024 10:26:46 -0500 Subject: [PATCH 4/7] feat: expose TxBuilder::allow_dust --- bdk-ffi/src/bdk.udl | 4 ++++ bdk-ffi/src/tx_builder.rs | 25 ++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index 9f14aa3c..14373b7a 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -717,6 +717,8 @@ interface TxBuilder { TxBuilder nlocktime(LockTime locktime); + TxBuilder allow_dust(boolean allow_dust); + [Throws=CreateTxError] Psbt finish([ByRef] Wallet wallet); }; @@ -730,6 +732,8 @@ interface BumpFeeTxBuilder { BumpFeeTxBuilder nlocktime(LockTime locktime); + BumpFeeTxBuilder allow_dust(boolean allow_dust); + [Throws=CreateTxError] Psbt finish([ByRef] Wallet wallet); }; diff --git a/bdk-ffi/src/tx_builder.rs b/bdk-ffi/src/tx_builder.rs index a19b133a..e0f88997 100644 --- a/bdk-ffi/src/tx_builder.rs +++ b/bdk-ffi/src/tx_builder.rs @@ -39,6 +39,7 @@ pub struct TxBuilder { pub(crate) data: Vec, pub(crate) current_height: Option, pub(crate) locktime: Option, + pub(crate) allow_dust: bool, } impl TxBuilder { @@ -60,6 +61,7 @@ impl TxBuilder { data: Vec::new(), current_height: None, locktime: None, + allow_dust: false, } } @@ -223,6 +225,13 @@ impl TxBuilder { }) } + pub(crate) fn allow_dust(&self, allow_dust: bool) -> Arc { + Arc::new(TxBuilder { + allow_dust, + ..self.clone() + }) + } + pub(crate) fn finish(&self, wallet: &Arc) -> Result, CreateTxError> { // TODO: I had to change the wallet here to be mutable. Why is that now required with the 1.0 API? let mut wallet = wallet.get_wallet(); @@ -274,11 +283,13 @@ impl TxBuilder { if let Some(height) = self.current_height { tx_builder.current_height(height); } - // let bdk_locktime = locktime.try_into().map_err(CreateTxError::LockTimeConversionError)?; if let Some(locktime) = &self.locktime { let bdk_locktime: BdkLockTime = locktime.try_into()?; tx_builder.nlocktime(bdk_locktime); } + if self.allow_dust { + tx_builder.allow_dust(self.allow_dust); + } let psbt = tx_builder.finish().map_err(CreateTxError::from)?; @@ -293,6 +304,7 @@ pub(crate) struct BumpFeeTxBuilder { pub(crate) sequence: Option, pub(crate) current_height: Option, pub(crate) locktime: Option, + pub(crate) allow_dust: bool, } impl BumpFeeTxBuilder { @@ -303,6 +315,7 @@ impl BumpFeeTxBuilder { sequence: None, current_height: None, locktime: None, + allow_dust: false, } } @@ -327,6 +340,13 @@ impl BumpFeeTxBuilder { }) } + pub(crate) fn allow_dust(&self, allow_dust: bool) -> Arc { + Arc::new(BumpFeeTxBuilder { + allow_dust, + ..self.clone() + }) + } + pub(crate) fn finish(&self, wallet: &Arc) -> Result, CreateTxError> { let txid = Txid::from_str(self.txid.as_str()).map_err(|_| CreateTxError::UnknownUtxo { outpoint: self.txid.clone(), @@ -344,6 +364,9 @@ impl BumpFeeTxBuilder { let bdk_locktime: BdkLockTime = locktime.try_into()?; tx_builder.nlocktime(bdk_locktime); } + if self.allow_dust { + tx_builder.allow_dust(self.allow_dust); + } let psbt: BdkPsbt = tx_builder.finish()?; From d0bec77a07a3a73096f3faa02e384b01ae22d753 Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Fri, 22 Nov 2024 10:46:20 -0500 Subject: [PATCH 5/7] feat: expose TxBuilder::version --- bdk-ffi/src/bdk.udl | 4 ++++ bdk-ffi/src/tx_builder.rs | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index 14373b7a..a8cab3ba 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -719,6 +719,8 @@ interface TxBuilder { TxBuilder allow_dust(boolean allow_dust); + TxBuilder version(i32 version); + [Throws=CreateTxError] Psbt finish([ByRef] Wallet wallet); }; @@ -734,6 +736,8 @@ interface BumpFeeTxBuilder { BumpFeeTxBuilder allow_dust(boolean allow_dust); + BumpFeeTxBuilder version(i32 version); + [Throws=CreateTxError] Psbt finish([ByRef] Wallet wallet); }; diff --git a/bdk-ffi/src/tx_builder.rs b/bdk-ffi/src/tx_builder.rs index e0f88997..f6efae8f 100644 --- a/bdk-ffi/src/tx_builder.rs +++ b/bdk-ffi/src/tx_builder.rs @@ -40,6 +40,7 @@ pub struct TxBuilder { pub(crate) current_height: Option, pub(crate) locktime: Option, pub(crate) allow_dust: bool, + pub(crate) version: Option, } impl TxBuilder { @@ -62,6 +63,7 @@ impl TxBuilder { current_height: None, locktime: None, allow_dust: false, + version: None, } } @@ -232,6 +234,13 @@ impl TxBuilder { }) } + pub(crate) fn version(&self, version: i32) -> Arc { + Arc::new(TxBuilder { + version: Some(version), + ..self.clone() + }) + } + pub(crate) fn finish(&self, wallet: &Arc) -> Result, CreateTxError> { // TODO: I had to change the wallet here to be mutable. Why is that now required with the 1.0 API? let mut wallet = wallet.get_wallet(); @@ -290,6 +299,9 @@ impl TxBuilder { if self.allow_dust { tx_builder.allow_dust(self.allow_dust); } + if let Some(version) = self.version { + tx_builder.version(version); + } let psbt = tx_builder.finish().map_err(CreateTxError::from)?; @@ -305,6 +317,7 @@ pub(crate) struct BumpFeeTxBuilder { pub(crate) current_height: Option, pub(crate) locktime: Option, pub(crate) allow_dust: bool, + pub(crate) version: Option, } impl BumpFeeTxBuilder { @@ -316,6 +329,7 @@ impl BumpFeeTxBuilder { current_height: None, locktime: None, allow_dust: false, + version: None, } } @@ -347,6 +361,13 @@ impl BumpFeeTxBuilder { }) } + pub(crate) fn version(&self, version: i32) -> Arc { + Arc::new(BumpFeeTxBuilder { + version: Some(version), + ..self.clone() + }) + } + pub(crate) fn finish(&self, wallet: &Arc) -> Result, CreateTxError> { let txid = Txid::from_str(self.txid.as_str()).map_err(|_| CreateTxError::UnknownUtxo { outpoint: self.txid.clone(), @@ -367,6 +388,9 @@ impl BumpFeeTxBuilder { if self.allow_dust { tx_builder.allow_dust(self.allow_dust); } + if let Some(version) = self.version { + tx_builder.version(version); + } let psbt: BdkPsbt = tx_builder.finish()?; From ee80d5d96ca41f4092f3001b01da2691d464256a Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Mon, 25 Nov 2024 15:19:39 -0500 Subject: [PATCH 6/7] docs: add docstrings for TxBuilder --- bdk-ffi/src/bdk.udl | 92 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index a8cab3ba..b126c195 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -676,51 +676,143 @@ dictionary Condition { LockTime? timelock; }; +/// A `TxBuilder` is created by calling `build_tx` or `build_fee_bump` on a wallet. After assigning it, you set options on it +/// until finally calling `finish` to consume the builder and generate the transaction. interface TxBuilder { constructor(); + /// Fill-in the `PSBT_GLOBAL_XPUB` field with the extended keys contained in both the external and internal + /// descriptors. + /// + /// This is useful for offline signers that take part to a multisig. Some hardware wallets like BitBox and ColdCard + /// are known to require this. TxBuilder add_global_xpubs(); + /// Add a recipient to the internal list of recipients. TxBuilder add_recipient([ByRef] Script script, Amount amount); + /// Replace the recipients already added with a new list of recipients. TxBuilder set_recipients(sequence recipients); + /// Add a utxo to the internal list of unspendable utxos. + /// + /// It’s important to note that the "must-be-spent" utxos added with `TxBuilder::add_utxo` have priority over this. TxBuilder add_unspendable(OutPoint unspendable); + /// Replace the internal list of unspendable utxos with a new list. + /// + /// It’s important to note that the "must-be-spent" utxos added with `TxBuilder::add_utxo` have priority over these. TxBuilder unspendable(sequence unspendable); + /// Add a utxo to the internal list of utxos that must be spent. + /// + /// These have priority over the "unspendable" utxos, meaning that if a utxo is present both in the "utxos" and the + /// "unspendable" list, it will be spent. TxBuilder add_utxo(OutPoint outpoint); + /// The TxBuilder::policy_path is a complex API. See the Rust docs for complete information: https://docs.rs/bdk_wallet/latest/bdk_wallet/struct.TxBuilder.html#method.policy_path TxBuilder policy_path(record> policy_path, KeychainKind keychain); + /// Set a specific `ChangeSpendPolicy`. See `TxBuilder::do_not_spend_change` and `TxBuilder::only_spend_change` for + /// some shortcuts. This method assumes the presence of an internal keychain, otherwise it has no effect. TxBuilder change_policy(ChangeSpendPolicy change_policy); + /// Do not spend change outputs. + /// + /// This effectively adds all the change outputs to the "unspendable" list. See `TxBuilder::unspendable`. This method + /// assumes the presence of an internal keychain, otherwise it has no effect. TxBuilder do_not_spend_change(); + /// Only spend change outputs. + /// + /// This effectively adds all the non-change outputs to the "unspendable" list. See `TxBuilder::unspendable`. This + /// method assumes the presence of an internal keychain, otherwise it has no effect. TxBuilder only_spend_change(); + /// Only spend utxos added by `TxBuilder::add_utxo`. + /// + /// The wallet will not add additional utxos to the transaction even if they are needed to make the transaction valid. TxBuilder manually_selected_only(); + /// Set a custom fee rate. + /// + /// This method sets the mining fee paid by the transaction as a rate on its size. This means that the total fee paid + /// is equal to fee_rate times the size of the transaction. Default is 1 sat/vB in accordance with Bitcoin Core’s + /// default relay policy. + /// + /// Note that this is really a minimum feerate – it’s possible to overshoot it slightly since adding a change output + /// to drain the remaining excess might not be viable. TxBuilder fee_rate([ByRef] FeeRate fee_rate); + /// Set an absolute fee The `fee_absolute` method refers to the absolute transaction fee in `Amount`. If anyone sets + /// both the `fee_absolute` method and the `fee_rate` method, the `FeePolicy` enum will be set by whichever method was + /// called last, as the `FeeRate` and `FeeAmount` are mutually exclusive. + /// + /// Note that this is really a minimum absolute fee – it’s possible to overshoot it slightly since adding a change output to drain the remaining excess might not be viable. TxBuilder fee_absolute(Amount fee); + /// Spend all the available inputs. This respects filters like `TxBuilder::unspendable` and the change policy. TxBuilder drain_wallet(); + /// Sets the address to drain excess coins to. + /// + /// Usually, when there are excess coins they are sent to a change address generated by the wallet. This option + /// replaces the usual change address with an arbitrary script_pubkey of your choosing. Just as with a change output, + /// if the drain output is not needed (the excess coins are too small) it will not be included in the resulting + /// transaction. The only difference is that it is valid to use `drain_to` without setting any ordinary recipients + /// with `add_recipient` (but it is perfectly fine to add recipients as well). + /// + /// If you choose not to set any recipients, you should provide the utxos that the transaction should spend via + /// `add_utxos`. `drain_to` is very useful for draining all the coins in a wallet with `drain_wallet` to a single + /// address. TxBuilder drain_to([ByRef] Script script); + /// Set an exact `nSequence` value. + /// + /// This can cause conflicts if the wallet’s descriptors contain an "older" (`OP_CSV`) operator and the given + /// `nsequence` is lower than the CSV value. TxBuilder set_exact_sequence(u32 nsequence); + /// Add data as an output using `OP_RETURN`. TxBuilder add_data(sequence data); + /// Set the current blockchain height. + /// + /// This will be used to: + /// + /// 1. Set the `nLockTime` for preventing fee sniping. Note: This will be ignored if you manually specify a + /// `nlocktime` using `TxBuilder::nlocktime`. + /// + /// 2. Decide whether coinbase outputs are mature or not. If the coinbase outputs are not mature at `current_height`, + /// we ignore them in the coin selection. If you want to create a transaction that spends immature coinbase inputs, + /// manually add them using `TxBuilder::add_utxos`. + /// In both cases, if you don’t provide a current height, we use the last sync height. TxBuilder current_height(u32 height); + /// Use a specific nLockTime while creating the transaction. + /// + /// This can cause conflicts if the wallet’s descriptors contain an "after" (`OP_CLTV`) operator. TxBuilder nlocktime(LockTime locktime); + /// Set whether or not the dust limit is checked. + /// + /// Note: by avoiding a dust limit check you may end up with a transaction that is non-standard. TxBuilder allow_dust(boolean allow_dust); + /// Build a transaction with a specific version. + /// + /// The version should always be greater than 0 and greater than 1 if the wallet’s descriptors contain an "older" + /// (`OP_CSV`) operator. TxBuilder version(i32 version); + /// Finish building the transaction. + /// + /// Uses the thread-local random number generator (rng). + /// + /// Returns a new `Psbt` per BIP174. + /// + /// WARNING: To avoid change address reuse you must persist the changes resulting from one or more calls to this + /// method before closing the wallet. See `Wallet::reveal_next_address`. [Throws=CreateTxError] Psbt finish([ByRef] Wallet wallet); }; From 4f702ebf1edc964ca2cc3b2c5144149fd63b76f5 Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Mon, 25 Nov 2024 15:23:42 -0500 Subject: [PATCH 7/7] docs: add docstrings for BumpFeeTxBuilder --- bdk-ffi/src/bdk.udl | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index b126c195..44eb2723 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -676,8 +676,8 @@ dictionary Condition { LockTime? timelock; }; -/// A `TxBuilder` is created by calling `build_tx` or `build_fee_bump` on a wallet. After assigning it, you set options on it -/// until finally calling `finish` to consume the builder and generate the transaction. +/// A `TxBuilder` is created by calling `build_tx` on a wallet. After assigning it, you set options on it until finally +/// calling `finish` to consume the builder and generate the transaction. interface TxBuilder { constructor(); @@ -817,19 +817,54 @@ interface TxBuilder { Psbt finish([ByRef] Wallet wallet); }; +/// A `BumpFeeTxBuilder` is created by calling `build_fee_bump` on a wallet. After assigning it, you set options on it +/// until finally calling `finish` to consume the builder and generate the transaction. interface BumpFeeTxBuilder { constructor(string txid, FeeRate fee_rate); + /// Set an exact `nSequence` value. + /// + /// This can cause conflicts if the wallet’s descriptors contain an "older" (`OP_CSV`) operator and the given + /// `nsequence` is lower than the CSV value. BumpFeeTxBuilder set_exact_sequence(u32 nsequence); + /// Set the current blockchain height. + /// + /// This will be used to: + /// + /// 1. Set the `nLockTime` for preventing fee sniping. Note: This will be ignored if you manually specify a + /// `nlocktime` using `TxBuilder::nlocktime`. + /// + /// 2. Decide whether coinbase outputs are mature or not. If the coinbase outputs are not mature at `current_height`, + /// we ignore them in the coin selection. If you want to create a transaction that spends immature coinbase inputs, + /// manually add them using `TxBuilder::add_utxos`. + /// In both cases, if you don’t provide a current height, we use the last sync height. BumpFeeTxBuilder current_height(u32 height); + /// Use a specific nLockTime while creating the transaction. + /// + /// This can cause conflicts if the wallet’s descriptors contain an "after" (`OP_CLTV`) operator. BumpFeeTxBuilder nlocktime(LockTime locktime); + /// Set whether or not the dust limit is checked. + /// + /// Note: by avoiding a dust limit check you may end up with a transaction that is non-standard. BumpFeeTxBuilder allow_dust(boolean allow_dust); + /// Build a transaction with a specific version. + /// + /// The version should always be greater than 0 and greater than 1 if the wallet’s descriptors contain an "older" + /// (`OP_CSV`) operator. BumpFeeTxBuilder version(i32 version); + /// Finish building the transaction. + /// + /// Uses the thread-local random number generator (rng). + /// + /// Returns a new `Psbt` per BIP174. + /// + /// WARNING: To avoid change address reuse you must persist the changes resulting from one or more calls to this + /// method before closing the wallet. See `Wallet::reveal_next_address`. [Throws=CreateTxError] Psbt finish([ByRef] Wallet wallet); };