From 8a33d98db977a07e130ad57fa9c658a5c90d4a4b Mon Sep 17 00:00:00 2001 From: Leonardo Lima Date: Wed, 24 Apr 2024 18:12:45 -0300 Subject: [PATCH 1/3] feat: update `wallet::Balance` to use `bitcoin::Amount` - update all fields `immature`, ` trusted_pending`, `unstrusted_pending` and `confirmed` to use the `bitcoin::Amount` instead of `u64` - update all `impl Balance` methods to use `bitcoin::Amount` - update all tests that relies on `keychain::Balance` --- crates/bdk/src/wallet/mod.rs | 6 +- crates/bdk/src/wallet/tx_builder.rs | 1 + crates/bdk/tests/psbt.rs | 4 +- crates/bdk/tests/wallet.rs | 24 ++-- crates/bitcoind_rpc/tests/test_emitter.rs | 6 +- crates/chain/src/keychain.rs | 13 ++- crates/chain/src/spk_txout_index.rs | 1 + crates/chain/src/tx_graph.rs | 19 ++-- crates/chain/tests/test_indexed_tx_graph.rs | 40 +++---- crates/chain/tests/test_tx_graph_conflicts.rs | 106 +++++++++--------- crates/electrum/tests/test_electrum.rs | 8 +- example-crates/example_cli/src/lib.rs | 4 +- example-crates/wallet_electrum/src/main.rs | 5 +- .../wallet_esplora_async/src/main.rs | 5 +- .../wallet_esplora_blocking/src/main.rs | 5 +- 15 files changed, 127 insertions(+), 120 deletions(-) diff --git a/crates/bdk/src/wallet/mod.rs b/crates/bdk/src/wallet/mod.rs index cbe1a8241..7b1bf0be5 100644 --- a/crates/bdk/src/wallet/mod.rs +++ b/crates/bdk/src/wallet/mod.rs @@ -32,14 +32,14 @@ use bdk_chain::{ IndexedTxGraph, }; use bdk_persist::{Persist, PersistBackend}; -use bitcoin::constants::genesis_block; use bitcoin::secp256k1::{All, Secp256k1}; use bitcoin::sighash::{EcdsaSighashType, TapSighashType}; use bitcoin::{ absolute, psbt, Address, Block, FeeRate, Network, OutPoint, Script, ScriptBuf, Sequence, Transaction, TxOut, Txid, Witness, }; -use bitcoin::{consensus::encode::serialize, transaction, Amount, BlockHash, Psbt}; +use bitcoin::{consensus::encode::serialize, transaction, BlockHash, Psbt}; +use bitcoin::{constants::genesis_block, Amount}; use core::fmt; use core::ops::Deref; use descriptor::error::Error as DescriptorError; @@ -950,7 +950,7 @@ impl Wallet { /// [`insert_txout`]: Self::insert_txout pub fn calculate_fee_rate(&self, tx: &Transaction) -> Result { self.calculate_fee(tx) - .map(|fee| bitcoin::Amount::from_sat(fee) / tx.weight()) + .map(|fee| Amount::from_sat(fee) / tx.weight()) } /// Compute the `tx`'s sent and received amounts (in satoshis). diff --git a/crates/bdk/src/wallet/tx_builder.rs b/crates/bdk/src/wallet/tx_builder.rs index 355d80d3b..bf6d404a2 100644 --- a/crates/bdk/src/wallet/tx_builder.rs +++ b/crates/bdk/src/wallet/tx_builder.rs @@ -718,6 +718,7 @@ impl<'a, Cs: CoinSelectionAlgorithm> TxBuilder<'a, Cs, CreateTx> { self } + // TODO: (@leonardo) Should this expect/use `bitcoin::Amount` instead ? Would it be a huge breaking change ? /// Add a recipient to the internal list pub fn add_recipient(&mut self, script_pubkey: ScriptBuf, amount: u64) -> &mut Self { self.params.recipients.push((script_pubkey, amount)); diff --git a/crates/bdk/tests/psbt.rs b/crates/bdk/tests/psbt.rs index ed7a88ad2..1a81f6e80 100644 --- a/crates/bdk/tests/psbt.rs +++ b/crates/bdk/tests/psbt.rs @@ -1,4 +1,4 @@ -use bdk::bitcoin::{Amount, FeeRate, Psbt, TxIn}; +use bdk::bitcoin::{FeeRate, Psbt, TxIn}; use bdk::{psbt, KeychainKind, SignOptions}; use core::str::FromStr; mod common; @@ -201,7 +201,7 @@ fn test_psbt_multiple_internalkey_signers() { // the prevout we're spending let prevouts = &[TxOut { script_pubkey: send_to.script_pubkey(), - value: Amount::from_sat(to_spend), + value: to_spend, }]; let prevouts = Prevouts::All(prevouts); let input_index = 0; diff --git a/crates/bdk/tests/wallet.rs b/crates/bdk/tests/wallet.rs index 9a0dd3bfc..1246790c9 100644 --- a/crates/bdk/tests/wallet.rs +++ b/crates/bdk/tests/wallet.rs @@ -200,7 +200,7 @@ fn test_get_funded_wallet_balance() { // The funded wallet contains a tx with a 76_000 sats input and two outputs, one spending 25_000 // to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000 // sats are the transaction fee. - assert_eq!(wallet.get_balance().confirmed, 50_000); + assert_eq!(wallet.get_balance().confirmed.to_sat(), 50_000); } #[test] @@ -3582,10 +3582,10 @@ fn test_spend_coinbase() { assert_eq!( balance, Balance { - immature: 25_000, - trusted_pending: 0, - untrusted_pending: 0, - confirmed: 0 + immature: Amount::from_sat(25_000), + trusted_pending: Amount::ZERO, + untrusted_pending: Amount::ZERO, + confirmed: Amount::ZERO } ); @@ -3596,7 +3596,7 @@ fn test_spend_coinbase() { .assume_checked(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), balance.immature / 2) + .add_recipient(addr.script_pubkey(), balance.immature.to_sat() / 2) .current_height(confirmation_height); assert!(matches!( builder.finish(), @@ -3611,7 +3611,7 @@ fn test_spend_coinbase() { // Still unspendable... let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), balance.immature / 2) + .add_recipient(addr.script_pubkey(), balance.immature.to_sat() / 2) .current_height(not_yet_mature_time); assert_matches!( builder.finish(), @@ -3633,15 +3633,15 @@ fn test_spend_coinbase() { assert_eq!( balance, Balance { - immature: 0, - trusted_pending: 0, - untrusted_pending: 0, - confirmed: 25_000 + immature: Amount::ZERO, + trusted_pending: Amount::ZERO, + untrusted_pending: Amount::ZERO, + confirmed: Amount::from_sat(25_000) } ); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), balance.confirmed / 2) + .add_recipient(addr.script_pubkey(), balance.confirmed.to_sat() / 2) .current_height(maturity_time); builder.finish().unwrap(); } diff --git a/crates/bitcoind_rpc/tests/test_emitter.rs b/crates/bitcoind_rpc/tests/test_emitter.rs index c517740f5..963e22280 100644 --- a/crates/bitcoind_rpc/tests/test_emitter.rs +++ b/crates/bitcoind_rpc/tests/test_emitter.rs @@ -377,7 +377,7 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> { assert_eq!( get_balance(&recv_chain, &recv_graph)?, Balance { - confirmed: SEND_AMOUNT.to_sat() * ADDITIONAL_COUNT as u64, + confirmed: SEND_AMOUNT * ADDITIONAL_COUNT as u64, ..Balance::default() }, "initial balance must be correct", @@ -391,8 +391,8 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> { assert_eq!( get_balance(&recv_chain, &recv_graph)?, Balance { - confirmed: SEND_AMOUNT.to_sat() * (ADDITIONAL_COUNT - reorg_count) as u64, - trusted_pending: SEND_AMOUNT.to_sat() * reorg_count as u64, + confirmed: SEND_AMOUNT * (ADDITIONAL_COUNT - reorg_count) as u64, + trusted_pending: SEND_AMOUNT * reorg_count as u64, ..Balance::default() }, "reorg_count: {}", diff --git a/crates/chain/src/keychain.rs b/crates/chain/src/keychain.rs index c20d1f6cd..240d944ef 100644 --- a/crates/chain/src/keychain.rs +++ b/crates/chain/src/keychain.rs @@ -14,6 +14,7 @@ use crate::{collections::BTreeMap, Append}; #[cfg(feature = "miniscript")] mod txout_index; +use bitcoin::Amount; #[cfg(feature = "miniscript")] pub use txout_index::*; @@ -90,13 +91,13 @@ impl AsRef> for ChangeSet { )] pub struct Balance { /// All coinbase outputs not yet matured - pub immature: u64, + pub immature: Amount, /// Unconfirmed UTXOs generated by a wallet tx - pub trusted_pending: u64, + pub trusted_pending: Amount, /// Unconfirmed UTXOs received from an external wallet - pub untrusted_pending: u64, + pub untrusted_pending: Amount, /// Confirmed and immediately spendable balance - pub confirmed: u64, + pub confirmed: Amount, } impl Balance { @@ -104,12 +105,12 @@ impl Balance { /// /// This is the balance you can spend right now that shouldn't get cancelled via another party /// double spending it. - pub fn trusted_spendable(&self) -> u64 { + pub fn trusted_spendable(&self) -> Amount { self.confirmed + self.trusted_pending } /// Get the whole balance visible to the wallet. - pub fn total(&self) -> u64 { + pub fn total(&self) -> Amount { self.confirmed + self.trusted_pending + self.untrusted_pending + self.immature } } diff --git a/crates/chain/src/spk_txout_index.rs b/crates/chain/src/spk_txout_index.rs index a3ad48d0a..a2e718dd4 100644 --- a/crates/chain/src/spk_txout_index.rs +++ b/crates/chain/src/spk_txout_index.rs @@ -270,6 +270,7 @@ impl SpkTxOutIndex { self.spk_indices.get(script) } + // TODO: (@leonardo) Should this also be updated to return `(bitcoin::Amount, bitcoin::Amount)` instead of (u64, u64) /// Computes the total value transfer effect `tx` has on the script pubkeys in `range`. Value is /// *sent* when a script pubkey in the `range` is on an input and *received* when it is on an /// output. For `sent` to be computed correctly, the output being spent must have already been diff --git a/crates/chain/src/tx_graph.rs b/crates/chain/src/tx_graph.rs index f6144e7a2..4acba7aac 100644 --- a/crates/chain/src/tx_graph.rs +++ b/crates/chain/src/tx_graph.rs @@ -95,7 +95,7 @@ use crate::{ use alloc::collections::vec_deque::VecDeque; use alloc::sync::Arc; use alloc::vec::Vec; -use bitcoin::{OutPoint, Script, Transaction, TxOut, Txid}; +use bitcoin::{Amount, OutPoint, Script, Transaction, TxOut, Txid}; use core::fmt::{self, Formatter}; use core::{ convert::Infallible, @@ -1155,27 +1155,28 @@ impl TxGraph { outpoints: impl IntoIterator, mut trust_predicate: impl FnMut(&OI, &Script) -> bool, ) -> Result { - let mut immature = 0; - let mut trusted_pending = 0; - let mut untrusted_pending = 0; - let mut confirmed = 0; + let mut immature = Amount::ZERO; + let mut trusted_pending = Amount::ZERO; + let mut untrusted_pending = Amount::ZERO; + let mut confirmed = Amount::ZERO; for res in self.try_filter_chain_unspents(chain, chain_tip, outpoints) { let (spk_i, txout) = res?; + // TODO: (@leonardo) Should these operations use `bitcoin::Amount::checked_add()` instead ? match &txout.chain_position { ChainPosition::Confirmed(_) => { if txout.is_confirmed_and_spendable(chain_tip.height) { - confirmed += txout.txout.value.to_sat(); + confirmed += txout.txout.value; } else if !txout.is_mature(chain_tip.height) { - immature += txout.txout.value.to_sat(); + immature += txout.txout.value; } } ChainPosition::Unconfirmed(_) => { if trust_predicate(&spk_i, &txout.txout.script_pubkey) { - trusted_pending += txout.txout.value.to_sat(); + trusted_pending += txout.txout.value; } else { - untrusted_pending += txout.txout.value.to_sat(); + untrusted_pending += txout.txout.value; } } } diff --git a/crates/chain/tests/test_indexed_tx_graph.rs b/crates/chain/tests/test_indexed_tx_graph.rs index 1c8bf398b..528418be7 100644 --- a/crates/chain/tests/test_indexed_tx_graph.rs +++ b/crates/chain/tests/test_indexed_tx_graph.rs @@ -341,10 +341,10 @@ fn test_list_owned_txouts() { assert_eq!( balance, Balance { - immature: 70000, // immature coinbase - trusted_pending: 25000, // tx3 + tx5 - untrusted_pending: 20000, // tx4 - confirmed: 0 // Nothing is confirmed yet + immature: Amount::from_sat(70000), // immature coinbase + trusted_pending: Amount::from_sat(25000), // tx3 + tx5 + untrusted_pending: Amount::from_sat(20000), // tx4 + confirmed: Amount::ZERO // Nothing is confirmed yet } ); } @@ -376,10 +376,10 @@ fn test_list_owned_txouts() { assert_eq!( balance, Balance { - immature: 70000, // immature coinbase - trusted_pending: 25000, // tx3 + tx5 - untrusted_pending: 20000, // tx4 - confirmed: 0 // Nothing is confirmed yet + immature: Amount::from_sat(70000), // immature coinbase + trusted_pending: Amount::from_sat(25000), // tx3 + tx5 + untrusted_pending: Amount::from_sat(20000), // tx4 + confirmed: Amount::ZERO // Nothing is confirmed yet } ); } @@ -408,10 +408,10 @@ fn test_list_owned_txouts() { assert_eq!( balance, Balance { - immature: 70000, // immature coinbase - trusted_pending: 15000, // tx5 - untrusted_pending: 20000, // tx4 - confirmed: 10000 // tx3 got confirmed + immature: Amount::from_sat(70000), // immature coinbase + trusted_pending: Amount::from_sat(15000), // tx5 + untrusted_pending: Amount::from_sat(20000), // tx4 + confirmed: Amount::from_sat(10000) // tx3 got confirmed } ); } @@ -439,10 +439,10 @@ fn test_list_owned_txouts() { assert_eq!( balance, Balance { - immature: 70000, // immature coinbase - trusted_pending: 15000, // tx5 - untrusted_pending: 20000, // tx4 - confirmed: 10000 // tx1 got matured + immature: Amount::from_sat(70000), // immature coinbase + trusted_pending: Amount::from_sat(15000), // tx5 + untrusted_pending: Amount::from_sat(20000), // tx4 + confirmed: Amount::from_sat(10000) // tx1 got matured } ); } @@ -455,10 +455,10 @@ fn test_list_owned_txouts() { assert_eq!( balance, Balance { - immature: 0, // coinbase matured - trusted_pending: 15000, // tx5 - untrusted_pending: 20000, // tx4 - confirmed: 80000 // tx1 + tx3 + immature: Amount::ZERO, // coinbase matured + trusted_pending: Amount::from_sat(15000), // tx5 + untrusted_pending: Amount::from_sat(20000), // tx4 + confirmed: Amount::from_sat(80000) // tx1 + tx3 } ); } diff --git a/crates/chain/tests/test_tx_graph_conflicts.rs b/crates/chain/tests/test_tx_graph_conflicts.rs index 8ac440f3e..96d7b49fa 100644 --- a/crates/chain/tests/test_tx_graph_conflicts.rs +++ b/crates/chain/tests/test_tx_graph_conflicts.rs @@ -4,7 +4,7 @@ mod common; use std::collections::{BTreeSet, HashSet}; use bdk_chain::{keychain::Balance, BlockId}; -use bitcoin::{OutPoint, Script}; +use bitcoin::{Amount, OutPoint, Script}; use common::*; #[allow(dead_code)] @@ -79,10 +79,10 @@ fn test_tx_conflict_handling() { exp_chain_txouts: HashSet::from([("confirmed_genesis", 0), ("confirmed_conflict", 0)]), exp_unspents: HashSet::from([("confirmed_conflict", 0)]), exp_balance: Balance { - immature: 0, - trusted_pending: 0, - untrusted_pending: 0, - confirmed: 20000, + immature: Amount::ZERO, + trusted_pending: Amount::ZERO, + untrusted_pending: Amount::ZERO, + confirmed: Amount::from_sat(20000), }, }, Scenario { @@ -115,10 +115,10 @@ fn test_tx_conflict_handling() { exp_chain_txouts: HashSet::from([("tx1", 0), ("tx_conflict_2", 0)]), exp_unspents: HashSet::from([("tx_conflict_2", 0)]), exp_balance: Balance { - immature: 0, - trusted_pending: 30000, - untrusted_pending: 0, - confirmed: 0, + immature: Amount::ZERO, + trusted_pending: Amount::from_sat(30000), + untrusted_pending: Amount::ZERO, + confirmed: Amount::ZERO, }, }, Scenario { @@ -150,10 +150,10 @@ fn test_tx_conflict_handling() { exp_chain_txouts: HashSet::from([("tx1", 0), ("tx1", 1), ("tx_conflict_2", 0)]), exp_unspents: HashSet::from([("tx_conflict_2", 0)]), exp_balance: Balance { - immature: 0, - trusted_pending: 30000, - untrusted_pending: 0, - confirmed: 0, + immature: Amount::ZERO, + trusted_pending: Amount::from_sat(30000), + untrusted_pending: Amount::ZERO, + confirmed: Amount::ZERO, }, }, Scenario { @@ -192,10 +192,10 @@ fn test_tx_conflict_handling() { exp_chain_txouts: HashSet::from([("tx1", 0), ("tx_conflict_3", 0)]), exp_unspents: HashSet::from([("tx_conflict_3", 0)]), exp_balance: Balance { - immature: 0, - trusted_pending: 40000, - untrusted_pending: 0, - confirmed: 0, + immature: Amount::ZERO, + trusted_pending: Amount::from_sat(40000), + untrusted_pending: Amount::ZERO, + confirmed: Amount::ZERO, }, }, Scenario { @@ -227,10 +227,10 @@ fn test_tx_conflict_handling() { exp_chain_txouts: HashSet::from([("tx1", 0), ("tx_orphaned_conflict", 0)]), exp_unspents: HashSet::from([("tx_orphaned_conflict", 0)]), exp_balance: Balance { - immature: 0, - trusted_pending: 30000, - untrusted_pending: 0, - confirmed: 0, + immature: Amount::ZERO, + trusted_pending: Amount::from_sat(30000), + untrusted_pending: Amount::ZERO, + confirmed: Amount::ZERO, }, }, Scenario { @@ -262,10 +262,10 @@ fn test_tx_conflict_handling() { exp_chain_txouts: HashSet::from([("tx1", 0), ("tx_conflict_1", 0)]), exp_unspents: HashSet::from([("tx_conflict_1", 0)]), exp_balance: Balance { - immature: 0, - trusted_pending: 20000, - untrusted_pending: 0, - confirmed: 0, + immature: Amount::ZERO, + trusted_pending: Amount::from_sat(20000), + untrusted_pending: Amount::ZERO, + confirmed: Amount::ZERO, }, }, Scenario { @@ -311,10 +311,10 @@ fn test_tx_conflict_handling() { exp_chain_txouts: HashSet::from([("tx1", 0), ("tx_confirmed_conflict", 0)]), exp_unspents: HashSet::from([("tx_confirmed_conflict", 0)]), exp_balance: Balance { - immature: 0, - trusted_pending: 0, - untrusted_pending: 0, - confirmed: 50000, + immature: Amount::ZERO, + trusted_pending: Amount::ZERO, + untrusted_pending: Amount::ZERO, + confirmed: Amount::from_sat(50000), }, }, Scenario { @@ -356,10 +356,10 @@ fn test_tx_conflict_handling() { exp_chain_txouts: HashSet::from([("A", 0), ("B", 0), ("C", 0)]), exp_unspents: HashSet::from([("C", 0)]), exp_balance: Balance { - immature: 0, - trusted_pending: 30000, - untrusted_pending: 0, - confirmed: 0, + immature: Amount::ZERO, + trusted_pending: Amount::from_sat(30000), + untrusted_pending: Amount::ZERO, + confirmed: Amount::ZERO, }, }, Scenario { @@ -397,10 +397,10 @@ fn test_tx_conflict_handling() { exp_chain_txouts: HashSet::from([("A", 0), ("B'", 0)]), exp_unspents: HashSet::from([("B'", 0)]), exp_balance: Balance { - immature: 0, - trusted_pending: 0, - untrusted_pending: 0, - confirmed: 20000, + immature: Amount::ZERO, + trusted_pending: Amount::ZERO, + untrusted_pending: Amount::ZERO, + confirmed: Amount::from_sat(20000), }, }, Scenario { @@ -442,10 +442,10 @@ fn test_tx_conflict_handling() { ]), exp_unspents: HashSet::from([("C", 0)]), exp_balance: Balance { - immature: 0, - trusted_pending: 30000, - untrusted_pending: 0, - confirmed: 0, + immature: Amount::ZERO, + trusted_pending: Amount::from_sat(30000), + untrusted_pending: Amount::ZERO, + confirmed: Amount::ZERO, }, }, Scenario { @@ -487,10 +487,10 @@ fn test_tx_conflict_handling() { exp_chain_txouts: HashSet::from([("A", 0), ("B'", 0)]), exp_unspents: HashSet::from([("B'", 0)]), exp_balance: Balance { - immature: 0, - trusted_pending: 30000, - untrusted_pending: 0, - confirmed: 0, + immature: Amount::ZERO, + trusted_pending: Amount::from_sat(30000), + untrusted_pending: Amount::ZERO, + confirmed: Amount::ZERO, }, }, Scenario { @@ -532,10 +532,10 @@ fn test_tx_conflict_handling() { exp_chain_txouts: HashSet::from([("A", 0), ("B'", 0)]), exp_unspents: HashSet::from([("B'", 0)]), exp_balance: Balance { - immature: 0, - trusted_pending: 0, - untrusted_pending: 0, - confirmed: 50000, + immature: Amount::ZERO, + trusted_pending: Amount::ZERO, + untrusted_pending: Amount::ZERO, + confirmed: Amount::from_sat(50000), }, }, Scenario { @@ -583,10 +583,10 @@ fn test_tx_conflict_handling() { exp_chain_txouts: HashSet::from([("A", 0), ("B'", 0)]), exp_unspents: HashSet::from([("B'", 0)]), exp_balance: Balance { - immature: 0, - trusted_pending: 0, - untrusted_pending: 0, - confirmed: 50000, + immature: Amount::ZERO, + trusted_pending: Amount::ZERO, + untrusted_pending: Amount::ZERO, + confirmed: Amount::from_sat(50000), }, }, ]; diff --git a/crates/electrum/tests/test_electrum.rs b/crates/electrum/tests/test_electrum.rs index 8f77209fc..07e1a8720 100644 --- a/crates/electrum/tests/test_electrum.rs +++ b/crates/electrum/tests/test_electrum.rs @@ -78,7 +78,7 @@ fn scan_detects_confirmed_tx() -> Result<()> { assert_eq!( get_balance(&recv_chain, &recv_graph)?, Balance { - confirmed: SEND_AMOUNT.to_sat(), + confirmed: SEND_AMOUNT, ..Balance::default() }, ); @@ -147,7 +147,7 @@ fn tx_can_become_unconfirmed_after_reorg() -> Result<()> { assert_eq!( get_balance(&recv_chain, &recv_graph)?, Balance { - confirmed: SEND_AMOUNT.to_sat() * REORG_COUNT as u64, + confirmed: SEND_AMOUNT * REORG_COUNT as u64, ..Balance::default() }, "initial balance must be correct", @@ -178,8 +178,8 @@ fn tx_can_become_unconfirmed_after_reorg() -> Result<()> { assert_eq!( get_balance(&recv_chain, &recv_graph)?, Balance { - confirmed: SEND_AMOUNT.to_sat() * (REORG_COUNT - depth) as u64, - trusted_pending: SEND_AMOUNT.to_sat() * depth as u64, + confirmed: SEND_AMOUNT * (REORG_COUNT - depth) as u64, + trusted_pending: SEND_AMOUNT * depth as u64, ..Balance::default() }, "reorg_count: {}", diff --git a/example-crates/example_cli/src/lib.rs b/example-crates/example_cli/src/lib.rs index 5671a6b82..be9e4f01c 100644 --- a/example-crates/example_cli/src/lib.rs +++ b/example-crates/example_cli/src/lib.rs @@ -506,11 +506,11 @@ where let chain = &*chain.lock().unwrap(); fn print_balances<'a>( title_str: &'a str, - items: impl IntoIterator, + items: impl IntoIterator, ) { println!("{}:", title_str); for (name, amount) in items.into_iter() { - println!(" {:<10} {:>12} sats", name, amount) + println!(" {:<10} {:>12} sats", name, amount.to_sat()) } } diff --git a/example-crates/wallet_electrum/src/main.rs b/example-crates/wallet_electrum/src/main.rs index 4af9e71de..c71592f59 100644 --- a/example-crates/wallet_electrum/src/main.rs +++ b/example-crates/wallet_electrum/src/main.rs @@ -6,7 +6,7 @@ const BATCH_SIZE: usize = 5; use std::io::Write; use std::str::FromStr; -use bdk::bitcoin::Address; +use bdk::bitcoin::{Address, Amount}; use bdk::wallet::Update; use bdk::{bitcoin::Network, Wallet}; use bdk::{KeychainKind, SignOptions}; @@ -81,7 +81,8 @@ fn main() -> Result<(), anyhow::Error> { let balance = wallet.get_balance(); println!("Wallet balance after syncing: {} sats", balance.total()); - if balance.total() < SEND_AMOUNT { + // TODO: (@leonardo) Should we format here, or update on constant and TxBuilder::add_recipient() instead ? + if balance.total() < Amount::from_sat(SEND_AMOUNT) { println!( "Please send at least {} sats to the receiving address", SEND_AMOUNT diff --git a/example-crates/wallet_esplora_async/src/main.rs b/example-crates/wallet_esplora_async/src/main.rs index d89e5fd20..42dd6ae37 100644 --- a/example-crates/wallet_esplora_async/src/main.rs +++ b/example-crates/wallet_esplora_async/src/main.rs @@ -1,7 +1,7 @@ use std::{collections::BTreeSet, io::Write, str::FromStr}; use bdk::{ - bitcoin::{Address, Network, Script}, + bitcoin::{Address, Amount, Network, Script}, KeychainKind, SignOptions, Wallet, }; use bdk_esplora::{esplora_client, EsploraAsyncExt}; @@ -81,7 +81,8 @@ async fn main() -> Result<(), anyhow::Error> { let balance = wallet.get_balance(); println!("Wallet balance after syncing: {} sats", balance.total()); - if balance.total() < SEND_AMOUNT { + // TODO: (@leonardo) Should we format here, or update on constant and TxBuilder::add_recipient() instead ? + if balance.total() < Amount::from_sat(SEND_AMOUNT) { println!( "Please send at least {} sats to the receiving address", SEND_AMOUNT diff --git a/example-crates/wallet_esplora_blocking/src/main.rs b/example-crates/wallet_esplora_blocking/src/main.rs index 6028bb7d0..95acf5de5 100644 --- a/example-crates/wallet_esplora_blocking/src/main.rs +++ b/example-crates/wallet_esplora_blocking/src/main.rs @@ -6,7 +6,7 @@ const PARALLEL_REQUESTS: usize = 1; use std::{collections::BTreeSet, io::Write, str::FromStr}; use bdk::{ - bitcoin::{Address, Network}, + bitcoin::{Address, Amount, Network}, KeychainKind, SignOptions, Wallet, }; use bdk_esplora::{esplora_client, EsploraExt}; @@ -57,7 +57,8 @@ fn main() -> Result<(), anyhow::Error> { let balance = wallet.get_balance(); println!("Wallet balance after syncing: {} sats", balance.total()); - if balance.total() < SEND_AMOUNT { + // TODO: (@leonardo) Should we format here, or update on constant and TxBuilder::add_recipient() instead ? + if balance.total() < Amount::from_sat(SEND_AMOUNT) { println!( "Please send at least {} sats to the receiving address", SEND_AMOUNT From d5c0e7200cba0c3b4d3e3fbea168cd07ee6c1d2c Mon Sep 17 00:00:00 2001 From: Leonardo Lima Date: Fri, 26 Apr 2024 11:17:23 -0300 Subject: [PATCH 2/3] feat: use `Amount` on `spk_txout_index` and related - update `wallet.rs` fns: `sent_and_received` fn - update `keychain` `txout_index` fn: `sent_and_received and `net_value` --- crates/bdk/src/wallet/mod.rs | 4 +- crates/bdk/tests/wallet.rs | 118 +++++++++++++-------- crates/chain/src/keychain/txout_index.rs | 10 +- crates/chain/src/spk_txout_index.rs | 22 ++-- crates/chain/tests/test_spk_txout_index.rs | 40 +++++-- 5 files changed, 125 insertions(+), 69 deletions(-) diff --git a/crates/bdk/src/wallet/mod.rs b/crates/bdk/src/wallet/mod.rs index 7b1bf0be5..3e8bfb161 100644 --- a/crates/bdk/src/wallet/mod.rs +++ b/crates/bdk/src/wallet/mod.rs @@ -953,7 +953,7 @@ impl Wallet { .map(|fee| Amount::from_sat(fee) / tx.weight()) } - /// Compute the `tx`'s sent and received amounts (in satoshis). + /// Compute the `tx`'s sent and received [`Amount`]s. /// /// This method returns a tuple `(sent, received)`. Sent is the sum of the txin amounts /// that spend from previous txouts tracked by this wallet. Received is the summation @@ -978,7 +978,7 @@ impl Wallet { /// let tx = &psbt.clone().extract_tx().expect("tx"); /// let (sent, received) = wallet.sent_and_received(tx); /// ``` - pub fn sent_and_received(&self, tx: &Transaction) -> (u64, u64) { + pub fn sent_and_received(&self, tx: &Transaction) -> (Amount, Amount) { self.indexed_graph.index.sent_and_received(tx, ..) } diff --git a/crates/bdk/tests/wallet.rs b/crates/bdk/tests/wallet.rs index 1246790c9..0211e3ada 100644 --- a/crates/bdk/tests/wallet.rs +++ b/crates/bdk/tests/wallet.rs @@ -207,7 +207,7 @@ fn test_get_funded_wallet_balance() { fn test_get_funded_wallet_sent_and_received() { let (wallet, txid) = get_funded_wallet(get_test_wpkh()); - let mut tx_amounts: Vec<(Txid, (u64, u64))> = wallet + let mut tx_amounts: Vec<(Txid, (Amount, Amount))> = wallet .transactions() .map(|ct| (ct.tx_node.txid, wallet.sent_and_received(&ct.tx_node))) .collect(); @@ -219,8 +219,8 @@ fn test_get_funded_wallet_sent_and_received() { // The funded wallet contains a tx with a 76_000 sats input and two outputs, one spending 25_000 // to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000 // sats are the transaction fee. - assert_eq!(sent, 76_000); - assert_eq!(received, 50_000); + assert_eq!(sent.to_sat(), 76_000); + assert_eq!(received.to_sat(), 50_000); } #[test] @@ -1034,7 +1034,8 @@ fn test_create_tx_add_utxo() { "should add an additional input since 25_000 < 30_000" ); assert_eq!( - sent_received.0, 75_000, + sent_received.0, + Amount::from_sat(75_000), "total should be sum of both inputs" ); } @@ -1225,7 +1226,7 @@ fn test_add_foreign_utxo() { wallet1.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx")); assert_eq!( - sent_received.0 - sent_received.1, + (sent_received.0 - sent_received.1).to_sat(), 10_000 + fee.unwrap_or(0), "we should have only net spent ~10_000" ); @@ -1622,8 +1623,8 @@ fn test_bump_fee_reduce_change() { assert_eq!(sent_received.0, original_sent_received.0); assert_eq!( - sent_received.1 + fee.unwrap_or(0), - original_sent_received.1 + original_fee.unwrap_or(0) + sent_received.1 + Amount::from_sat(fee.unwrap_or(0)), + original_sent_received.1 + Amount::from_sat(original_fee.unwrap_or(0)) ); assert!(fee.unwrap_or(0) > original_fee.unwrap_or(0)); @@ -1642,8 +1643,7 @@ fn test_bump_fee_reduce_change() { .iter() .find(|txout| txout.script_pubkey != addr.script_pubkey()) .unwrap() - .value - .to_sat(), + .value, sent_received.1 ); @@ -1659,8 +1659,8 @@ fn test_bump_fee_reduce_change() { assert_eq!(sent_received.0, original_sent_received.0); assert_eq!( - sent_received.1 + fee.unwrap_or(0), - original_sent_received.1 + original_fee.unwrap_or(0) + sent_received.1 + Amount::from_sat(fee.unwrap_or(0)), + original_sent_received.1 + Amount::from_sat(original_fee.unwrap_or(0)) ); assert!( fee.unwrap_or(0) > original_fee.unwrap_or(0), @@ -1684,8 +1684,7 @@ fn test_bump_fee_reduce_change() { .iter() .find(|txout| txout.script_pubkey != addr.script_pubkey()) .unwrap() - .value - .to_sat(), + .value, sent_received.1 ); @@ -1729,7 +1728,7 @@ fn test_bump_fee_reduce_single_recipient() { let tx = &psbt.unsigned_tx; assert_eq!(tx.output.len(), 1); assert_eq!( - tx.output[0].value.to_sat() + fee.unwrap_or(0), + tx.output[0].value + Amount::from_sat(fee.unwrap_or(0)), sent_received.0 ); @@ -1771,7 +1770,7 @@ fn test_bump_fee_absolute_reduce_single_recipient() { assert_eq!(tx.output.len(), 1); assert_eq!( - tx.output[0].value.to_sat() + fee.unwrap_or(0), + tx.output[0].value + Amount::from_sat(fee.unwrap_or(0)), sent_received.0 ); @@ -1825,7 +1824,7 @@ fn test_bump_fee_drain_wallet() { wallet .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) .unwrap(); - assert_eq!(original_sent_received.0, 25_000); + assert_eq!(original_sent_received.0, Amount::from_sat(25_000)); // for the new feerate, it should be enough to reduce the output, but since we specify // `drain_wallet` we expect to spend everything @@ -1838,7 +1837,7 @@ fn test_bump_fee_drain_wallet() { let psbt = builder.finish().unwrap(); let sent_received = wallet.sent_and_received(&psbt.extract_tx().expect("failed to extract tx")); - assert_eq!(sent_received.0, 75_000); + assert_eq!(sent_received.0, Amount::from_sat(75_000)); } #[test] @@ -1895,7 +1894,7 @@ fn test_bump_fee_remove_output_manually_selected_only() { wallet .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) .unwrap(); - assert_eq!(original_sent_received.0, 25_000); + assert_eq!(original_sent_received.0, Amount::from_sat(25_000)); let mut builder = wallet.build_fee_bump(txid).unwrap(); builder @@ -1949,8 +1948,14 @@ fn test_bump_fee_add_input() { let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx")); let fee = check_fee!(wallet, psbt); - assert_eq!(sent_received.0, original_details.0 + 25_000); - assert_eq!(fee.unwrap_or(0) + sent_received.1, 30_000); + assert_eq!( + sent_received.0, + original_details.0 + Amount::from_sat(25_000) + ); + assert_eq!( + Amount::from_sat(fee.unwrap_or(0)) + sent_received.1, + Amount::from_sat(30_000) + ); let tx = &psbt.unsigned_tx; assert_eq!(tx.input.len(), 2); @@ -1968,8 +1973,7 @@ fn test_bump_fee_add_input() { .iter() .find(|txout| txout.script_pubkey != addr.script_pubkey()) .unwrap() - .value - .to_sat(), + .value, sent_received.1 ); @@ -2002,8 +2006,14 @@ fn test_bump_fee_absolute_add_input() { wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx")); let fee = check_fee!(wallet, psbt); - assert_eq!(sent_received.0, original_sent_received.0 + 25_000); - assert_eq!(fee.unwrap_or(0) + sent_received.1, 30_000); + assert_eq!( + sent_received.0, + original_sent_received.0 + Amount::from_sat(25_000) + ); + assert_eq!( + Amount::from_sat(fee.unwrap_or(0)) + sent_received.1, + Amount::from_sat(30_000) + ); let tx = &psbt.unsigned_tx; assert_eq!(tx.input.len(), 2); @@ -2021,8 +2031,7 @@ fn test_bump_fee_absolute_add_input() { .iter() .find(|txout| txout.script_pubkey != addr.script_pubkey()) .unwrap() - .value - .to_sat(), + .value, sent_received.1 ); @@ -2065,11 +2074,15 @@ fn test_bump_fee_no_change_add_input_and_change() { wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx")); let fee = check_fee!(wallet, psbt); - let original_send_all_amount = original_sent_received.0 - original_fee.unwrap_or(0); - assert_eq!(sent_received.0, original_sent_received.0 + 50_000); + let original_send_all_amount = + original_sent_received.0 - Amount::from_sat(original_fee.unwrap_or(0)); + assert_eq!( + sent_received.0, + original_sent_received.0 + Amount::from_sat(50_000) + ); assert_eq!( sent_received.1, - 75_000 - original_send_all_amount - fee.unwrap_or(0) + Amount::from_sat(75_000) - original_send_all_amount - Amount::from_sat(fee.unwrap_or(0)) ); let tx = &psbt.unsigned_tx; @@ -2081,16 +2094,15 @@ fn test_bump_fee_no_change_add_input_and_change() { .find(|txout| txout.script_pubkey == addr.script_pubkey()) .unwrap() .value, - Amount::from_sat(original_send_all_amount) + original_send_all_amount ); assert_eq!( tx.output .iter() .find(|txout| txout.script_pubkey != addr.script_pubkey()) .unwrap() - .value - .to_sat(), - 75_000 - original_send_all_amount - fee.unwrap_or(0) + .value, + Amount::from_sat(75_000) - original_send_all_amount - Amount::from_sat(fee.unwrap_or(0)) ); assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb_unchecked(50), @add_signature); @@ -2145,11 +2157,17 @@ fn test_bump_fee_add_input_change_dust() { wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx")); let fee = check_fee!(wallet, psbt); - assert_eq!(original_sent_received.1, 5_000 - original_fee.unwrap_or(0)); + assert_eq!( + original_sent_received.1, + Amount::from_sat(5_000 - original_fee.unwrap_or(0)) + ); - assert_eq!(sent_received.0, original_sent_received.0 + 25_000); + assert_eq!( + sent_received.0, + original_sent_received.0 + Amount::from_sat(25_000) + ); assert_eq!(fee.unwrap_or(0), 30_000); - assert_eq!(sent_received.1, 0); + assert_eq!(sent_received.1, Amount::ZERO); let tx = &psbt.unsigned_tx; assert_eq!(tx.input.len(), 2); @@ -2200,8 +2218,14 @@ fn test_bump_fee_force_add_input() { wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx")); let fee = check_fee!(wallet, psbt); - assert_eq!(sent_received.0, original_sent_received.0 + 25_000); - assert_eq!(fee.unwrap_or(0) + sent_received.1, 30_000); + assert_eq!( + sent_received.0, + original_sent_received.0 + Amount::from_sat(25_000) + ); + assert_eq!( + Amount::from_sat(fee.unwrap_or(0)) + sent_received.1, + Amount::from_sat(30_000) + ); let tx = &psbt.unsigned_tx; assert_eq!(tx.input.len(), 2); @@ -2219,8 +2243,7 @@ fn test_bump_fee_force_add_input() { .iter() .find(|txout| txout.script_pubkey != addr.script_pubkey()) .unwrap() - .value - .to_sat(), + .value, sent_received.1 ); @@ -2260,8 +2283,14 @@ fn test_bump_fee_absolute_force_add_input() { wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx")); let fee = check_fee!(wallet, psbt); - assert_eq!(sent_received.0, original_sent_received.0 + 25_000); - assert_eq!(fee.unwrap_or(0) + sent_received.1, 30_000); + assert_eq!( + sent_received.0, + original_sent_received.0 + Amount::from_sat(25_000) + ); + assert_eq!( + Amount::from_sat(fee.unwrap_or(0)) + sent_received.1, + Amount::from_sat(30_000) + ); let tx = &psbt.unsigned_tx; assert_eq!(tx.input.len(), 2); @@ -2279,8 +2308,7 @@ fn test_bump_fee_absolute_force_add_input() { .iter() .find(|txout| txout.script_pubkey != addr.script_pubkey()) .unwrap() - .value - .to_sat(), + .value, sent_received.1 ); @@ -3228,7 +3256,7 @@ fn test_taproot_foreign_utxo() { assert_eq!( sent_received.0 - sent_received.1, - 10_000 + fee.unwrap_or(0), + Amount::from_sat(10_000 + fee.unwrap_or(0)), "we should have only net spent ~10_000" ); diff --git a/crates/chain/src/keychain/txout_index.rs b/crates/chain/src/keychain/txout_index.rs index 4b2479ec9..789db6c6f 100644 --- a/crates/chain/src/keychain/txout_index.rs +++ b/crates/chain/src/keychain/txout_index.rs @@ -5,7 +5,7 @@ use crate::{ spk_iter::BIP32_MAX_INDEX, SpkIterator, SpkTxOutIndex, }; -use bitcoin::{OutPoint, Script, Transaction, TxOut, Txid}; +use bitcoin::{Amount, OutPoint, Script, SignedAmount, Transaction, TxOut, Txid}; use core::{ fmt::Debug, ops::{Bound, RangeBounds}, @@ -273,7 +273,11 @@ impl KeychainTxOutIndex { /// *received* when it is on an output. For `sent` to be computed correctly, the output being /// spent must have already been scanned by the index. Calculating received just uses the /// [`Transaction`] outputs directly, so it will be correct even if it has not been scanned. - pub fn sent_and_received(&self, tx: &Transaction, range: impl RangeBounds) -> (u64, u64) { + pub fn sent_and_received( + &self, + tx: &Transaction, + range: impl RangeBounds, + ) -> (Amount, Amount) { self.inner .sent_and_received(tx, Self::map_to_inner_bounds(range)) } @@ -285,7 +289,7 @@ impl KeychainTxOutIndex { /// This calls [`SpkTxOutIndex::net_value`] internally. /// /// [`sent_and_received`]: Self::sent_and_received - pub fn net_value(&self, tx: &Transaction, range: impl RangeBounds) -> i64 { + pub fn net_value(&self, tx: &Transaction, range: impl RangeBounds) -> SignedAmount { self.inner.net_value(tx, Self::map_to_inner_bounds(range)) } } diff --git a/crates/chain/src/spk_txout_index.rs b/crates/chain/src/spk_txout_index.rs index a2e718dd4..2cad129cf 100644 --- a/crates/chain/src/spk_txout_index.rs +++ b/crates/chain/src/spk_txout_index.rs @@ -4,7 +4,7 @@ use crate::{ collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap}, indexed_tx_graph::Indexer, }; -use bitcoin::{OutPoint, Script, ScriptBuf, Transaction, TxOut, Txid}; +use bitcoin::{Amount, OutPoint, Script, ScriptBuf, SignedAmount, Transaction, TxOut, Txid}; /// An index storing [`TxOut`]s that have a script pubkey that matches those in a list. /// @@ -270,27 +270,30 @@ impl SpkTxOutIndex { self.spk_indices.get(script) } - // TODO: (@leonardo) Should this also be updated to return `(bitcoin::Amount, bitcoin::Amount)` instead of (u64, u64) /// Computes the total value transfer effect `tx` has on the script pubkeys in `range`. Value is /// *sent* when a script pubkey in the `range` is on an input and *received* when it is on an /// output. For `sent` to be computed correctly, the output being spent must have already been /// scanned by the index. Calculating received just uses the [`Transaction`] outputs directly, /// so it will be correct even if it has not been scanned. - pub fn sent_and_received(&self, tx: &Transaction, range: impl RangeBounds) -> (u64, u64) { - let mut sent = 0; - let mut received = 0; + pub fn sent_and_received( + &self, + tx: &Transaction, + range: impl RangeBounds, + ) -> (Amount, Amount) { + let mut sent = Amount::ZERO; + let mut received = Amount::ZERO; for txin in &tx.input { if let Some((index, txout)) = self.txout(txin.previous_output) { if range.contains(index) { - sent += txout.value.to_sat(); + sent += txout.value; } } } for txout in &tx.output { if let Some(index) = self.index_of_spk(&txout.script_pubkey) { if range.contains(index) { - received += txout.value.to_sat(); + received += txout.value; } } } @@ -302,9 +305,10 @@ impl SpkTxOutIndex { /// for calling [`sent_and_received`] and subtracting sent from received. /// /// [`sent_and_received`]: Self::sent_and_received - pub fn net_value(&self, tx: &Transaction, range: impl RangeBounds) -> i64 { + pub fn net_value(&self, tx: &Transaction, range: impl RangeBounds) -> SignedAmount { let (sent, received) = self.sent_and_received(tx, range); - received as i64 - sent as i64 + received.to_signed().expect("valid `SignedAmount`") + - sent.to_signed().expect("valid `SignedAmount`") } /// Whether any of the inputs of this transaction spend a txout tracked or whether any output diff --git a/crates/chain/tests/test_spk_txout_index.rs b/crates/chain/tests/test_spk_txout_index.rs index e6c390805..2317b0a7d 100644 --- a/crates/chain/tests/test_spk_txout_index.rs +++ b/crates/chain/tests/test_spk_txout_index.rs @@ -1,5 +1,7 @@ use bdk_chain::{indexed_tx_graph::Indexer, SpkTxOutIndex}; -use bitcoin::{absolute, transaction, Amount, OutPoint, ScriptBuf, Transaction, TxIn, TxOut}; +use bitcoin::{ + absolute, transaction, Amount, OutPoint, ScriptBuf, SignedAmount, Transaction, TxIn, TxOut, +}; #[test] fn spk_txout_sent_and_received() { @@ -20,14 +22,23 @@ fn spk_txout_sent_and_received() { }], }; - assert_eq!(index.sent_and_received(&tx1, ..), (0, 42_000)); - assert_eq!(index.sent_and_received(&tx1, ..1), (0, 42_000)); - assert_eq!(index.sent_and_received(&tx1, 1..), (0, 0)); - assert_eq!(index.net_value(&tx1, ..), 42_000); + assert_eq!( + index.sent_and_received(&tx1, ..), + (Amount::from_sat(0), Amount::from_sat(42_000)) + ); + assert_eq!( + index.sent_and_received(&tx1, ..1), + (Amount::from_sat(0), Amount::from_sat(42_000)) + ); + assert_eq!( + index.sent_and_received(&tx1, 1..), + (Amount::from_sat(0), Amount::from_sat(0)) + ); + assert_eq!(index.net_value(&tx1, ..), SignedAmount::from_sat(42_000)); index.index_tx(&tx1); assert_eq!( index.sent_and_received(&tx1, ..), - (0, 42_000), + (Amount::from_sat(0), Amount::from_sat(42_000)), "shouldn't change after scanning" ); @@ -53,10 +64,19 @@ fn spk_txout_sent_and_received() { ], }; - assert_eq!(index.sent_and_received(&tx2, ..), (42_000, 50_000)); - assert_eq!(index.sent_and_received(&tx2, ..1), (42_000, 30_000)); - assert_eq!(index.sent_and_received(&tx2, 1..), (0, 20_000)); - assert_eq!(index.net_value(&tx2, ..), 8_000); + assert_eq!( + index.sent_and_received(&tx2, ..), + (Amount::from_sat(42_000), Amount::from_sat(50_000)) + ); + assert_eq!( + index.sent_and_received(&tx2, ..1), + (Amount::from_sat(42_000), Amount::from_sat(30_000)) + ); + assert_eq!( + index.sent_and_received(&tx2, 1..), + (Amount::from_sat(0), Amount::from_sat(20_000)) + ); + assert_eq!(index.net_value(&tx2, ..), SignedAmount::from_sat(8_000)); } #[test] From 22aa534d7648e5808414ea3adfcfb702572bd6c9 Mon Sep 17 00:00:00 2001 From: Leonardo Lima Date: Fri, 26 Apr 2024 11:41:17 -0300 Subject: [PATCH 3/3] feat: use `Amount` on `TxBuilder::add_recipient` --- crates/bdk/src/wallet/coin_selection.rs | 2 +- crates/bdk/src/wallet/mod.rs | 6 +- crates/bdk/src/wallet/tx_builder.rs | 28 ++-- crates/bdk/tests/psbt.rs | 10 +- crates/bdk/tests/wallet.rs | 142 +++++++++--------- crates/chain/src/tx_graph.rs | 1 - example-crates/wallet_electrum/src/main.rs | 5 +- .../wallet_esplora_async/src/main.rs | 5 +- .../wallet_esplora_blocking/src/main.rs | 5 +- 9 files changed, 102 insertions(+), 102 deletions(-) diff --git a/crates/bdk/src/wallet/coin_selection.rs b/crates/bdk/src/wallet/coin_selection.rs index f1897677b..6be3cb971 100644 --- a/crates/bdk/src/wallet/coin_selection.rs +++ b/crates/bdk/src/wallet/coin_selection.rs @@ -92,7 +92,7 @@ //! .unwrap(); //! let psbt = { //! let mut builder = wallet.build_tx().coin_selection(AlwaysSpendEverything); -//! builder.add_recipient(to_address.script_pubkey(), 50_000); +//! builder.add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000)); //! builder.finish()? //! }; //! diff --git a/crates/bdk/src/wallet/mod.rs b/crates/bdk/src/wallet/mod.rs index 3e8bfb161..3850bc30a 100644 --- a/crates/bdk/src/wallet/mod.rs +++ b/crates/bdk/src/wallet/mod.rs @@ -1197,7 +1197,7 @@ impl Wallet { /// let psbt = { /// let mut builder = wallet.build_tx(); /// builder - /// .add_recipient(to_address.script_pubkey(), 50_000); + /// .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000)); /// builder.finish()? /// }; /// @@ -1579,7 +1579,7 @@ impl Wallet { /// let mut psbt = { /// let mut builder = wallet.build_tx(); /// builder - /// .add_recipient(to_address.script_pubkey(), 50_000) + /// .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000)) /// .enable_rbf(); /// builder.finish()? /// }; @@ -1752,7 +1752,7 @@ impl Wallet { /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked(); /// let mut psbt = { /// let mut builder = wallet.build_tx(); - /// builder.add_recipient(to_address.script_pubkey(), 50_000); + /// builder.add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000)); /// builder.finish()? /// }; /// let finalized = wallet.sign(&mut psbt, SignOptions::default())?; diff --git a/crates/bdk/src/wallet/tx_builder.rs b/crates/bdk/src/wallet/tx_builder.rs index bf6d404a2..5c3e70d54 100644 --- a/crates/bdk/src/wallet/tx_builder.rs +++ b/crates/bdk/src/wallet/tx_builder.rs @@ -29,7 +29,7 @@ //! //! tx_builder //! // Create a transaction with one output to `to_address` of 50_000 satoshi -//! .add_recipient(to_address.script_pubkey(), 50_000) +//! .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000)) //! // With a custom fee rate of 5.0 satoshi/vbyte //! .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate")) //! // Only spend non-change outputs @@ -47,7 +47,7 @@ use core::marker::PhantomData; use bitcoin::psbt::{self, Psbt}; use bitcoin::script::PushBytes; -use bitcoin::{absolute, FeeRate, OutPoint, ScriptBuf, Sequence, Transaction, Txid}; +use bitcoin::{absolute, Amount, FeeRate, OutPoint, ScriptBuf, Sequence, Transaction, Txid}; use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm}; use super::{CreateTxError, Wallet}; @@ -94,8 +94,8 @@ impl TxBuilderContext for BumpFee {} /// let mut builder = wallet.build_tx(); /// builder /// .ordering(TxOrdering::Untouched) -/// .add_recipient(addr1.script_pubkey(), 50_000) -/// .add_recipient(addr2.script_pubkey(), 50_000); +/// .add_recipient(addr1.script_pubkey(), Amount::from_sat(50_000)) +/// .add_recipient(addr2.script_pubkey(), Amount::from_sat(50_000)); /// builder.finish()? /// }; /// @@ -104,7 +104,7 @@ impl TxBuilderContext for BumpFee {} /// let mut builder = wallet.build_tx(); /// builder.ordering(TxOrdering::Untouched); /// for addr in &[addr1, addr2] { -/// builder.add_recipient(addr.script_pubkey(), 50_000); +/// builder.add_recipient(addr.script_pubkey(), Amount::from_sat(50_000)); /// } /// builder.finish()? /// }; @@ -274,7 +274,7 @@ impl<'a, Cs, Ctx> TxBuilder<'a, Cs, Ctx> { /// /// let builder = wallet /// .build_tx() - /// .add_recipient(to_address.script_pubkey(), 50_000) + /// .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000)) /// .policy_path(path, KeychainKind::External); /// /// # Ok::<(), anyhow::Error>(()) @@ -713,22 +713,26 @@ impl std::error::Error for AllowShrinkingError {} impl<'a, Cs: CoinSelectionAlgorithm> TxBuilder<'a, Cs, CreateTx> { /// Replace the recipients already added with a new list - pub fn set_recipients(&mut self, recipients: Vec<(ScriptBuf, u64)>) -> &mut Self { - self.params.recipients = recipients; + pub fn set_recipients(&mut self, recipients: Vec<(ScriptBuf, Amount)>) -> &mut Self { + self.params.recipients = recipients + .into_iter() + .map(|(script, amount)| (script, amount.to_sat())) + .collect(); self } - // TODO: (@leonardo) Should this expect/use `bitcoin::Amount` instead ? Would it be a huge breaking change ? /// Add a recipient to the internal list - pub fn add_recipient(&mut self, script_pubkey: ScriptBuf, amount: u64) -> &mut Self { - self.params.recipients.push((script_pubkey, amount)); + pub fn add_recipient(&mut self, script_pubkey: ScriptBuf, amount: Amount) -> &mut Self { + self.params + .recipients + .push((script_pubkey, amount.to_sat())); self } /// Add data as an output, using OP_RETURN pub fn add_data>(&mut self, data: &T) -> &mut Self { let script = ScriptBuf::new_op_return(data); - self.add_recipient(script, 0u64); + self.add_recipient(script, Amount::ZERO); self } diff --git a/crates/bdk/tests/psbt.rs b/crates/bdk/tests/psbt.rs index 1a81f6e80..820d2d1d1 100644 --- a/crates/bdk/tests/psbt.rs +++ b/crates/bdk/tests/psbt.rs @@ -1,4 +1,4 @@ -use bdk::bitcoin::{FeeRate, Psbt, TxIn}; +use bdk::bitcoin::{Amount, FeeRate, Psbt, TxIn}; use bdk::{psbt, KeychainKind, SignOptions}; use core::str::FromStr; mod common; @@ -14,7 +14,7 @@ fn test_psbt_malformed_psbt_input_legacy() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let send_to = wallet.peek_address(KeychainKind::External, 0); let mut builder = wallet.build_tx(); - builder.add_recipient(send_to.script_pubkey(), 10_000); + builder.add_recipient(send_to.script_pubkey(), Amount::from_sat(10_000)); let mut psbt = builder.finish().unwrap(); psbt.inputs.push(psbt_bip.inputs[0].clone()); let options = SignOptions { @@ -31,7 +31,7 @@ fn test_psbt_malformed_psbt_input_segwit() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let send_to = wallet.peek_address(KeychainKind::External, 0); let mut builder = wallet.build_tx(); - builder.add_recipient(send_to.script_pubkey(), 10_000); + builder.add_recipient(send_to.script_pubkey(), Amount::from_sat(10_000)); let mut psbt = builder.finish().unwrap(); psbt.inputs.push(psbt_bip.inputs[1].clone()); let options = SignOptions { @@ -47,7 +47,7 @@ fn test_psbt_malformed_tx_input() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let send_to = wallet.peek_address(KeychainKind::External, 0); let mut builder = wallet.build_tx(); - builder.add_recipient(send_to.script_pubkey(), 10_000); + builder.add_recipient(send_to.script_pubkey(), Amount::from_sat(10_000)); let mut psbt = builder.finish().unwrap(); psbt.unsigned_tx.input.push(TxIn::default()); let options = SignOptions { @@ -63,7 +63,7 @@ fn test_psbt_sign_with_finalized() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let send_to = wallet.peek_address(KeychainKind::External, 0); let mut builder = wallet.build_tx(); - builder.add_recipient(send_to.script_pubkey(), 10_000); + builder.add_recipient(send_to.script_pubkey(), Amount::from_sat(10_000)); let mut psbt = builder.finish().unwrap(); // add a finalized input diff --git a/crates/bdk/tests/wallet.rs b/crates/bdk/tests/wallet.rs index 0211e3ada..fe683d152 100644 --- a/crates/bdk/tests/wallet.rs +++ b/crates/bdk/tests/wallet.rs @@ -200,7 +200,7 @@ fn test_get_funded_wallet_balance() { // The funded wallet contains a tx with a 76_000 sats input and two outputs, one spending 25_000 // to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000 // sats are the transaction fee. - assert_eq!(wallet.get_balance().confirmed.to_sat(), 50_000); + assert_eq!(wallet.get_balance().confirmed, Amount::from_sat(50_000)); } #[test] @@ -347,7 +347,7 @@ fn test_create_tx_manually_selected_empty_utxos() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .manually_selected_only(); builder.finish().unwrap(); } @@ -358,7 +358,7 @@ fn test_create_tx_version_0() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .version(0); assert!(matches!(builder.finish(), Err(CreateTxError::Version0))); } @@ -369,7 +369,7 @@ fn test_create_tx_version_1_csv() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .version(1); assert!(matches!(builder.finish(), Err(CreateTxError::Version1Csv))); } @@ -380,7 +380,7 @@ fn test_create_tx_custom_version() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .version(42); let psbt = builder.finish().unwrap(); @@ -393,7 +393,7 @@ fn test_create_tx_default_locktime_is_last_sync_height() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 25_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let psbt = builder.finish().unwrap(); // Since we never synced the wallet we don't have a last_sync_height @@ -406,7 +406,7 @@ fn test_create_tx_fee_sniping_locktime_last_sync() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 25_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let psbt = builder.finish().unwrap(); @@ -422,7 +422,7 @@ fn test_create_tx_default_locktime_cltv() { let (mut wallet, _) = get_funded_wallet(get_test_single_sig_cltv()); let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 25_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let psbt = builder.finish().unwrap(); assert_eq!(psbt.unsigned_tx.lock_time.to_consensus_u32(), 100_000); @@ -434,7 +434,7 @@ fn test_create_tx_custom_locktime() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .current_height(630_001) .nlocktime(absolute::LockTime::from_height(630_000).unwrap()); let psbt = builder.finish().unwrap(); @@ -451,7 +451,7 @@ fn test_create_tx_custom_locktime_compatible_with_cltv() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .nlocktime(absolute::LockTime::from_height(630_000).unwrap()); let psbt = builder.finish().unwrap(); @@ -464,7 +464,7 @@ fn test_create_tx_custom_locktime_incompatible_with_cltv() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .nlocktime(absolute::LockTime::from_height(50000).unwrap()); assert!(matches!(builder.finish(), Err(CreateTxError::LockTime { requested, required }) @@ -476,7 +476,7 @@ fn test_create_tx_no_rbf_csv() { let (mut wallet, _) = get_funded_wallet(get_test_single_sig_csv()); let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 25_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let psbt = builder.finish().unwrap(); assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(6)); @@ -488,7 +488,7 @@ fn test_create_tx_with_default_rbf_csv() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .enable_rbf(); let psbt = builder.finish().unwrap(); // When CSV is enabled it takes precedence over the rbf value (unless forced by the user). @@ -502,7 +502,7 @@ fn test_create_tx_with_custom_rbf_csv() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .enable_rbf_with_sequence(Sequence(3)); assert!(matches!(builder.finish(), Err(CreateTxError::RbfSequenceCsv { rbf, csv }) @@ -514,7 +514,7 @@ fn test_create_tx_no_rbf_cltv() { let (mut wallet, _) = get_funded_wallet(get_test_single_sig_cltv()); let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 25_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let psbt = builder.finish().unwrap(); assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFE)); @@ -526,7 +526,7 @@ fn test_create_tx_invalid_rbf_sequence() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .enable_rbf_with_sequence(Sequence(0xFFFFFFFE)); assert!(matches!(builder.finish(), Err(CreateTxError::RbfSequence))); } @@ -537,7 +537,7 @@ fn test_create_tx_custom_rbf_sequence() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .enable_rbf_with_sequence(Sequence(0xDEADBEEF)); let psbt = builder.finish().unwrap(); @@ -549,7 +549,7 @@ fn test_create_tx_default_sequence() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 25_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let psbt = builder.finish().unwrap(); assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFE)); @@ -561,7 +561,7 @@ fn test_create_tx_change_policy_no_internal() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .do_not_spend_change(); assert!(matches!( builder.finish(), @@ -603,7 +603,7 @@ fn test_create_tx_drain_wallet_and_drain_to_and_with_recipient() { let drain_addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 20_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(20_000)) .drain_to(drain_addr.script_pubkey()) .drain_wallet(); let psbt = builder.finish().unwrap(); @@ -658,7 +658,7 @@ fn test_create_tx_default_fee_rate() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 25_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let psbt = builder.finish().unwrap(); let fee = check_fee!(wallet, psbt); @@ -671,7 +671,7 @@ fn test_create_tx_custom_fee_rate() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .fee_rate(FeeRate::from_sat_per_vb_unchecked(5)); let psbt = builder.finish().unwrap(); let fee = check_fee!(wallet, psbt); @@ -740,7 +740,7 @@ fn test_create_tx_add_change() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .ordering(TxOrdering::Untouched); let psbt = builder.finish().unwrap(); let fee = check_fee!(wallet, psbt); @@ -758,7 +758,7 @@ fn test_create_tx_skip_change_dust() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 49_800); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(49_800)); let psbt = builder.finish().unwrap(); let fee = check_fee!(wallet, psbt); @@ -787,8 +787,8 @@ fn test_create_tx_ordering_respected() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 30_000) - .add_recipient(addr.script_pubkey(), 10_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000)) + .add_recipient(addr.script_pubkey(), Amount::from_sat(10_000)) .ordering(bdk::wallet::tx_builder::TxOrdering::Bip69Lexicographic); let psbt = builder.finish().unwrap(); let fee = check_fee!(wallet, psbt); @@ -807,7 +807,7 @@ fn test_create_tx_default_sighash() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 30_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(30_000)); let psbt = builder.finish().unwrap(); assert_eq!(psbt.inputs[0].sighash_type, None); @@ -819,7 +819,7 @@ fn test_create_tx_custom_sighash() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 30_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000)) .sighash(EcdsaSighashType::Single.into()); let psbt = builder.finish().unwrap(); @@ -1018,7 +1018,7 @@ fn test_create_tx_add_utxo() { .assume_checked(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 30_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000)) .add_utxo(OutPoint { txid: small_output_tx.txid(), vout: 0, @@ -1069,7 +1069,7 @@ fn test_create_tx_manually_selected_insufficient() { .assume_checked(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 30_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000)) .add_utxo(OutPoint { txid: small_output_tx.txid(), vout: 0, @@ -1088,7 +1088,7 @@ fn test_create_tx_policy_path_required() { .unwrap() .assume_checked(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 30_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(10_000)); builder.finish().unwrap(); } @@ -1123,7 +1123,7 @@ fn test_create_tx_policy_path_no_csv() { .assume_checked(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 30_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000)) .policy_path(path, KeychainKind::External); let psbt = builder.finish().unwrap(); @@ -1144,7 +1144,7 @@ fn test_create_tx_policy_path_use_csv() { .assume_checked(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 30_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000)) .policy_path(path, KeychainKind::External); let psbt = builder.finish().unwrap(); @@ -1165,7 +1165,7 @@ fn test_create_tx_policy_path_ignored_subtree_with_csv() { .assume_checked(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 30_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000)) .policy_path(path, KeychainKind::External); let psbt = builder.finish().unwrap(); @@ -1181,7 +1181,7 @@ fn test_create_tx_global_xpubs_with_origin() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .add_global_xpubs(); let psbt = builder.finish().unwrap(); @@ -1215,7 +1215,7 @@ fn test_add_foreign_utxo() { let mut builder = wallet1.build_tx(); builder - .add_recipient(addr.script_pubkey(), 60_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(60_000)) .only_witness_utxo() .add_foreign_utxo(utxo.outpoint, psbt_input, foreign_utxo_satisfaction) .unwrap(); @@ -1291,7 +1291,7 @@ fn test_calculate_fee_with_missing_foreign_utxo() { let mut builder = wallet1.build_tx(); builder - .add_recipient(addr.script_pubkey(), 60_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(60_000)) .only_witness_utxo() .add_foreign_utxo(utxo.outpoint, psbt_input, foreign_utxo_satisfaction) .unwrap(); @@ -1375,7 +1375,7 @@ fn test_add_foreign_utxo_only_witness_utxo() { .unwrap(); let mut builder = wallet1.build_tx(); - builder.add_recipient(addr.script_pubkey(), 60_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(60_000)); { let mut builder = builder.clone(); @@ -1444,7 +1444,7 @@ fn test_create_tx_global_xpubs_origin_missing() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .add_global_xpubs(); builder.finish().unwrap(); } @@ -1458,7 +1458,7 @@ fn test_create_tx_global_xpubs_master_without_origin() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .add_global_xpubs(); let psbt = builder.finish().unwrap(); @@ -1478,7 +1478,7 @@ fn test_bump_fee_irreplaceable_tx() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 25_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); @@ -1495,7 +1495,7 @@ fn test_bump_fee_confirmed_tx() { let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 25_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); @@ -1520,7 +1520,7 @@ fn test_bump_fee_low_fee_rate() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .enable_rbf(); let psbt = builder.finish().unwrap(); let feerate = psbt.fee_rate().unwrap(); @@ -1554,7 +1554,7 @@ fn test_bump_fee_low_abs() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .enable_rbf(); let psbt = builder.finish().unwrap(); @@ -1577,7 +1577,7 @@ fn test_bump_fee_zero_abs() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .enable_rbf(); let psbt = builder.finish().unwrap(); @@ -1600,7 +1600,7 @@ fn test_bump_fee_reduce_change() { .assume_checked(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .enable_rbf(); let psbt = builder.finish().unwrap(); let original_sent_received = @@ -1932,7 +1932,7 @@ fn test_bump_fee_add_input() { .assume_checked(); let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection); builder - .add_recipient(addr.script_pubkey(), 45_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000)) .enable_rbf(); let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); @@ -1989,7 +1989,7 @@ fn test_bump_fee_absolute_add_input() { .assume_checked(); let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection); builder - .add_recipient(addr.script_pubkey(), 45_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000)) .enable_rbf(); let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); @@ -2117,7 +2117,7 @@ fn test_bump_fee_add_input_change_dust() { .assume_checked(); let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection); builder - .add_recipient(addr.script_pubkey(), 45_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000)) .enable_rbf(); let psbt = builder.finish().unwrap(); let original_sent_received = @@ -2194,7 +2194,7 @@ fn test_bump_fee_force_add_input() { .assume_checked(); let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection); builder - .add_recipient(addr.script_pubkey(), 45_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000)) .enable_rbf(); let psbt = builder.finish().unwrap(); let mut tx = psbt.extract_tx().expect("failed to extract tx"); @@ -2260,7 +2260,7 @@ fn test_bump_fee_absolute_force_add_input() { .assume_checked(); let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection); builder - .add_recipient(addr.script_pubkey(), 45_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000)) .enable_rbf(); let psbt = builder.finish().unwrap(); let mut tx = psbt.extract_tx().expect("failed to extract tx"); @@ -2410,7 +2410,7 @@ fn test_fee_amount_negative_drain_val() { let mut builder = wallet.build_tx(); builder - .add_recipient(send_to.script_pubkey(), 8630) + .add_recipient(send_to.script_pubkey(), Amount::from_sat(8630)) .add_utxo(incoming_op) .unwrap() .enable_rbf() @@ -2524,7 +2524,7 @@ fn test_include_output_redeem_witness_script() { .assume_checked(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 45_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000)) .include_output_redeem_witness_script(); let psbt = builder.finish().unwrap(); @@ -2543,7 +2543,7 @@ fn test_signing_only_one_of_multiple_inputs() { .assume_checked(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 45_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000)) .include_output_redeem_witness_script(); let mut psbt = builder.finish().unwrap(); @@ -2888,7 +2888,7 @@ fn test_sending_to_bip350_bech32m_address() { .unwrap() .assume_checked(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 45_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(45_000)); builder.finish().unwrap(); } @@ -3021,7 +3021,7 @@ fn test_taproot_psbt_populate_tap_key_origins() { let addr = wallet.reveal_next_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 25_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let psbt = builder.finish().unwrap(); assert_eq!( @@ -3061,7 +3061,7 @@ fn test_taproot_psbt_populate_tap_key_origins_repeated_key() { let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 25_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)) .policy_path(path, KeychainKind::External); let psbt = builder.finish().unwrap(); @@ -3245,7 +3245,7 @@ fn test_taproot_foreign_utxo() { let mut builder = wallet1.build_tx(); builder - .add_recipient(addr.script_pubkey(), 60_000) + .add_recipient(addr.script_pubkey(), Amount::from_sat(60_000)) .add_foreign_utxo(utxo.outpoint, psbt_input, foreign_utxo_satisfaction) .unwrap(); let psbt = builder.finish().unwrap(); @@ -3273,7 +3273,7 @@ fn test_spend_from_wallet(mut wallet: Wallet) { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 25_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let mut psbt = builder.finish().unwrap(); assert!( @@ -3297,7 +3297,7 @@ fn test_taproot_no_key_spend() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 25_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let mut psbt = builder.finish().unwrap(); assert!( @@ -3332,7 +3332,7 @@ fn test_taproot_script_spend_sign_all_leaves() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 25_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let mut psbt = builder.finish().unwrap(); assert!( @@ -3363,7 +3363,7 @@ fn test_taproot_script_spend_sign_include_some_leaves() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 25_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let mut psbt = builder.finish().unwrap(); let mut script_leaves: Vec<_> = psbt.inputs[0] .tap_scripts @@ -3403,7 +3403,7 @@ fn test_taproot_script_spend_sign_exclude_some_leaves() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 25_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let mut psbt = builder.finish().unwrap(); let mut script_leaves: Vec<_> = psbt.inputs[0] .tap_scripts @@ -3441,7 +3441,7 @@ fn test_taproot_script_spend_sign_no_leaves() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 25_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let mut psbt = builder.finish().unwrap(); wallet @@ -3464,7 +3464,7 @@ fn test_taproot_sign_derive_index_from_psbt() { let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 25_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000)); let mut psbt = builder.finish().unwrap(); // re-create the wallet with an empty db @@ -3624,7 +3624,7 @@ fn test_spend_coinbase() { .assume_checked(); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), balance.immature.to_sat() / 2) + .add_recipient(addr.script_pubkey(), balance.immature / 2) .current_height(confirmation_height); assert!(matches!( builder.finish(), @@ -3639,7 +3639,7 @@ fn test_spend_coinbase() { // Still unspendable... let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), balance.immature.to_sat() / 2) + .add_recipient(addr.script_pubkey(), balance.immature / 2) .current_height(not_yet_mature_time); assert_matches!( builder.finish(), @@ -3669,7 +3669,7 @@ fn test_spend_coinbase() { ); let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), balance.confirmed.to_sat() / 2) + .add_recipient(addr.script_pubkey(), balance.confirmed / 2) .current_height(maturity_time); builder.finish().unwrap(); } @@ -3682,7 +3682,7 @@ fn test_allow_dust_limit() { let mut builder = wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 0); + builder.add_recipient(addr.script_pubkey(), Amount::ZERO); assert_matches!( builder.finish(), @@ -3693,7 +3693,7 @@ fn test_allow_dust_limit() { builder .allow_dust(true) - .add_recipient(addr.script_pubkey(), 0); + .add_recipient(addr.script_pubkey(), Amount::ZERO); assert!(builder.finish().is_ok()); } @@ -3821,7 +3821,7 @@ fn test_tx_cancellation() { .unwrap() .assume_checked(); let mut builder = $wallet.build_tx(); - builder.add_recipient(addr.script_pubkey(), 10_000); + builder.add_recipient(addr.script_pubkey(), Amount::from_sat(10_000)); let psbt = builder.finish().unwrap(); diff --git a/crates/chain/src/tx_graph.rs b/crates/chain/src/tx_graph.rs index 4acba7aac..cf5148554 100644 --- a/crates/chain/src/tx_graph.rs +++ b/crates/chain/src/tx_graph.rs @@ -1163,7 +1163,6 @@ impl TxGraph { for res in self.try_filter_chain_unspents(chain, chain_tip, outpoints) { let (spk_i, txout) = res?; - // TODO: (@leonardo) Should these operations use `bitcoin::Amount::checked_add()` instead ? match &txout.chain_position { ChainPosition::Confirmed(_) => { if txout.is_confirmed_and_spendable(chain_tip.height) { diff --git a/example-crates/wallet_electrum/src/main.rs b/example-crates/wallet_electrum/src/main.rs index c71592f59..e2c5fd9fd 100644 --- a/example-crates/wallet_electrum/src/main.rs +++ b/example-crates/wallet_electrum/src/main.rs @@ -1,5 +1,5 @@ const DB_MAGIC: &str = "bdk_wallet_electrum_example"; -const SEND_AMOUNT: u64 = 5000; +const SEND_AMOUNT: Amount = Amount::from_sat(5000); const STOP_GAP: usize = 50; const BATCH_SIZE: usize = 5; @@ -81,8 +81,7 @@ fn main() -> Result<(), anyhow::Error> { let balance = wallet.get_balance(); println!("Wallet balance after syncing: {} sats", balance.total()); - // TODO: (@leonardo) Should we format here, or update on constant and TxBuilder::add_recipient() instead ? - if balance.total() < Amount::from_sat(SEND_AMOUNT) { + if balance.total() < SEND_AMOUNT { println!( "Please send at least {} sats to the receiving address", SEND_AMOUNT diff --git a/example-crates/wallet_esplora_async/src/main.rs b/example-crates/wallet_esplora_async/src/main.rs index 42dd6ae37..7664ec32e 100644 --- a/example-crates/wallet_esplora_async/src/main.rs +++ b/example-crates/wallet_esplora_async/src/main.rs @@ -8,7 +8,7 @@ use bdk_esplora::{esplora_client, EsploraAsyncExt}; use bdk_file_store::Store; const DB_MAGIC: &str = "bdk_wallet_esplora_async_example"; -const SEND_AMOUNT: u64 = 5000; +const SEND_AMOUNT: Amount = Amount::from_sat(5000); const STOP_GAP: usize = 50; const PARALLEL_REQUESTS: usize = 5; @@ -81,8 +81,7 @@ async fn main() -> Result<(), anyhow::Error> { let balance = wallet.get_balance(); println!("Wallet balance after syncing: {} sats", balance.total()); - // TODO: (@leonardo) Should we format here, or update on constant and TxBuilder::add_recipient() instead ? - if balance.total() < Amount::from_sat(SEND_AMOUNT) { + if balance.total() < SEND_AMOUNT { println!( "Please send at least {} sats to the receiving address", SEND_AMOUNT diff --git a/example-crates/wallet_esplora_blocking/src/main.rs b/example-crates/wallet_esplora_blocking/src/main.rs index 95acf5de5..4d713156a 100644 --- a/example-crates/wallet_esplora_blocking/src/main.rs +++ b/example-crates/wallet_esplora_blocking/src/main.rs @@ -1,5 +1,5 @@ const DB_MAGIC: &str = "bdk_wallet_esplora_example"; -const SEND_AMOUNT: u64 = 1000; +const SEND_AMOUNT: Amount = Amount::from_sat(1000); const STOP_GAP: usize = 5; const PARALLEL_REQUESTS: usize = 1; @@ -57,8 +57,7 @@ fn main() -> Result<(), anyhow::Error> { let balance = wallet.get_balance(); println!("Wallet balance after syncing: {} sats", balance.total()); - // TODO: (@leonardo) Should we format here, or update on constant and TxBuilder::add_recipient() instead ? - if balance.total() < Amount::from_sat(SEND_AMOUNT) { + if balance.total() < SEND_AMOUNT { println!( "Please send at least {} sats to the receiving address", SEND_AMOUNT