//! # 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;
//! ```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};
// 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};
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(),
// 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};
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>;
}
// 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;
c
}
+/// Compute the checksum of a descriptor
pub fn get_checksum(desc: &str) -> Result<String, Error> {
let mut c = 1;
let mut cls = 0;
// 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,
// 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;
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,
) -> Result<Option<Policy>, Error>;
}
-pub trait XKeyUtils {
+pub(crate) trait XKeyUtils {
fn full_path(&self, append: &[ChildNumber]) -> DerivationPath;
fn root_fingerprint(&self) -> Fingerprint;
}
}
}
-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;
-> Option<Self>;
}
-pub trait DescriptorScripts {
+pub(crate) trait DescriptorScripts {
fn psbt_redeem_script(&self) -> Option<Script>;
fn psbt_witness_script(&self) -> Option<Script>;
}
// 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;
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")]
}
}
+/// An item that need to be satisfied
#[derive(Debug, Clone, Serialize)]
#[serde(tag = "type", rename_all = "UPPERCASE")]
pub enum SatisfiableItem {
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,
}
}
}
+/// Descriptor spending policy
#[derive(Debug, Clone, Serialize)]
pub struct Policy {
id: String,
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")]
}
}
+/// Errors that can happen while extracting and manipulating policies
#[derive(Debug)]
pub enum PolicyError {
NotEnoughItemsSelected(String),
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> {
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
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),
extern crate serial_test;
#[macro_use]
-pub mod error;
+pub(crate) mod error;
pub mod blockchain;
pub mod database;
pub mod descriptor;
pub(crate) mod types;
pub mod wallet;
+pub use descriptor::HDKeyPaths;
pub use error::Error;
pub use types::*;
pub use wallet::address_validator;
use serde::{Deserialize, Serialize};
-// TODO serde flatten?
+/// Types of script
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ScriptType {
External = 0,
}
}
+/// 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
}
}
}
+/// A wallet unspent output
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct UTXO {
pub outpoint: OutPoint,
pub is_internal: bool,
}
+/// A wallet transaction
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
pub struct TransactionDetails {
pub transaction: Option<Transaction>,
// 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;
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 {
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,
// 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>,
) -> 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;
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 {