Skip to content

Commit 3e4678d

Browse files
committed
Merge bitcoindevkit#535: Remove blockchain from wallet
0cc4700 Fix typo in CHANGELOG and doc in wallet/mod.rs (Steve Myers) 660faab Fix typo in CHANGELOG (LLFourn) 45767fc Remove max_addresses sync param (LLFourn) fbb50ad apply doc suggestions from @notmandatory (Lloyd Fournier) 035307e [rpc] Filter out unrelated transactions (LLFourn) c0e75fc Split get_tx into its own trait (LLFourn) dcd90f8 Restore but depreciate new_offline (LLFourn) 410a513 Add SyncOptions as the second argument to Wallet::sync (LLFourn) 326bfe8 Remove Blockchain from wallet (LLFourn) Pull request description: While trying to finish bitcoindevkit#490 I thought that it'd be better to try the idea of getting rid of a lot of the async macros and just having tow different traits for sync and async stuff. While trying to do that I felt that this needed to be done first. The goal of this change is to decouple the wallet from the blockchain trait. A wallet is something that keeps track of UTXOs and transactions (and can sign things). The only reason it should need to talk to the blockchain is if doing a `sync`. So we remove all superfluous calls to the blockchain and precisely define the requirements for the blockchain to be used to sync with two new traits: `WalletSync` and `GetHeight`. 1. Stop requesting height when wallet is created 2. `new_offline` is just `new` now. 3. a `WalletSync + GetHeight` is now the first argument to `sync`. 4. `SyncOptions` replaces the existing options to `sync` and allows for less friction when making breaking changes in the fuutre (no more noop_progress!). 5. We `Box` the `Progress` now to avoid type parameters 6. broadcast has been removed from `Wallet`. You just use the blockchain directly to broadcast now. ### Notes to the reviewers This needs bitcoindevkit#502 before it can be merged but can reviewed now. Be on the look up for stale documentation from this change. Our doc build looks broken because of ureq or something. ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing * [x] I've updated `CHANGELOG.md` Top commit has no ACKs. Tree-SHA512: a8f3e21e45359be642b1f30d4ac1ba74785439e1b770dbeab0a5a4b8aab1eef4bb6b855aea4382e289257bc890fa713ca832a8b6c9655f7a59e96d412b4da3e6
2 parents adf7d0c + 0cc4700 commit 3e4678d

23 files changed

+577
-595
lines changed

CHANGELOG.md

+13-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
- Add `get_internal_address` to allow you to get internal addresses just as you get external addresses.
1212
- added `ensure_addresses_cached` to `Wallet` to let offline wallets load and cache addresses in their database
1313

14+
### Sync API change
15+
16+
To decouple the `Wallet` from the `Blockchain` we've made major changes:
17+
18+
- Removed `Blockchain` from Wallet.
19+
- Removed `Wallet::broadcast` (just use `Blockchain::broadcast`)
20+
- Deprecated `Wallet::new_offline` (all wallets are offline now)
21+
- Changed `Wallet::sync` to take a `Blockchain`.
22+
- Stop making a request for the block height when calling `Wallet:new`.
23+
- Added `SyncOptions` to capture extra (future) arguments to `Wallet::sync`.
24+
- Removed `max_addresses` sync parameter which determined how many addresses to cache before syncing since this can just be done with `ensure_addresses_cached`.
25+
1426
## [v0.16.1] - [v0.16.0]
1527

1628
- Pin tokio dependency version to ~1.14 to prevent errors due to their new MSRV 1.49.0
@@ -421,4 +433,4 @@ final transaction is created by calling `finish` on the builder.
421433
[v0.14.0]: https://github.com/bitcoindevkit/bdk/compare/v0.13.0...v0.14.0
422434
[v0.15.0]: https://github.com/bitcoindevkit/bdk/compare/v0.14.0...v0.15.0
423435
[v0.16.0]: https://github.com/bitcoindevkit/bdk/compare/v0.15.0...v0.16.0
424-
[v0.16.1]: https://github.com/bitcoindevkit/bdk/compare/v0.16.0...v0.16.1
436+
[v0.16.1]: https://github.com/bitcoindevkit/bdk/compare/v0.16.0...v0.16.1

README.md

+10-11
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,21 @@ The `bdk` library aims to be the core building block for Bitcoin wallets of any
4141
```rust,no_run
4242
use bdk::Wallet;
4343
use bdk::database::MemoryDatabase;
44-
use bdk::blockchain::{noop_progress, ElectrumBlockchain};
44+
use bdk::blockchain::ElectrumBlockchain;
45+
use bdk::SyncOptions;
4546
4647
use bdk::electrum_client::Client;
4748
4849
fn main() -> Result<(), bdk::Error> {
49-
let client = Client::new("ssl://electrum.blockstream.info:60002")?;
50+
let blockchain = ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?);
5051
let wallet = Wallet::new(
5152
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
5253
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
5354
bitcoin::Network::Testnet,
5455
MemoryDatabase::default(),
55-
ElectrumBlockchain::from(client)
5656
)?;
5757
58-
wallet.sync(noop_progress(), None)?;
58+
wallet.sync(&blockchain, SyncOptions::default())?;
5959
6060
println!("Descriptor balance: {} SAT", wallet.get_balance()?);
6161
@@ -70,7 +70,7 @@ use bdk::{Wallet, database::MemoryDatabase};
7070
use bdk::wallet::AddressIndex::New;
7171

7272
fn main() -> Result<(), bdk::Error> {
73-
let wallet = Wallet::new_offline(
73+
let wallet = Wallet::new(
7474
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
7575
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
7676
bitcoin::Network::Testnet,
@@ -88,26 +88,25 @@ fn main() -> Result<(), bdk::Error> {
8888
### Create a transaction
8989

9090
```rust,no_run
91-
use bdk::{FeeRate, Wallet};
91+
use bdk::{FeeRate, Wallet, SyncOptions};
9292
use bdk::database::MemoryDatabase;
93-
use bdk::blockchain::{noop_progress, ElectrumBlockchain};
93+
use bdk::blockchain::ElectrumBlockchain;
9494
9595
use bdk::electrum_client::Client;
9696
use bdk::wallet::AddressIndex::New;
9797
9898
use bitcoin::consensus::serialize;
9999
100100
fn main() -> Result<(), bdk::Error> {
101-
let client = Client::new("ssl://electrum.blockstream.info:60002")?;
101+
let blockchain = ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?);
102102
let wallet = Wallet::new(
103103
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
104104
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
105105
bitcoin::Network::Testnet,
106106
MemoryDatabase::default(),
107-
ElectrumBlockchain::from(client)
108107
)?;
109108
110-
wallet.sync(noop_progress(), None)?;
109+
wallet.sync(&blockchain, SyncOptions::default())?;
111110
112111
let send_to = wallet.get_address(New)?;
113112
let (psbt, details) = {
@@ -135,7 +134,7 @@ use bdk::{Wallet, SignOptions, database::MemoryDatabase};
135134
use bitcoin::consensus::deserialize;
136135
137136
fn main() -> Result<(), bdk::Error> {
138-
let wallet = Wallet::new_offline(
137+
let wallet = Wallet::new(
139138
"wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)",
140139
Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"),
141140
bitcoin::Network::Testnet,

examples/address_validator.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@ impl AddressValidator for DummyValidator {
4848

4949
fn main() -> Result<(), bdk::Error> {
5050
let descriptor = "sh(and_v(v:pk(tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd/*),after(630000)))";
51-
let mut wallet =
52-
Wallet::new_offline(descriptor, None, Network::Regtest, MemoryDatabase::new())?;
51+
let mut wallet = Wallet::new(descriptor, None, Network::Regtest, MemoryDatabase::new())?;
5352

5453
wallet.add_address_validator(Arc::new(DummyValidator));
5554

examples/compact_filters_balance.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
// licenses.
1111

1212
use bdk::blockchain::compact_filters::*;
13-
use bdk::blockchain::noop_progress;
1413
use bdk::database::MemoryDatabase;
1514
use bdk::*;
1615
use bitcoin::*;
@@ -35,9 +34,8 @@ fn main() -> Result<(), CompactFiltersError> {
3534
let descriptor = "wpkh(tpubD6NzVbkrYhZ4X2yy78HWrr1M9NT8dKeWfzNiQqDdMqqa9UmmGztGGz6TaLFGsLfdft5iu32gxq1T4eMNxExNNWzVCpf9Y6JZi5TnqoC9wJq/*)";
3635

3736
let database = MemoryDatabase::default();
38-
let wallet =
39-
Arc::new(Wallet::new(descriptor, None, Network::Testnet, database, blockchain).unwrap());
40-
wallet.sync(noop_progress(), None).unwrap();
37+
let wallet = Arc::new(Wallet::new(descriptor, None, Network::Testnet, database).unwrap());
38+
wallet.sync(&blockchain, SyncOptions::default()).unwrap();
4139
info!("balance: {}", wallet.get_balance()?);
4240
Ok(())
4341
}

examples/compiler.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ fn main() -> Result<(), Box<dyn Error>> {
8989
.transpose()
9090
.unwrap()
9191
.unwrap_or(Network::Testnet);
92-
let wallet = Wallet::new_offline(&format!("{}", descriptor), None, network, database)?;
92+
let wallet = Wallet::new(&format!("{}", descriptor), None, network, database)?;
9393

9494
info!("... First address: {}", wallet.get_address(New)?);
9595

src/blockchain/any.rs

+43-65
Original file line numberDiff line numberDiff line change
@@ -16,61 +16,17 @@
1616
//!
1717
//! ## Example
1818
//!
19-
//! In this example both `wallet_electrum` and `wallet_esplora` have the same type of
20-
//! `Wallet<AnyBlockchain, MemoryDatabase>`. This means that they could both, for instance, be
21-
//! assigned to a struct member.
22-
//!
23-
//! ```no_run
24-
//! # use bitcoin::Network;
25-
//! # use bdk::blockchain::*;
26-
//! # use bdk::database::MemoryDatabase;
27-
//! # use bdk::Wallet;
28-
//! # #[cfg(feature = "electrum")]
29-
//! # {
30-
//! let electrum_blockchain = ElectrumBlockchain::from(electrum_client::Client::new("...")?);
31-
//! let wallet_electrum: Wallet<AnyBlockchain, _> = Wallet::new(
32-
//! "...",
33-
//! None,
34-
//! Network::Testnet,
35-
//! MemoryDatabase::default(),
36-
//! electrum_blockchain.into(),
37-
//! )?;
38-
//! # }
39-
//!
40-
//! # #[cfg(all(feature = "esplora", feature = "ureq"))]
41-
//! # {
42-
//! let esplora_blockchain = EsploraBlockchain::new("...", 20);
43-
//! let wallet_esplora: Wallet<AnyBlockchain, _> = Wallet::new(
44-
//! "...",
45-
//! None,
46-
//! Network::Testnet,
47-
//! MemoryDatabase::default(),
48-
//! esplora_blockchain.into(),
49-
//! )?;
50-
//! # }
51-
//!
52-
//! # Ok::<(), bdk::Error>(())
53-
//! ```
54-
//!
55-
//! When paired with the use of [`ConfigurableBlockchain`], it allows creating wallets with any
19+
//! When paired with the use of [`ConfigurableBlockchain`], it allows creating any
5620
//! blockchain type supported using a single line of code:
5721
//!
5822
//! ```no_run
5923
//! # use bitcoin::Network;
6024
//! # use bdk::blockchain::*;
61-
//! # use bdk::database::MemoryDatabase;
62-
//! # use bdk::Wallet;
6325
//! # #[cfg(all(feature = "esplora", feature = "ureq"))]
6426
//! # {
6527
//! let config = serde_json::from_str("...")?;
6628
//! let blockchain = AnyBlockchain::from_config(&config)?;
67-
//! let wallet = Wallet::new(
68-
//! "...",
69-
//! None,
70-
//! Network::Testnet,
71-
//! MemoryDatabase::default(),
72-
//! blockchain,
73-
//! )?;
29+
//! let height = blockchain.get_height();
7430
//! # }
7531
//! # Ok::<(), bdk::Error>(())
7632
//! ```
@@ -133,33 +89,55 @@ impl Blockchain for AnyBlockchain {
13389
maybe_await!(impl_inner_method!(self, get_capabilities))
13490
}
13591

136-
fn setup<D: BatchDatabase, P: 'static + Progress>(
137-
&self,
138-
database: &mut D,
139-
progress_update: P,
140-
) -> Result<(), Error> {
141-
maybe_await!(impl_inner_method!(self, setup, database, progress_update))
92+
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
93+
maybe_await!(impl_inner_method!(self, broadcast, tx))
14294
}
143-
fn sync<D: BatchDatabase, P: 'static + Progress>(
144-
&self,
145-
database: &mut D,
146-
progress_update: P,
147-
) -> Result<(), Error> {
148-
maybe_await!(impl_inner_method!(self, sync, database, progress_update))
95+
96+
fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error> {
97+
maybe_await!(impl_inner_method!(self, estimate_fee, target))
14998
}
99+
}
100+
101+
#[maybe_async]
102+
impl GetHeight for AnyBlockchain {
103+
fn get_height(&self) -> Result<u32, Error> {
104+
maybe_await!(impl_inner_method!(self, get_height))
105+
}
106+
}
150107

108+
#[maybe_async]
109+
impl GetTx for AnyBlockchain {
151110
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
152111
maybe_await!(impl_inner_method!(self, get_tx, txid))
153112
}
154-
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
155-
maybe_await!(impl_inner_method!(self, broadcast, tx))
156-
}
113+
}
157114

158-
fn get_height(&self) -> Result<u32, Error> {
159-
maybe_await!(impl_inner_method!(self, get_height))
115+
#[maybe_async]
116+
impl WalletSync for AnyBlockchain {
117+
fn wallet_sync<D: BatchDatabase>(
118+
&self,
119+
database: &mut D,
120+
progress_update: Box<dyn Progress>,
121+
) -> Result<(), Error> {
122+
maybe_await!(impl_inner_method!(
123+
self,
124+
wallet_sync,
125+
database,
126+
progress_update
127+
))
160128
}
161-
fn estimate_fee(&self, target: usize) -> Result<FeeRate, Error> {
162-
maybe_await!(impl_inner_method!(self, estimate_fee, target))
129+
130+
fn wallet_setup<D: BatchDatabase>(
131+
&self,
132+
database: &mut D,
133+
progress_update: Box<dyn Progress>,
134+
) -> Result<(), Error> {
135+
maybe_await!(impl_inner_method!(
136+
self,
137+
wallet_setup,
138+
database,
139+
progress_update
140+
))
163141
}
164142
}
165143

src/blockchain/compact_filters/mod.rs

+30-24
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ mod peer;
6767
mod store;
6868
mod sync;
6969

70-
use super::{Blockchain, Capability, ConfigurableBlockchain, Progress};
70+
use crate::blockchain::*;
7171
use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils};
7272
use crate::error::Error;
7373
use crate::types::{KeychainKind, LocalUtxo, TransactionDetails};
@@ -225,11 +225,38 @@ impl Blockchain for CompactFiltersBlockchain {
225225
vec![Capability::FullHistory].into_iter().collect()
226226
}
227227

228+
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
229+
self.peers[0].broadcast_tx(tx.clone())?;
230+
231+
Ok(())
232+
}
233+
234+
fn estimate_fee(&self, _target: usize) -> Result<FeeRate, Error> {
235+
// TODO
236+
Ok(FeeRate::default())
237+
}
238+
}
239+
240+
impl GetHeight for CompactFiltersBlockchain {
241+
fn get_height(&self) -> Result<u32, Error> {
242+
Ok(self.headers.get_height()? as u32)
243+
}
244+
}
245+
246+
impl GetTx for CompactFiltersBlockchain {
247+
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
248+
Ok(self.peers[0]
249+
.get_mempool()
250+
.get_tx(&Inventory::Transaction(*txid)))
251+
}
252+
}
253+
254+
impl WalletSync for CompactFiltersBlockchain {
228255
#[allow(clippy::mutex_atomic)] // Mutex is easier to understand than a CAS loop.
229-
fn setup<D: BatchDatabase, P: 'static + Progress>(
256+
fn wallet_setup<D: BatchDatabase>(
230257
&self,
231258
database: &mut D,
232-
progress_update: P,
259+
progress_update: Box<dyn Progress>,
233260
) -> Result<(), Error> {
234261
let first_peer = &self.peers[0];
235262

@@ -430,27 +457,6 @@ impl Blockchain for CompactFiltersBlockchain {
430457

431458
Ok(())
432459
}
433-
434-
fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
435-
Ok(self.peers[0]
436-
.get_mempool()
437-
.get_tx(&Inventory::Transaction(*txid)))
438-
}
439-
440-
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
441-
self.peers[0].broadcast_tx(tx.clone())?;
442-
443-
Ok(())
444-
}
445-
446-
fn get_height(&self) -> Result<u32, Error> {
447-
Ok(self.headers.get_height()? as u32)
448-
}
449-
450-
fn estimate_fee(&self, _target: usize) -> Result<FeeRate, Error> {
451-
// TODO
452-
Ok(FeeRate::default())
453-
}
454460
}
455461

456462
/// Data to connect to a Bitcoin P2P peer

0 commit comments

Comments
 (0)