Add inline documentation (#171)

* Add docs for AddressInfo and AddressIndex structs

* Add docs for DatabaseConfig and ElectrumConfig

* Add docs for EsploraConfig struct

* Add docs for TransactionDetails struct

* Add docs for OutPoint struct

* Add docs for TxOut struct

* Add docs for TxBuilder
This commit is contained in:
thunderbiscuit 2022-08-29 15:47:37 -04:00 committed by GitHub
parent 3b028ecab1
commit bfe03b91b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -42,8 +42,11 @@ pub struct AddressAmount {
pub amount: u64, pub amount: u64,
} }
/// A derived address and the index it was found at.
pub struct AddressInfo { pub struct AddressInfo {
/// Child index of this address
pub index: u32, pub index: u32,
/// Address
pub address: String, pub address: String,
} }
@ -56,8 +59,17 @@ impl From<BdkAddressInfo> for AddressInfo {
} }
} }
/// The address index selection strategy to use to derived an address from the wallet's external
/// descriptor.
pub enum AddressIndex { pub enum AddressIndex {
/// Return a new address after incrementing the current descriptor index.
New, New,
/// Return the address for the current descriptor index if it has not been used in a received
/// transaction. Otherwise return a new address as with AddressIndex::New.
/// Use with caution, if the wallet has not yet detected an address has been used it could
/// return an already used address. This function is primarily meant for situations where the
/// caller is untrusted; for example when deriving donation addresses on-demand for a public
/// web page.
LastUnused, LastUnused,
} }
@ -70,48 +82,94 @@ impl From<AddressIndex> for BdkAddressIndex {
} }
} }
/// Type that can contain any of the database configurations defined by the library
/// This allows storing a single configuration that can be loaded into an AnyDatabaseConfig
/// instance. Wallets that plan to offer users the ability to switch blockchain backend at runtime
/// will find this particularly useful.
pub enum DatabaseConfig { pub enum DatabaseConfig {
/// Memory database has no config
Memory, Memory,
/// Simple key-value embedded database based on sled
Sled { config: SledDbConfiguration }, Sled { config: SledDbConfiguration },
/// Sqlite embedded database using rusqlite
Sqlite { config: SqliteDbConfiguration }, Sqlite { config: SqliteDbConfiguration },
} }
/// Configuration for an ElectrumBlockchain
pub struct ElectrumConfig { pub struct ElectrumConfig {
/// URL of the Electrum server (such as ElectrumX, Esplora, BWT) may start with ssl:// or tcp:// and include a port
/// e.g. ssl://electrum.blockstream.info:60002
pub url: String, pub url: String,
/// URL of the socks5 proxy server or a Tor service
pub socks5: Option<String>, pub socks5: Option<String>,
/// Request retry count
pub retry: u8, pub retry: u8,
/// Request timeout (seconds)
pub timeout: Option<u8>, pub timeout: Option<u8>,
/// Stop searching addresses for transactions after finding an unused gap of this length
pub stop_gap: u64, pub stop_gap: u64,
} }
/// Configuration for an EsploraBlockchain
pub struct EsploraConfig { pub struct EsploraConfig {
/// Base URL of the esplora service
/// e.g. https://blockstream.info/api/
pub base_url: String, pub base_url: String,
/// Optional URL of the proxy to use to make requests to the Esplora server
/// The string should be formatted as: <protocol>://<user>:<password>@host:<port>.
/// Note that the format of this value and the supported protocols change slightly between the
/// sync version of esplora (using ureq) and the async version (using reqwest). For more
/// details check with the documentation of the two crates. Both of them are compiled with
/// the socks feature enabled.
/// The proxy is ignored when targeting wasm32.
pub proxy: Option<String>, pub proxy: Option<String>,
/// Number of parallel requests sent to the esplora service (default: 4)
pub concurrency: Option<u8>, pub concurrency: Option<u8>,
/// Stop searching addresses for transactions after finding an unused gap of this length.
pub stop_gap: u64, pub stop_gap: u64,
/// Socket timeout.
pub timeout: Option<u64>, pub timeout: Option<u64>,
} }
/// Type that can contain any of the blockchain configurations defined by the library.
pub enum BlockchainConfig { pub enum BlockchainConfig {
/// Electrum client
Electrum { config: ElectrumConfig }, Electrum { config: ElectrumConfig },
/// Esplora client
Esplora { config: EsploraConfig }, Esplora { config: EsploraConfig },
} }
/// A wallet transaction
#[derive(Debug, Clone, PartialEq, Eq, Default)] #[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct TransactionDetails { pub struct TransactionDetails {
pub fee: Option<u64>, /// Transaction id.
pub received: u64,
pub sent: u64,
pub txid: String, pub txid: String,
/// Received value (sats)
/// Sum of owned outputs of this transaction.
pub received: u64,
/// Sent value (sats)
/// Sum of owned inputs of this transaction.
pub sent: u64,
/// Fee value (sats) if available.
/// The availability of the fee depends on the backend. It's never None with an Electrum
/// Server backend, but it could be None with a Bitcoin RPC node without txindex that receive
/// funds while offline.
pub fee: Option<u64>,
} }
/// A transaction, either of type Confirmed or Unconfirmed
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum Transaction { pub enum Transaction {
/// A transaction that has yet to be included in a block
Unconfirmed { Unconfirmed {
/// The details of wallet transaction.
details: TransactionDetails, details: TransactionDetails,
}, },
/// A transaction that has been mined and is part of a block
Confirmed { Confirmed {
/// The details of wallet transaction
details: TransactionDetails, details: TransactionDetails,
/// Timestamp and block height of the block in which this transaction was mined
confirmation: BlockTime, confirmation: BlockTime,
}, },
} }
@ -197,9 +255,12 @@ struct Wallet {
wallet_mutex: Mutex<BdkWallet<AnyDatabase>>, wallet_mutex: Mutex<BdkWallet<AnyDatabase>>,
} }
/// A reference to a transaction output.
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct OutPoint { pub struct OutPoint {
/// The referenced transaction's txid.
txid: String, txid: String,
/// The index of the referenced output in its transaction's vout.
vout: u32, vout: u32,
} }
@ -212,8 +273,11 @@ impl From<&OutPoint> for BdkOutPoint {
} }
} }
/// A transaction output, which defines new coins to be created from old ones.
pub struct TxOut { pub struct TxOut {
/// The value of the output, in satoshis.
value: u64, value: u64,
/// The address of the output.
address: String, address: String,
} }
@ -252,7 +316,10 @@ impl NetworkLocalUtxo for LocalUtxo {
} }
} }
/// Trait that logs at level INFO every update received (if any).
pub trait Progress: Send + Sync + 'static { 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>); fn update(&self, progress: f32, message: Option<String>);
} }
@ -298,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 { impl Wallet {
fn new( fn new(
descriptor: String, descriptor: String,
@ -324,10 +396,12 @@ impl Wallet {
self.wallet_mutex.lock().expect("wallet") self.wallet_mutex.lock().expect("wallet")
} }
/// Get the Bitcoin network the wallet is using.
fn network(&self) -> Network { fn network(&self) -> Network {
self.get_wallet().network() self.get_wallet().network()
} }
/// Sync the internal database with the blockchain.
fn sync( fn sync(
&self, &self,
blockchain: &Blockchain, blockchain: &Blockchain,
@ -344,26 +418,35 @@ impl Wallet {
self.get_wallet().sync(blockchain.deref(), bdk_sync_opts) 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> { fn get_address(&self, address_index: AddressIndex) -> Result<AddressInfo, BdkError> {
self.get_wallet() self.get_wallet()
.get_address(address_index.into()) .get_address(address_index.into())
.map(AddressInfo::from) .map(AddressInfo::from)
} }
/// Return the balance, meaning the sum of this wallets 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> { fn get_balance(&self) -> Result<u64, Error> {
self.get_wallet().get_balance() self.get_wallet().get_balance()
} }
/// Sign a transaction with all the wallets signers.
fn sign(&self, psbt: &PartiallySignedBitcoinTransaction) -> Result<bool, Error> { fn sign(&self, psbt: &PartiallySignedBitcoinTransaction) -> Result<bool, Error> {
let mut psbt = psbt.internal.lock().unwrap(); let mut psbt = psbt.internal.lock().unwrap();
self.get_wallet().sign(&mut psbt, SignOptions::default()) 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> { fn list_transactions(&self) -> Result<Vec<Transaction>, Error> {
let transactions = self.get_wallet().list_transactions(true)?; let transactions = self.get_wallet().list_transactions(true)?;
Ok(transactions.iter().map(Transaction::from).collect()) 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> { fn list_unspent(&self) -> Result<Vec<LocalUtxo>, Error> {
let unspents = self.get_wallet().list_unspent()?; let unspents = self.get_wallet().list_unspent()?;
Ok(unspents Ok(unspents
@ -385,6 +468,9 @@ enum RbfValue {
Value(u32), 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)] #[derive(Clone, Debug)]
struct TxBuilder { struct TxBuilder {
recipients: Vec<(String, u64)>, recipients: Vec<(String, u64)>,
@ -417,6 +503,7 @@ impl TxBuilder {
} }
} }
/// Add a recipient to the internal list.
fn add_recipient(&self, recipient: String, amount: u64) -> Arc<Self> { fn add_recipient(&self, recipient: String, amount: u64) -> Arc<Self> {
let mut recipients = self.recipients.to_vec(); let mut recipients = self.recipients.to_vec();
recipients.append(&mut vec![(recipient, amount)]); recipients.append(&mut vec![(recipient, amount)]);
@ -437,6 +524,8 @@ impl TxBuilder {
}) })
} }
/// Add a utxo to the internal list of unspendable utxos. Its 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> { fn add_unspendable(&self, unspendable: OutPoint) -> Arc<Self> {
let mut unspendable_hash_set = self.unspendable.clone(); let mut unspendable_hash_set = self.unspendable.clone();
unspendable_hash_set.insert(unspendable); unspendable_hash_set.insert(unspendable);
@ -446,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> { fn add_utxo(&self, outpoint: OutPoint) -> Arc<Self> {
self.add_utxos(vec![outpoint]) 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> { fn add_utxos(&self, mut outpoints: Vec<OutPoint>) -> Arc<Self> {
let mut utxos = self.utxos.to_vec(); let mut utxos = self.utxos.to_vec();
utxos.append(&mut outpoints); utxos.append(&mut outpoints);
@ -459,6 +553,7 @@ 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> { fn do_not_spend_change(&self) -> Arc<Self> {
Arc::new(TxBuilder { Arc::new(TxBuilder {
change_policy: ChangeSpendPolicy::ChangeForbidden, change_policy: ChangeSpendPolicy::ChangeForbidden,
@ -466,6 +561,8 @@ impl TxBuilder {
}) })
} }
/// 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> { fn manually_selected_only(&self) -> Arc<Self> {
Arc::new(TxBuilder { Arc::new(TxBuilder {
manually_selected_only: true, manually_selected_only: true,
@ -473,6 +570,7 @@ impl TxBuilder {
}) })
} }
/// 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> { fn only_spend_change(&self) -> Arc<Self> {
Arc::new(TxBuilder { Arc::new(TxBuilder {
change_policy: ChangeSpendPolicy::OnlyChange, change_policy: ChangeSpendPolicy::OnlyChange,
@ -480,6 +578,8 @@ impl TxBuilder {
}) })
} }
/// Replace the internal list of unspendable utxos with a new list. Its 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> { fn unspendable(&self, unspendable: Vec<OutPoint>) -> Arc<Self> {
Arc::new(TxBuilder { Arc::new(TxBuilder {
unspendable: unspendable.into_iter().collect(), unspendable: unspendable.into_iter().collect(),
@ -487,6 +587,7 @@ impl TxBuilder {
}) })
} }
/// Set a custom fee rate.
fn fee_rate(&self, sat_per_vb: f32) -> Arc<Self> { fn fee_rate(&self, sat_per_vb: f32) -> Arc<Self> {
Arc::new(TxBuilder { Arc::new(TxBuilder {
fee_rate: Some(sat_per_vb), fee_rate: Some(sat_per_vb),
@ -494,6 +595,7 @@ impl TxBuilder {
}) })
} }
/// Set an absolute fee.
fn fee_absolute(&self, fee_amount: u64) -> Arc<Self> { fn fee_absolute(&self, fee_amount: u64) -> Arc<Self> {
Arc::new(TxBuilder { Arc::new(TxBuilder {
fee_absolute: Some(fee_amount), fee_absolute: Some(fee_amount),
@ -501,6 +603,7 @@ impl TxBuilder {
}) })
} }
/// Spend all the available inputs. This respects filters like TxBuilder.unspendable and the change policy.
fn drain_wallet(&self) -> Arc<Self> { fn drain_wallet(&self) -> Arc<Self> {
Arc::new(TxBuilder { Arc::new(TxBuilder {
drain_wallet: true, drain_wallet: true,
@ -508,6 +611,14 @@ impl TxBuilder {
}) })
} }
/// 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> { fn drain_to(&self, address: String) -> Arc<Self> {
Arc::new(TxBuilder { Arc::new(TxBuilder {
drain_to: Some(address), drain_to: Some(address),
@ -515,6 +626,7 @@ impl TxBuilder {
}) })
} }
/// Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`.
fn enable_rbf(&self) -> Arc<Self> { fn enable_rbf(&self) -> Arc<Self> {
Arc::new(TxBuilder { Arc::new(TxBuilder {
rbf: Some(RbfValue::Default), rbf: Some(RbfValue::Default),
@ -522,6 +634,9 @@ impl TxBuilder {
}) })
} }
/// 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> { fn enable_rbf_with_sequence(&self, nsequence: u32) -> Arc<Self> {
Arc::new(TxBuilder { Arc::new(TxBuilder {
rbf: Some(RbfValue::Value(nsequence)), rbf: Some(RbfValue::Value(nsequence)),
@ -529,6 +644,7 @@ impl TxBuilder {
}) })
} }
/// Add data as an output using OP_RETURN.
fn add_data(&self, data: Vec<u8>) -> Arc<Self> { fn add_data(&self, data: Vec<u8>) -> Arc<Self> {
Arc::new(TxBuilder { Arc::new(TxBuilder {
data, data,
@ -536,6 +652,7 @@ impl TxBuilder {
}) })
} }
/// Finish building the transaction. Returns the BIP174 PSBT.
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();
@ -591,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)] #[derive(Clone)]
struct BumpFeeTxBuilder { struct BumpFeeTxBuilder {
txid: String, txid: String,
@ -609,6 +727,11 @@ 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
/// cant be found among the recipients of the transaction we are bumping.
fn allow_shrinking(&self, address: String) -> Arc<Self> { fn allow_shrinking(&self, address: String) -> Arc<Self> {
Arc::new(Self { Arc::new(Self {
allow_shrinking: Some(address), allow_shrinking: Some(address),
@ -616,6 +739,7 @@ impl BumpFeeTxBuilder {
}) })
} }
/// Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`.
fn enable_rbf(&self) -> Arc<Self> { fn enable_rbf(&self) -> Arc<Self> {
Arc::new(Self { Arc::new(Self {
rbf: Some(RbfValue::Default), rbf: Some(RbfValue::Default),
@ -623,6 +747,9 @@ impl BumpFeeTxBuilder {
}) })
} }
/// 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> { fn enable_rbf_with_sequence(&self, nsequence: u32) -> Arc<Self> {
Arc::new(Self { Arc::new(Self {
rbf: Some(RbfValue::Value(nsequence)), rbf: Some(RbfValue::Value(nsequence)),
@ -630,6 +757,7 @@ impl BumpFeeTxBuilder {
}) })
} }
/// Finish building the transaction. Returns the BIP174 PSBT.
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 txid = Txid::from_str(self.txid.as_str())?; let txid = Txid::from_str(self.txid.as_str())?;