From 410a51355bb47dbf3401cbe6f19546255c6721e0 Mon Sep 17 00:00:00 2001 From: LLFourn Date: Thu, 27 Jan 2022 16:52:53 +1100 Subject: [PATCH] Add SyncOptions as the second argument to Wallet::sync The current options are awkward and it would be good if we could introduce more in the future without breaking changes. --- CHANGELOG.md | 13 +++- README.md | 17 +++-- examples/compact_filters_balance.rs | 3 +- src/blockchain/any.rs | 8 +-- src/blockchain/compact_filters/mod.rs | 4 +- src/blockchain/electrum.rs | 4 +- src/blockchain/esplora/reqwest.rs | 4 +- src/blockchain/esplora/ureq.rs | 4 +- src/blockchain/mod.rs | 22 +++--- src/blockchain/rpc.rs | 8 +-- src/lib.rs | 13 ++-- src/testutils/blockchain_tests.rs | 96 +++++++++++++-------------- src/wallet/mod.rs | 38 ++++++----- 13 files changed, 124 insertions(+), 110 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73a1bfe0..319fb628 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed default verification from `wallet::sync`. sync-time verification is added in `script_sync` and is activated by `verify` feature flag. - `verify` flag removed from `TransactionDetails`. -- Removed Blockchain from Wallet. -- Removed `Wallet::broadcast` (just use blockchain.broadcast) + +### Sync API change + +To decouple the `Wallet` from the `Blockchain` we've made major changes: + +- Removed `Blockchain` from Wallet. +- Removed `Wallet::broadcast` (just use `Blockchain::broadcast`) - Depreciated `Wallet::new_offline` (all wallets are offline now) -- Changed `Wallet::sync` to take a blockchain argument. +- Changed `Wallet::sync` to take a `Blockchain`. +- Stop making a request for the block height when calling `Wallet:new`. +- Added `SyncOptions` to capture extra (future) arguments to `Wallet::sync`. ## [v0.16.1] - [v0.16.0] diff --git a/README.md b/README.md index af449ac0..8f2c7e74 100644 --- a/README.md +++ b/README.md @@ -41,21 +41,21 @@ The `bdk` library aims to be the core building block for Bitcoin wallets of any ```rust,no_run use bdk::Wallet; use bdk::database::MemoryDatabase; -use bdk::blockchain::{noop_progress, ElectrumBlockchain}; +use bdk::blockchain::ElectrumBlockchain; +use bdk::SyncOptions; use bdk::electrum_client::Client; fn main() -> Result<(), bdk::Error> { - let client = Client::new("ssl://electrum.blockstream.info:60002")?; + let blockchain = ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?); let wallet = Wallet::new( "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"), bitcoin::Network::Testnet, MemoryDatabase::default(), - ElectrumBlockchain::from(client) )?; - wallet.sync(noop_progress(), None)?; + wallet.sync(&blockchain, SyncOptions::default())?; println!("Descriptor balance: {} SAT", wallet.get_balance()?); @@ -88,9 +88,9 @@ fn main() -> Result<(), bdk::Error> { ### Create a transaction ```rust,no_run -use bdk::{FeeRate, Wallet}; +use bdk::{FeeRate, Wallet, SyncOptions}; use bdk::database::MemoryDatabase; -use bdk::blockchain::{noop_progress, ElectrumBlockchain}; +use bdk::blockchain::ElectrumBlockchain; use bdk::electrum_client::Client; use bdk::wallet::AddressIndex::New; @@ -98,16 +98,15 @@ use bdk::wallet::AddressIndex::New; use bitcoin::consensus::serialize; fn main() -> Result<(), bdk::Error> { - let client = Client::new("ssl://electrum.blockstream.info:60002")?; + let blockchain = ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?); let wallet = Wallet::new( "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"), bitcoin::Network::Testnet, MemoryDatabase::default(), - ElectrumBlockchain::from(client) )?; - wallet.sync(noop_progress(), None)?; + wallet.sync(&blockchain, SyncOptions::default())?; let send_to = wallet.get_address(New)?; let (psbt, details) = { diff --git a/examples/compact_filters_balance.rs b/examples/compact_filters_balance.rs index 2154de4f..ce875b4d 100644 --- a/examples/compact_filters_balance.rs +++ b/examples/compact_filters_balance.rs @@ -10,7 +10,6 @@ // licenses. use bdk::blockchain::compact_filters::*; -use bdk::blockchain::noop_progress; use bdk::database::MemoryDatabase; use bdk::*; use bitcoin::*; @@ -36,7 +35,7 @@ fn main() -> Result<(), CompactFiltersError> { let database = MemoryDatabase::default(); let wallet = Arc::new(Wallet::new(descriptor, None, Network::Testnet, database).unwrap()); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); info!("balance: {}", wallet.get_balance()?); Ok(()) } diff --git a/src/blockchain/any.rs b/src/blockchain/any.rs index 4ff9459f..ef4abffa 100644 --- a/src/blockchain/any.rs +++ b/src/blockchain/any.rs @@ -110,10 +110,10 @@ impl GetHeight for AnyBlockchain { #[maybe_async] impl WalletSync for AnyBlockchain { - fn wallet_sync( + fn wallet_sync( &self, database: &mut D, - progress_update: P, + progress_update: Box, ) -> Result<(), Error> { maybe_await!(impl_inner_method!( self, @@ -123,10 +123,10 @@ impl WalletSync for AnyBlockchain { )) } - fn wallet_setup( + fn wallet_setup( &self, database: &mut D, - progress_update: P, + progress_update: Box, ) -> Result<(), Error> { maybe_await!(impl_inner_method!( self, diff --git a/src/blockchain/compact_filters/mod.rs b/src/blockchain/compact_filters/mod.rs index 1fd99fc3..d0b96421 100644 --- a/src/blockchain/compact_filters/mod.rs +++ b/src/blockchain/compact_filters/mod.rs @@ -251,10 +251,10 @@ impl GetHeight for CompactFiltersBlockchain { impl WalletSync for CompactFiltersBlockchain { #[allow(clippy::mutex_atomic)] // Mutex is easier to understand than a CAS loop. - fn wallet_setup( + fn wallet_setup( &self, database: &mut D, - progress_update: P, + progress_update: Box, ) -> Result<(), Error> { let first_peer = &self.peers[0]; diff --git a/src/blockchain/electrum.rs b/src/blockchain/electrum.rs index 40efdd55..b10b90d5 100644 --- a/src/blockchain/electrum.rs +++ b/src/blockchain/electrum.rs @@ -95,10 +95,10 @@ impl GetHeight for ElectrumBlockchain { } impl WalletSync for ElectrumBlockchain { - fn wallet_setup( + fn wallet_setup( &self, database: &mut D, - _progress_update: P, + _progress_update: Box, ) -> Result<(), Error> { let mut request = script_sync::start(database, self.stop_gap)?; let mut block_times = HashMap::::new(); diff --git a/src/blockchain/esplora/reqwest.rs b/src/blockchain/esplora/reqwest.rs index d64bf563..0a9ed4dd 100644 --- a/src/blockchain/esplora/reqwest.rs +++ b/src/blockchain/esplora/reqwest.rs @@ -114,10 +114,10 @@ impl GetHeight for EsploraBlockchain { #[maybe_async] impl WalletSync for EsploraBlockchain { - fn wallet_setup( + fn wallet_setup( &self, database: &mut D, - _progress_update: P, + _progress_update: Box, ) -> Result<(), Error> { use crate::blockchain::script_sync::Request; let mut request = script_sync::start(database, self.stop_gap)?; diff --git a/src/blockchain/esplora/ureq.rs b/src/blockchain/esplora/ureq.rs index ad0dd773..3e240f43 100644 --- a/src/blockchain/esplora/ureq.rs +++ b/src/blockchain/esplora/ureq.rs @@ -109,10 +109,10 @@ impl GetHeight for EsploraBlockchain { } impl WalletSync for EsploraBlockchain { - fn wallet_setup( + fn wallet_setup( &self, database: &mut D, - _progress_update: P, + _progress_update: Box, ) -> Result<(), Error> { use crate::blockchain::script_sync::Request; let mut request = script_sync::start(database, self.stop_gap)?; diff --git a/src/blockchain/mod.rs b/src/blockchain/mod.rs index 8f3e2523..5962fca2 100644 --- a/src/blockchain/mod.rs +++ b/src/blockchain/mod.rs @@ -118,10 +118,10 @@ pub trait WalletSync { /// For types that do not have that distinction, only this method can be implemented, since /// [`WalletSync::wallet_sync`] defaults to calling this internally if not overridden. /// Populate the internal database with transactions and UTXOs - fn wallet_setup( + fn wallet_setup( &self, database: &mut D, - progress_update: P, + progress_update: Box, ) -> Result<(), Error>; /// If not overridden, it defaults to calling [`Self::wallet_setup`] internally. @@ -141,10 +141,10 @@ pub trait WalletSync { /// [`BatchOperations::set_tx`]: crate::database::BatchOperations::set_tx /// [`BatchOperations::set_utxo`]: crate::database::BatchOperations::set_utxo /// [`BatchOperations::del_utxo`]: crate::database::BatchOperations::del_utxo - fn wallet_sync( + fn wallet_sync( &self, database: &mut D, - progress_update: P, + progress_update: Box, ) -> Result<(), Error> { maybe_await!(self.wallet_setup(database, progress_update)) } @@ -164,7 +164,7 @@ pub type ProgressData = (f32, Option); /// Trait for types that can receive and process progress updates during [`WalletSync::wallet_sync`] and /// [`WalletSync::wallet_setup`] -pub trait Progress: Send + 'static { +pub trait Progress: Send + 'static + core::fmt::Debug { /// Send a new progress update /// /// The `progress` value should be in the range 0.0 - 100.0, and the `message` value is an @@ -189,7 +189,7 @@ impl Progress for Sender { } /// Type that implements [`Progress`] and drops every update received -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default, Debug)] pub struct NoopProgress; /// Create a new instance of [`NoopProgress`] @@ -204,7 +204,7 @@ impl Progress for NoopProgress { } /// Type that implements [`Progress`] and logs at level `INFO` every update received -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default, Debug)] pub struct LogProgress; /// Create a new instance of [`LogProgress`] @@ -251,18 +251,18 @@ impl GetHeight for Arc { #[maybe_async] impl WalletSync for Arc { - fn wallet_setup( + fn wallet_setup( &self, database: &mut D, - progress_update: P, + progress_update: Box, ) -> Result<(), Error> { maybe_await!(self.deref().wallet_setup(database, progress_update)) } - fn wallet_sync( + fn wallet_sync( &self, database: &mut D, - progress_update: P, + progress_update: Box, ) -> Result<(), Error> { maybe_await!(self.deref().wallet_sync(database, progress_update)) } diff --git a/src/blockchain/rpc.rs b/src/blockchain/rpc.rs index 403f0bbd..b96af993 100644 --- a/src/blockchain/rpc.rs +++ b/src/blockchain/rpc.rs @@ -168,10 +168,10 @@ impl GetHeight for RpcBlockchain { } impl WalletSync for RpcBlockchain { - fn wallet_setup( + fn wallet_setup( &self, database: &mut D, - progress_update: P, + progress_update: Box, ) -> Result<(), Error> { let mut scripts_pubkeys = database.iter_script_pubkeys(Some(KeychainKind::External))?; scripts_pubkeys.extend(database.iter_script_pubkeys(Some(KeychainKind::Internal))?); @@ -219,10 +219,10 @@ impl WalletSync for RpcBlockchain { self.wallet_sync(database, progress_update) } - fn wallet_sync( + fn wallet_sync( &self, db: &mut D, - _progress_update: P, + _progress_update: Box, ) -> Result<(), Error> { let mut indexes = HashMap::new(); for keykind in &[KeychainKind::External, KeychainKind::Internal] { diff --git a/src/lib.rs b/src/lib.rs index b998d36a..4b8a2228 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,9 +53,9 @@ ### Example ```no_run -use bdk::Wallet; +use bdk::{Wallet, SyncOptions}; use bdk::database::MemoryDatabase; -use bdk::blockchain::{noop_progress, ElectrumBlockchain}; +use bdk::blockchain::ElectrumBlockchain; use bdk::electrum_client::Client; fn main() -> Result<(), bdk::Error> { @@ -68,7 +68,7 @@ fn main() -> Result<(), bdk::Error> { MemoryDatabase::default(), )?; - wallet.sync(&blockchain, noop_progress(), None)?; + wallet.sync(&blockchain, SyncOptions::default())?; println!("Descriptor balance: {} SAT", wallet.get_balance()?); @@ -108,9 +108,9 @@ fn main() -> Result<(), bdk::Error> { ### Example ```no_run -use bdk::{FeeRate, Wallet}; +use bdk::{FeeRate, Wallet, SyncOptions}; use bdk::database::MemoryDatabase; -use bdk::blockchain::{noop_progress, ElectrumBlockchain}; +use bdk::blockchain::ElectrumBlockchain; use bdk::electrum_client::Client; use bitcoin::consensus::serialize; @@ -126,7 +126,7 @@ fn main() -> Result<(), bdk::Error> { )?; let blockchain = ElectrumBlockchain::from(client); - wallet.sync(&blockchain, noop_progress(), None)?; + wallet.sync(&blockchain, SyncOptions::default())?; let send_to = wallet.get_address(New)?; let (psbt, details) = { @@ -272,6 +272,7 @@ pub use wallet::address_validator; pub use wallet::signer; pub use wallet::signer::SignOptions; pub use wallet::tx_builder::TxBuilder; +pub use wallet::SyncOptions; pub use wallet::Wallet; /// Get the version of BDK at runtime diff --git a/src/testutils/blockchain_tests.rs b/src/testutils/blockchain_tests.rs index cf6be5a7..68858a84 100644 --- a/src/testutils/blockchain_tests.rs +++ b/src/testutils/blockchain_tests.rs @@ -361,10 +361,10 @@ macro_rules! bdk_blockchain_tests { mod bdk_blockchain_tests { use $crate::bitcoin::{Transaction, Network}; use $crate::testutils::blockchain_tests::TestClient; - use $crate::blockchain::{Blockchain, noop_progress}; + use $crate::blockchain::Blockchain; use $crate::database::MemoryDatabase; use $crate::types::KeychainKind; - use $crate::{Wallet, FeeRate}; + use $crate::{Wallet, FeeRate, SyncOptions}; use $crate::testutils; use super::*; @@ -392,7 +392,7 @@ macro_rules! bdk_blockchain_tests { // rpc need to call import_multi before receiving any tx, otherwise will not see tx in the mempool #[cfg(feature = "test-rpc")] - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); (wallet, blockchain, descriptors, test_client) } @@ -415,7 +415,7 @@ macro_rules! bdk_blockchain_tests { #[cfg(not(feature = "test-rpc"))] assert!(wallet.database().deref().get_sync_time().unwrap().is_none(), "initial sync_time not none"); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert!(wallet.database().deref().get_sync_time().unwrap().is_some(), "sync_time hasn't been updated"); assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance"); @@ -439,7 +439,7 @@ macro_rules! bdk_blockchain_tests { @tx ( (@external descriptors, 25) => 50_000 ) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 100_000, "incorrect balance"); assert_eq!(wallet.list_transactions(false).unwrap().len(), 2, "incorrect number of txs"); @@ -449,14 +449,14 @@ macro_rules! bdk_blockchain_tests { fn test_sync_before_and_after_receive() { let (wallet, blockchain, descriptors, mut test_client) = init_single_sig(); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 0); test_client.receive(testutils! { @tx ( (@external descriptors, 0) => 50_000 ) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance"); assert_eq!(wallet.list_transactions(false).unwrap().len(), 1, "incorrect number of txs"); @@ -470,7 +470,7 @@ macro_rules! bdk_blockchain_tests { @tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000, (@external descriptors, 5) => 30_000 ) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 105_000, "incorrect balance"); assert_eq!(wallet.list_transactions(false).unwrap().len(), 1, "incorrect number of txs"); @@ -494,7 +494,7 @@ macro_rules! bdk_blockchain_tests { @tx ( (@external descriptors, 5) => 25_000 ) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 75_000, "incorrect balance"); assert_eq!(wallet.list_transactions(false).unwrap().len(), 2, "incorrect number of txs"); @@ -509,14 +509,14 @@ macro_rules! bdk_blockchain_tests { @tx ( (@external descriptors, 0) => 50_000 ) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000); test_client.receive(testutils! { @tx ( (@external descriptors, 0) => 25_000 ) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 75_000, "incorrect balance"); } @@ -528,7 +528,7 @@ macro_rules! bdk_blockchain_tests { @tx ( (@external descriptors, 0) => 50_000 ) ( @replaceable true ) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance"); assert_eq!(wallet.list_transactions(false).unwrap().len(), 1, "incorrect number of txs"); @@ -542,7 +542,7 @@ macro_rules! bdk_blockchain_tests { let new_txid = test_client.bump_fee(&txid); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance after bump"); assert_eq!(wallet.list_transactions(false).unwrap().len(), 1, "incorrect number of txs after bump"); @@ -566,7 +566,7 @@ macro_rules! bdk_blockchain_tests { @tx ( (@external descriptors, 0) => 50_000 ) ( @confirmations 1 ) ( @replaceable true ) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance"); assert_eq!(wallet.list_transactions(false).unwrap().len(), 1, "incorrect number of txs"); @@ -579,7 +579,7 @@ macro_rules! bdk_blockchain_tests { // Invalidate 1 block test_client.invalidate(1); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance after invalidate"); @@ -598,7 +598,7 @@ macro_rules! bdk_blockchain_tests { @tx ( (@external descriptors, 0) => 50_000 ) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance"); let mut builder = wallet.build_tx(); @@ -609,7 +609,7 @@ macro_rules! bdk_blockchain_tests { let tx = psbt.extract_tx(); println!("{}", bitcoin::consensus::encode::serialize_hex(&tx)); blockchain.broadcast(&tx).unwrap(); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), details.received, "incorrect balance after send"); assert_eq!(wallet.list_transactions(false).unwrap().len(), 2, "incorrect number of txs"); @@ -623,13 +623,13 @@ macro_rules! bdk_blockchain_tests { let (wallet, blockchain, descriptors, mut test_client) = init_single_sig(); let receiver_wallet = get_wallet_from_descriptors(&("wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)".to_string(), None)); // need to sync so rpc can start watching - receiver_wallet.sync(&blockchain, noop_progress(), None).unwrap(); + receiver_wallet.sync(&blockchain, SyncOptions::default()).unwrap(); test_client.receive(testutils! { @tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000 ) (@confirmations 1) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 75_000, "incorrect balance"); let target_addr = receiver_wallet.get_address($crate::wallet::AddressIndex::New).unwrap().address; @@ -654,7 +654,7 @@ macro_rules! bdk_blockchain_tests { blockchain.broadcast(&tx1).unwrap(); blockchain.broadcast(&tx2).unwrap(); - receiver_wallet.sync(&blockchain, noop_progress(), None).unwrap(); + receiver_wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(receiver_wallet.get_balance().unwrap(), 49_000, "should have received coins once and only once"); } @@ -679,7 +679,7 @@ macro_rules! bdk_blockchain_tests { }); } - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 100_000); } @@ -694,7 +694,7 @@ macro_rules! bdk_blockchain_tests { @tx ( (@external descriptors, 0) => 50_000 ) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance"); let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::>(); @@ -702,7 +702,7 @@ macro_rules! bdk_blockchain_tests { assert!(details.confirmation_time.is_none()); test_client.generate(1, Some(node_addr)); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::>(); let details = tx_map.get(&received_txid).unwrap(); @@ -718,7 +718,7 @@ macro_rules! bdk_blockchain_tests { @tx ( (@external descriptors, 0) => 50_000 ) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance"); let mut builder = wallet.build_tx(); @@ -730,7 +730,7 @@ macro_rules! bdk_blockchain_tests { let sent_tx = psbt.extract_tx(); blockchain.broadcast(&sent_tx).unwrap(); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), details.received, "incorrect balance after receive"); // empty wallet @@ -739,7 +739,7 @@ macro_rules! bdk_blockchain_tests { #[cfg(feature = "rpc")] // rpc cannot see mempool tx before importmulti test_client.generate(1, Some(node_addr)); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::>(); let received = tx_map.get(&received_txid).unwrap(); @@ -761,7 +761,7 @@ macro_rules! bdk_blockchain_tests { @tx ( (@external descriptors, 0) => 50_000 ) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance"); let mut total_sent = 0; @@ -773,12 +773,12 @@ macro_rules! bdk_blockchain_tests { assert!(finalized, "Cannot finalize transaction"); blockchain.broadcast(&psbt.extract_tx()).unwrap(); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); total_sent += 5_000 + details.fee.unwrap_or(0); } - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000 - total_sent, "incorrect balance after chain"); // empty wallet @@ -788,7 +788,7 @@ macro_rules! bdk_blockchain_tests { #[cfg(feature = "rpc")] // rpc cannot see mempool tx before importmulti test_client.generate(1, Some(node_addr)); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000 - total_sent, "incorrect balance empty wallet"); } @@ -802,7 +802,7 @@ macro_rules! bdk_blockchain_tests { @tx ( (@external descriptors, 0) => 50_000 ) (@confirmations 1) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance"); let mut builder = wallet.build_tx(); @@ -811,7 +811,7 @@ macro_rules! bdk_blockchain_tests { let finalized = wallet.sign(&mut psbt, Default::default()).unwrap(); assert!(finalized, "Cannot finalize transaction"); blockchain.broadcast(&psbt.extract_tx()).unwrap(); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000 - details.fee.unwrap_or(0) - 5_000, "incorrect balance from fees"); assert_eq!(wallet.get_balance().unwrap(), details.received, "incorrect balance from received"); @@ -821,7 +821,7 @@ macro_rules! bdk_blockchain_tests { let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap(); assert!(finalized, "Cannot finalize transaction"); blockchain.broadcast(&new_psbt.extract_tx()).unwrap(); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000 - new_details.fee.unwrap_or(0) - 5_000, "incorrect balance from fees after bump"); assert_eq!(wallet.get_balance().unwrap(), new_details.received, "incorrect balance from received after bump"); @@ -837,7 +837,7 @@ macro_rules! bdk_blockchain_tests { @tx ( (@external descriptors, 0) => 50_000 ) (@confirmations 1) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance"); let mut builder = wallet.build_tx(); @@ -846,7 +846,7 @@ macro_rules! bdk_blockchain_tests { let finalized = wallet.sign(&mut psbt, Default::default()).unwrap(); assert!(finalized, "Cannot finalize transaction"); blockchain.broadcast(&psbt.extract_tx()).unwrap(); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 1_000 - details.fee.unwrap_or(0), "incorrect balance after send"); assert_eq!(wallet.get_balance().unwrap(), details.received, "incorrect received after send"); @@ -856,7 +856,7 @@ macro_rules! bdk_blockchain_tests { let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap(); assert!(finalized, "Cannot finalize transaction"); blockchain.broadcast(&new_psbt.extract_tx()).unwrap(); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 0, "incorrect balance after change removal"); assert_eq!(new_details.received, 0, "incorrect received after change removal"); @@ -872,7 +872,7 @@ macro_rules! bdk_blockchain_tests { @tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000 ) (@confirmations 1) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 75_000, "incorrect balance"); let mut builder = wallet.build_tx(); @@ -881,7 +881,7 @@ macro_rules! bdk_blockchain_tests { let finalized = wallet.sign(&mut psbt, Default::default()).unwrap(); assert!(finalized, "Cannot finalize transaction"); blockchain.broadcast(&psbt.extract_tx()).unwrap(); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fee.unwrap_or(0), "incorrect balance after send"); assert_eq!(details.received, 1_000 - details.fee.unwrap_or(0), "incorrect received after send"); @@ -891,7 +891,7 @@ macro_rules! bdk_blockchain_tests { let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap(); assert!(finalized, "Cannot finalize transaction"); blockchain.broadcast(&new_psbt.extract_tx()).unwrap(); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(new_details.sent, 75_000, "incorrect sent"); assert_eq!(wallet.get_balance().unwrap(), new_details.received, "incorrect balance after add input"); } @@ -905,7 +905,7 @@ macro_rules! bdk_blockchain_tests { @tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000 ) (@confirmations 1) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 75_000, "incorrect balance"); let mut builder = wallet.build_tx(); @@ -914,7 +914,7 @@ macro_rules! bdk_blockchain_tests { let finalized = wallet.sign(&mut psbt, Default::default()).unwrap(); assert!(finalized, "Cannot finalize transaction"); blockchain.broadcast(&psbt.extract_tx()).unwrap(); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fee.unwrap_or(0), "incorrect balance after send"); assert_eq!(details.received, 1_000 - details.fee.unwrap_or(0), "incorrect received after send"); @@ -926,7 +926,7 @@ macro_rules! bdk_blockchain_tests { let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap(); assert!(finalized, "Cannot finalize transaction"); blockchain.broadcast(&new_psbt.extract_tx()).unwrap(); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(new_details.sent, 75_000, "incorrect sent"); assert_eq!(wallet.get_balance().unwrap(), 0, "incorrect balance after add input"); assert_eq!(new_details.received, 0, "incorrect received after add input"); @@ -941,7 +941,7 @@ macro_rules! bdk_blockchain_tests { @tx ( (@external descriptors, 0) => 50_000 ) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance"); let mut builder = wallet.build_tx(); @@ -956,7 +956,7 @@ macro_rules! bdk_blockchain_tests { assert!(serialized_tx.windows(data.len()).any(|e| e==data), "cannot find op_return data in transaction"); blockchain.broadcast(&tx).unwrap(); test_client.generate(1, Some(node_addr)); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000 - details.fee.unwrap_or(0), "incorrect balance after send"); let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::>(); @@ -969,7 +969,7 @@ macro_rules! bdk_blockchain_tests { let wallet_addr = wallet.get_address($crate::wallet::AddressIndex::New).unwrap().address; - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 0, "incorrect balance"); test_client.generate(1, Some(wallet_addr)); @@ -982,7 +982,7 @@ macro_rules! bdk_blockchain_tests { } - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert!(wallet.get_balance().unwrap() > 0, "incorrect balance after receiving coinbase"); } @@ -1058,7 +1058,7 @@ macro_rules! bdk_blockchain_tests { @tx ( (@external descriptors, 0) => 50_000 ) }); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), 50_000, "wallet has incorrect balance"); // 4. Send 25_000 sats from test BDK wallet to test bitcoind node taproot wallet @@ -1070,7 +1070,7 @@ macro_rules! bdk_blockchain_tests { assert!(finalized, "wallet cannot finalize transaction"); let tx = psbt.extract_tx(); blockchain.broadcast(&tx).unwrap(); - wallet.sync(&blockchain, noop_progress(), None).unwrap(); + wallet.sync(&blockchain, SyncOptions::default()).unwrap(); assert_eq!(wallet.get_balance().unwrap(), details.received, "wallet has incorrect balance after send"); assert_eq!(wallet.list_transactions(false).unwrap().len(), 2, "wallet has incorrect number of txs"); assert_eq!(wallet.list_unspent().unwrap().len(), 1, "wallet has incorrect number of unspents"); diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index 9effd1c0..a8d6a62c 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -55,7 +55,7 @@ use signer::{SignOptions, Signer, SignerOrdering, SignersContainer}; use tx_builder::{BumpFee, CreateTx, FeePolicy, TxBuilder, TxParams}; use utils::{check_nlocktime, check_nsequence_rbf, After, Older, SecpCtx}; -use crate::blockchain::{GetHeight, Progress, WalletSync}; +use crate::blockchain::{GetHeight, NoopProgress, Progress, WalletSync}; use crate::database::memory::MemoryDatabase; use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils, SyncTime}; use crate::descriptor::derived::AsDerived; @@ -156,6 +156,17 @@ impl fmt::Display for AddressInfo { } } +#[derive(Debug, Default)] +/// Options to a [`sync`]. +/// +/// [`sync`]: Wallet::sync +pub struct SyncOptions { + /// The progress tracker which may be informed when progress is made. + pub progress: Option>, + /// The maximum number of addresses sync on. + pub max_addresses: Option, +} + impl Wallet where D: BatchDatabase, @@ -1431,27 +1442,26 @@ where pub fn database(&self) -> impl std::ops::Deref + '_ { self.database.borrow() } -} -impl Wallet -where - D: BatchDatabase, -{ /// Sync the internal database with the blockchain #[maybe_async] - pub fn sync( + pub fn sync( &self, blockchain: &B, - progress_update: P, - max_address_param: Option, + sync_opts: SyncOptions, ) -> Result<(), Error> { debug!("Begin sync..."); let mut run_setup = false; + let SyncOptions { + max_addresses, + progress, + } = sync_opts; + let progress = progress.unwrap_or_else(|| Box::new(NoopProgress)); let max_address = match self.descriptor.is_deriveable() { false => 0, - true => max_address_param.unwrap_or(CACHE_ADDR_BATCH_SIZE), + true => max_addresses.unwrap_or(CACHE_ADDR_BATCH_SIZE), }; debug!("max_address {}", max_address); if self @@ -1468,7 +1478,7 @@ where if let Some(change_descriptor) = &self.change_descriptor { let max_address = match change_descriptor.is_deriveable() { false => 0, - true => max_address_param.unwrap_or(CACHE_ADDR_BATCH_SIZE), + true => max_addresses.unwrap_or(CACHE_ADDR_BATCH_SIZE), }; if self @@ -1488,12 +1498,10 @@ where // TODO: we should sync if generating an address triggers a new batch to be stored if run_setup { maybe_await!( - blockchain.wallet_setup(self.database.borrow_mut().deref_mut(), progress_update,) + blockchain.wallet_setup(self.database.borrow_mut().deref_mut(), progress,) )?; } else { - maybe_await!( - blockchain.wallet_sync(self.database.borrow_mut().deref_mut(), progress_update,) - )?; + maybe_await!(blockchain.wallet_sync(self.database.borrow_mut().deref_mut(), progress,))?; } let sync_time = SyncTime {