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;
/// [`insert_txout`]: Self::insert_txout
pub fn calculate_fee_rate(&self, tx: &Transaction) -> Result<FeeRate, CalculateFeeError> {
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).
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));
-use bdk::bitcoin::{Amount, FeeRate, Psbt, TxIn};
+use bdk::bitcoin::{FeeRate, Psbt, TxIn};
use bdk::{psbt, KeychainKind, SignOptions};
use core::str::FromStr;
mod common;
// 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;
// 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]
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
}
);
.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(),
// 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(),
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();
}
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",
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: {}",
#[cfg(feature = "miniscript")]
mod txout_index;
+use bitcoin::Amount;
#[cfg(feature = "miniscript")]
pub use txout_index::*;
)]
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 {
///
/// 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
}
}
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
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,
outpoints: impl IntoIterator<Item = (OI, OutPoint)>,
mut trust_predicate: impl FnMut(&OI, &Script) -> bool,
) -> Result<Balance, C::Error> {
- 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;
}
}
}
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
}
);
}
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
}
);
}
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
}
);
}
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
}
);
}
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
}
);
}
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)]
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
]),
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 {
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 {
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 {
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),
},
},
];
assert_eq!(
get_balance(&recv_chain, &recv_graph)?,
Balance {
- confirmed: SEND_AMOUNT.to_sat(),
+ confirmed: SEND_AMOUNT,
..Balance::default()
},
);
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",
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: {}",
let chain = &*chain.lock().unwrap();
fn print_balances<'a>(
title_str: &'a str,
- items: impl IntoIterator<Item = (&'a str, u64)>,
+ items: impl IntoIterator<Item = (&'a str, Amount)>,
) {
println!("{}:", title_str);
for (name, amount) in items.into_iter() {
- println!(" {:<10} {:>12} sats", name, amount)
+ println!(" {:<10} {:>12} sats", name, amount.to_sat())
}
}
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};
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
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};
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
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};
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