Remove Blockchain from wallet
Although somewhat convenient to have, coupling the Wallet with the blockchain trait causes development friction and complexity. What if sometimes the wallet is "offline" (no access to the blockchain) but sometimes its online? The only thing the Wallet needs the blockchain for is to sync. But not all applications will even use the sync method and the sync method doesn't require the full blockchain functionality. So we instead pass the blockchain in when we want to sync. - To further reduce the coupling with blockchain I removed the get_height call from `new` and just use the height of the last sync in the database. - I split up the blockchain trait a bit into subtraits.
This commit is contained in:
parent
b23a0747b5
commit
326bfe82a8
@ -8,6 +8,10 @@ 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.
|
- 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`.
|
- `verify` flag removed from `TransactionDetails`.
|
||||||
|
- 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.
|
||||||
|
|
||||||
## [v0.16.1] - [v0.16.0]
|
## [v0.16.1] - [v0.16.0]
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ use bdk::{Wallet, database::MemoryDatabase};
|
|||||||
use bdk::wallet::AddressIndex::New;
|
use bdk::wallet::AddressIndex::New;
|
||||||
|
|
||||||
fn main() -> Result<(), bdk::Error> {
|
fn main() -> Result<(), bdk::Error> {
|
||||||
let wallet = Wallet::new_offline(
|
let wallet = Wallet::new(
|
||||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||||
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
|
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
|
||||||
bitcoin::Network::Testnet,
|
bitcoin::Network::Testnet,
|
||||||
@ -135,7 +135,7 @@ use bdk::{Wallet, SignOptions, database::MemoryDatabase};
|
|||||||
use bitcoin::consensus::deserialize;
|
use bitcoin::consensus::deserialize;
|
||||||
|
|
||||||
fn main() -> Result<(), bdk::Error> {
|
fn main() -> Result<(), bdk::Error> {
|
||||||
let wallet = Wallet::new_offline(
|
let wallet = Wallet::new(
|
||||||
"wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)",
|
"wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)",
|
||||||
Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"),
|
Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"),
|
||||||
bitcoin::Network::Testnet,
|
bitcoin::Network::Testnet,
|
||||||
|
@ -48,8 +48,7 @@ impl AddressValidator for DummyValidator {
|
|||||||
|
|
||||||
fn main() -> Result<(), bdk::Error> {
|
fn main() -> Result<(), bdk::Error> {
|
||||||
let descriptor = "sh(and_v(v:pk(tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd/*),after(630000)))";
|
let descriptor = "sh(and_v(v:pk(tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd/*),after(630000)))";
|
||||||
let mut wallet =
|
let mut wallet = Wallet::new(descriptor, None, Network::Regtest, MemoryDatabase::new())?;
|
||||||
Wallet::new_offline(descriptor, None, Network::Regtest, MemoryDatabase::new())?;
|
|
||||||
|
|
||||||
wallet.add_address_validator(Arc::new(DummyValidator));
|
wallet.add_address_validator(Arc::new(DummyValidator));
|
||||||
|
|
||||||
|
@ -35,9 +35,8 @@ fn main() -> Result<(), CompactFiltersError> {
|
|||||||
let descriptor = "wpkh(tpubD6NzVbkrYhZ4X2yy78HWrr1M9NT8dKeWfzNiQqDdMqqa9UmmGztGGz6TaLFGsLfdft5iu32gxq1T4eMNxExNNWzVCpf9Y6JZi5TnqoC9wJq/*)";
|
let descriptor = "wpkh(tpubD6NzVbkrYhZ4X2yy78HWrr1M9NT8dKeWfzNiQqDdMqqa9UmmGztGGz6TaLFGsLfdft5iu32gxq1T4eMNxExNNWzVCpf9Y6JZi5TnqoC9wJq/*)";
|
||||||
|
|
||||||
let database = MemoryDatabase::default();
|
let database = MemoryDatabase::default();
|
||||||
let wallet =
|
let wallet = Arc::new(Wallet::new(descriptor, None, Network::Testnet, database).unwrap());
|
||||||
Arc::new(Wallet::new(descriptor, None, Network::Testnet, database, blockchain).unwrap());
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
info!("balance: {}", wallet.get_balance()?);
|
info!("balance: {}", wallet.get_balance()?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
.transpose()
|
.transpose()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap_or(Network::Testnet);
|
.unwrap_or(Network::Testnet);
|
||||||
let wallet = Wallet::new_offline(&format!("{}", descriptor), None, network, database)?;
|
let wallet = Wallet::new(&format!("{}", descriptor), None, network, database)?;
|
||||||
|
|
||||||
info!("... First address: {}", wallet.get_address(New)?);
|
info!("... First address: {}", wallet.get_address(New)?);
|
||||||
|
|
||||||
|
@ -16,61 +16,17 @@
|
|||||||
//!
|
//!
|
||||||
//! ## Example
|
//! ## Example
|
||||||
//!
|
//!
|
||||||
//! In this example both `wallet_electrum` and `wallet_esplora` have the same type of
|
|
||||||
//! `Wallet<AnyBlockchain, MemoryDatabase>`. This means that they could both, for instance, be
|
|
||||||
//! assigned to a struct member.
|
|
||||||
//!
|
|
||||||
//! ```no_run
|
|
||||||
//! # use bitcoin::Network;
|
|
||||||
//! # use bdk::blockchain::*;
|
|
||||||
//! # use bdk::database::MemoryDatabase;
|
|
||||||
//! # use bdk::Wallet;
|
|
||||||
//! # #[cfg(feature = "electrum")]
|
|
||||||
//! # {
|
|
||||||
//! let electrum_blockchain = ElectrumBlockchain::from(electrum_client::Client::new("...")?);
|
|
||||||
//! let wallet_electrum: Wallet<AnyBlockchain, _> = Wallet::new(
|
|
||||||
//! "...",
|
|
||||||
//! None,
|
|
||||||
//! Network::Testnet,
|
|
||||||
//! MemoryDatabase::default(),
|
|
||||||
//! electrum_blockchain.into(),
|
|
||||||
//! )?;
|
|
||||||
//! # }
|
|
||||||
//!
|
|
||||||
//! # #[cfg(all(feature = "esplora", feature = "ureq"))]
|
|
||||||
//! # {
|
|
||||||
//! let esplora_blockchain = EsploraBlockchain::new("...", 20);
|
|
||||||
//! let wallet_esplora: Wallet<AnyBlockchain, _> = Wallet::new(
|
|
||||||
//! "...",
|
|
||||||
//! None,
|
|
||||||
//! Network::Testnet,
|
|
||||||
//! MemoryDatabase::default(),
|
|
||||||
//! esplora_blockchain.into(),
|
|
||||||
//! )?;
|
|
||||||
//! # }
|
|
||||||
//!
|
|
||||||
//! # Ok::<(), bdk::Error>(())
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! When paired with the use of [`ConfigurableBlockchain`], it allows creating wallets with any
|
//! When paired with the use of [`ConfigurableBlockchain`], it allows creating wallets with any
|
||||||
//! blockchain type supported using a single line of code:
|
//! blockchain type supported using a single line of code:
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! # use bitcoin::Network;
|
//! # use bitcoin::Network;
|
||||||
//! # use bdk::blockchain::*;
|
//! # use bdk::blockchain::*;
|
||||||
//! # use bdk::database::MemoryDatabase;
|
|
||||||
//! # use bdk::Wallet;
|
|
||||||
//! # #[cfg(all(feature = "esplora", feature = "ureq"))]
|
//! # #[cfg(all(feature = "esplora", feature = "ureq"))]
|
||||||
//! # {
|
//! # {
|
||||||
//! let config = serde_json::from_str("...")?;
|
//! let config = serde_json::from_str("...")?;
|
||||||
//! let blockchain = AnyBlockchain::from_config(&config)?;
|
//! let blockchain = AnyBlockchain::from_config(&config)?;
|
||||||
//! let wallet = Wallet::new(
|
//! let height = blockchain.get_height();
|
||||||
//! "...",
|
|
||||||
//! None,
|
|
||||||
//! Network::Testnet,
|
|
||||||
//! MemoryDatabase::default(),
|
|
||||||
//! blockchain,
|
|
||||||
//! )?;
|
|
||||||
//! # }
|
//! # }
|
||||||
//! # Ok::<(), bdk::Error>(())
|
//! # Ok::<(), bdk::Error>(())
|
||||||
//! ```
|
//! ```
|
||||||
@ -133,21 +89,6 @@ impl Blockchain for AnyBlockchain {
|
|||||||
maybe_await!(impl_inner_method!(self, get_capabilities))
|
maybe_await!(impl_inner_method!(self, get_capabilities))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup<D: BatchDatabase, P: 'static + Progress>(
|
|
||||||
&self,
|
|
||||||
database: &mut D,
|
|
||||||
progress_update: P,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
maybe_await!(impl_inner_method!(self, setup, database, progress_update))
|
|
||||||
}
|
|
||||||
fn sync<D: BatchDatabase, P: 'static + Progress>(
|
|
||||||
&self,
|
|
||||||
database: &mut D,
|
|
||||||
progress_update: P,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
maybe_await!(impl_inner_method!(self, sync, database, progress_update))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
|
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
|
||||||
maybe_await!(impl_inner_method!(self, get_tx, txid))
|
maybe_await!(impl_inner_method!(self, get_tx, txid))
|
||||||
}
|
}
|
||||||
@ -155,11 +96,44 @@ impl Blockchain for AnyBlockchain {
|
|||||||
maybe_await!(impl_inner_method!(self, broadcast, tx))
|
maybe_await!(impl_inner_method!(self, broadcast, tx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error> {
|
||||||
|
maybe_await!(impl_inner_method!(self, estimate_fee, target))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[maybe_async]
|
||||||
|
impl GetHeight for AnyBlockchain {
|
||||||
fn get_height(&self) -> Result<u32, Error> {
|
fn get_height(&self) -> Result<u32, Error> {
|
||||||
maybe_await!(impl_inner_method!(self, get_height))
|
maybe_await!(impl_inner_method!(self, get_height))
|
||||||
}
|
}
|
||||||
fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error> {
|
}
|
||||||
maybe_await!(impl_inner_method!(self, estimate_fee, target))
|
|
||||||
|
#[maybe_async]
|
||||||
|
impl WalletSync for AnyBlockchain {
|
||||||
|
fn wallet_sync<D: BatchDatabase, P: Progress>(
|
||||||
|
&self,
|
||||||
|
database: &mut D,
|
||||||
|
progress_update: P,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
maybe_await!(impl_inner_method!(
|
||||||
|
self,
|
||||||
|
wallet_sync,
|
||||||
|
database,
|
||||||
|
progress_update
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wallet_setup<D: BatchDatabase, P: Progress>(
|
||||||
|
&self,
|
||||||
|
database: &mut D,
|
||||||
|
progress_update: P,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
maybe_await!(impl_inner_method!(
|
||||||
|
self,
|
||||||
|
wallet_setup,
|
||||||
|
database,
|
||||||
|
progress_update
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ mod peer;
|
|||||||
mod store;
|
mod store;
|
||||||
mod sync;
|
mod sync;
|
||||||
|
|
||||||
use super::{Blockchain, Capability, ConfigurableBlockchain, Progress};
|
use super::{Blockchain, Capability, ConfigurableBlockchain, GetHeight, Progress, WalletSync};
|
||||||
use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils};
|
use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils};
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::types::{KeychainKind, LocalUtxo, TransactionDetails};
|
use crate::types::{KeychainKind, LocalUtxo, TransactionDetails};
|
||||||
@ -225,8 +225,33 @@ impl Blockchain for CompactFiltersBlockchain {
|
|||||||
vec![Capability::FullHistory].into_iter().collect()
|
vec![Capability::FullHistory].into_iter().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
|
||||||
|
Ok(self.peers[0]
|
||||||
|
.get_mempool()
|
||||||
|
.get_tx(&Inventory::Transaction(*txid)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
|
||||||
|
self.peers[0].broadcast_tx(tx.clone())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn estimate_fee(&self, _target: usize) -> Result<FeeRate, Error> {
|
||||||
|
// TODO
|
||||||
|
Ok(FeeRate::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetHeight for CompactFiltersBlockchain {
|
||||||
|
fn get_height(&self) -> Result<u32, Error> {
|
||||||
|
Ok(self.headers.get_height()? as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WalletSync for CompactFiltersBlockchain {
|
||||||
#[allow(clippy::mutex_atomic)] // Mutex is easier to understand than a CAS loop.
|
#[allow(clippy::mutex_atomic)] // Mutex is easier to understand than a CAS loop.
|
||||||
fn setup<D: BatchDatabase, P: 'static + Progress>(
|
fn wallet_setup<D: BatchDatabase, P: Progress>(
|
||||||
&self,
|
&self,
|
||||||
database: &mut D,
|
database: &mut D,
|
||||||
progress_update: P,
|
progress_update: P,
|
||||||
@ -430,27 +455,6 @@ impl Blockchain for CompactFiltersBlockchain {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
|
|
||||||
Ok(self.peers[0]
|
|
||||||
.get_mempool()
|
|
||||||
.get_tx(&Inventory::Transaction(*txid)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
|
|
||||||
self.peers[0].broadcast_tx(tx.clone())?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_height(&self) -> Result<u32, Error> {
|
|
||||||
Ok(self.headers.get_height()? as u32)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn estimate_fee(&self, _target: usize) -> Result<FeeRate, Error> {
|
|
||||||
// TODO
|
|
||||||
Ok(FeeRate::default())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data to connect to a Bitcoin P2P peer
|
/// Data to connect to a Bitcoin P2P peer
|
||||||
|
@ -68,7 +68,34 @@ impl Blockchain for ElectrumBlockchain {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup<D: BatchDatabase, P: Progress>(
|
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
|
||||||
|
Ok(self.client.transaction_get(txid).map(Option::Some)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
|
||||||
|
Ok(self.client.transaction_broadcast(tx).map(|_| ())?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error> {
|
||||||
|
Ok(FeeRate::from_btc_per_kvb(
|
||||||
|
self.client.estimate_fee(target)? as f32
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetHeight for ElectrumBlockchain {
|
||||||
|
fn get_height(&self) -> Result<u32, Error> {
|
||||||
|
// TODO: unsubscribe when added to the client, or is there a better call to use here?
|
||||||
|
|
||||||
|
Ok(self
|
||||||
|
.client
|
||||||
|
.block_headers_subscribe()
|
||||||
|
.map(|data| data.height as u32)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WalletSync for ElectrumBlockchain {
|
||||||
|
fn wallet_setup<D: BatchDatabase, P: Progress>(
|
||||||
&self,
|
&self,
|
||||||
database: &mut D,
|
database: &mut D,
|
||||||
_progress_update: P,
|
_progress_update: P,
|
||||||
@ -207,29 +234,6 @@ impl Blockchain for ElectrumBlockchain {
|
|||||||
database.commit_batch(batch_update)?;
|
database.commit_batch(batch_update)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
|
|
||||||
Ok(self.client.transaction_get(txid).map(Option::Some)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
|
|
||||||
Ok(self.client.transaction_broadcast(tx).map(|_| ())?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_height(&self) -> Result<u32, Error> {
|
|
||||||
// TODO: unsubscribe when added to the client, or is there a better call to use here?
|
|
||||||
|
|
||||||
Ok(self
|
|
||||||
.client
|
|
||||||
.block_headers_subscribe()
|
|
||||||
.map(|data| data.height as u32)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error> {
|
|
||||||
Ok(FeeRate::from_btc_per_kvb(
|
|
||||||
self.client.estimate_fee(target)? as f32
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TxCache<'a, 'b, D> {
|
struct TxCache<'a, 'b, D> {
|
||||||
|
@ -91,7 +91,30 @@ impl Blockchain for EsploraBlockchain {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup<D: BatchDatabase, P: Progress>(
|
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
|
||||||
|
Ok(await_or_block!(self.url_client._get_tx(txid))?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
|
||||||
|
Ok(await_or_block!(self.url_client._broadcast(tx))?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error> {
|
||||||
|
let estimates = await_or_block!(self.url_client._get_fee_estimates())?;
|
||||||
|
super::into_fee_rate(target, estimates)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[maybe_async]
|
||||||
|
impl GetHeight for EsploraBlockchain {
|
||||||
|
fn get_height(&self) -> Result<u32, Error> {
|
||||||
|
Ok(await_or_block!(self.url_client._get_height())?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[maybe_async]
|
||||||
|
impl WalletSync for EsploraBlockchain {
|
||||||
|
fn wallet_setup<D: BatchDatabase, P: Progress>(
|
||||||
&self,
|
&self,
|
||||||
database: &mut D,
|
database: &mut D,
|
||||||
_progress_update: P,
|
_progress_update: P,
|
||||||
@ -180,23 +203,6 @@ impl Blockchain for EsploraBlockchain {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
|
|
||||||
Ok(await_or_block!(self.url_client._get_tx(txid))?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
|
|
||||||
Ok(await_or_block!(self.url_client._broadcast(tx))?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_height(&self) -> Result<u32, Error> {
|
|
||||||
Ok(await_or_block!(self.url_client._get_height())?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error> {
|
|
||||||
let estimates = await_or_block!(self.url_client._get_fee_estimates())?;
|
|
||||||
super::into_fee_rate(target, estimates)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UrlClient {
|
impl UrlClient {
|
||||||
|
@ -87,7 +87,29 @@ impl Blockchain for EsploraBlockchain {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup<D: BatchDatabase, P: Progress>(
|
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
|
||||||
|
Ok(self.url_client._get_tx(txid)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
|
||||||
|
let _txid = self.url_client._broadcast(tx)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error> {
|
||||||
|
let estimates = self.url_client._get_fee_estimates()?;
|
||||||
|
super::into_fee_rate(target, estimates)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetHeight for EsploraBlockchain {
|
||||||
|
fn get_height(&self) -> Result<u32, Error> {
|
||||||
|
Ok(self.url_client._get_height()?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WalletSync for EsploraBlockchain {
|
||||||
|
fn wallet_setup<D: BatchDatabase, P: Progress>(
|
||||||
&self,
|
&self,
|
||||||
database: &mut D,
|
database: &mut D,
|
||||||
_progress_update: P,
|
_progress_update: P,
|
||||||
@ -179,24 +201,6 @@ impl Blockchain for EsploraBlockchain {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
|
|
||||||
Ok(self.url_client._get_tx(txid)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
|
|
||||||
let _txid = self.url_client._broadcast(tx)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_height(&self) -> Result<u32, Error> {
|
|
||||||
Ok(self.url_client._get_height()?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error> {
|
|
||||||
let estimates = self.url_client._get_fee_estimates()?;
|
|
||||||
super::into_fee_rate(target, estimates)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UrlClient {
|
impl UrlClient {
|
||||||
|
@ -86,28 +86,45 @@ pub enum Capability {
|
|||||||
|
|
||||||
/// Trait that defines the actions that must be supported by a blockchain backend
|
/// Trait that defines the actions that must be supported by a blockchain backend
|
||||||
#[maybe_async]
|
#[maybe_async]
|
||||||
pub trait Blockchain {
|
pub trait Blockchain: WalletSync + GetHeight {
|
||||||
/// Return the set of [`Capability`] supported by this backend
|
/// Return the set of [`Capability`] supported by this backend
|
||||||
fn get_capabilities(&self) -> HashSet<Capability>;
|
fn get_capabilities(&self) -> HashSet<Capability>;
|
||||||
|
/// Fetch a transaction from the blockchain given its txid
|
||||||
|
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error>;
|
||||||
|
/// Broadcast a transaction
|
||||||
|
fn broadcast(&self, tx: &Transaction) -> Result<(), Error>;
|
||||||
|
/// Estimate the fee rate required to confirm a transaction in a given `target` of blocks
|
||||||
|
fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait for getting the current height of the blockchain.
|
||||||
|
#[maybe_async]
|
||||||
|
pub trait GetHeight {
|
||||||
|
/// Return the current height
|
||||||
|
fn get_height(&self) -> Result<u32, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait for blockchains that can sync by updating the database directly.
|
||||||
|
#[maybe_async]
|
||||||
|
pub trait WalletSync {
|
||||||
/// Setup the backend and populate the internal database for the first time
|
/// Setup the backend and populate the internal database for the first time
|
||||||
///
|
///
|
||||||
/// This method is the equivalent of [`Blockchain::sync`], but it's guaranteed to only be
|
/// This method is the equivalent of [`Self::wallet_sync`], but it's guaranteed to only be
|
||||||
/// called once, at the first [`Wallet::sync`](crate::wallet::Wallet::sync).
|
/// called once, at the first [`Wallet::sync`](crate::wallet::Wallet::sync).
|
||||||
///
|
///
|
||||||
/// The rationale behind the distinction between `sync` and `setup` is that some custom backends
|
/// The rationale behind the distinction between `sync` and `setup` is that some custom backends
|
||||||
/// might need to perform specific actions only the first time they are synced.
|
/// might need to perform specific actions only the first time they are synced.
|
||||||
///
|
///
|
||||||
/// For types that do not have that distinction, only this method can be implemented, since
|
/// For types that do not have that distinction, only this method can be implemented, since
|
||||||
/// [`Blockchain::sync`] defaults to calling this internally if not overridden.
|
/// [`WalletSync::wallet_sync`] defaults to calling this internally if not overridden.
|
||||||
fn setup<D: BatchDatabase, P: 'static + Progress>(
|
/// Populate the internal database with transactions and UTXOs
|
||||||
|
fn wallet_setup<D: BatchDatabase, P: Progress>(
|
||||||
&self,
|
&self,
|
||||||
database: &mut D,
|
database: &mut D,
|
||||||
progress_update: P,
|
progress_update: P,
|
||||||
) -> Result<(), Error>;
|
) -> Result<(), Error>;
|
||||||
/// Populate the internal database with transactions and UTXOs
|
|
||||||
///
|
/// If not overridden, it defaults to calling [`Self::wallet_setup`] internally.
|
||||||
/// If not overridden, it defaults to calling [`Blockchain::setup`] internally.
|
|
||||||
///
|
///
|
||||||
/// This method should implement the logic required to iterate over the list of the wallet's
|
/// This method should implement the logic required to iterate over the list of the wallet's
|
||||||
/// script_pubkeys using [`Database::iter_script_pubkeys`] and look for relevant transactions
|
/// script_pubkeys using [`Database::iter_script_pubkeys`] and look for relevant transactions
|
||||||
@ -124,23 +141,13 @@ pub trait Blockchain {
|
|||||||
/// [`BatchOperations::set_tx`]: crate::database::BatchOperations::set_tx
|
/// [`BatchOperations::set_tx`]: crate::database::BatchOperations::set_tx
|
||||||
/// [`BatchOperations::set_utxo`]: crate::database::BatchOperations::set_utxo
|
/// [`BatchOperations::set_utxo`]: crate::database::BatchOperations::set_utxo
|
||||||
/// [`BatchOperations::del_utxo`]: crate::database::BatchOperations::del_utxo
|
/// [`BatchOperations::del_utxo`]: crate::database::BatchOperations::del_utxo
|
||||||
fn sync<D: BatchDatabase, P: 'static + Progress>(
|
fn wallet_sync<D: BatchDatabase, P: Progress>(
|
||||||
&self,
|
&self,
|
||||||
database: &mut D,
|
database: &mut D,
|
||||||
progress_update: P,
|
progress_update: P,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
maybe_await!(self.setup(database, progress_update))
|
maybe_await!(self.wallet_setup(database, progress_update))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch a transaction from the blockchain given its txid
|
|
||||||
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error>;
|
|
||||||
/// Broadcast a transaction
|
|
||||||
fn broadcast(&self, tx: &Transaction) -> Result<(), Error>;
|
|
||||||
|
|
||||||
/// Return the current height
|
|
||||||
fn get_height(&self) -> Result<u32, Error>;
|
|
||||||
/// Estimate the fee rate required to confirm a transaction in a given `target` of blocks
|
|
||||||
fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for [`Blockchain`] types that can be created given a configuration
|
/// Trait for [`Blockchain`] types that can be created given a configuration
|
||||||
@ -155,9 +162,9 @@ pub trait ConfigurableBlockchain: Blockchain + Sized {
|
|||||||
/// Data sent with a progress update over a [`channel`]
|
/// Data sent with a progress update over a [`channel`]
|
||||||
pub type ProgressData = (f32, Option<String>);
|
pub type ProgressData = (f32, Option<String>);
|
||||||
|
|
||||||
/// Trait for types that can receive and process progress updates during [`Blockchain::sync`] and
|
/// Trait for types that can receive and process progress updates during [`WalletSync::wallet_sync`] and
|
||||||
/// [`Blockchain::setup`]
|
/// [`WalletSync::wallet_setup`]
|
||||||
pub trait Progress: Send {
|
pub trait Progress: Send + 'static {
|
||||||
/// Send a new progress update
|
/// Send a new progress update
|
||||||
///
|
///
|
||||||
/// The `progress` value should be in the range 0.0 - 100.0, and the `message` value is an
|
/// The `progress` value should be in the range 0.0 - 100.0, and the `message` value is an
|
||||||
@ -223,22 +230,6 @@ impl<T: Blockchain> Blockchain for Arc<T> {
|
|||||||
maybe_await!(self.deref().get_capabilities())
|
maybe_await!(self.deref().get_capabilities())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup<D: BatchDatabase, P: 'static + Progress>(
|
|
||||||
&self,
|
|
||||||
database: &mut D,
|
|
||||||
progress_update: P,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
maybe_await!(self.deref().setup(database, progress_update))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sync<D: BatchDatabase, P: 'static + Progress>(
|
|
||||||
&self,
|
|
||||||
database: &mut D,
|
|
||||||
progress_update: P,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
maybe_await!(self.deref().sync(database, progress_update))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
|
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
|
||||||
maybe_await!(self.deref().get_tx(txid))
|
maybe_await!(self.deref().get_tx(txid))
|
||||||
}
|
}
|
||||||
@ -246,10 +237,33 @@ impl<T: Blockchain> Blockchain for Arc<T> {
|
|||||||
maybe_await!(self.deref().broadcast(tx))
|
maybe_await!(self.deref().broadcast(tx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_height(&self) -> Result<u32, Error> {
|
|
||||||
maybe_await!(self.deref().get_height())
|
|
||||||
}
|
|
||||||
fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error> {
|
fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error> {
|
||||||
maybe_await!(self.deref().estimate_fee(target))
|
maybe_await!(self.deref().estimate_fee(target))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[maybe_async]
|
||||||
|
impl<T: GetHeight> GetHeight for Arc<T> {
|
||||||
|
fn get_height(&self) -> Result<u32, Error> {
|
||||||
|
maybe_await!(self.deref().get_height())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[maybe_async]
|
||||||
|
impl<T: WalletSync> WalletSync for Arc<T> {
|
||||||
|
fn wallet_setup<D: BatchDatabase, P: Progress>(
|
||||||
|
&self,
|
||||||
|
database: &mut D,
|
||||||
|
progress_update: P,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
maybe_await!(self.deref().wallet_setup(database, progress_update))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wallet_sync<D: BatchDatabase, P: Progress>(
|
||||||
|
&self,
|
||||||
|
database: &mut D,
|
||||||
|
progress_update: P,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
maybe_await!(self.deref().wallet_sync(database, progress_update))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -33,7 +33,9 @@
|
|||||||
|
|
||||||
use crate::bitcoin::consensus::deserialize;
|
use crate::bitcoin::consensus::deserialize;
|
||||||
use crate::bitcoin::{Address, Network, OutPoint, Transaction, TxOut, Txid};
|
use crate::bitcoin::{Address, Network, OutPoint, Transaction, TxOut, Txid};
|
||||||
use crate::blockchain::{Blockchain, Capability, ConfigurableBlockchain, Progress};
|
use crate::blockchain::{
|
||||||
|
Blockchain, Capability, ConfigurableBlockchain, GetHeight, Progress, WalletSync,
|
||||||
|
};
|
||||||
use crate::database::{BatchDatabase, DatabaseUtils};
|
use crate::database::{BatchDatabase, DatabaseUtils};
|
||||||
use crate::{BlockTime, Error, FeeRate, KeychainKind, LocalUtxo, TransactionDetails};
|
use crate::{BlockTime, Error, FeeRate, KeychainKind, LocalUtxo, TransactionDetails};
|
||||||
use bitcoincore_rpc::json::{
|
use bitcoincore_rpc::json::{
|
||||||
@ -139,7 +141,34 @@ impl Blockchain for RpcBlockchain {
|
|||||||
self.capabilities.clone()
|
self.capabilities.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup<D: BatchDatabase, P: 'static + Progress>(
|
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
|
||||||
|
Ok(Some(self.client.get_raw_transaction(txid, None)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
|
||||||
|
Ok(self.client.send_raw_transaction(tx).map(|_| ())?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error> {
|
||||||
|
let sat_per_kb = self
|
||||||
|
.client
|
||||||
|
.estimate_smart_fee(target as u16, None)?
|
||||||
|
.fee_rate
|
||||||
|
.ok_or(Error::FeeRateUnavailable)?
|
||||||
|
.as_sat() as f64;
|
||||||
|
|
||||||
|
Ok(FeeRate::from_sat_per_vb((sat_per_kb / 1000f64) as f32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetHeight for RpcBlockchain {
|
||||||
|
fn get_height(&self) -> Result<u32, Error> {
|
||||||
|
Ok(self.client.get_blockchain_info().map(|i| i.blocks as u32)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WalletSync for RpcBlockchain {
|
||||||
|
fn wallet_setup<D: BatchDatabase, P: Progress>(
|
||||||
&self,
|
&self,
|
||||||
database: &mut D,
|
database: &mut D,
|
||||||
progress_update: P,
|
progress_update: P,
|
||||||
@ -187,10 +216,10 @@ impl Blockchain for RpcBlockchain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.sync(database, progress_update)
|
self.wallet_sync(database, progress_update)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sync<D: BatchDatabase, P: 'static + Progress>(
|
fn wallet_sync<D: BatchDatabase, P: Progress>(
|
||||||
&self,
|
&self,
|
||||||
db: &mut D,
|
db: &mut D,
|
||||||
_progress_update: P,
|
_progress_update: P,
|
||||||
@ -324,29 +353,6 @@ impl Blockchain for RpcBlockchain {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
|
|
||||||
Ok(Some(self.client.get_raw_transaction(txid, None)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
|
|
||||||
Ok(self.client.send_raw_transaction(tx).map(|_| ())?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_height(&self) -> Result<u32, Error> {
|
|
||||||
Ok(self.client.get_blockchain_info().map(|i| i.blocks as u32)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error> {
|
|
||||||
let sat_per_kb = self
|
|
||||||
.client
|
|
||||||
.estimate_smart_fee(target as u16, None)?
|
|
||||||
.fee_rate
|
|
||||||
.ok_or(Error::FeeRateUnavailable)?
|
|
||||||
.as_sat() as f64;
|
|
||||||
|
|
||||||
Ok(FeeRate::from_sat_per_vb((sat_per_kb / 1000f64) as f32))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigurableBlockchain for RpcBlockchain {
|
impl ConfigurableBlockchain for RpcBlockchain {
|
||||||
|
@ -23,12 +23,12 @@
|
|||||||
//! # use bdk::database::{AnyDatabase, MemoryDatabase};
|
//! # use bdk::database::{AnyDatabase, MemoryDatabase};
|
||||||
//! # use bdk::{Wallet};
|
//! # use bdk::{Wallet};
|
||||||
//! let memory = MemoryDatabase::default();
|
//! let memory = MemoryDatabase::default();
|
||||||
//! let wallet_memory = Wallet::new_offline("...", None, Network::Testnet, memory)?;
|
//! let wallet_memory = Wallet::new("...", None, Network::Testnet, memory)?;
|
||||||
//!
|
//!
|
||||||
//! # #[cfg(feature = "key-value-db")]
|
//! # #[cfg(feature = "key-value-db")]
|
||||||
//! # {
|
//! # {
|
||||||
//! let sled = sled::open("my-database")?.open_tree("default_tree")?;
|
//! let sled = sled::open("my-database")?.open_tree("default_tree")?;
|
||||||
//! let wallet_sled = Wallet::new_offline("...", None, Network::Testnet, sled)?;
|
//! let wallet_sled = Wallet::new("...", None, Network::Testnet, sled)?;
|
||||||
//! # }
|
//! # }
|
||||||
//! # Ok::<(), bdk::Error>(())
|
//! # Ok::<(), bdk::Error>(())
|
||||||
//! ```
|
//! ```
|
||||||
@ -42,7 +42,7 @@
|
|||||||
//! # use bdk::{Wallet};
|
//! # use bdk::{Wallet};
|
||||||
//! let config = serde_json::from_str("...")?;
|
//! let config = serde_json::from_str("...")?;
|
||||||
//! let database = AnyDatabase::from_config(&config)?;
|
//! let database = AnyDatabase::from_config(&config)?;
|
||||||
//! let wallet = Wallet::new_offline("...", None, Network::Testnet, database)?;
|
//! let wallet = Wallet::new("...", None, Network::Testnet, database)?;
|
||||||
//! # Ok::<(), bdk::Error>(())
|
//! # Ok::<(), bdk::Error>(())
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
@ -554,7 +554,7 @@ macro_rules! doctest_wallet {
|
|||||||
Some(100),
|
Some(100),
|
||||||
);
|
);
|
||||||
|
|
||||||
$crate::Wallet::new_offline(
|
$crate::Wallet::new(
|
||||||
&descriptors.0,
|
&descriptors.0,
|
||||||
descriptors.1.as_ref(),
|
descriptors.1.as_ref(),
|
||||||
Network::Regtest,
|
Network::Regtest,
|
||||||
|
@ -79,7 +79,7 @@ impl<T: DescriptorTemplate> IntoWalletDescriptor for T {
|
|||||||
///
|
///
|
||||||
/// let key =
|
/// let key =
|
||||||
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
|
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
|
||||||
/// let wallet = Wallet::new_offline(
|
/// let wallet = Wallet::new(
|
||||||
/// P2Pkh(key),
|
/// P2Pkh(key),
|
||||||
/// None,
|
/// None,
|
||||||
/// Network::Testnet,
|
/// Network::Testnet,
|
||||||
@ -113,7 +113,7 @@ impl<K: IntoDescriptorKey<Legacy>> DescriptorTemplate for P2Pkh<K> {
|
|||||||
///
|
///
|
||||||
/// let key =
|
/// let key =
|
||||||
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
|
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
|
||||||
/// let wallet = Wallet::new_offline(
|
/// let wallet = Wallet::new(
|
||||||
/// P2Wpkh_P2Sh(key),
|
/// P2Wpkh_P2Sh(key),
|
||||||
/// None,
|
/// None,
|
||||||
/// Network::Testnet,
|
/// Network::Testnet,
|
||||||
@ -148,7 +148,7 @@ impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh_P2Sh<K> {
|
|||||||
///
|
///
|
||||||
/// let key =
|
/// let key =
|
||||||
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
|
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
|
||||||
/// let wallet = Wallet::new_offline(
|
/// let wallet = Wallet::new(
|
||||||
/// P2Wpkh(key),
|
/// P2Wpkh(key),
|
||||||
/// None,
|
/// None,
|
||||||
/// Network::Testnet,
|
/// Network::Testnet,
|
||||||
@ -186,7 +186,7 @@ impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh<K> {
|
|||||||
/// use bdk::template::Bip44;
|
/// use bdk::template::Bip44;
|
||||||
///
|
///
|
||||||
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
|
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
|
||||||
/// let wallet = Wallet::new_offline(
|
/// let wallet = Wallet::new(
|
||||||
/// Bip44(key.clone(), KeychainKind::External),
|
/// Bip44(key.clone(), KeychainKind::External),
|
||||||
/// Some(Bip44(key, KeychainKind::Internal)),
|
/// Some(Bip44(key, KeychainKind::Internal)),
|
||||||
/// Network::Testnet,
|
/// Network::Testnet,
|
||||||
@ -226,7 +226,7 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44<K> {
|
|||||||
///
|
///
|
||||||
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU")?;
|
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU")?;
|
||||||
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
|
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
|
||||||
/// let wallet = Wallet::new_offline(
|
/// let wallet = Wallet::new(
|
||||||
/// Bip44Public(key.clone(), fingerprint, KeychainKind::External),
|
/// Bip44Public(key.clone(), fingerprint, KeychainKind::External),
|
||||||
/// Some(Bip44Public(key, fingerprint, KeychainKind::Internal)),
|
/// Some(Bip44Public(key, fingerprint, KeychainKind::Internal)),
|
||||||
/// Network::Testnet,
|
/// Network::Testnet,
|
||||||
@ -262,7 +262,7 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44Public<K> {
|
|||||||
/// use bdk::template::Bip49;
|
/// use bdk::template::Bip49;
|
||||||
///
|
///
|
||||||
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
|
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
|
||||||
/// let wallet = Wallet::new_offline(
|
/// let wallet = Wallet::new(
|
||||||
/// Bip49(key.clone(), KeychainKind::External),
|
/// Bip49(key.clone(), KeychainKind::External),
|
||||||
/// Some(Bip49(key, KeychainKind::Internal)),
|
/// Some(Bip49(key, KeychainKind::Internal)),
|
||||||
/// Network::Testnet,
|
/// Network::Testnet,
|
||||||
@ -302,7 +302,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49<K> {
|
|||||||
///
|
///
|
||||||
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L")?;
|
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L")?;
|
||||||
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
|
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
|
||||||
/// let wallet = Wallet::new_offline(
|
/// let wallet = Wallet::new(
|
||||||
/// Bip49Public(key.clone(), fingerprint, KeychainKind::External),
|
/// Bip49Public(key.clone(), fingerprint, KeychainKind::External),
|
||||||
/// Some(Bip49Public(key, fingerprint, KeychainKind::Internal)),
|
/// Some(Bip49Public(key, fingerprint, KeychainKind::Internal)),
|
||||||
/// Network::Testnet,
|
/// Network::Testnet,
|
||||||
@ -338,7 +338,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49Public<K> {
|
|||||||
/// use bdk::template::Bip84;
|
/// use bdk::template::Bip84;
|
||||||
///
|
///
|
||||||
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
|
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
|
||||||
/// let wallet = Wallet::new_offline(
|
/// let wallet = Wallet::new(
|
||||||
/// Bip84(key.clone(), KeychainKind::External),
|
/// Bip84(key.clone(), KeychainKind::External),
|
||||||
/// Some(Bip84(key, KeychainKind::Internal)),
|
/// Some(Bip84(key, KeychainKind::Internal)),
|
||||||
/// Network::Testnet,
|
/// Network::Testnet,
|
||||||
@ -378,7 +378,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84<K> {
|
|||||||
///
|
///
|
||||||
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
|
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
|
||||||
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
|
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
|
||||||
/// let wallet = Wallet::new_offline(
|
/// let wallet = Wallet::new(
|
||||||
/// Bip84Public(key.clone(), fingerprint, KeychainKind::External),
|
/// Bip84Public(key.clone(), fingerprint, KeychainKind::External),
|
||||||
/// Some(Bip84Public(key, fingerprint, KeychainKind::Internal)),
|
/// Some(Bip84Public(key, fingerprint, KeychainKind::Internal)),
|
||||||
/// Network::Testnet,
|
/// Network::Testnet,
|
||||||
|
12
src/lib.rs
12
src/lib.rs
@ -60,15 +60,15 @@ use bdk::electrum_client::Client;
|
|||||||
|
|
||||||
fn main() -> Result<(), bdk::Error> {
|
fn main() -> Result<(), bdk::Error> {
|
||||||
let client = Client::new("ssl://electrum.blockstream.info:60002")?;
|
let client = Client::new("ssl://electrum.blockstream.info:60002")?;
|
||||||
|
let blockchain = ElectrumBlockchain::from(client);
|
||||||
let wallet = Wallet::new(
|
let wallet = Wallet::new(
|
||||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||||
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
|
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
|
||||||
bitcoin::Network::Testnet,
|
bitcoin::Network::Testnet,
|
||||||
MemoryDatabase::default(),
|
MemoryDatabase::default(),
|
||||||
ElectrumBlockchain::from(client)
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None)?;
|
wallet.sync(&blockchain, noop_progress(), None)?;
|
||||||
|
|
||||||
println!("Descriptor balance: {} SAT", wallet.get_balance()?);
|
println!("Descriptor balance: {} SAT", wallet.get_balance()?);
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ fn main() -> Result<(), bdk::Error> {
|
|||||||
//! use bdk::wallet::AddressIndex::New;
|
//! use bdk::wallet::AddressIndex::New;
|
||||||
//!
|
//!
|
||||||
//! fn main() -> Result<(), bdk::Error> {
|
//! fn main() -> Result<(), bdk::Error> {
|
||||||
//! let wallet = Wallet::new_offline(
|
//! let wallet = Wallet::new(
|
||||||
//! "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
//! "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||||
//! Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
|
//! Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
|
||||||
//! bitcoin::Network::Testnet,
|
//! bitcoin::Network::Testnet,
|
||||||
@ -123,10 +123,10 @@ fn main() -> Result<(), bdk::Error> {
|
|||||||
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
|
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
|
||||||
bitcoin::Network::Testnet,
|
bitcoin::Network::Testnet,
|
||||||
MemoryDatabase::default(),
|
MemoryDatabase::default(),
|
||||||
ElectrumBlockchain::from(client)
|
|
||||||
)?;
|
)?;
|
||||||
|
let blockchain = ElectrumBlockchain::from(client);
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None)?;
|
wallet.sync(&blockchain, noop_progress(), None)?;
|
||||||
|
|
||||||
let send_to = wallet.get_address(New)?;
|
let send_to = wallet.get_address(New)?;
|
||||||
let (psbt, details) = {
|
let (psbt, details) = {
|
||||||
@ -160,7 +160,7 @@ fn main() -> Result<(), bdk::Error> {
|
|||||||
//! use bdk::database::MemoryDatabase;
|
//! use bdk::database::MemoryDatabase;
|
||||||
//!
|
//!
|
||||||
//! fn main() -> Result<(), bdk::Error> {
|
//! fn main() -> Result<(), bdk::Error> {
|
||||||
//! let wallet = Wallet::new_offline(
|
//! let wallet = Wallet::new(
|
||||||
//! "wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)",
|
//! "wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)",
|
||||||
//! Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"),
|
//! Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"),
|
||||||
//! bitcoin::Network::Testnet,
|
//! bitcoin::Network::Testnet,
|
||||||
|
@ -361,7 +361,7 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
mod bdk_blockchain_tests {
|
mod bdk_blockchain_tests {
|
||||||
use $crate::bitcoin::{Transaction, Network};
|
use $crate::bitcoin::{Transaction, Network};
|
||||||
use $crate::testutils::blockchain_tests::TestClient;
|
use $crate::testutils::blockchain_tests::TestClient;
|
||||||
use $crate::blockchain::noop_progress;
|
use $crate::blockchain::{Blockchain, noop_progress};
|
||||||
use $crate::database::MemoryDatabase;
|
use $crate::database::MemoryDatabase;
|
||||||
use $crate::types::KeychainKind;
|
use $crate::types::KeychainKind;
|
||||||
use $crate::{Wallet, FeeRate};
|
use $crate::{Wallet, FeeRate};
|
||||||
@ -375,11 +375,11 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
$block
|
$block
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_wallet_from_descriptors(descriptors: &(String, Option<String>), test_client: &TestClient) -> Wallet<$blockchain, MemoryDatabase> {
|
fn get_wallet_from_descriptors(descriptors: &(String, Option<String>)) -> Wallet<MemoryDatabase> {
|
||||||
Wallet::new(&descriptors.0.to_string(), descriptors.1.as_ref(), Network::Regtest, MemoryDatabase::new(), get_blockchain(test_client)).unwrap()
|
Wallet::new(&descriptors.0.to_string(), descriptors.1.as_ref(), Network::Regtest, MemoryDatabase::new()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_single_sig() -> (Wallet<$blockchain, MemoryDatabase>, (String, Option<String>), TestClient) {
|
fn init_single_sig() -> (Wallet<MemoryDatabase>, $blockchain, (String, Option<String>), TestClient) {
|
||||||
let _ = env_logger::try_init();
|
let _ = env_logger::try_init();
|
||||||
|
|
||||||
let descriptors = testutils! {
|
let descriptors = testutils! {
|
||||||
@ -387,13 +387,14 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let test_client = TestClient::default();
|
let test_client = TestClient::default();
|
||||||
let wallet = get_wallet_from_descriptors(&descriptors, &test_client);
|
let blockchain = get_blockchain(&test_client);
|
||||||
|
let wallet = get_wallet_from_descriptors(&descriptors);
|
||||||
|
|
||||||
// rpc need to call import_multi before receiving any tx, otherwise will not see tx in the mempool
|
// rpc need to call import_multi before receiving any tx, otherwise will not see tx in the mempool
|
||||||
#[cfg(feature = "test-rpc")]
|
#[cfg(feature = "test-rpc")]
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
|
|
||||||
(wallet, descriptors, test_client)
|
(wallet, blockchain, descriptors, test_client)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -401,7 +402,7 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use crate::database::Database;
|
use crate::database::Database;
|
||||||
|
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
let tx = testutils! {
|
let tx = testutils! {
|
||||||
@tx ( (@external descriptors, 0) => 50_000 )
|
@tx ( (@external descriptors, 0) => 50_000 )
|
||||||
@ -414,7 +415,7 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
#[cfg(not(feature = "test-rpc"))]
|
#[cfg(not(feature = "test-rpc"))]
|
||||||
assert!(wallet.database().deref().get_sync_time().unwrap().is_none(), "initial sync_time not none");
|
assert!(wallet.database().deref().get_sync_time().unwrap().is_none(), "initial sync_time not none");
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert!(wallet.database().deref().get_sync_time().unwrap().is_some(), "sync_time hasn't been updated");
|
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");
|
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
||||||
@ -429,7 +430,7 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sync_stop_gap_20() {
|
fn test_sync_stop_gap_20() {
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
test_client.receive(testutils! {
|
||||||
@tx ( (@external descriptors, 5) => 50_000 )
|
@tx ( (@external descriptors, 5) => 50_000 )
|
||||||
@ -438,7 +439,7 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
@tx ( (@external descriptors, 25) => 50_000 )
|
@tx ( (@external descriptors, 25) => 50_000 )
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 100_000, "incorrect balance");
|
assert_eq!(wallet.get_balance().unwrap(), 100_000, "incorrect balance");
|
||||||
assert_eq!(wallet.list_transactions(false).unwrap().len(), 2, "incorrect number of txs");
|
assert_eq!(wallet.list_transactions(false).unwrap().len(), 2, "incorrect number of txs");
|
||||||
@ -446,16 +447,16 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sync_before_and_after_receive() {
|
fn test_sync_before_and_after_receive() {
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 0);
|
assert_eq!(wallet.get_balance().unwrap(), 0);
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
test_client.receive(testutils! {
|
||||||
@tx ( (@external descriptors, 0) => 50_000 )
|
@tx ( (@external descriptors, 0) => 50_000 )
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
||||||
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1, "incorrect number of txs");
|
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1, "incorrect number of txs");
|
||||||
@ -463,13 +464,13 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sync_multiple_outputs_same_tx() {
|
fn test_sync_multiple_outputs_same_tx() {
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
let txid = test_client.receive(testutils! {
|
let txid = test_client.receive(testutils! {
|
||||||
@tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000, (@external descriptors, 5) => 30_000 )
|
@tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000, (@external descriptors, 5) => 30_000 )
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 105_000, "incorrect balance");
|
assert_eq!(wallet.get_balance().unwrap(), 105_000, "incorrect balance");
|
||||||
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1, "incorrect number of txs");
|
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1, "incorrect number of txs");
|
||||||
@ -484,7 +485,7 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sync_receive_multi() {
|
fn test_sync_receive_multi() {
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
test_client.receive(testutils! {
|
||||||
@tx ( (@external descriptors, 0) => 50_000 )
|
@tx ( (@external descriptors, 0) => 50_000 )
|
||||||
@ -493,7 +494,7 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
@tx ( (@external descriptors, 5) => 25_000 )
|
@tx ( (@external descriptors, 5) => 25_000 )
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 75_000, "incorrect balance");
|
assert_eq!(wallet.get_balance().unwrap(), 75_000, "incorrect balance");
|
||||||
assert_eq!(wallet.list_transactions(false).unwrap().len(), 2, "incorrect number of txs");
|
assert_eq!(wallet.list_transactions(false).unwrap().len(), 2, "incorrect number of txs");
|
||||||
@ -502,32 +503,32 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sync_address_reuse() {
|
fn test_sync_address_reuse() {
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
test_client.receive(testutils! {
|
||||||
@tx ( (@external descriptors, 0) => 50_000 )
|
@tx ( (@external descriptors, 0) => 50_000 )
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
test_client.receive(testutils! {
|
||||||
@tx ( (@external descriptors, 0) => 25_000 )
|
@tx ( (@external descriptors, 0) => 25_000 )
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 75_000, "incorrect balance");
|
assert_eq!(wallet.get_balance().unwrap(), 75_000, "incorrect balance");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sync_receive_rbf_replaced() {
|
fn test_sync_receive_rbf_replaced() {
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
let txid = test_client.receive(testutils! {
|
let txid = test_client.receive(testutils! {
|
||||||
@tx ( (@external descriptors, 0) => 50_000 ) ( @replaceable true )
|
@tx ( (@external descriptors, 0) => 50_000 ) ( @replaceable true )
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
||||||
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1, "incorrect number of txs");
|
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1, "incorrect number of txs");
|
||||||
@ -541,7 +542,7 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
|
|
||||||
let new_txid = test_client.bump_fee(&txid);
|
let new_txid = test_client.bump_fee(&txid);
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance after bump");
|
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");
|
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1, "incorrect number of txs after bump");
|
||||||
@ -559,13 +560,13 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
#[cfg(not(feature = "esplora"))]
|
#[cfg(not(feature = "esplora"))]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sync_reorg_block() {
|
fn test_sync_reorg_block() {
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
let txid = test_client.receive(testutils! {
|
let txid = test_client.receive(testutils! {
|
||||||
@tx ( (@external descriptors, 0) => 50_000 ) ( @confirmations 1 ) ( @replaceable true )
|
@tx ( (@external descriptors, 0) => 50_000 ) ( @confirmations 1 ) ( @replaceable true )
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
||||||
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1, "incorrect number of txs");
|
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1, "incorrect number of txs");
|
||||||
@ -578,7 +579,7 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
// Invalidate 1 block
|
// Invalidate 1 block
|
||||||
test_client.invalidate(1);
|
test_client.invalidate(1);
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance after invalidate");
|
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance after invalidate");
|
||||||
|
|
||||||
@ -589,15 +590,15 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sync_after_send() {
|
fn test_sync_after_send() {
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
println!("{}", descriptors.0);
|
println!("{}", descriptors.0);
|
||||||
let node_addr = test_client.get_node_address(None);
|
let node_addr = test_client.get_node_address(None);
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
test_client.receive(testutils! {
|
||||||
@tx ( (@external descriptors, 0) => 50_000 )
|
@tx ( (@external descriptors, 0) => 50_000 )
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
||||||
|
|
||||||
let mut builder = wallet.build_tx();
|
let mut builder = wallet.build_tx();
|
||||||
@ -607,8 +608,8 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
let tx = psbt.extract_tx();
|
let tx = psbt.extract_tx();
|
||||||
println!("{}", bitcoin::consensus::encode::serialize_hex(&tx));
|
println!("{}", bitcoin::consensus::encode::serialize_hex(&tx));
|
||||||
wallet.broadcast(&tx).unwrap();
|
blockchain.broadcast(&tx).unwrap();
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), details.received, "incorrect balance after send");
|
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");
|
assert_eq!(wallet.list_transactions(false).unwrap().len(), 2, "incorrect number of txs");
|
||||||
@ -619,16 +620,16 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
/// The coins should only be received once!
|
/// The coins should only be received once!
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sync_double_receive() {
|
fn test_sync_double_receive() {
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
let receiver_wallet = get_wallet_from_descriptors(&("wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)".to_string(), None), &test_client);
|
let receiver_wallet = get_wallet_from_descriptors(&("wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)".to_string(), None));
|
||||||
// need to sync so rpc can start watching
|
// need to sync so rpc can start watching
|
||||||
receiver_wallet.sync(noop_progress(), None).unwrap();
|
receiver_wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
test_client.receive(testutils! {
|
||||||
@tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000 ) (@confirmations 1)
|
@tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000 ) (@confirmations 1)
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 75_000, "incorrect balance");
|
assert_eq!(wallet.get_balance().unwrap(), 75_000, "incorrect balance");
|
||||||
let target_addr = receiver_wallet.get_address($crate::wallet::AddressIndex::New).unwrap().address;
|
let target_addr = receiver_wallet.get_address($crate::wallet::AddressIndex::New).unwrap().address;
|
||||||
|
|
||||||
@ -650,16 +651,16 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
psbt.extract_tx()
|
psbt.extract_tx()
|
||||||
};
|
};
|
||||||
|
|
||||||
wallet.broadcast(&tx1).unwrap();
|
blockchain.broadcast(&tx1).unwrap();
|
||||||
wallet.broadcast(&tx2).unwrap();
|
blockchain.broadcast(&tx2).unwrap();
|
||||||
|
|
||||||
receiver_wallet.sync(noop_progress(), None).unwrap();
|
receiver_wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(receiver_wallet.get_balance().unwrap(), 49_000, "should have received coins once and only once");
|
assert_eq!(receiver_wallet.get_balance().unwrap(), 49_000, "should have received coins once and only once");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sync_many_sends_to_a_single_address() {
|
fn test_sync_many_sends_to_a_single_address() {
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
for _ in 0..4 {
|
for _ in 0..4 {
|
||||||
// split this up into multiple blocks so rpc doesn't get angry
|
// split this up into multiple blocks so rpc doesn't get angry
|
||||||
@ -678,22 +679,22 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 100_000);
|
assert_eq!(wallet.get_balance().unwrap(), 100_000);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_update_confirmation_time_after_generate() {
|
fn test_update_confirmation_time_after_generate() {
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
println!("{}", descriptors.0);
|
println!("{}", descriptors.0);
|
||||||
let node_addr = test_client.get_node_address(None);
|
let node_addr = test_client.get_node_address(None);
|
||||||
|
|
||||||
let received_txid = test_client.receive(testutils! {
|
let received_txid = test_client.receive(testutils! {
|
||||||
@tx ( (@external descriptors, 0) => 50_000 )
|
@tx ( (@external descriptors, 0) => 50_000 )
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
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::<std::collections::HashMap<_, _>>();
|
let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::<std::collections::HashMap<_, _>>();
|
||||||
@ -701,7 +702,7 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
assert!(details.confirmation_time.is_none());
|
assert!(details.confirmation_time.is_none());
|
||||||
|
|
||||||
test_client.generate(1, Some(node_addr));
|
test_client.generate(1, Some(node_addr));
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
|
|
||||||
let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::<std::collections::HashMap<_, _>>();
|
let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::<std::collections::HashMap<_, _>>();
|
||||||
let details = tx_map.get(&received_txid).unwrap();
|
let details = tx_map.get(&received_txid).unwrap();
|
||||||
@ -711,13 +712,13 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sync_outgoing_from_scratch() {
|
fn test_sync_outgoing_from_scratch() {
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
let node_addr = test_client.get_node_address(None);
|
let node_addr = test_client.get_node_address(None);
|
||||||
let received_txid = test_client.receive(testutils! {
|
let received_txid = test_client.receive(testutils! {
|
||||||
@tx ( (@external descriptors, 0) => 50_000 )
|
@tx ( (@external descriptors, 0) => 50_000 )
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
||||||
|
|
||||||
let mut builder = wallet.build_tx();
|
let mut builder = wallet.build_tx();
|
||||||
@ -726,25 +727,26 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
|
|
||||||
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
let sent_txid = wallet.broadcast(&psbt.extract_tx()).unwrap();
|
let sent_tx = psbt.extract_tx();
|
||||||
|
blockchain.broadcast(&sent_tx).unwrap();
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), details.received, "incorrect balance after receive");
|
assert_eq!(wallet.get_balance().unwrap(), details.received, "incorrect balance after receive");
|
||||||
|
|
||||||
// empty wallet
|
// empty wallet
|
||||||
let wallet = get_wallet_from_descriptors(&descriptors, &test_client);
|
let wallet = get_wallet_from_descriptors(&descriptors);
|
||||||
|
|
||||||
#[cfg(feature = "rpc")] // rpc cannot see mempool tx before importmulti
|
#[cfg(feature = "rpc")] // rpc cannot see mempool tx before importmulti
|
||||||
test_client.generate(1, Some(node_addr));
|
test_client.generate(1, Some(node_addr));
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::<std::collections::HashMap<_, _>>();
|
let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::<std::collections::HashMap<_, _>>();
|
||||||
|
|
||||||
let received = tx_map.get(&received_txid).unwrap();
|
let received = tx_map.get(&received_txid).unwrap();
|
||||||
assert_eq!(received.received, 50_000, "incorrect received from receiver");
|
assert_eq!(received.received, 50_000, "incorrect received from receiver");
|
||||||
assert_eq!(received.sent, 0, "incorrect sent from receiver");
|
assert_eq!(received.sent, 0, "incorrect sent from receiver");
|
||||||
|
|
||||||
let sent = tx_map.get(&sent_txid).unwrap();
|
let sent = tx_map.get(&sent_tx.txid()).unwrap();
|
||||||
assert_eq!(sent.received, details.received, "incorrect received from sender");
|
assert_eq!(sent.received, details.received, "incorrect received from sender");
|
||||||
assert_eq!(sent.sent, details.sent, "incorrect sent from sender");
|
assert_eq!(sent.sent, details.sent, "incorrect sent from sender");
|
||||||
assert_eq!(sent.fee.unwrap_or(0), details.fee.unwrap_or(0), "incorrect fees from sender");
|
assert_eq!(sent.fee.unwrap_or(0), details.fee.unwrap_or(0), "incorrect fees from sender");
|
||||||
@ -752,14 +754,14 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sync_long_change_chain() {
|
fn test_sync_long_change_chain() {
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
let node_addr = test_client.get_node_address(None);
|
let node_addr = test_client.get_node_address(None);
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
test_client.receive(testutils! {
|
||||||
@tx ( (@external descriptors, 0) => 50_000 )
|
@tx ( (@external descriptors, 0) => 50_000 )
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
||||||
|
|
||||||
let mut total_sent = 0;
|
let mut total_sent = 0;
|
||||||
@ -769,38 +771,38 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
let (mut psbt, details) = builder.finish().unwrap();
|
let (mut psbt, details) = builder.finish().unwrap();
|
||||||
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
wallet.broadcast(&psbt.extract_tx()).unwrap();
|
blockchain.broadcast(&psbt.extract_tx()).unwrap();
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
|
|
||||||
total_sent += 5_000 + details.fee.unwrap_or(0);
|
total_sent += 5_000 + details.fee.unwrap_or(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000 - total_sent, "incorrect balance after chain");
|
assert_eq!(wallet.get_balance().unwrap(), 50_000 - total_sent, "incorrect balance after chain");
|
||||||
|
|
||||||
// empty wallet
|
// empty wallet
|
||||||
|
|
||||||
let wallet = get_wallet_from_descriptors(&descriptors, &test_client);
|
let wallet = get_wallet_from_descriptors(&descriptors);
|
||||||
|
|
||||||
#[cfg(feature = "rpc")] // rpc cannot see mempool tx before importmulti
|
#[cfg(feature = "rpc")] // rpc cannot see mempool tx before importmulti
|
||||||
test_client.generate(1, Some(node_addr));
|
test_client.generate(1, Some(node_addr));
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000 - total_sent, "incorrect balance empty wallet");
|
assert_eq!(wallet.get_balance().unwrap(), 50_000 - total_sent, "incorrect balance empty wallet");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sync_bump_fee_basic() {
|
fn test_sync_bump_fee_basic() {
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
let node_addr = test_client.get_node_address(None);
|
let node_addr = test_client.get_node_address(None);
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
test_client.receive(testutils! {
|
||||||
@tx ( (@external descriptors, 0) => 50_000 ) (@confirmations 1)
|
@tx ( (@external descriptors, 0) => 50_000 ) (@confirmations 1)
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
||||||
|
|
||||||
let mut builder = wallet.build_tx();
|
let mut builder = wallet.build_tx();
|
||||||
@ -808,8 +810,8 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
let (mut psbt, details) = builder.finish().unwrap();
|
let (mut psbt, details) = builder.finish().unwrap();
|
||||||
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
wallet.broadcast(&psbt.extract_tx()).unwrap();
|
blockchain.broadcast(&psbt.extract_tx()).unwrap();
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).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(), 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");
|
assert_eq!(wallet.get_balance().unwrap(), details.received, "incorrect balance from received");
|
||||||
|
|
||||||
@ -818,8 +820,8 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
let (mut new_psbt, new_details) = builder.finish().unwrap();
|
let (mut new_psbt, new_details) = builder.finish().unwrap();
|
||||||
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
wallet.broadcast(&new_psbt.extract_tx()).unwrap();
|
blockchain.broadcast(&new_psbt.extract_tx()).unwrap();
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).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(), 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");
|
assert_eq!(wallet.get_balance().unwrap(), new_details.received, "incorrect balance from received after bump");
|
||||||
|
|
||||||
@ -828,14 +830,14 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sync_bump_fee_remove_change() {
|
fn test_sync_bump_fee_remove_change() {
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
let node_addr = test_client.get_node_address(None);
|
let node_addr = test_client.get_node_address(None);
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
test_client.receive(testutils! {
|
||||||
@tx ( (@external descriptors, 0) => 50_000 ) (@confirmations 1)
|
@tx ( (@external descriptors, 0) => 50_000 ) (@confirmations 1)
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
||||||
|
|
||||||
let mut builder = wallet.build_tx();
|
let mut builder = wallet.build_tx();
|
||||||
@ -843,8 +845,8 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
let (mut psbt, details) = builder.finish().unwrap();
|
let (mut psbt, details) = builder.finish().unwrap();
|
||||||
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
wallet.broadcast(&psbt.extract_tx()).unwrap();
|
blockchain.broadcast(&psbt.extract_tx()).unwrap();
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 1_000 - details.fee.unwrap_or(0), "incorrect balance after send");
|
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");
|
assert_eq!(wallet.get_balance().unwrap(), details.received, "incorrect received after send");
|
||||||
|
|
||||||
@ -853,8 +855,8 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
let (mut new_psbt, new_details) = builder.finish().unwrap();
|
let (mut new_psbt, new_details) = builder.finish().unwrap();
|
||||||
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
wallet.broadcast(&new_psbt.extract_tx()).unwrap();
|
blockchain.broadcast(&new_psbt.extract_tx()).unwrap();
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 0, "incorrect balance after change removal");
|
assert_eq!(wallet.get_balance().unwrap(), 0, "incorrect balance after change removal");
|
||||||
assert_eq!(new_details.received, 0, "incorrect received after change removal");
|
assert_eq!(new_details.received, 0, "incorrect received after change removal");
|
||||||
|
|
||||||
@ -863,14 +865,14 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sync_bump_fee_add_input_simple() {
|
fn test_sync_bump_fee_add_input_simple() {
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
let node_addr = test_client.get_node_address(None);
|
let node_addr = test_client.get_node_address(None);
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
test_client.receive(testutils! {
|
||||||
@tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000 ) (@confirmations 1)
|
@tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000 ) (@confirmations 1)
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 75_000, "incorrect balance");
|
assert_eq!(wallet.get_balance().unwrap(), 75_000, "incorrect balance");
|
||||||
|
|
||||||
let mut builder = wallet.build_tx();
|
let mut builder = wallet.build_tx();
|
||||||
@ -878,8 +880,8 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
let (mut psbt, details) = builder.finish().unwrap();
|
let (mut psbt, details) = builder.finish().unwrap();
|
||||||
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
wallet.broadcast(&psbt.extract_tx()).unwrap();
|
blockchain.broadcast(&psbt.extract_tx()).unwrap();
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fee.unwrap_or(0), "incorrect balance after send");
|
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");
|
assert_eq!(details.received, 1_000 - details.fee.unwrap_or(0), "incorrect received after send");
|
||||||
|
|
||||||
@ -888,22 +890,22 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
let (mut new_psbt, new_details) = builder.finish().unwrap();
|
let (mut new_psbt, new_details) = builder.finish().unwrap();
|
||||||
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
wallet.broadcast(&new_psbt.extract_tx()).unwrap();
|
blockchain.broadcast(&new_psbt.extract_tx()).unwrap();
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(new_details.sent, 75_000, "incorrect sent");
|
assert_eq!(new_details.sent, 75_000, "incorrect sent");
|
||||||
assert_eq!(wallet.get_balance().unwrap(), new_details.received, "incorrect balance after add input");
|
assert_eq!(wallet.get_balance().unwrap(), new_details.received, "incorrect balance after add input");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sync_bump_fee_add_input_no_change() {
|
fn test_sync_bump_fee_add_input_no_change() {
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
let node_addr = test_client.get_node_address(None);
|
let node_addr = test_client.get_node_address(None);
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
test_client.receive(testutils! {
|
||||||
@tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000 ) (@confirmations 1)
|
@tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000 ) (@confirmations 1)
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 75_000, "incorrect balance");
|
assert_eq!(wallet.get_balance().unwrap(), 75_000, "incorrect balance");
|
||||||
|
|
||||||
let mut builder = wallet.build_tx();
|
let mut builder = wallet.build_tx();
|
||||||
@ -911,8 +913,8 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
let (mut psbt, details) = builder.finish().unwrap();
|
let (mut psbt, details) = builder.finish().unwrap();
|
||||||
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
wallet.broadcast(&psbt.extract_tx()).unwrap();
|
blockchain.broadcast(&psbt.extract_tx()).unwrap();
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fee.unwrap_or(0), "incorrect balance after send");
|
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");
|
assert_eq!(details.received, 1_000 - details.fee.unwrap_or(0), "incorrect received after send");
|
||||||
|
|
||||||
@ -923,8 +925,8 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
|
|
||||||
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
wallet.broadcast(&new_psbt.extract_tx()).unwrap();
|
blockchain.broadcast(&new_psbt.extract_tx()).unwrap();
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(new_details.sent, 75_000, "incorrect sent");
|
assert_eq!(new_details.sent, 75_000, "incorrect sent");
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 0, "incorrect balance after add input");
|
assert_eq!(wallet.get_balance().unwrap(), 0, "incorrect balance after add input");
|
||||||
assert_eq!(new_details.received, 0, "incorrect received after add input");
|
assert_eq!(new_details.received, 0, "incorrect received after add input");
|
||||||
@ -933,13 +935,13 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_add_data() {
|
fn test_add_data() {
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
let node_addr = test_client.get_node_address(None);
|
let node_addr = test_client.get_node_address(None);
|
||||||
let _ = test_client.receive(testutils! {
|
let _ = test_client.receive(testutils! {
|
||||||
@tx ( (@external descriptors, 0) => 50_000 )
|
@tx ( (@external descriptors, 0) => 50_000 )
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
|
||||||
|
|
||||||
let mut builder = wallet.build_tx();
|
let mut builder = wallet.build_tx();
|
||||||
@ -952,22 +954,22 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
let tx = psbt.extract_tx();
|
let tx = psbt.extract_tx();
|
||||||
let serialized_tx = bitcoin::consensus::encode::serialize(&tx);
|
let serialized_tx = bitcoin::consensus::encode::serialize(&tx);
|
||||||
assert!(serialized_tx.windows(data.len()).any(|e| e==data), "cannot find op_return data in transaction");
|
assert!(serialized_tx.windows(data.len()).any(|e| e==data), "cannot find op_return data in transaction");
|
||||||
let sent_txid = wallet.broadcast(&tx).unwrap();
|
blockchain.broadcast(&tx).unwrap();
|
||||||
test_client.generate(1, Some(node_addr));
|
test_client.generate(1, Some(node_addr));
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000 - details.fee.unwrap_or(0), "incorrect balance after send");
|
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::<std::collections::HashMap<_, _>>();
|
let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::<std::collections::HashMap<_, _>>();
|
||||||
let _ = tx_map.get(&sent_txid).unwrap();
|
let _ = tx_map.get(&tx.txid()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sync_receive_coinbase() {
|
fn test_sync_receive_coinbase() {
|
||||||
let (wallet, _, mut test_client) = init_single_sig();
|
let (wallet, blockchain, _, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
let wallet_addr = wallet.get_address($crate::wallet::AddressIndex::New).unwrap().address;
|
let wallet_addr = wallet.get_address($crate::wallet::AddressIndex::New).unwrap().address;
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 0, "incorrect balance");
|
assert_eq!(wallet.get_balance().unwrap(), 0, "incorrect balance");
|
||||||
|
|
||||||
test_client.generate(1, Some(wallet_addr));
|
test_client.generate(1, Some(wallet_addr));
|
||||||
@ -980,7 +982,7 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert!(wallet.get_balance().unwrap() > 0, "incorrect balance after receiving coinbase");
|
assert!(wallet.get_balance().unwrap() > 0, "incorrect balance after receiving coinbase");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -993,7 +995,7 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
use bitcoincore_rpc::jsonrpc::serde_json::Value;
|
use bitcoincore_rpc::jsonrpc::serde_json::Value;
|
||||||
use bitcoincore_rpc::{Auth, Client, RpcApi};
|
use bitcoincore_rpc::{Auth, Client, RpcApi};
|
||||||
|
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
let (wallet, blockchain, descriptors, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
// TODO remove once rust-bitcoincore-rpc with PR 199 released
|
// TODO remove once rust-bitcoincore-rpc with PR 199 released
|
||||||
// https://github.com/rust-bitcoin/rust-bitcoincore-rpc/pull/199
|
// https://github.com/rust-bitcoin/rust-bitcoincore-rpc/pull/199
|
||||||
@ -1056,7 +1058,7 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
@tx ( (@external descriptors, 0) => 50_000 )
|
@tx ( (@external descriptors, 0) => 50_000 )
|
||||||
});
|
});
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000, "wallet has incorrect balance");
|
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
|
// 4. Send 25_000 sats from test BDK wallet to test bitcoind node taproot wallet
|
||||||
@ -1067,8 +1069,8 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
||||||
assert!(finalized, "wallet cannot finalize transaction");
|
assert!(finalized, "wallet cannot finalize transaction");
|
||||||
let tx = psbt.extract_tx();
|
let tx = psbt.extract_tx();
|
||||||
wallet.broadcast(&tx).unwrap();
|
blockchain.broadcast(&tx).unwrap();
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(&blockchain, noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), details.received, "wallet has incorrect balance after send");
|
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_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");
|
assert_eq!(wallet.list_unspent().unwrap().len(), 1, "wallet has incorrect number of unspents");
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
|
//! let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
|
||||||
//! let mut wallet = Wallet::new_offline(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
|
//! let mut wallet = Wallet::new(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
|
||||||
//! wallet.add_address_validator(Arc::new(PrintAddressAndContinue));
|
//! wallet.add_address_validator(Arc::new(PrintAddressAndContinue));
|
||||||
//!
|
//!
|
||||||
//! let address = wallet.get_address(New)?;
|
//! let address = wallet.get_address(New)?;
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
//! }"#;
|
//! }"#;
|
||||||
//!
|
//!
|
||||||
//! let import = WalletExport::from_str(import)?;
|
//! let import = WalletExport::from_str(import)?;
|
||||||
//! let wallet = Wallet::new_offline(
|
//! let wallet = Wallet::new(
|
||||||
//! &import.descriptor(),
|
//! &import.descriptor(),
|
||||||
//! import.change_descriptor().as_ref(),
|
//! import.change_descriptor().as_ref(),
|
||||||
//! Network::Testnet,
|
//! Network::Testnet,
|
||||||
@ -45,7 +45,7 @@
|
|||||||
//! # use bdk::database::*;
|
//! # use bdk::database::*;
|
||||||
//! # use bdk::wallet::export::*;
|
//! # use bdk::wallet::export::*;
|
||||||
//! # use bdk::*;
|
//! # use bdk::*;
|
||||||
//! let wallet = Wallet::new_offline(
|
//! let wallet = Wallet::new(
|
||||||
//! "wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/0/*)",
|
//! "wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/0/*)",
|
||||||
//! Some("wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/1/*)"),
|
//! Some("wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/1/*)"),
|
||||||
//! Network::Testnet,
|
//! Network::Testnet,
|
||||||
@ -111,8 +111,8 @@ impl WalletExport {
|
|||||||
///
|
///
|
||||||
/// If the database is empty or `include_blockheight` is false, the `blockheight` field
|
/// If the database is empty or `include_blockheight` is false, the `blockheight` field
|
||||||
/// returned will be `0`.
|
/// returned will be `0`.
|
||||||
pub fn export_wallet<B, D: BatchDatabase>(
|
pub fn export_wallet<D: BatchDatabase>(
|
||||||
wallet: &Wallet<B, D>,
|
wallet: &Wallet<D>,
|
||||||
label: &str,
|
label: &str,
|
||||||
include_blockheight: bool,
|
include_blockheight: bool,
|
||||||
) -> Result<Self, &'static str> {
|
) -> Result<Self, &'static str> {
|
||||||
@ -241,7 +241,7 @@ mod test {
|
|||||||
let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
|
let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
|
||||||
let change_descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/1/*)";
|
let change_descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/1/*)";
|
||||||
|
|
||||||
let wallet = Wallet::new_offline(
|
let wallet = Wallet::new(
|
||||||
descriptor,
|
descriptor,
|
||||||
Some(change_descriptor),
|
Some(change_descriptor),
|
||||||
Network::Bitcoin,
|
Network::Bitcoin,
|
||||||
@ -265,8 +265,7 @@ mod test {
|
|||||||
|
|
||||||
let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
|
let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
|
||||||
|
|
||||||
let wallet =
|
let wallet = Wallet::new(descriptor, None, Network::Bitcoin, get_test_db()).unwrap();
|
||||||
Wallet::new_offline(descriptor, None, Network::Bitcoin, get_test_db()).unwrap();
|
|
||||||
WalletExport::export_wallet(&wallet, "Test Label", true).unwrap();
|
WalletExport::export_wallet(&wallet, "Test Label", true).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,7 +278,7 @@ mod test {
|
|||||||
let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
|
let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
|
||||||
let change_descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/50'/0'/1/*)";
|
let change_descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/50'/0'/1/*)";
|
||||||
|
|
||||||
let wallet = Wallet::new_offline(
|
let wallet = Wallet::new(
|
||||||
descriptor,
|
descriptor,
|
||||||
Some(change_descriptor),
|
Some(change_descriptor),
|
||||||
Network::Bitcoin,
|
Network::Bitcoin,
|
||||||
@ -302,7 +301,7 @@ mod test {
|
|||||||
[c98b1535/48'/0'/0'/2']tpubDCDi5W4sP6zSnzJeowy8rQDVhBdRARaPhK1axABi8V1661wEPeanpEXj4ZLAUEoikVtoWcyK26TKKJSecSfeKxwHCcRrge9k1ybuiL71z4a/1/*\
|
[c98b1535/48'/0'/0'/2']tpubDCDi5W4sP6zSnzJeowy8rQDVhBdRARaPhK1axABi8V1661wEPeanpEXj4ZLAUEoikVtoWcyK26TKKJSecSfeKxwHCcRrge9k1ybuiL71z4a/1/*\
|
||||||
))";
|
))";
|
||||||
|
|
||||||
let wallet = Wallet::new_offline(
|
let wallet = Wallet::new(
|
||||||
descriptor,
|
descriptor,
|
||||||
Some(change_descriptor),
|
Some(change_descriptor),
|
||||||
Network::Testnet,
|
Network::Testnet,
|
||||||
@ -322,7 +321,7 @@ mod test {
|
|||||||
let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
|
let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
|
||||||
let change_descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/1/*)";
|
let change_descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/1/*)";
|
||||||
|
|
||||||
let wallet = Wallet::new_offline(
|
let wallet = Wallet::new(
|
||||||
descriptor,
|
descriptor,
|
||||||
Some(change_descriptor),
|
Some(change_descriptor),
|
||||||
Network::Bitcoin,
|
Network::Bitcoin,
|
||||||
|
@ -55,7 +55,7 @@ use signer::{SignOptions, Signer, SignerOrdering, SignersContainer};
|
|||||||
use tx_builder::{BumpFee, CreateTx, FeePolicy, TxBuilder, TxParams};
|
use tx_builder::{BumpFee, CreateTx, FeePolicy, TxBuilder, TxParams};
|
||||||
use utils::{check_nlocktime, check_nsequence_rbf, After, Older, SecpCtx};
|
use utils::{check_nlocktime, check_nsequence_rbf, After, Older, SecpCtx};
|
||||||
|
|
||||||
use crate::blockchain::{Blockchain, Progress};
|
use crate::blockchain::{GetHeight, Progress, WalletSync};
|
||||||
use crate::database::memory::MemoryDatabase;
|
use crate::database::memory::MemoryDatabase;
|
||||||
use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils, SyncTime};
|
use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils, SyncTime};
|
||||||
use crate::descriptor::derived::AsDerived;
|
use crate::descriptor::derived::AsDerived;
|
||||||
@ -75,16 +75,17 @@ const CACHE_ADDR_BATCH_SIZE: u32 = 100;
|
|||||||
|
|
||||||
/// A Bitcoin wallet
|
/// A Bitcoin wallet
|
||||||
///
|
///
|
||||||
/// A wallet takes descriptors, a [`database`](trait@crate::database::Database) and a
|
/// The `Wallet` struct acts as a way of coherently interfacing with output descriptors and related transactions.
|
||||||
/// [`blockchain`](trait@crate::blockchain::Blockchain) and implements the basic functions that a Bitcoin wallets
|
/// Its main components are:
|
||||||
/// needs to operate, like [generating addresses](Wallet::get_address), [returning the balance](Wallet::get_balance),
|
|
||||||
/// [creating transactions](Wallet::build_tx), etc.
|
|
||||||
///
|
///
|
||||||
/// A wallet can be either "online" if the [`blockchain`](crate::blockchain) type provided
|
/// 1. output *descriptors* from which it can derive addresses.
|
||||||
/// implements [`Blockchain`], or "offline" if it is the unit type `()`. Offline wallets only expose
|
/// 2. A [`Database`] where it tracks transactions and utxos related to the descriptors.
|
||||||
/// methods that don't need any interaction with the blockchain to work.
|
/// 3. [`Signer`]s that can contribute signatures to addresses instantiated from the descriptors.
|
||||||
|
///
|
||||||
|
/// [`Database`]: crate::database::Database
|
||||||
|
/// [`Signer`]: crate::signer::Signer
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Wallet<B, D> {
|
pub struct Wallet<D> {
|
||||||
descriptor: ExtendedDescriptor,
|
descriptor: ExtendedDescriptor,
|
||||||
change_descriptor: Option<ExtendedDescriptor>,
|
change_descriptor: Option<ExtendedDescriptor>,
|
||||||
|
|
||||||
@ -95,88 +96,11 @@ pub struct Wallet<B, D> {
|
|||||||
|
|
||||||
network: Network,
|
network: Network,
|
||||||
|
|
||||||
current_height: Option<u32>,
|
|
||||||
|
|
||||||
client: B,
|
|
||||||
database: RefCell<D>,
|
database: RefCell<D>,
|
||||||
|
|
||||||
secp: SecpCtx,
|
secp: SecpCtx,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D> Wallet<(), D>
|
|
||||||
where
|
|
||||||
D: BatchDatabase,
|
|
||||||
{
|
|
||||||
/// Create a new "offline" wallet
|
|
||||||
pub fn new_offline<E: IntoWalletDescriptor>(
|
|
||||||
descriptor: E,
|
|
||||||
change_descriptor: Option<E>,
|
|
||||||
network: Network,
|
|
||||||
database: D,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
Self::_new(descriptor, change_descriptor, network, database, (), None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B, D> Wallet<B, D>
|
|
||||||
where
|
|
||||||
D: BatchDatabase,
|
|
||||||
{
|
|
||||||
fn _new<E: IntoWalletDescriptor>(
|
|
||||||
descriptor: E,
|
|
||||||
change_descriptor: Option<E>,
|
|
||||||
network: Network,
|
|
||||||
mut database: D,
|
|
||||||
client: B,
|
|
||||||
current_height: Option<u32>,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
let secp = Secp256k1::new();
|
|
||||||
|
|
||||||
let (descriptor, keymap) = into_wallet_descriptor_checked(descriptor, &secp, network)?;
|
|
||||||
database.check_descriptor_checksum(
|
|
||||||
KeychainKind::External,
|
|
||||||
get_checksum(&descriptor.to_string())?.as_bytes(),
|
|
||||||
)?;
|
|
||||||
let signers = Arc::new(SignersContainer::from(keymap));
|
|
||||||
let (change_descriptor, change_signers) = match change_descriptor {
|
|
||||||
Some(desc) => {
|
|
||||||
let (change_descriptor, change_keymap) =
|
|
||||||
into_wallet_descriptor_checked(desc, &secp, network)?;
|
|
||||||
database.check_descriptor_checksum(
|
|
||||||
KeychainKind::Internal,
|
|
||||||
get_checksum(&change_descriptor.to_string())?.as_bytes(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let change_signers = Arc::new(SignersContainer::from(change_keymap));
|
|
||||||
// if !parsed.same_structure(descriptor.as_ref()) {
|
|
||||||
// return Err(Error::DifferentDescriptorStructure);
|
|
||||||
// }
|
|
||||||
|
|
||||||
(Some(change_descriptor), change_signers)
|
|
||||||
}
|
|
||||||
None => (None, Arc::new(SignersContainer::new())),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Wallet {
|
|
||||||
descriptor,
|
|
||||||
change_descriptor,
|
|
||||||
signers,
|
|
||||||
change_signers,
|
|
||||||
address_validators: Vec::new(),
|
|
||||||
network,
|
|
||||||
current_height,
|
|
||||||
client,
|
|
||||||
database: RefCell::new(database),
|
|
||||||
secp,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the Bitcoin network the wallet is using.
|
|
||||||
pub fn network(&self) -> Network {
|
|
||||||
self.network
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The address index selection strategy to use to derived an address from the wallet's external
|
/// 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`.
|
/// descriptor. See [`Wallet::get_address`]. If you're unsure which one to use use `WalletIndex::New`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -232,11 +156,63 @@ impl fmt::Display for AddressInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// offline actions, always available
|
impl<D> Wallet<D>
|
||||||
impl<B, D> Wallet<B, D>
|
|
||||||
where
|
where
|
||||||
D: BatchDatabase,
|
D: BatchDatabase,
|
||||||
{
|
{
|
||||||
|
/// Create a wallet.
|
||||||
|
///
|
||||||
|
/// The only way this can fail is if the descriptors passed in do not match the checksums in `database`.
|
||||||
|
pub fn new<E: IntoWalletDescriptor>(
|
||||||
|
descriptor: E,
|
||||||
|
change_descriptor: Option<E>,
|
||||||
|
network: Network,
|
||||||
|
mut database: D,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
let secp = Secp256k1::new();
|
||||||
|
|
||||||
|
let (descriptor, keymap) = into_wallet_descriptor_checked(descriptor, &secp, network)?;
|
||||||
|
database.check_descriptor_checksum(
|
||||||
|
KeychainKind::External,
|
||||||
|
get_checksum(&descriptor.to_string())?.as_bytes(),
|
||||||
|
)?;
|
||||||
|
let signers = Arc::new(SignersContainer::from(keymap));
|
||||||
|
let (change_descriptor, change_signers) = match change_descriptor {
|
||||||
|
Some(desc) => {
|
||||||
|
let (change_descriptor, change_keymap) =
|
||||||
|
into_wallet_descriptor_checked(desc, &secp, network)?;
|
||||||
|
database.check_descriptor_checksum(
|
||||||
|
KeychainKind::Internal,
|
||||||
|
get_checksum(&change_descriptor.to_string())?.as_bytes(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let change_signers = Arc::new(SignersContainer::from(change_keymap));
|
||||||
|
// if !parsed.same_structure(descriptor.as_ref()) {
|
||||||
|
// return Err(Error::DifferentDescriptorStructure);
|
||||||
|
// }
|
||||||
|
|
||||||
|
(Some(change_descriptor), change_signers)
|
||||||
|
}
|
||||||
|
None => (None, Arc::new(SignersContainer::new())),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Wallet {
|
||||||
|
descriptor,
|
||||||
|
change_descriptor,
|
||||||
|
signers,
|
||||||
|
change_signers,
|
||||||
|
address_validators: Vec::new(),
|
||||||
|
network,
|
||||||
|
database: RefCell::new(database),
|
||||||
|
secp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the Bitcoin network the wallet is using.
|
||||||
|
pub fn network(&self) -> Network {
|
||||||
|
self.network
|
||||||
|
}
|
||||||
|
|
||||||
// Return a newly derived address using the external descriptor
|
// Return a newly derived address using the external descriptor
|
||||||
fn get_new_address(&self) -> Result<AddressInfo, Error> {
|
fn get_new_address(&self) -> Result<AddressInfo, Error> {
|
||||||
let incremented_index = self.fetch_and_increment_index(KeychainKind::External)?;
|
let incremented_index = self.fetch_and_increment_index(KeychainKind::External)?;
|
||||||
@ -422,7 +398,7 @@ where
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`TxBuilder`]: crate::TxBuilder
|
/// [`TxBuilder`]: crate::TxBuilder
|
||||||
pub fn build_tx(&self) -> TxBuilder<'_, B, D, DefaultCoinSelectionAlgorithm, CreateTx> {
|
pub fn build_tx(&self) -> TxBuilder<'_, D, DefaultCoinSelectionAlgorithm, CreateTx> {
|
||||||
TxBuilder {
|
TxBuilder {
|
||||||
wallet: self,
|
wallet: self,
|
||||||
params: TxParams::default(),
|
params: TxParams::default(),
|
||||||
@ -762,7 +738,7 @@ where
|
|||||||
pub fn build_fee_bump(
|
pub fn build_fee_bump(
|
||||||
&self,
|
&self,
|
||||||
txid: Txid,
|
txid: Txid,
|
||||||
) -> Result<TxBuilder<'_, B, D, DefaultCoinSelectionAlgorithm, BumpFee>, Error> {
|
) -> Result<TxBuilder<'_, D, DefaultCoinSelectionAlgorithm, BumpFee>, Error> {
|
||||||
let mut details = match self.database.borrow().get_tx(&txid, true)? {
|
let mut details = match self.database.borrow().get_tx(&txid, true)? {
|
||||||
None => return Err(Error::TransactionNotFound),
|
None => return Err(Error::TransactionNotFound),
|
||||||
Some(tx) if tx.transaction.is_none() => return Err(Error::TransactionNotFound),
|
Some(tx) if tx.transaction.is_none() => return Err(Error::TransactionNotFound),
|
||||||
@ -993,7 +969,11 @@ where
|
|||||||
.borrow()
|
.borrow()
|
||||||
.get_tx(&input.previous_output.txid, false)?
|
.get_tx(&input.previous_output.txid, false)?
|
||||||
.map(|tx| tx.confirmation_time.map(|c| c.height).unwrap_or(u32::MAX));
|
.map(|tx| tx.confirmation_time.map(|c| c.height).unwrap_or(u32::MAX));
|
||||||
let current_height = sign_options.assume_height.or(self.current_height);
|
let last_sync_height = self
|
||||||
|
.database()
|
||||||
|
.get_sync_time()?
|
||||||
|
.map(|sync_time| sync_time.block_time.height);
|
||||||
|
let current_height = sign_options.assume_height.or(last_sync_height);
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"Input #{} - {}, using `create_height` = {:?}, `current_height` = {:?}",
|
"Input #{} - {}, using `create_height` = {:?}, `current_height` = {:?}",
|
||||||
@ -1453,35 +1433,15 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B, D> Wallet<B, D>
|
impl<D> Wallet<D>
|
||||||
where
|
where
|
||||||
B: Blockchain,
|
|
||||||
D: BatchDatabase,
|
D: BatchDatabase,
|
||||||
{
|
{
|
||||||
/// Create a new "online" wallet
|
|
||||||
#[maybe_async]
|
|
||||||
pub fn new<E: IntoWalletDescriptor>(
|
|
||||||
descriptor: E,
|
|
||||||
change_descriptor: Option<E>,
|
|
||||||
network: Network,
|
|
||||||
database: D,
|
|
||||||
client: B,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
let current_height = Some(maybe_await!(client.get_height())? as u32);
|
|
||||||
Self::_new(
|
|
||||||
descriptor,
|
|
||||||
change_descriptor,
|
|
||||||
network,
|
|
||||||
database,
|
|
||||||
client,
|
|
||||||
current_height,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sync the internal database with the blockchain
|
/// Sync the internal database with the blockchain
|
||||||
#[maybe_async]
|
#[maybe_async]
|
||||||
pub fn sync<P: 'static + Progress>(
|
pub fn sync<P: 'static + Progress, B: WalletSync + GetHeight>(
|
||||||
&self,
|
&self,
|
||||||
|
blockchain: &B,
|
||||||
progress_update: P,
|
progress_update: P,
|
||||||
max_address_param: Option<u32>,
|
max_address_param: Option<u32>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
@ -1527,18 +1487,18 @@ where
|
|||||||
// TODO: what if i generate an address first and cache some addresses?
|
// TODO: what if i generate an address first and cache some addresses?
|
||||||
// TODO: we should sync if generating an address triggers a new batch to be stored
|
// TODO: we should sync if generating an address triggers a new batch to be stored
|
||||||
if run_setup {
|
if run_setup {
|
||||||
maybe_await!(self
|
maybe_await!(
|
||||||
.client
|
blockchain.wallet_setup(self.database.borrow_mut().deref_mut(), progress_update,)
|
||||||
.setup(self.database.borrow_mut().deref_mut(), progress_update,))?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
maybe_await!(self
|
maybe_await!(
|
||||||
.client
|
blockchain.wallet_sync(self.database.borrow_mut().deref_mut(), progress_update,)
|
||||||
.sync(self.database.borrow_mut().deref_mut(), progress_update,))?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let sync_time = SyncTime {
|
let sync_time = SyncTime {
|
||||||
block_time: BlockTime {
|
block_time: BlockTime {
|
||||||
height: maybe_await!(self.client.get_height())?,
|
height: maybe_await!(blockchain.get_height())?,
|
||||||
timestamp: time::get_timestamp(),
|
timestamp: time::get_timestamp(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -1547,31 +1507,18 @@ where
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a reference to the internal blockchain client
|
|
||||||
pub fn client(&self) -> &B {
|
|
||||||
&self.client
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Broadcast a transaction to the network
|
|
||||||
#[maybe_async]
|
|
||||||
pub fn broadcast(&self, tx: &Transaction) -> Result<Txid, Error> {
|
|
||||||
maybe_await!(self.client.broadcast(tx))?;
|
|
||||||
|
|
||||||
Ok(tx.txid())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a fake wallet that appears to be funded for testing.
|
/// Return a fake wallet that appears to be funded for testing.
|
||||||
pub fn get_funded_wallet(
|
pub fn get_funded_wallet(
|
||||||
descriptor: &str,
|
descriptor: &str,
|
||||||
) -> (
|
) -> (
|
||||||
Wallet<(), MemoryDatabase>,
|
Wallet<MemoryDatabase>,
|
||||||
(String, Option<String>),
|
(String, Option<String>),
|
||||||
bitcoin::Txid,
|
bitcoin::Txid,
|
||||||
) {
|
) {
|
||||||
let descriptors = testutils!(@descriptors (descriptor));
|
let descriptors = testutils!(@descriptors (descriptor));
|
||||||
let wallet = Wallet::new_offline(
|
let wallet = Wallet::new(
|
||||||
&descriptors.0,
|
&descriptors.0,
|
||||||
None,
|
None,
|
||||||
Network::Regtest,
|
Network::Regtest,
|
||||||
@ -1621,7 +1568,7 @@ pub(crate) mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_cache_addresses_fixed() {
|
fn test_cache_addresses_fixed() {
|
||||||
let db = MemoryDatabase::new();
|
let db = MemoryDatabase::new();
|
||||||
let wallet = Wallet::new_offline(
|
let wallet = Wallet::new(
|
||||||
"wpkh(L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6)",
|
"wpkh(L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6)",
|
||||||
None,
|
None,
|
||||||
Network::Testnet,
|
Network::Testnet,
|
||||||
@ -1655,7 +1602,7 @@ pub(crate) mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_cache_addresses() {
|
fn test_cache_addresses() {
|
||||||
let db = MemoryDatabase::new();
|
let db = MemoryDatabase::new();
|
||||||
let wallet = Wallet::new_offline("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)", None, Network::Testnet, db).unwrap();
|
let wallet = Wallet::new("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)", None, Network::Testnet, db).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
wallet.get_address(New).unwrap().to_string(),
|
wallet.get_address(New).unwrap().to_string(),
|
||||||
@ -1683,7 +1630,7 @@ pub(crate) mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_cache_addresses_refill() {
|
fn test_cache_addresses_refill() {
|
||||||
let db = MemoryDatabase::new();
|
let db = MemoryDatabase::new();
|
||||||
let wallet = Wallet::new_offline("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)", None, Network::Testnet, db).unwrap();
|
let wallet = Wallet::new("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)", None, Network::Testnet, db).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
wallet.get_address(New).unwrap().to_string(),
|
wallet.get_address(New).unwrap().to_string(),
|
||||||
@ -3781,7 +3728,7 @@ pub(crate) mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_unused_address() {
|
fn test_unused_address() {
|
||||||
let db = MemoryDatabase::new();
|
let db = MemoryDatabase::new();
|
||||||
let wallet = Wallet::new_offline("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
|
let wallet = Wallet::new("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
|
||||||
None, Network::Testnet, db).unwrap();
|
None, Network::Testnet, db).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -3798,7 +3745,7 @@ pub(crate) mod test {
|
|||||||
fn test_next_unused_address() {
|
fn test_next_unused_address() {
|
||||||
let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
|
let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
|
||||||
let descriptors = testutils!(@descriptors (descriptor));
|
let descriptors = testutils!(@descriptors (descriptor));
|
||||||
let wallet = Wallet::new_offline(
|
let wallet = Wallet::new(
|
||||||
&descriptors.0,
|
&descriptors.0,
|
||||||
None,
|
None,
|
||||||
Network::Testnet,
|
Network::Testnet,
|
||||||
@ -3827,7 +3774,7 @@ pub(crate) mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_peek_address_at_index() {
|
fn test_peek_address_at_index() {
|
||||||
let db = MemoryDatabase::new();
|
let db = MemoryDatabase::new();
|
||||||
let wallet = Wallet::new_offline("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
|
let wallet = Wallet::new("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
|
||||||
None, Network::Testnet, db).unwrap();
|
None, Network::Testnet, db).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -3860,7 +3807,7 @@ pub(crate) mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_peek_address_at_index_not_derivable() {
|
fn test_peek_address_at_index_not_derivable() {
|
||||||
let db = MemoryDatabase::new();
|
let db = MemoryDatabase::new();
|
||||||
let wallet = Wallet::new_offline("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/1)",
|
let wallet = Wallet::new("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/1)",
|
||||||
None, Network::Testnet, db).unwrap();
|
None, Network::Testnet, db).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -3882,7 +3829,7 @@ pub(crate) mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_reset_address_index() {
|
fn test_reset_address_index() {
|
||||||
let db = MemoryDatabase::new();
|
let db = MemoryDatabase::new();
|
||||||
let wallet = Wallet::new_offline("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
|
let wallet = Wallet::new("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
|
||||||
None, Network::Testnet, db).unwrap();
|
None, Network::Testnet, db).unwrap();
|
||||||
|
|
||||||
// new index 0
|
// new index 0
|
||||||
@ -3919,7 +3866,7 @@ pub(crate) mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_returns_index_and_address() {
|
fn test_returns_index_and_address() {
|
||||||
let db = MemoryDatabase::new();
|
let db = MemoryDatabase::new();
|
||||||
let wallet = Wallet::new_offline("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
|
let wallet = Wallet::new("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
|
||||||
None, Network::Testnet, db).unwrap();
|
None, Network::Testnet, db).unwrap();
|
||||||
|
|
||||||
// new index 0
|
// new index 0
|
||||||
|
@ -72,7 +72,7 @@
|
|||||||
//! let custom_signer = CustomSigner::connect();
|
//! let custom_signer = CustomSigner::connect();
|
||||||
//!
|
//!
|
||||||
//! let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
|
//! let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
|
||||||
//! let mut wallet = Wallet::new_offline(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
|
//! let mut wallet = Wallet::new(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
|
||||||
//! wallet.add_signer(
|
//! wallet.add_signer(
|
||||||
//! KeychainKind::External,
|
//! KeychainKind::External,
|
||||||
//! SignerOrdering(200),
|
//! SignerOrdering(200),
|
||||||
|
@ -120,8 +120,8 @@ impl TxBuilderContext for BumpFee {}
|
|||||||
/// [`finish`]: Self::finish
|
/// [`finish`]: Self::finish
|
||||||
/// [`coin_selection`]: Self::coin_selection
|
/// [`coin_selection`]: Self::coin_selection
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TxBuilder<'a, B, D, Cs, Ctx> {
|
pub struct TxBuilder<'a, D, Cs, Ctx> {
|
||||||
pub(crate) wallet: &'a Wallet<B, D>,
|
pub(crate) wallet: &'a Wallet<D>,
|
||||||
pub(crate) params: TxParams,
|
pub(crate) params: TxParams,
|
||||||
pub(crate) coin_selection: Cs,
|
pub(crate) coin_selection: Cs,
|
||||||
pub(crate) phantom: PhantomData<Ctx>,
|
pub(crate) phantom: PhantomData<Ctx>,
|
||||||
@ -170,7 +170,7 @@ impl std::default::Default for FeePolicy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Cs: Clone, Ctx, B, D> Clone for TxBuilder<'a, B, D, Cs, Ctx> {
|
impl<'a, Cs: Clone, Ctx, D> Clone for TxBuilder<'a, D, Cs, Ctx> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
TxBuilder {
|
TxBuilder {
|
||||||
wallet: self.wallet,
|
wallet: self.wallet,
|
||||||
@ -182,8 +182,8 @@ impl<'a, Cs: Clone, Ctx, B, D> Clone for TxBuilder<'a, B, D, Cs, Ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// methods supported by both contexts, for any CoinSelectionAlgorithm
|
// methods supported by both contexts, for any CoinSelectionAlgorithm
|
||||||
impl<'a, B, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext>
|
impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext>
|
||||||
TxBuilder<'a, B, D, Cs, Ctx>
|
TxBuilder<'a, D, Cs, Ctx>
|
||||||
{
|
{
|
||||||
/// Set a custom fee rate
|
/// Set a custom fee rate
|
||||||
pub fn fee_rate(&mut self, fee_rate: FeeRate) -> &mut Self {
|
pub fn fee_rate(&mut self, fee_rate: FeeRate) -> &mut Self {
|
||||||
@ -508,7 +508,7 @@ impl<'a, B, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderConte
|
|||||||
pub fn coin_selection<P: CoinSelectionAlgorithm<D>>(
|
pub fn coin_selection<P: CoinSelectionAlgorithm<D>>(
|
||||||
self,
|
self,
|
||||||
coin_selection: P,
|
coin_selection: P,
|
||||||
) -> TxBuilder<'a, B, D, P, Ctx> {
|
) -> TxBuilder<'a, D, P, Ctx> {
|
||||||
TxBuilder {
|
TxBuilder {
|
||||||
wallet: self.wallet,
|
wallet: self.wallet,
|
||||||
params: self.params,
|
params: self.params,
|
||||||
@ -547,7 +547,7 @@ impl<'a, B, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderConte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, B, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>> TxBuilder<'a, B, D, Cs, CreateTx> {
|
impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>> TxBuilder<'a, D, Cs, CreateTx> {
|
||||||
/// Replace the recipients already added with a new list
|
/// Replace the recipients already added with a new list
|
||||||
pub fn set_recipients(&mut self, recipients: Vec<(Script, u64)>) -> &mut Self {
|
pub fn set_recipients(&mut self, recipients: Vec<(Script, u64)>) -> &mut Self {
|
||||||
self.params.recipients = recipients;
|
self.params.recipients = recipients;
|
||||||
@ -614,7 +614,7 @@ impl<'a, B, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>> TxBuilder<'a, B, D,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// methods supported only by bump_fee
|
// methods supported only by bump_fee
|
||||||
impl<'a, B, D: BatchDatabase> TxBuilder<'a, B, D, DefaultCoinSelectionAlgorithm, BumpFee> {
|
impl<'a, D: BatchDatabase> TxBuilder<'a, D, DefaultCoinSelectionAlgorithm, BumpFee> {
|
||||||
/// Explicitly tells the wallet that it is allowed to reduce the fee of the output matching this
|
/// Explicitly tells the wallet that it is allowed to reduce the fee of the output matching this
|
||||||
/// `script_pubkey` in order to bump the transaction fee. Without specifying this the wallet
|
/// `script_pubkey` in order to bump the transaction fee. Without specifying this the wallet
|
||||||
/// will attempt to find a change output to shrink instead.
|
/// will attempt to find a change output to shrink instead.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user