From: LLFourn Date: Thu, 3 Mar 2022 02:13:45 +0000 (+1100) Subject: Merge branch 'master' into remove-blockchain-from-wallet X-Git-Tag: v0.17.0~3^2~3 X-Git-Url: http://internal-gitweb-vhost/script/%22https:/database/-status-code/static/gitweb.css?a=commitdiff_plain;h=d03aa851086c68592770942ecd18d1add7d204ee;p=bdk Merge branch 'master' into remove-blockchain-from-wallet --- d03aa851086c68592770942ecd18d1add7d204ee diff --cc CHANGELOG.md index 319fb628,4b35dd13..cad538d5 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@@ -8,18 -8,9 +8,20 @@@ and this project adheres to [Semantic V - 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`. + - Add `get_internal_address` to allow you to get internal addresses just as you get external addresses. + - added `ensure_addresses_cached` to `Wallet` to let offline wallets load and cache addresses in their database +### 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`. +- 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] - Pin tokio dependency version to ~1.14 to prevent errors due to their new MSRV 1.49.0 diff --cc src/testutils/blockchain_tests.rs index b54da105,30d926ce..6ac91e88 --- a/src/testutils/blockchain_tests.rs +++ b/src/testutils/blockchain_tests.rs @@@ -817,11 -815,11 +817,11 @@@ macro_rules! bdk_blockchain_tests let mut builder = wallet.build_fee_bump(details.txid).unwrap(); builder.fee_rate(FeeRate::from_sat_per_vb(2.1)); - let (mut new_psbt, new_details) = builder.finish().unwrap(); + let (mut new_psbt, new_details) = builder.finish().expect("fee bump tx"); let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap(); assert!(finalized, "Cannot finalize transaction"); - wallet.broadcast(&new_psbt.extract_tx()).unwrap(); - wallet.sync(noop_progress(), None).unwrap(); + blockchain.broadcast(&new_psbt.extract_tx()).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"); diff --cc src/wallet/mod.rs index 4006ccb2,93a0d01e..97f7bbb8 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@@ -234,13 -175,74 +234,13 @@@ wher pub fn network(&self) -> Network { self.network } -} - -/// The address index selection strategy to use to derived an address from the wallet's external -/// descriptor. See [`Wallet::get_address`]. If you're unsure which one to use use `WalletIndex::New`. -#[derive(Debug)] -pub enum AddressIndex { - /// Return a new address after incrementing the current descriptor index. - New, - /// Return the address for the current descriptor index if it has not been used in a received - /// transaction. Otherwise return a new address as with [`AddressIndex::New`]. - /// - /// Use with caution, if the wallet has not yet detected an address has been used it could - /// return an already used address. This function is primarily meant for situations where the - /// caller is untrusted; for example when deriving donation addresses on-demand for a public - /// web page. - LastUnused, - /// Return the address for a specific descriptor index. Does not change the current descriptor - /// index used by `AddressIndex::New` and `AddressIndex::LastUsed`. - /// - /// Use with caution, if an index is given that is less than the current descriptor index - /// then the returned address may have already been used. - Peek(u32), - /// Return the address for a specific descriptor index and reset the current descriptor index - /// used by `AddressIndex::New` and `AddressIndex::LastUsed` to this value. - /// - /// Use with caution, if an index is given that is less than the current descriptor index - /// then the returned address and subsequent addresses returned by calls to `AddressIndex::New` - /// and `AddressIndex::LastUsed` may have already been used. Also if the index is reset to a - /// value earlier than the [`crate::blockchain::Blockchain`] stop_gap (default is 20) then a - /// larger stop_gap should be used to monitor for all possibly used addresses. - Reset(u32), -} - -/// A derived address and the index it was found at -/// For convenience this automatically derefs to `Address` -#[derive(Debug, PartialEq)] -pub struct AddressInfo { - /// Child index of this address - pub index: u32, - /// Address - pub address: Address, -} - -impl Deref for AddressInfo { - type Target = Address; - - fn deref(&self) -> &Self::Target { - &self.address - } -} - -impl fmt::Display for AddressInfo { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.address) - } -} -// offline actions, always available -impl Wallet -where - D: BatchDatabase, -{ - // Return a newly derived address for the specified `keychain`. + // Return a newly derived address using the external descriptor - fn get_new_address(&self) -> Result { - let incremented_index = self.fetch_and_increment_index(KeychainKind::External)?; + fn get_new_address(&self, keychain: KeychainKind) -> Result { + let incremented_index = self.fetch_and_increment_index(keychain)?; let address_result = self - .descriptor + .get_descriptor_for_keychain(keychain) .as_derived(incremented_index, &self.secp) .address(self.network); @@@ -1463,46 -1548,8 +1524,14 @@@ ) -> 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_addresses.unwrap_or(CACHE_ADDR_BATCH_SIZE), - }; - debug!("max_address {}", max_address); - if self - .database - .borrow() - .get_script_pubkey_from_path(KeychainKind::External, max_address.saturating_sub(1))? - .is_none() - { - debug!("caching external addresses"); - run_setup = true; - self.cache_addresses(KeychainKind::External, 0, max_address)?; - } - - if let Some(change_descriptor) = &self.change_descriptor { - let max_address = match change_descriptor.is_deriveable() { - false => 0, - true => max_addresses.unwrap_or(CACHE_ADDR_BATCH_SIZE), - }; - - if self - .database - .borrow() - .get_script_pubkey_from_path(KeychainKind::Internal, max_address.saturating_sub(1))? - .is_none() - { - debug!("caching internal addresses"); - run_setup = true; - self.cache_addresses(KeychainKind::Internal, 0, max_address)?; - } - } + let run_setup = - self.ensure_addresses_cached(max_address_param.unwrap_or(CACHE_ADDR_BATCH_SIZE))?; ++ self.ensure_addresses_cached(max_addresses.unwrap_or(CACHE_ADDR_BATCH_SIZE))?; debug!("run_setup: {}", run_setup); // TODO: what if i generate an address first and cache some addresses? @@@ -3953,6 -4015,48 +3982,48 @@@ pub(crate) mod test builder.add_recipient(addr.script_pubkey(), 45_000); builder.finish().unwrap(); } + + #[test] + fn test_get_address() { + use crate::descriptor::template::Bip84; + let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap(); - let wallet = Wallet::new_offline( ++ let wallet = Wallet::new( + Bip84(key, KeychainKind::External), + Some(Bip84(key, KeychainKind::Internal)), + Network::Regtest, + MemoryDatabase::default(), + ) + .unwrap(); + + assert_eq!( + wallet.get_address(AddressIndex::New).unwrap().address, + Address::from_str("bcrt1qkmvk2nadgplmd57ztld8nf8v2yxkzmdvwtjf8s").unwrap() + ); + assert_eq!( + wallet + .get_internal_address(AddressIndex::New) + .unwrap() + .address, + Address::from_str("bcrt1qtrwtz00wxl69e5xex7amy4xzlxkaefg3gfdkxa").unwrap() + ); + - let wallet = Wallet::new_offline( ++ let wallet = Wallet::new( + Bip84(key, KeychainKind::External), + None, + Network::Regtest, + MemoryDatabase::default(), + ) + .unwrap(); + + assert_eq!( + wallet + .get_internal_address(AddressIndex::New) + .unwrap() + .address, + Address::from_str("bcrt1qkmvk2nadgplmd57ztld8nf8v2yxkzmdvwtjf8s").unwrap(), + "when there's no internal descriptor it should just use external" + ); + } } /// Deterministically generate a unique name given the descriptors defining the wallet