Skip to content

Commit

Permalink
Add docs for TxBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
thunderbiscuit committed Aug 29, 2022
1 parent ccf8679 commit 37bad1a
Showing 1 changed file with 73 additions and 0 deletions.
73 changes: 73 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,11 @@ pub struct EsploraConfig {
pub timeout: Option<u64>,
}

/// Type that can contain any of the blockchain configurations defined by the library.
pub enum BlockchainConfig {
/// Electrum client
Electrum { config: ElectrumConfig },
/// Esplora client
Esplora { config: EsploraConfig },
}

Expand All @@ -154,13 +157,19 @@ pub struct TransactionDetails {
pub fee: Option<u64>,
}

/// A transaction, either of type Confirmed or Unconfirmed
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Transaction {
/// A transaction that has yet to be included in a block
Unconfirmed {
/// The details of wallet transaction.
details: TransactionDetails,
},
/// A transaction that has been mined and is part of a block
Confirmed {
/// The details of wallet transaction
details: TransactionDetails,
/// Timestamp and block height of the block in which this transaction was mined
confirmation: BlockTime,
},
}
Expand Down Expand Up @@ -307,7 +316,10 @@ impl NetworkLocalUtxo for LocalUtxo {
}
}

/// Trait that logs at level INFO every update received (if any).
pub trait Progress: Send + Sync + 'static {
/// Send a new progress update. The progress value should be in the range 0.0 - 100.0, and the message value is an
/// optional text message that can be displayed to the user.
fn update(&self, progress: f32, message: Option<String>);
}

Expand Down Expand Up @@ -353,6 +365,11 @@ impl PartiallySignedBitcoinTransaction {
}
}

/// A Bitcoin wallet.
/// The Wallet acts as a way of coherently interfacing with output descriptors and related transactions. Its main components are:
/// 1. Output descriptors from which it can derive addresses.
/// 2. A Database where it tracks transactions and utxos related to the descriptors.
/// 3. Signers that can contribute signatures to addresses instantiated from the descriptors.
impl Wallet {
fn new(
descriptor: String,
Expand All @@ -379,10 +396,12 @@ impl Wallet {
self.wallet_mutex.lock().expect("wallet")
}

/// Get the Bitcoin network the wallet is using.
fn network(&self) -> Network {
self.get_wallet().network()
}

/// Sync the internal database with the blockchain.
fn sync(
&self,
blockchain: &Blockchain,
Expand All @@ -399,26 +418,35 @@ impl Wallet {
self.get_wallet().sync(blockchain.deref(), bdk_sync_opts)
}

/// Return a derived address using the external descriptor, see AddressIndex for available address index selection
/// strategies. If none of the keys in the descriptor are derivable (i.e. the descriptor does not end with a * character)
/// then the same address will always be returned for any AddressIndex.
fn get_address(&self, address_index: AddressIndex) -> Result<AddressInfo, BdkError> {
self.get_wallet()
.get_address(address_index.into())
.map(AddressInfo::from)
}

/// Return the balance, meaning the sum of this wallet’s unspent outputs’ values. Note that this method only operates
/// on the internal database, which first needs to be Wallet.sync manually.
fn get_balance(&self) -> Result<u64, Error> {
self.get_wallet().get_balance()
}

/// Sign a transaction with all the wallet’s signers.
fn sign(&self, psbt: &PartiallySignedBitcoinTransaction) -> Result<bool, Error> {
let mut psbt = psbt.internal.lock().unwrap();
self.get_wallet().sign(&mut psbt, SignOptions::default())
}

/// Return the list of transactions made and received by the wallet. Note that this method only operate on the internal database, which first needs to be [Wallet.sync] manually.
fn list_transactions(&self) -> Result<Vec<Transaction>, Error> {
let transactions = self.get_wallet().list_transactions(true)?;
Ok(transactions.iter().map(Transaction::from).collect())
}

/// Return the list of unspent outputs of this wallet. Note that this method only operates on the internal database,
/// which first needs to be Wallet.sync manually.
fn list_unspent(&self) -> Result<Vec<LocalUtxo>, Error> {
let unspents = self.get_wallet().list_unspent()?;
Ok(unspents
Expand All @@ -440,6 +468,9 @@ enum RbfValue {
Value(u32),
}

/// A transaction builder.
/// After creating the TxBuilder, you set options on it until finally calling finish to consume the builder and generate the transaction.
/// Each method on the TxBuilder returns an instance of a new TxBuilder with the option set/added.
#[derive(Clone, Debug)]
struct TxBuilder {
recipients: Vec<(String, u64)>,
Expand Down Expand Up @@ -472,6 +503,7 @@ impl TxBuilder {
}
}

/// Add a recipient to the internal list.
fn add_recipient(&self, recipient: String, amount: u64) -> Arc<Self> {
let mut recipients = self.recipients.to_vec();
recipients.append(&mut vec![(recipient, amount)]);
Expand All @@ -492,6 +524,8 @@ impl TxBuilder {
})
}

/// Add a utxo to the internal list of unspendable utxos. It’s important to note that the "must-be-spent"
/// utxos added with [TxBuilder.addUtxo] have priority over this. See the Rust docs of the two linked methods for more details.
fn add_unspendable(&self, unspendable: OutPoint) -> Arc<Self> {
let mut unspendable_hash_set = self.unspendable.clone();
unspendable_hash_set.insert(unspendable);
Expand All @@ -501,10 +535,15 @@ impl TxBuilder {
})
}

/// Add an outpoint 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.
fn add_utxo(&self, outpoint: OutPoint) -> Arc<Self> {
self.add_utxos(vec![outpoint])
}

/// Add the list of outpoints to the internal list of UTXOs that must be spent. If an error occurs while adding
/// any of the UTXOs then none of them are added and the error is returned. 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.
fn add_utxos(&self, mut outpoints: Vec<OutPoint>) -> Arc<Self> {
let mut utxos = self.utxos.to_vec();
utxos.append(&mut outpoints);
Expand All @@ -514,83 +553,106 @@ impl TxBuilder {
})
}

/// Do not spend change outputs. This effectively adds all the change outputs to the "unspendable" list. See TxBuilder.unspendable.
fn do_not_spend_change(&self) -> Arc<Self> {
Arc::new(TxBuilder {
change_policy: ChangeSpendPolicy::ChangeForbidden,
..self.clone()
})
}

/// Only spend utxos added by [add_utxo]. The wallet will not add additional utxos to the transaction even if they are
/// needed to make the transaction valid.
fn manually_selected_only(&self) -> Arc<Self> {
Arc::new(TxBuilder {
manually_selected_only: true,
..self.clone()
})
}

/// Only spend change outputs. This effectively adds all the non-change outputs to the "unspendable" list. See TxBuilder.unspendable.
fn only_spend_change(&self) -> Arc<Self> {
Arc::new(TxBuilder {
change_policy: ChangeSpendPolicy::OnlyChange,
..self.clone()
})
}

/// 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.addUtxo have priority over these. See the Rust docs of the two linked methods for more details.
fn unspendable(&self, unspendable: Vec<OutPoint>) -> Arc<Self> {
Arc::new(TxBuilder {
unspendable: unspendable.into_iter().collect(),
..self.clone()
})
}

/// Set a custom fee rate.
fn fee_rate(&self, sat_per_vb: f32) -> Arc<Self> {
Arc::new(TxBuilder {
fee_rate: Some(sat_per_vb),
..self.clone()
})
}

/// Set an absolute fee.
fn fee_absolute(&self, fee_amount: u64) -> Arc<Self> {
Arc::new(TxBuilder {
fee_absolute: Some(fee_amount),
..self.clone()
})
}

/// Spend all the available inputs. This respects filters like TxBuilder.unspendable and the change policy.
fn drain_wallet(&self) -> Arc<Self> {
Arc::new(TxBuilder {
drain_wallet: true,
..self.clone()
})
}

/// 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 ScriptPubKey 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
/// either provide the utxos that the transaction should spend via add_utxos, or set drain_wallet to spend all of them.
/// When bumping the fees of a transaction made with this option, you probably want to use BumpFeeTxBuilder.allow_shrinking
/// to allow this output to be reduced to pay for the extra fees.
fn drain_to(&self, address: String) -> Arc<Self> {
Arc::new(TxBuilder {
drain_to: Some(address),
..self.clone()
})
}

/// Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`.
fn enable_rbf(&self) -> Arc<Self> {
Arc::new(TxBuilder {
rbf: Some(RbfValue::Default),
..self.clone()
})
}

/// Enable signaling RBF with a specific 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. If the `nsequence` is higher than `0xFFFFFFFD`
/// an error will be thrown, since it would not be a valid nSequence to signal RBF.
fn enable_rbf_with_sequence(&self, nsequence: u32) -> Arc<Self> {
Arc::new(TxBuilder {
rbf: Some(RbfValue::Value(nsequence)),
..self.clone()
})
}

/// Add data as an output using OP_RETURN.
fn add_data(&self, data: Vec<u8>) -> Arc<Self> {
Arc::new(TxBuilder {
data,
..self.clone()
})
}

/// Finish building the transaction. Returns the BIP174 PSBT.
fn finish(&self, wallet: &Wallet) -> Result<Arc<PartiallySignedBitcoinTransaction>, Error> {
let wallet = wallet.get_wallet();
let mut tx_builder = wallet.build_tx();
Expand Down Expand Up @@ -646,6 +708,7 @@ impl TxBuilder {
}
}

/// The BumpFeeTxBuilder is used to bump the fee on a transaction that has been broadcast and has its RBF flag set to true.
#[derive(Clone)]
struct BumpFeeTxBuilder {
txid: String,
Expand All @@ -664,27 +727,37 @@ impl BumpFeeTxBuilder {
}
}

/// Explicitly tells the wallet that it is allowed to reduce the amount of the output matching this script_pubkey
/// in order to bump the transaction fee. Without specifying this the wallet will attempt to find a change output to
/// shrink instead. Note that the output may shrink to below the dust limit and therefore be removed. If it is preserved
/// then it is currently not guaranteed to be in the same position as it was originally. Returns an error if script_pubkey
/// can’t be found among the recipients of the transaction we are bumping.
fn allow_shrinking(&self, address: String) -> Arc<Self> {
Arc::new(Self {
allow_shrinking: Some(address),
..self.clone()
})
}

/// Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`.
fn enable_rbf(&self) -> Arc<Self> {
Arc::new(Self {
rbf: Some(RbfValue::Default),
..self.clone()
})
}

/// Enable signaling RBF with a specific 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. If the `nsequence` is higher than `0xFFFFFFFD`
/// an error will be thrown, since it would not be a valid nSequence to signal RBF.
fn enable_rbf_with_sequence(&self, nsequence: u32) -> Arc<Self> {
Arc::new(Self {
rbf: Some(RbfValue::Value(nsequence)),
..self.clone()
})
}

/// Finish building the transaction. Returns the BIP174 PSBT.
fn finish(&self, wallet: &Wallet) -> Result<Arc<PartiallySignedBitcoinTransaction>, Error> {
let wallet = wallet.get_wallet();
let txid = Txid::from_str(self.txid.as_str())?;
Expand Down

0 comments on commit 37bad1a

Please sign in to comment.