Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rpc): introduce FilterIter #1614

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions crates/bitcoind_rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@ bitcoincore-rpc = { version = "0.19.0" }
bdk_core = { path = "../core", version = "0.3.0", default-features = false }

[dev-dependencies]
bdk_bitcoind_rpc = { path = ".", default-features = true }
bdk_testenv = { path = "../testenv", default-features = false }
bdk_chain = { path = "../chain" }

[features]
default = ["std"]
std = ["bitcoin/std", "bdk_core/std"]
serde = ["bitcoin/serde", "bdk_core/serde"]

[[example]]
name = "bip158"
required-features = ["std"]
106 changes: 106 additions & 0 deletions crates/bitcoind_rpc/examples/bip158.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#![allow(clippy::print_stdout)]
use std::time::Instant;

use anyhow::Context;
use bdk_bitcoind_rpc::bip158::{Event, EventInner, FilterIter};
use bdk_chain::bitcoin::{constants::genesis_block, secp256k1::Secp256k1, Network};
use bdk_chain::indexer::keychain_txout::KeychainTxOutIndex;
use bdk_chain::local_chain::LocalChain;
use bdk_chain::miniscript::Descriptor;
use bdk_chain::{BlockId, ConfirmationBlockTime, IndexedTxGraph, SpkIterator};
use bdk_testenv::anyhow;

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

// Usage: `cargo run -p bdk_bitcoind_rpc --example bip158`

const EXTERNAL: &str = "tr([7d94197e]tprv8ZgxMBicQKsPe1chHGzaa84k1inY2nAXUL8iPSyWESPrEst4E5oCFXhPATqj5fvw34LDknJz7rtXyEC4fKoXryUdc9q87pTTzfQyv61cKdE/86'/1'/0'/0/*)#uswl2jj7";
const INTERNAL: &str = "tr([7d94197e]tprv8ZgxMBicQKsPe1chHGzaa84k1inY2nAXUL8iPSyWESPrEst4E5oCFXhPATqj5fvw34LDknJz7rtXyEC4fKoXryUdc9q87pTTzfQyv61cKdE/86'/1'/0'/1/*)#dyt7h8zx";
const SPK_COUNT: u32 = 25;
const NETWORK: Network = Network::Signet;

const START_HEIGHT: u32 = 170_000;
const START_HASH: &str = "00000041c812a89f084f633e4cf47e819a2f6b1c0a15162355a930410522c99d";

fn main() -> anyhow::Result<()> {
// Setup receiving chain and graph structures.
let secp = Secp256k1::new();
let (descriptor, _) = Descriptor::parse_descriptor(&secp, EXTERNAL)?;
let (change_descriptor, _) = Descriptor::parse_descriptor(&secp, INTERNAL)?;
let (mut chain, _) = LocalChain::from_genesis_hash(genesis_block(NETWORK).block_hash());
let mut graph = IndexedTxGraph::<ConfirmationBlockTime, KeychainTxOutIndex<&str>>::new({
let mut index = KeychainTxOutIndex::default();
index.insert_descriptor("external", descriptor.clone())?;
index.insert_descriptor("internal", change_descriptor.clone())?;
index
});

// Assume a minimum birthday height
let block = BlockId {
height: START_HEIGHT,
hash: START_HASH.parse()?,
};
let _ = chain.insert_block(block)?;

// Configure RPC client
let url = std::env::var("RPC_URL").context("must set RPC_URL")?;
let cookie = std::env::var("RPC_COOKIE").context("must set RPC_COOKIE")?;
let rpc_client =
bitcoincore_rpc::Client::new(&url, bitcoincore_rpc::Auth::CookieFile(cookie.into()))?;

// Initialize block emitter
let cp = chain.tip();
let start_height = cp.height();
let mut emitter = FilterIter::new_with_checkpoint(&rpc_client, cp);
for (_, desc) in graph.index.keychains() {
let spks = SpkIterator::new_with_range(desc, 0..SPK_COUNT).map(|(_, spk)| spk);
emitter.add_spks(spks);
}

let start = Instant::now();

// Sync
if let Some(tip) = emitter.get_tip()? {
let blocks_to_scan = tip.height - start_height;

for event in emitter.by_ref() {
let event = event?;
let curr = event.height();
// apply relevant blocks
if let Event::Block(EventInner { height, ref block }) = event {
let _ = graph.apply_block_relevant(block, height);
println!("Matched block {}", curr);
}
if curr % 1000 == 0 {
let progress = (curr - start_height) as f32 / blocks_to_scan as f32;
println!("[{:.2}%]", progress * 100.0);
}
}
// update chain
if let Some(tip) = emitter.chain_update() {
let _ = chain.apply_update(tip)?;
}
}

println!("\ntook: {}s", start.elapsed().as_secs());
println!("Local tip: {}", chain.tip().height());
let unspent: Vec<_> = graph
.graph()
.filter_chain_unspents(
&chain,
chain.tip().block_id(),
graph.index.outpoints().clone(),
)
.collect();
if !unspent.is_empty() {
println!("\nUnspent");
for (index, utxo) in unspent {
// (k, index) | value | outpoint |
println!("{:?} | {} | {}", index, utxo.txout.value, utxo.outpoint,);
}
}

Ok(())
}
Loading
Loading