Introduce WalletBuilder for creating test instances of wallet

This commit is contained in:
Thomas Eizinger 2021-08-12 18:08:06 +10:00
parent 148fdb8d0a
commit e4b5e28a93
No known key found for this signature in database
GPG Key ID: 651AC83A6C6C8B96
3 changed files with 73 additions and 33 deletions

View File

@ -21,6 +21,9 @@ pub use ecdsa_fun::fun::Scalar;
pub use ecdsa_fun::Signature; pub use ecdsa_fun::Signature;
pub use wallet::Wallet; pub use wallet::Wallet;
#[cfg(test)]
pub use wallet::WalletBuilder;
use crate::bitcoin::wallet::ScriptStatus; use crate::bitcoin::wallet::ScriptStatus;
use ::bitcoin::hashes::hex::ToHex; use ::bitcoin::hashes::hex::ToHex;
use ::bitcoin::hashes::Hash; use ::bitcoin::hashes::Hash;
@ -317,8 +320,8 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn calculate_transaction_weights() { async fn calculate_transaction_weights() {
let alice_wallet = Wallet::new_funded_default_fees(Amount::ONE_BTC.as_sat()); let alice_wallet = WalletBuilder::new(Amount::ONE_BTC.as_sat()).build();
let bob_wallet = Wallet::new_funded_default_fees(Amount::ONE_BTC.as_sat()); let bob_wallet = WalletBuilder::new(Amount::ONE_BTC.as_sat()).build();
let spending_fee = Amount::from_sat(1_000); let spending_fee = Amount::from_sat(1_000);
let btc_amount = Amount::from_sat(500_000); let btc_amount = Amount::from_sat(500_000);
let xmr_amount = crate::monero::Amount::from_piconero(10000); let xmr_amount = crate::monero::Amount::from_piconero(10000);

View File

@ -183,11 +183,12 @@ impl Watchable for TxLock {
mod tests { mod tests {
use super::*; use super::*;
use crate::bitcoin::wallet::StaticFeeRate; use crate::bitcoin::wallet::StaticFeeRate;
use crate::bitcoin::WalletBuilder;
#[tokio::test] #[tokio::test]
async fn given_bob_sends_good_psbt_when_reconstructing_then_succeeeds() { async fn given_bob_sends_good_psbt_when_reconstructing_then_succeeeds() {
let (A, B) = alice_and_bob(); let (A, B) = alice_and_bob();
let wallet = Wallet::new_funded_default_fees(50000); let wallet = WalletBuilder::new(50_000).build();
let agreed_amount = Amount::from_sat(10000); let agreed_amount = Amount::from_sat(10000);
let psbt = bob_make_psbt(A, B, &wallet, agreed_amount).await; let psbt = bob_make_psbt(A, B, &wallet, agreed_amount).await;
@ -201,7 +202,7 @@ mod tests {
let (A, B) = alice_and_bob(); let (A, B) = alice_and_bob();
let fees = 610; let fees = 610;
let agreed_amount = Amount::from_sat(10000); let agreed_amount = Amount::from_sat(10000);
let wallet = Wallet::new_funded_default_fees(agreed_amount.as_sat() + fees); let wallet = WalletBuilder::new(agreed_amount.as_sat() + fees).build();
let psbt = bob_make_psbt(A, B, &wallet, agreed_amount).await; let psbt = bob_make_psbt(A, B, &wallet, agreed_amount).await;
assert_eq!( assert_eq!(
@ -217,7 +218,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn given_bob_is_sending_less_than_agreed_when_reconstructing_txlock_then_fails() { async fn given_bob_is_sending_less_than_agreed_when_reconstructing_txlock_then_fails() {
let (A, B) = alice_and_bob(); let (A, B) = alice_and_bob();
let wallet = Wallet::new_funded_default_fees(50000); let wallet = WalletBuilder::new(50_000).build();
let agreed_amount = Amount::from_sat(10000); let agreed_amount = Amount::from_sat(10000);
let bad_amount = Amount::from_sat(5000); let bad_amount = Amount::from_sat(5000);
@ -230,7 +231,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn given_bob_is_sending_to_a_bad_output_reconstructing_txlock_then_fails() { async fn given_bob_is_sending_to_a_bad_output_reconstructing_txlock_then_fails() {
let (A, B) = alice_and_bob(); let (A, B) = alice_and_bob();
let wallet = Wallet::new_funded_default_fees(50000); let wallet = WalletBuilder::new(50_000).build();
let agreed_amount = Amount::from_sat(10000); let agreed_amount = Amount::from_sat(10000);
let E = eve(); let E = eve();

View File

@ -547,48 +547,84 @@ impl EstimateFeeRate for StaticFeeRate {
} }
#[cfg(test)] #[cfg(test)]
impl Wallet<(), bdk::database::MemoryDatabase, StaticFeeRate> { pub struct WalletBuilder {
utxo_amount: u64,
sats_per_vb: f32,
min_relay_fee_sats: u64,
key: bitcoin::util::bip32::ExtendedPrivKey,
num_utxos: u8,
}
#[cfg(test)]
impl WalletBuilder {
/// Creates a new, funded wallet with sane default fees. /// Creates a new, funded wallet with sane default fees.
/// ///
/// Unless you are testing things related to fees, this is likely what you /// Unless you are testing things related to fees, this is likely what you
/// want. /// want.
pub fn new_funded_default_fees(amount: u64) -> Self { pub fn new(amount: u64) -> Self {
Self::new_funded(amount, 1.0, 1000) WalletBuilder {
utxo_amount: amount,
sats_per_vb: 1.0,
min_relay_fee_sats: 1000,
key: "tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m".parse().unwrap(),
num_utxos: 1,
}
} }
/// Creates a new, funded wallet that doesn't pay any fees. pub fn with_zero_fees(self) -> Self {
/// Self {
/// This will create invalid transactions but can be useful if you want full sats_per_vb: 0.0,
/// control over the output amounts. min_relay_fee_sats: 0,
pub fn new_funded_zero_fees(amount: u64) -> Self { ..self
Self::new_funded(amount, 0.0, 0) }
} }
/// Creates a new, funded wallet to be used within tests. pub fn with_fees(self, sats_per_vb: f32, min_relay_fee_sats: u64) -> Self {
pub fn new_funded(amount: u64, sats_per_vb: f32, min_relay_fee_sats: u64) -> Self { Self {
sats_per_vb,
min_relay_fee_sats,
..self
}
}
pub fn with_key(self, key: bitcoin::util::bip32::ExtendedPrivKey) -> Self {
Self { key, ..self }
}
pub fn with_num_utxos(self, number: u8) -> Self {
Self {
num_utxos: number,
..self
}
}
pub fn build(self) -> Wallet<(), bdk::database::MemoryDatabase, StaticFeeRate> {
use bdk::database::MemoryDatabase; use bdk::database::MemoryDatabase;
use bdk::{LocalUtxo, TransactionDetails}; use bdk::{LocalUtxo, TransactionDetails};
use bitcoin::OutPoint; use bitcoin::OutPoint;
use testutils::testutils; use testutils::testutils;
let descriptors = testutils!(@descriptors ("wpkh(tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m/*)")); let descriptors = testutils!(@descriptors (&format!("wpkh({}/*)", self.key)));
let mut database = MemoryDatabase::new(); let mut database = MemoryDatabase::new();
bdk::populate_test_db!(
&mut database, for index in 0..self.num_utxos {
testutils! { bdk::populate_test_db!(
@tx ( (@external descriptors, 0) => amount ) (@confirmations 1) &mut database,
}, testutils! {
Some(100) @tx ( (@external descriptors, index as u32) => self.utxo_amount ) (@confirmations 1)
); },
Some(100)
);
}
let wallet = let wallet =
bdk::Wallet::new_offline(&descriptors.0, None, Network::Regtest, database).unwrap(); bdk::Wallet::new_offline(&descriptors.0, None, Network::Regtest, database).unwrap();
Self { Wallet {
client: Arc::new(Mutex::new(StaticFeeRate { client: Arc::new(Mutex::new(StaticFeeRate {
fee_rate: FeeRate::from_sat_per_vb(sats_per_vb), fee_rate: FeeRate::from_sat_per_vb(self.sats_per_vb),
min_relay_fee: bitcoin::Amount::from_sat(min_relay_fee_sats), min_relay_fee: bitcoin::Amount::from_sat(self.min_relay_fee_sats),
})), })),
wallet: Arc::new(Mutex::new(wallet)), wallet: Arc::new(Mutex::new(wallet)),
finality_confirmations: 1, finality_confirmations: 1,
@ -1047,7 +1083,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn given_no_balance_returns_amount_0() { async fn given_no_balance_returns_amount_0() {
let wallet = Wallet::new_funded(0, 1.0, 1); let wallet = WalletBuilder::new(0).with_fees(1.0, 1).build();
let amount = wallet.max_giveable(TxLock::script_size()).await.unwrap(); let amount = wallet.max_giveable(TxLock::script_size()).await.unwrap();
assert_eq!(amount, Amount::ZERO); assert_eq!(amount, Amount::ZERO);
@ -1055,7 +1091,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn given_balance_below_min_relay_fee_returns_amount_0() { async fn given_balance_below_min_relay_fee_returns_amount_0() {
let wallet = Wallet::new_funded(1000, 1.0, 1001); let wallet = WalletBuilder::new(1000).with_fees(1.0, 1001).build();
let amount = wallet.max_giveable(TxLock::script_size()).await.unwrap(); let amount = wallet.max_giveable(TxLock::script_size()).await.unwrap();
assert_eq!(amount, Amount::ZERO); assert_eq!(amount, Amount::ZERO);
@ -1063,7 +1099,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn given_balance_above_relay_fee_returns_amount_greater_0() { async fn given_balance_above_relay_fee_returns_amount_greater_0() {
let wallet = Wallet::new_funded_default_fees(10_000); let wallet = WalletBuilder::new(10_000).build();
let amount = wallet.max_giveable(TxLock::script_size()).await.unwrap(); let amount = wallet.max_giveable(TxLock::script_size()).await.unwrap();
assert!(amount.as_sat() > 0); assert!(amount.as_sat() > 0);
@ -1083,7 +1119,7 @@ mod tests {
let balance = 2000; let balance = 2000;
// We don't care about fees in this test, thus use a zero fee rate // We don't care about fees in this test, thus use a zero fee rate
let wallet = Wallet::new_funded_zero_fees(balance); let wallet = WalletBuilder::new(balance).with_zero_fees().build();
// sorting is only relevant for amounts that have a change output // sorting is only relevant for amounts that have a change output
// if the change output is below dust it will be dropped by the BDK // if the change output is below dust it will be dropped by the BDK
@ -1108,7 +1144,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn can_override_change_address() { async fn can_override_change_address() {
let wallet = Wallet::new_funded_default_fees(50_000); let wallet = WalletBuilder::new(50_000).build();
let custom_change = "bcrt1q08pfqpsyrt7acllzyjm8q5qsz5capvyahm49rw" let custom_change = "bcrt1q08pfqpsyrt7acllzyjm8q5qsz5capvyahm49rw"
.parse::<Address>() .parse::<Address>()
.unwrap(); .unwrap();