Skip to content

Commit

Permalink
blockbook: integrate with bdk wallet
Browse files Browse the repository at this point in the history
The new code is heavily inspired by
the esplora integration and example
crate maintained within the bdk repo
[1, 2].

`bdk` depends on an older version of
the `bitcoin` crate than the `blockbook`
library. Where necessary we thus pass
through a (de-)serialization boundary
to create types of one version from the
other. This is not possible through the
implementation of suitable `From` traits
due to Rust's orphan rules.

The tested xpub stems from a previous
blockbook test where we burned some
satoshis.

[1] https://github.com/bitcoindevkit/bdk/tree/3569acca0b3f3540e1f1a2278794eac4642a05e4/crates/esplora

[2] https://github.com/bitcoindevkit/bdk/tree/master/example-crates/wallet_esplora_async
  • Loading branch information
dspicher committed Nov 28, 2023
1 parent 1fd21d1 commit a368ec8
Show file tree
Hide file tree
Showing 5 changed files with 594 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Bumped the minimum supported Blockbook version to [commit `95ee9b5b`](https://github.com/trezor/blockbook/commit/95ee9b5b).
- Bumped `bitcoin` to version 0.31.
- Adopted the block and transaction version to the corresponding `bitcoin` type.
- Added a `bdk` feature that gates functionality to use Blockbook as a data provider for
the popular [`bdk`](https://crates.io/crates/bdk) wallet library.

## 0.1.0

Expand Down
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ keywords = ["crypto", "bitcoin", "blockexplorer", "blockbook", "client"]
categories = ["api-bindings"]

[dependencies]
bdk = { version = "1.0.0-alpha.2", optional = true }
bitcoin = { version = "0.31", default-features = false, features = [ "std", "serde" ] }
chrono = { version = "0.4", default-features = false, features = ["serde"] }
erased-serde = "0.3"
Expand All @@ -35,3 +36,7 @@ tokio-test = "0.4"

[features]
test = []

[[example]]
name = "xpub_balance"
required-features = ["bdk"]
66 changes: 66 additions & 0 deletions examples/xpub_balance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use bdk::{
bitcoin::Network,
wallet::{AddressIndex, Update},
Wallet,
};

const STOP_GAP: u32 = 50;
const PARALLEL_REQUESTS: usize = 5;

/// Demonstrates the usage of a `Blockbook` client as a
/// data backend for a `bdk` wallet. Here, we sync the
/// balance of a watch-only xpub wallet.
///
/// For more involved examples of how to use `bdk` wallets, see their
/// [example crates](https://github.com/bitcoindevkit/bdk/tree/3569acca0b3f3540e1f1a2278794eac4642a05e4/example-crates).
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let blockbook_server = std::env::var("BLOCKBOOK_SERVER")
.expect("please set the `BLOCKBOOK_SERVER` environment variable");

// https://www.blockchain.com/explorer/assets/btc/xpub/xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz
let external_descriptor = "pkh(xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/0/*)";
let internal_descriptor = "pkh(xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/1/*)";

let mut wallet = Wallet::new_no_persist(
external_descriptor,
Some(internal_descriptor),
Network::Bitcoin,
)?;

let address = wallet.get_address(AddressIndex::New);
println!("Generated Address: {address}");

let balance = wallet.get_balance();
println!("Wallet balance before syncing: {} sats", balance.total());

println!("Syncing...");
let client = blockbook::Client::new(blockbook_server.parse().unwrap())
.await
.unwrap();

let (update_graph, last_active_indices) = client
.scan_txs_with_keychains(
wallet.spks_of_all_keychains(),
&Network::Bitcoin,
None,
None,
STOP_GAP,
PARALLEL_REQUESTS,
)
.await?;
let prev_tip = wallet.latest_checkpoint();
let missing_heights = update_graph.missing_heights(wallet.local_chain());
let chain_update = client.update_local_chain(prev_tip, missing_heights).await?;
let update = Update {
last_active_indices,
graph: update_graph,
chain: Some(chain_update),
};
wallet.apply_update(update)?;
wallet.commit()?;

let balance = wallet.get_balance();
println!("Wallet balance after syncing: {} sats", balance.total());
Ok(())
}
Loading

0 comments on commit a368ec8

Please sign in to comment.