]> Untitled Git - bdk/commitdiff
Write more docs
authorAlekos Filini <alekos.filini@gmail.com>
Fri, 4 Sep 2020 09:44:49 +0000 (11:44 +0200)
committerAlekos Filini <alekos.filini@gmail.com>
Fri, 4 Sep 2020 09:44:49 +0000 (11:44 +0200)
14 files changed:
src/blockchain/electrum.rs
src/blockchain/esplora.rs
src/database/memory.rs
src/database/mod.rs
src/descriptor/checksum.rs
src/descriptor/error.rs
src/descriptor/mod.rs
src/descriptor/policy.rs
src/error.rs
src/lib.rs
src/types.rs
src/wallet/address_validator.rs
src/wallet/coin_selection.rs
src/wallet/mod.rs

index f65454c81ecfceaa01c67f997cf332954d23afd4..a46c7f831358c670853216831db99f58e0512f66 100644 (file)
@@ -34,7 +34,7 @@
 //! # use magical_bitcoin_wallet::blockchain::electrum::ElectrumBlockchain;
 //! let client = electrum_client::Client::new("ssl://electrum.blockstream.info:50002", None)?;
 //! let blockchain = ElectrumBlockchain::from(client);
-//! # Ok::<(), magical_bitcoin_wallet::error::Error>(())
+//! # Ok::<(), magical_bitcoin_wallet::Error>(())
 //! ```
 
 use std::collections::HashSet;
index 13f05f20d7bc68f3b18db223a82f908fcb6a2fec..999a131eabb4d0cad311e62a94c4f1d5d832eee9 100644 (file)
@@ -32,7 +32,7 @@
 //! ```no_run
 //! # use magical_bitcoin_wallet::blockchain::esplora::EsploraBlockchain;
 //! let blockchain = EsploraBlockchain::new("https://blockstream.info/testnet/");
-//! # Ok::<(), magical_bitcoin_wallet::error::Error>(())
+//! # Ok::<(), magical_bitcoin_wallet::Error>(())
 //! ```
 
 use std::collections::{HashMap, HashSet};
index fb25c7a3426b03cf88d68ba96cacff9f0b1a9af9..d1ecd645df98b2ed15718dd13d62cbf0ea6efeaf 100644 (file)
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 // SOFTWARE.
 
+//! In-memory ephemeral database
+//!
+//! This module defines an in-memory database type called [`MemoryDatabase`] that is based on a
+//! [`BTreeMap`].
+
 use std::collections::BTreeMap;
 use std::ops::Bound::{Excluded, Included};
 
@@ -91,22 +96,39 @@ impl MapKey<'_> {
 
 fn after(key: &Vec<u8>) -> Vec<u8> {
     let mut key = key.clone();
-    let len = key.len();
-    if len > 0 {
-        // TODO i guess it could break if the value is 0xFF, but it's fine for now
-        key[len - 1] += 1;
+    let mut idx = key.len();
+    while idx > 0 {
+        if key[idx - 1] == 0xFF {
+            idx -= 1;
+            continue;
+        } else {
+            key[idx - 1] += 1;
+            break;
+        }
     }
 
     key
 }
 
-#[derive(Debug)]
+/// In-memory ephemeral database
+///
+/// This database can be used as a temporary storage for wallets that are not kept permanently on
+/// a device, or on platforms that don't provide a filesystem, like `wasm32`.
+///
+/// Once it's dropped its content will be lost.
+///
+/// If you are looking for a permanent storage solution, you can try with the default key-value
+/// database called [`sled`]. See the [`database`] module documentation for more defailts.
+///
+/// [`database`]: crate::database
+#[derive(Debug, Default)]
 pub struct MemoryDatabase {
     map: BTreeMap<Vec<u8>, Box<dyn std::any::Any>>,
     deleted_keys: Vec<Vec<u8>>,
 }
 
 impl MemoryDatabase {
+    /// Create a new empty database
     pub fn new() -> Self {
         MemoryDatabase {
             map: BTreeMap::new(),
index c91b385eed463393e3b758a7be5b924b7ab4e7c3..45cedcd1bf0ccca0db1e4aee9bc5a6b79204532f 100644 (file)
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 // SOFTWARE.
 
+//! Database types
+//!
+//! This module provides the implementation of some defaults database types, along with traits that
+//! can be implemented externally to let [`Wallet`]s use customized databases.
+//!
+//! It's important to note that the databases defined here only contains "blockchain-related" data.
+//! They can be seen more as a cache than a critical piece of storage that contains secrets and
+//! keys.
+//!
+//! The currently recommended database is [`sled`], which is a pretty simple key-value embedded
+//! database written in Rust. If the `key-value-db` feature is enabled (which by default is),
+//! this library automatically implements all the required traits for [`sled::Tree`].
+//!
+//! [`Wallet`]: crate::wallet::Wallet
+
 use bitcoin::hash_types::Txid;
 use bitcoin::{OutPoint, Script, Transaction, TxOut};
 
@@ -34,71 +49,113 @@ pub(crate) mod keyvalue;
 pub mod memory;
 pub use memory::MemoryDatabase;
 
+/// Trait for operations that can be batched
+///
+/// This trait defines the list of operations that must be implemented on the [`Database`] type and
+/// the [`BatchDatabase::Batch`] type.
 pub trait BatchOperations {
+    /// Store a script_pubkey along with its script type and child number
     fn set_script_pubkey(
         &mut self,
         script: &Script,
         script_type: ScriptType,
         child: u32,
     ) -> Result<(), Error>;
+    /// Store a [`UTXO`]
     fn set_utxo(&mut self, utxo: &UTXO) -> Result<(), Error>;
+    /// Store a raw transaction
     fn set_raw_tx(&mut self, transaction: &Transaction) -> Result<(), Error>;
+    /// Store the metadata of a transaction
     fn set_tx(&mut self, transaction: &TransactionDetails) -> Result<(), Error>;
+    /// Store the last derivation index for a given script type
     fn set_last_index(&mut self, script_type: ScriptType, value: u32) -> Result<(), Error>;
 
+    /// Delete a script_pubkey given the script type and its child number
     fn del_script_pubkey_from_path(
         &mut self,
         script_type: ScriptType,
         child: u32,
     ) -> Result<Option<Script>, Error>;
+    /// Delete the data related to a specific script_pubkey, meaning the script type and the child
+    /// number
     fn del_path_from_script_pubkey(
         &mut self,
         script: &Script,
     ) -> Result<Option<(ScriptType, u32)>, Error>;
+    /// Delete a [`UTXO`] given its [`OutPoint`]
     fn del_utxo(&mut self, outpoint: &OutPoint) -> Result<Option<UTXO>, Error>;
+    /// Delete a raw transaction given its [`Txid`]
     fn del_raw_tx(&mut self, txid: &Txid) -> Result<Option<Transaction>, Error>;
+    /// Delete the metadata of a transaction and optionally the raw transaction itself
     fn del_tx(
         &mut self,
         txid: &Txid,
         include_raw: bool,
     ) -> Result<Option<TransactionDetails>, Error>;
+    /// Delete the last derivation index for a script type
     fn del_last_index(&mut self, script_type: ScriptType) -> Result<Option<u32>, Error>;
 }
 
+/// Trait for reading data from a database
+///
+/// This traits defines the operations that can be used to read data out of a database
 pub trait Database: BatchOperations {
+    /// Read and checks the descriptor checksum for a given script type
+    ///
+    /// Should return [`Error::ChecksumMismatch`](crate::error::Error::ChecksumMismatch) if the
+    /// checksum doesn't match. If there's no checksum in the database, simply store it for the
+    /// next time.
     fn check_descriptor_checksum<B: AsRef<[u8]>>(
         &mut self,
         script_type: ScriptType,
         bytes: B,
     ) -> Result<(), Error>;
 
+    /// Return the list of script_pubkeys
     fn iter_script_pubkeys(&self, script_type: Option<ScriptType>) -> Result<Vec<Script>, Error>;
+    /// Return the list of [`UTXO`]s
     fn iter_utxos(&self) -> Result<Vec<UTXO>, Error>;
+    /// Return the list of raw transactions
     fn iter_raw_txs(&self) -> Result<Vec<Transaction>, Error>;
+    /// Return the list of transactions metadata
     fn iter_txs(&self, include_raw: bool) -> Result<Vec<TransactionDetails>, Error>;
 
+    /// Fetch a script_pubkey given the script type and child number
     fn get_script_pubkey_from_path(
         &self,
         script_type: ScriptType,
         child: u32,
     ) -> Result<Option<Script>, Error>;
+    /// Fetch the script type and child number of a given script_pubkey
     fn get_path_from_script_pubkey(
         &self,
         script: &Script,
     ) -> Result<Option<(ScriptType, u32)>, Error>;
+    /// Fetch a [`UTXO`] given its [`OutPoint`]
     fn get_utxo(&self, outpoint: &OutPoint) -> Result<Option<UTXO>, Error>;
+    /// Fetch a raw transaction given its [`Txid`]
     fn get_raw_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error>;
+    /// Fetch the transaction metadata and optionally also the raw transaction
     fn get_tx(&self, txid: &Txid, include_raw: bool) -> Result<Option<TransactionDetails>, Error>;
+    /// Return the last defivation index for a script type
     fn get_last_index(&self, script_type: ScriptType) -> Result<Option<u32>, Error>;
 
-    // inserts 0 if not present
+    /// Increment the last derivation index for a script type and returns it
+    ///
+    /// It should insert and return `0` if not present in the database
     fn increment_last_index(&mut self, script_type: ScriptType) -> Result<u32, Error>;
 }
 
+/// Trait for a database that supports batch operations
+///
+/// This trait defines the methods to start and apply a batch of operations.
 pub trait BatchDatabase: Database {
+    /// Container for the operations
     type Batch: BatchOperations;
 
+    /// Create a new batch container
     fn begin_batch(&self) -> Self::Batch;
+    /// Consume and apply a batch of operations
     fn commit_batch(&mut self, batch: Self::Batch) -> Result<(), Error>;
 }
 
index 63a55443a18b555b95483bfbaa9ee0f4b76912be..15f80055aadde403c691a52b0df0e7c1f7e5ddcc 100644 (file)
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 // SOFTWARE.
 
+//! Descriptor checksum
+//!
+//! This module contains a re-implementation of the function used by Bitcoin Core to calculate the
+//! checksum of a descriptor
+
 use std::iter::FromIterator;
 
 use crate::descriptor::Error;
@@ -51,6 +56,7 @@ fn poly_mod(mut c: u64, val: u64) -> u64 {
     c
 }
 
+/// Compute the checksum of a descriptor
 pub fn get_checksum(desc: &str) -> Result<String, Error> {
     let mut c = 1;
     let mut cls = 0;
index 08bc2baae8ffe5d76cfacaae95e264fd97c9d6ea..47a2473d80d6abd9cb94cd4818200f87bc622a72 100644 (file)
@@ -22,6 +22,9 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 // SOFTWARE.
 
+//! Descriptor errors
+
+/// Errors related to the parsing and usage of descriptors
 #[derive(Debug)]
 pub enum Error {
     InternalError,
index 7ab1896fdf192b62482628a9a310b9241a7b1b9d..d75cdbfdd43da7e5ac644818b906c95792e303d6 100644 (file)
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 // SOFTWARE.
 
+//! Descriptors
+//!
+//! This module contains generic utilities to work with descriptors, plus some re-exported types
+//! from [`miniscript`].
+
 use std::collections::{BTreeMap, HashMap};
 use std::fmt;
 use std::sync::Arc;
@@ -46,9 +51,17 @@ use self::error::Error;
 pub use self::policy::Policy;
 use crate::wallet::signer::SignersContainer;
 
+/// Alias for a [`Descriptor`] that can contain extended keys using [`DescriptorPublicKey`]
 pub type ExtendedDescriptor = Descriptor<DescriptorPublicKey>;
+
+/// Alias for the type of maps that represent derivation paths in a [`psbt::Input`] or
+/// [`psbt::Output`]
+///
+/// [`psbt::Input`]: bitcoin::util::psbt::Input
+/// [`psbt::Output`]: bitcoin::util::psbt::Output
 pub type HDKeyPaths = BTreeMap<PublicKey, (Fingerprint, DerivationPath)>;
 
+/// Trait implemented on [`Descriptor`]s to add a method to extract the spending [`policy`]
 pub trait ExtractPolicy {
     fn extract_policy(
         &self,
@@ -56,7 +69,7 @@ pub trait ExtractPolicy {
     ) -> Result<Option<Policy>, Error>;
 }
 
-pub trait XKeyUtils {
+pub(crate) trait XKeyUtils {
     fn full_path(&self, append: &[ChildNumber]) -> DerivationPath;
     fn root_fingerprint(&self) -> Fingerprint;
 }
@@ -91,7 +104,7 @@ impl<K: InnerXKey> XKeyUtils for DescriptorXKey<K> {
     }
 }
 
-pub trait DescriptorMeta: Sized {
+pub(crate) trait DescriptorMeta: Sized {
     fn is_witness(&self) -> bool;
     fn get_hd_keypaths(&self, index: u32) -> Result<HDKeyPaths, Error>;
     fn is_fixed(&self) -> bool;
@@ -100,7 +113,7 @@ pub trait DescriptorMeta: Sized {
         -> Option<Self>;
 }
 
-pub trait DescriptorScripts {
+pub(crate) trait DescriptorScripts {
     fn psbt_redeem_script(&self) -> Option<Script>;
     fn psbt_witness_script(&self) -> Option<Script>;
 }
index c8834bd502676d1e0290034fd2a3fc67c98bd13e..7f54e6eb041b904b4d5b3d2f9aafc735128744dd 100644 (file)
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 // SOFTWARE.
 
+//! Descriptor policy
+//!
+//! This module implements the logic to extract and represent the spending policies of a descriptor
+//! in a more human-readable format.
+//!
+//! ## Example
+//!
+//! ```
+//! # use std::sync::Arc;
+//! # use magical_bitcoin_wallet::descriptor::*;
+//! let desc = "wsh(and_v(v:pk(cV3oCth6zxZ1UVsHLnGothsWNsaoxRhC6aeNi5VbSdFpwUkgkEci),or_d(pk(cVMTy7uebJgvFaSBwcgvwk8qn8xSLc97dKow4MBetjrrahZoimm2),older(12960))))";
+//!
+//! let (extended_desc, key_map) = ExtendedDescriptor::parse_secret(desc)?;
+//! println!("{:?}", extended_desc);
+//!
+//! let signers = Arc::new(key_map.into());
+//! let policy = extended_desc.extract_policy(signers)?;
+//! println!("policy: {}", serde_json::to_string(&policy)?);
+//! # Ok::<(), magical_bitcoin_wallet::Error>(())
+//! ```
+
 use std::cmp::max;
 use std::collections::{BTreeMap, HashSet, VecDeque};
 use std::fmt;
@@ -47,6 +68,7 @@ use super::checksum::get_checksum;
 use super::error::Error;
 use super::XKeyUtils;
 
+/// Raw public key or extended key fingerprint
 #[derive(Debug, Clone, Default, Serialize)]
 pub struct PKOrF {
     #[serde(skip_serializing_if = "Option::is_none")]
@@ -79,6 +101,7 @@ impl PKOrF {
     }
 }
 
+/// An item that need to be satisfied
 #[derive(Debug, Clone, Serialize)]
 #[serde(tag = "type", rename_all = "UPPERCASE")]
 pub enum SatisfiableItem {
@@ -208,30 +231,44 @@ where
     map.end()
 }
 
+/// Represent if and how much a policy item is satisfied by the wallet's descriptor
 #[derive(Debug, Clone, Serialize)]
 #[serde(tag = "type", rename_all = "UPPERCASE")]
 pub enum Satisfaction {
+    /// Only a partial satisfaction of some kind of threshold policy
     Partial {
+        /// Total number of items
         n: usize,
+        /// Threshold
         m: usize,
+        /// The items that can be satisfied by the descriptor
         items: Vec<usize>,
         #[serde(skip_serializing_if = "BTreeMap::is_empty")]
+        /// Extra conditions that also need to be satisfied
         conditions: ConditionMap,
     },
+    /// Can reach the threshold of some kind of threshold policy
     PartialComplete {
+        /// Total number of items
         n: usize,
+        /// Threshold
         m: usize,
+        /// The items that can be satisfied by the descriptor
         items: Vec<usize>,
         #[serde(
             serialize_with = "serialize_folded_cond_map",
             skip_serializing_if = "BTreeMap::is_empty"
         )]
+        /// Extra conditions that also need to be satisfied
         conditions: FoldedConditionMap,
     },
 
+    /// Can satisfy the policy item
     Complete {
+        /// Extra conditions that also need to be satisfied
         condition: Condition,
     },
+    /// Cannot satisfy or contribute to the policy item
     None,
 }
 
@@ -363,6 +400,7 @@ impl From<bool> for Satisfaction {
     }
 }
 
+/// Descriptor spending policy
 #[derive(Debug, Clone, Serialize)]
 pub struct Policy {
     id: String,
@@ -373,6 +411,7 @@ pub struct Policy {
     contribution: Satisfaction,
 }
 
+/// An extra condition that must be satisfied but that is out of control of the user
 #[derive(Hash, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Serialize)]
 pub struct Condition {
     #[serde(skip_serializing_if = "Option::is_none")]
@@ -413,6 +452,7 @@ impl Condition {
     }
 }
 
+/// Errors that can happen while extracting and manipulating policies
 #[derive(Debug)]
 pub enum PolicyError {
     NotEnoughItemsSelected(String),
@@ -519,11 +559,19 @@ impl Policy {
         Ok(Some(policy))
     }
 
+    /// Return whether or not a specific path in the policy tree is required to unambiguously
+    /// create a transaction
+    ///
+    /// What this means is that for some spending policies the user should select which paths in
+    /// the tree it intends to satisfy while signing, because the transaction must be created differently based
+    /// on that.
     pub fn requires_path(&self) -> bool {
-        self.get_requirements(&BTreeMap::new()).is_err()
+        self.get_condition(&BTreeMap::new()).is_err()
     }
 
-    pub fn get_requirements(
+    /// Return the conditions that are set by the spending policy for a given path in the
+    /// policy tree
+    pub fn get_condition(
         &self,
         path: &BTreeMap<String, Vec<usize>>,
     ) -> Result<Condition, PolicyError> {
@@ -544,7 +592,7 @@ impl Policy {
             SatisfiableItem::Thresh { items, threshold } => {
                 let mapped_req = items
                     .iter()
-                    .map(|i| i.get_requirements(path))
+                    .map(|i| i.get_condition(path))
                     .collect::<Result<Vec<_>, _>>()?;
 
                 // if all the requirements are null we don't care about `selected` because there
index 7bee83ff78911652ca2f783d0e953799d655f28c..b773f5f9e99301bfac45515f601c0326c30016a9 100644 (file)
@@ -26,6 +26,7 @@ use std::fmt;
 
 use bitcoin::{Address, OutPoint};
 
+/// Errors that can be thrown by the [`Wallet`](crate::wallet::Wallet)
 #[derive(Debug)]
 pub enum Error {
     KeyMismatch(bitcoin::secp256k1::PublicKey, bitcoin::secp256k1::PublicKey),
index 0437088bd46bb5ebec91d8cc3a7ee95d15d3e144..d215df13df49052634d80931d3a4c6923b71abf5 100644 (file)
@@ -66,7 +66,7 @@ extern crate testutils_macros;
 extern crate serial_test;
 
 #[macro_use]
-pub mod error;
+pub(crate) mod error;
 pub mod blockchain;
 pub mod database;
 pub mod descriptor;
@@ -74,6 +74,7 @@ pub(crate) mod psbt;
 pub(crate) mod types;
 pub mod wallet;
 
+pub use descriptor::HDKeyPaths;
 pub use error::Error;
 pub use types::*;
 pub use wallet::address_validator;
index 08e7131cdb80226d15a2d50515cc9b31cb4e50a8..14a5b8de7582665fd08adb9588989e3383b0778d 100644 (file)
@@ -29,7 +29,7 @@ use bitcoin::hash_types::Txid;
 
 use serde::{Deserialize, Serialize};
 
-// TODO serde flatten?
+/// Types of script
 #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub enum ScriptType {
     External = 0,
@@ -58,23 +58,28 @@ impl AsRef<[u8]> for ScriptType {
     }
 }
 
+/// Fee rate
 #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
 // Internally stored as satoshi/vbyte
 pub struct FeeRate(f32);
 
 impl FeeRate {
+    /// Create a new instance of [`FeeRate`] given a float fee rate in btc/kvbytes
     pub fn from_btc_per_kvb(btc_per_kvb: f32) -> Self {
         FeeRate(btc_per_kvb * 1e5)
     }
 
+    /// Create a new instance of [`FeeRate`] given a float fee rate in satoshi/vbyte
     pub fn from_sat_per_vb(sat_per_vb: f32) -> Self {
         FeeRate(sat_per_vb)
     }
 
+    /// Create a new [`FeeRate`] with the default min relay fee value
     pub fn default_min_relay_fee() -> Self {
         FeeRate(1.0)
     }
 
+    /// Return the value as satoshi/vbyte
     pub fn as_sat_vb(&self) -> f32 {
         self.0
     }
@@ -86,6 +91,7 @@ impl std::default::Default for FeeRate {
     }
 }
 
+/// A wallet unspent output
 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
 pub struct UTXO {
     pub outpoint: OutPoint,
@@ -93,6 +99,7 @@ pub struct UTXO {
     pub is_internal: bool,
 }
 
+/// A wallet transaction
 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
 pub struct TransactionDetails {
     pub transaction: Option<Transaction>,
index a96df3e61e1888cae015e5976f51cfb94ac0c021..2b0d3ab379718937f44158fb121e4559ec8b1fa6 100644 (file)
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 // SOFTWARE.
 
+//! Address validation callbacks
+//!
+//! The typical usage of those callbacks is for displaying the newly-generated address on a
+//! hardware wallet, so that the user can cross-check its correctness.
+//!
+//! More generally speaking though, these callbacks can also be used to "do something" every time
+//! an address is generated, without necessarily checking or validating it.
+//!
+//! An address validator can be attached to a [`Wallet`](super::Wallet) by using the
+//! [`Wallet::add_address_validator`](super::Wallet::add_address_validator) method, and
+//! whenever a new address is generated (either explicitly by the user with
+//! [`Wallet::get_new_address`](super::Wallet::get_new_address) or internally to create a change
+//! address) all the attached validators will be polled, in sequence. All of them must complete
+//! successfully to continue.
+//!
+//! ## Example
+//!
+//! ```
+//! # use std::sync::Arc;
+//! # use bitcoin::*;
+//! # use magical_bitcoin_wallet::address_validator::*;
+//! # use magical_bitcoin_wallet::database::*;
+//! # use magical_bitcoin_wallet::*;
+//! struct PrintAddressAndContinue;
+//!
+//! impl AddressValidator for PrintAddressAndContinue {
+//!     fn validate(
+//!         &self,
+//!         script_type: ScriptType,
+//!         hd_keypaths: &HDKeyPaths,
+//!         script: &Script
+//!     ) -> Result<(), AddressValidatorError> {
+//!         let address = Address::from_script(script, Network::Testnet)
+//!                           .as_ref()
+//!                           .map(Address::to_string)
+//!                           .unwrap_or(script.to_string());
+//!         println!("New address of type {:?}: {}", script_type, address);
+//!         println!("HD keypaths: {:#?}", hd_keypaths);
+//!
+//!         Ok(())
+//!     }
+//! }
+//!
+//! let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
+//! let mut wallet: OfflineWallet<_> = Wallet::new_offline(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
+//! wallet.add_address_validator(Arc::new(Box::new(PrintAddressAndContinue)));
+//!
+//! let address = wallet.get_new_address()?;
+//! println!("Address: {}", address);
+//! # Ok::<(), magical_bitcoin_wallet::Error>(())
+//! ```
+
 use std::fmt;
 
 use bitcoin::Script;
@@ -29,12 +81,14 @@ use bitcoin::Script;
 use crate::descriptor::HDKeyPaths;
 use crate::types::ScriptType;
 
+/// Errors that can be returned to fail the validation of an address
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum AddressValidatorError {
     UserRejected,
     ConnectionError,
     TimeoutError,
     InvalidScript,
+    Message(String),
 }
 
 impl fmt::Display for AddressValidatorError {
@@ -45,7 +99,15 @@ impl fmt::Display for AddressValidatorError {
 
 impl std::error::Error for AddressValidatorError {}
 
+/// Trait to build address validators
+///
+/// All the address validators attached to a wallet with [`Wallet::add_address_validator`](super::Wallet::add_address_validator) will be polled
+/// every time an address (external or internal) is generated by the wallet. Errors returned in the
+/// validator will be propagated up to the original caller that triggered the address generation.
+///
+/// For a usage example see [this module](crate::address_validator)'s documentation.
 pub trait AddressValidator {
+    /// Validate or inspect an address
     fn validate(
         &self,
         script_type: ScriptType,
index d68360472e7e14f1d8070f7b8e2f2d02e09a8f10..9505b222aadc2178d48518e08dc44ce68776815d 100644 (file)
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 // SOFTWARE.
 
+//! Coin selection
+//!
+//! This module provides the trait [`CoinSelectionAlgorithm`] that can be implemented to
+//! define custom coin selection algorithms.
+//!
+//! The coin selection algorithm is not globally part of a [`Wallet`](super::Wallet), instead it
+//! is selected whenever a [`Wallet::create_tx`](super::Wallet::create_tx) call is made, through
+//! the use of the [`TxBuilder`] structure, specifically with
+//! [`TxBuilder::coin_selection`](super::tx_builder::TxBuilder::coin_selection) method.
+//!
+//! The [`DefaultCoinSelectionAlgorithm`] selects the default coin selection algorithm that
+//! [`TxBuilder`] uses, if it's not explicitly overridden.
+//!
+//! [`TxBuilder`]: super::tx_builder::TxBuilder
+//!
+//! ## Example
+//!
+//! ```no_run
+//! # use std::str::FromStr;
+//! # use bitcoin::*;
+//! # use bitcoin::consensus::serialize;
+//! # use magical_bitcoin_wallet::wallet::coin_selection::*;
+//! # use magical_bitcoin_wallet::*;
+//! #[derive(Debug)]
+//! struct AlwaysSpendEverything;
+//!
+//! impl CoinSelectionAlgorithm for AlwaysSpendEverything {
+//!     fn coin_select(
+//!         &self,
+//!         utxos: Vec<UTXO>,
+//!         _use_all_utxos: bool,
+//!         fee_rate: FeeRate,
+//!         amount_needed: u64,
+//!         input_witness_weight: usize,
+//!         fee_amount: f32,
+//!     ) -> Result<CoinSelectionResult, magical_bitcoin_wallet::Error> {
+//!         let selected_amount = utxos.iter().fold(0, |acc, utxo| acc + utxo.txout.value);
+//!         let all_utxos_selected = utxos
+//!             .into_iter()
+//!             .map(|utxo| {
+//!                 (
+//!                     TxIn {
+//!                         previous_output: utxo.outpoint,
+//!                         ..Default::default()
+//!                     },
+//!                     utxo.txout.script_pubkey,
+//!                 )
+//!             })
+//!             .collect::<Vec<_>>();
+//!         let additional_weight = all_utxos_selected.iter().fold(0, |acc, (txin, _)| {
+//!             acc + serialize(txin).len() * 4 + input_witness_weight
+//!         });
+//!         let additional_fees = additional_weight as f32 * fee_rate.as_sat_vb() / 4.0;
+//!
+//!         if (fee_amount + additional_fees).ceil() as u64 + amount_needed > selected_amount {
+//!             return Err(magical_bitcoin_wallet::Error::InsufficientFunds);
+//!         }
+//!
+//!         Ok(CoinSelectionResult {
+//!             txin: all_utxos_selected,
+//!             selected_amount,
+//!             fee_amount: fee_amount + additional_fees,
+//!         })
+//!     }
+//! }
+//!
+//! # let wallet: OfflineWallet<_> = Wallet::new_offline("", None, Network::Testnet, magical_bitcoin_wallet::database::MemoryDatabase::default())?;
+//! // create wallet, sync, ...
+//!
+//! let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
+//! let (psbt, details) = wallet.create_tx(
+//!     TxBuilder::with_recipients(vec![(to_address, 50_000)])
+//!         .coin_selection(AlwaysSpendEverything),
+//! )?;
+//!
+//! // inspect, sign, broadcast, ...
+//!
+//! # Ok::<(), magical_bitcoin_wallet::Error>(())
+//! ```
+
 use bitcoin::consensus::encode::serialize;
 use bitcoin::{Script, TxIn};
 
 use crate::error::Error;
 use crate::types::{FeeRate, UTXO};
 
+/// Default coin selection algorithm used by [`TxBuilder`](super::tx_builder::TxBuilder) if not
+/// overridden
 pub type DefaultCoinSelectionAlgorithm = DumbCoinSelection;
 
+/// Result of a successful coin selection
 #[derive(Debug)]
 pub struct CoinSelectionResult {
+    /// List of inputs to use, with the respective previous script_pubkey
     pub txin: Vec<(TxIn, Script)>,
+    /// Sum of the selected inputs' value
     pub selected_amount: u64,
+    /// Total fee amount in satoshi
     pub fee_amount: f32,
 }
 
+/// Trait for generalized coin selection algorithms
+///
+/// This trait can be implemented to make the [`Wallet`](super::Wallet) use a customized coin
+/// selection algorithm when it creates transactions.
+///
+/// For an example see [this module](crate::wallet::coin_selection)'s documentation.
 pub trait CoinSelectionAlgorithm: std::fmt::Debug {
+    /// Perform the coin selection
+    ///
+    /// - `utxos`: the list of spendable UTXOs
+    /// - `use_all_utxos`: if true all utxos should be spent unconditionally
+    /// - `fee_rate`: fee rate to use
+    /// - `amount_needed`: the amount in satoshi to select
+    /// - `input_witness_weight`: the weight of an input's witness to keep into account for the fees
+    /// - `fee_amount`: the amount of fees in satoshi already accumulated from adding outputs
     fn coin_select(
         &self,
         utxos: Vec<UTXO>,
@@ -49,6 +149,10 @@ pub trait CoinSelectionAlgorithm: std::fmt::Debug {
     ) -> Result<CoinSelectionResult, Error>;
 }
 
+/// Simple and dumb coin selection
+///
+/// This coin selection algorithm sorts the available UTXOs by value and then picks them starting
+/// from the largest ones until the required amount is reached.
 #[derive(Debug, Default)]
 pub struct DumbCoinSelection;
 
index 452b90eb851247a804bddaec5d1400aae3eda095..683533a7a5028266281fcb4dbde024925ab7ab66 100644 (file)
@@ -200,7 +200,7 @@ where
             return Err(Error::SpendingPolicyRequired);
         }
         let requirements =
-            policy.get_requirements(builder.policy_path.as_ref().unwrap_or(&BTreeMap::new()))?;
+            policy.get_condition(builder.policy_path.as_ref().unwrap_or(&BTreeMap::new()))?;
         debug!("requirements: {:?}", requirements);
 
         let version = match builder.version {