Skip to content

Commit b302336

Browse files
committed
feat(rpc)!: FilterIter API redesign
The API now consists of the methods `new` and `next`. The local checkpoint and SPK inventory are provided to the constructor. `next` is now responsible for locating the point of agreement. The next filter to fetch is determined by the `next_block_hash` field of `GetBlockHeaderResult`. If the next header has negative confirmations due to a reorg, we rewind the internal state until we find a header still in the best chain. Matched block events contain the most up to date checkpoint, so it can be applied directly to the local chain as events are processed. Added `Tip` variant to `Event` enum for emitting the tip checkpoint when all blocks have been scanned. Removed `EventInner`.
1 parent 8d9df97 commit b302336

File tree

3 files changed

+257
-351
lines changed

3 files changed

+257
-351
lines changed

crates/bitcoind_rpc/examples/filter_iter.rs

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,28 @@
1-
#![allow(clippy::print_stdout)]
1+
#![allow(clippy::print_stdout, clippy::print_stderr)]
22
use std::time::Instant;
33

44
use anyhow::Context;
5-
use bdk_bitcoind_rpc::bip158::{Event, EventInner, FilterIter};
5+
use bdk_bitcoind_rpc::bip158::{Event, FilterIter};
66
use bdk_chain::bitcoin::{constants::genesis_block, secp256k1::Secp256k1, Network};
77
use bdk_chain::indexer::keychain_txout::KeychainTxOutIndex;
88
use bdk_chain::local_chain::LocalChain;
99
use bdk_chain::miniscript::Descriptor;
1010
use bdk_chain::{BlockId, ConfirmationBlockTime, IndexedTxGraph, SpkIterator};
1111
use bdk_testenv::anyhow;
12-
use bitcoin::Address;
1312

1413
// This example shows how BDK chain and tx-graph structures are updated using compact
1514
// filters syncing. Assumes a connection can be made to a bitcoin node via environment
1615
// variables `RPC_URL` and `RPC_COOKIE`.
1716

1817
// Usage: `cargo run -p bdk_bitcoind_rpc --example filter_iter`
1918

20-
const EXTERNAL: &str = "tr([7d94197e]tprv8ZgxMBicQKsPe1chHGzaa84k1inY2nAXUL8iPSyWESPrEst4E5oCFXhPATqj5fvw34LDknJz7rtXyEC4fKoXryUdc9q87pTTzfQyv61cKdE/86'/1'/0'/0/*)#uswl2jj7";
21-
const INTERNAL: &str = "tr([7d94197e]tprv8ZgxMBicQKsPe1chHGzaa84k1inY2nAXUL8iPSyWESPrEst4E5oCFXhPATqj5fvw34LDknJz7rtXyEC4fKoXryUdc9q87pTTzfQyv61cKdE/86'/1'/0'/1/*)#dyt7h8zx";
19+
const EXTERNAL: &str = "tr([83737d5e/86'/1'/0']tpubDDR5GgtoxS8fJyjjvdahN4VzV5DV6jtbcyvVXhEKq2XtpxjxBXmxH3r8QrNbQqHg4bJM1EGkxi7Pjfkgnui9jQWqS7kxHvX6rhUeriLDKxz/0/*)";
20+
const INTERNAL: &str = "tr([83737d5e/86'/1'/0']tpubDDR5GgtoxS8fJyjjvdahN4VzV5DV6jtbcyvVXhEKq2XtpxjxBXmxH3r8QrNbQqHg4bJM1EGkxi7Pjfkgnui9jQWqS7kxHvX6rhUeriLDKxz/1/*)";
2221
const SPK_COUNT: u32 = 25;
2322
const NETWORK: Network = Network::Signet;
2423

25-
const START_HEIGHT: u32 = 170_000;
26-
const START_HASH: &str = "00000041c812a89f084f633e4cf47e819a2f6b1c0a15162355a930410522c99d";
24+
const START_HEIGHT: u32 = 205_000;
25+
const START_HASH: &str = "0000002bd0f82f8c0c0f1e19128f84c938763641dba85c44bdb6aed1678d16cb";
2726

2827
fn main() -> anyhow::Result<()> {
2928
// Setup receiving chain and graph structures.
@@ -52,38 +51,31 @@ fn main() -> anyhow::Result<()> {
5251
let rpc_client =
5352
bitcoincore_rpc::Client::new(&url, bitcoincore_rpc::Auth::CookieFile(cookie.into()))?;
5453

55-
// Initialize block emitter
56-
let cp = chain.tip();
57-
let start_height = cp.height();
58-
let mut emitter = FilterIter::new_with_checkpoint(&rpc_client, cp);
54+
// Initialize `FilterIter`
55+
let mut spks = vec![];
5956
for (_, desc) in graph.index.keychains() {
60-
let spks = SpkIterator::new_with_range(desc, 0..SPK_COUNT).map(|(_, spk)| spk);
61-
emitter.add_spks(spks);
57+
spks.extend(SpkIterator::new_with_range(desc, 0..SPK_COUNT).map(|(_, s)| s));
6258
}
59+
let iter = FilterIter::new(&rpc_client, chain.tip(), spks);
6360

6461
let start = Instant::now();
6562

66-
// Sync
67-
if let Some(tip) = emitter.get_tip()? {
68-
let blocks_to_scan = tip.height - start_height;
69-
70-
for event in emitter.by_ref() {
71-
let event = event?;
72-
let curr = event.height();
73-
// apply relevant blocks
74-
if let Event::Block(EventInner { height, ref block }) = event {
63+
for res in iter {
64+
let event = res?;
65+
match event {
66+
Event::NoMatch { .. } => {}
67+
Event::Block { cp, ref block } => {
68+
// Apply relevant tx data
69+
let height = cp.height();
7570
let _ = graph.apply_block_relevant(block, height);
76-
println!("Matched block {curr}");
71+
// Update chain tip
72+
let _ = chain.apply_update(cp)?;
73+
println!("Matched block {height}");
7774
}
78-
if curr % 1000 == 0 {
79-
let progress = (curr - start_height) as f32 / blocks_to_scan as f32;
80-
println!("[{:.2}%]", progress * 100.0);
75+
Event::Tip { cp } => {
76+
let _ = chain.apply_update(cp)?;
8177
}
8278
}
83-
// update chain
84-
if let Some(cp) = emitter.chain_update() {
85-
let _ = chain.apply_update(cp)?;
86-
}
8779
}
8880

8981
println!("\ntook: {}s", start.elapsed().as_secs());
@@ -105,9 +97,18 @@ fn main() -> anyhow::Result<()> {
10597
}
10698
}
10799

108-
let unused_spk = graph.index.reveal_next_spk("external").unwrap().0 .1;
109-
let unused_address = Address::from_script(&unused_spk, NETWORK)?;
110-
println!("Next external address: {unused_address}");
100+
for canon_tx in graph.graph().list_canonical_txs(
101+
&chain,
102+
chain.tip().block_id(),
103+
bdk_chain::CanonicalizationParams::default(),
104+
) {
105+
if !canon_tx.chain_position.is_confirmed() {
106+
eprintln!(
107+
"ERROR: canonical tx should be confirmed {}",
108+
canon_tx.tx_node.txid
109+
);
110+
}
111+
}
111112

112113
Ok(())
113114
}

0 commit comments

Comments
 (0)