Skip to content
Open
7 changes: 5 additions & 2 deletions crates/bitcoind_rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,21 @@ workspace = true

[dependencies]
bitcoin = { version = "0.32.0", default-features = false }
bitcoincore-rpc = { version = "0.19.0" }
bitcoind_client = { package = "bdk_bitcoind_client", version = "0.2.0", default-features = false, features = ["bitreq", "28_0"] }
bdk_core = { path = "../core", version = "0.6.1", default-features = false }

[dev-dependencies]
bdk_bitcoind_rpc = { path = "." }
bdk_bitcoind_rpc = { path = ".", features = ["28_0"] }
bdk_testenv = { path = "../testenv" }
bdk_chain = { path = "../chain" }

[features]
default = ["std"]
std = ["bitcoin/std", "bdk_core/std"]
serde = ["bitcoin/serde", "bdk_core/serde"]
28_0 = ["bitcoind_client/28_0"]
29_0 = ["bitcoind_client/29_0"]
30_0 = ["bitcoind_client/30_0"]

[[example]]
name = "filter_iter"
Expand Down
6 changes: 4 additions & 2 deletions crates/bitcoind_rpc/examples/filter_iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ fn main() -> anyhow::Result<()> {
// 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()))?;
let rpc_client = bitcoind_client::bitreq::Client::with_auth(
&url,
bitcoind_client::bitreq::Auth::CookieFile(cookie.into()),
)?;

// Initialize `FilterIter`
let mut spks = vec![];
Expand Down
49 changes: 16 additions & 33 deletions crates/bitcoind_rpc/src/bip158.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ use bdk_core::bitcoin;
use bdk_core::CheckPoint;
use bitcoin::BlockHash;
use bitcoin::{bip158::BlockFilter, Block, ScriptBuf};
use bitcoincore_rpc;
use bitcoincore_rpc::{json::GetBlockHeaderResult, RpcApi};
use bitcoind_client::bitreq::Client;

use crate::corepc_types::model::GetBlockHeaderVerbose;

/// Type that returns Bitcoin blocks by matching a list of script pubkeys (SPKs) against a
/// [`bip158::BlockFilter`](bitcoin::bip158::BlockFilter).
///
/// * `FilterIter` talks to bitcoind via JSON-RPC interface, which is handled by the
/// [`bitcoincore_rpc::Client`].
/// [`bitcoind_client::bitreq::Client`].
/// * Collect the script pubkeys (SPKs) you want to watch. These will usually correspond to wallet
/// addresses that have been handed out for receiving payments.
/// * Construct `FilterIter` with the RPC client, SPKs, and [`CheckPoint`]. The checkpoint tip
Expand All @@ -31,19 +32,19 @@ use bitcoincore_rpc::{json::GetBlockHeaderResult, RpcApi};
#[derive(Debug)]
pub struct FilterIter<'a> {
/// RPC client
client: &'a bitcoincore_rpc::Client,
client: &'a Client,
/// SPK inventory
spks: Vec<ScriptBuf>,
/// checkpoint
cp: CheckPoint<BlockHash>,
/// Header info, contains the prev and next hashes for each header.
header: Option<GetBlockHeaderResult>,
header: Option<GetBlockHeaderVerbose>,
}

impl<'a> FilterIter<'a> {
/// Construct [`FilterIter`] with checkpoint, RPC client and SPKs.
pub fn new(
client: &'a bitcoincore_rpc::Client,
client: &'a Client,
cp: CheckPoint,
spks: impl IntoIterator<Item = ScriptBuf>,
) -> Self {
Expand All @@ -58,10 +59,10 @@ impl<'a> FilterIter<'a> {
/// Return the agreement header with the remote node.
///
/// Error if no agreement header is found.
fn find_base(&self) -> Result<GetBlockHeaderResult, Error> {
fn find_base(&self) -> Result<GetBlockHeaderVerbose, Error> {
for cp in self.cp.iter() {
match self.client.get_block_header_info(&cp.hash()) {
Err(e) if is_not_found(&e) => continue,
match self.client.get_block_header_verbose(&cp.hash()) {
Err(e) if e.is_not_found_error() => continue,
Ok(header) if header.confirmations <= 0 => continue,
Ok(header) => return Ok(header),
Err(e) => return Err(Error::Rpc(e)),
Expand Down Expand Up @@ -111,20 +112,20 @@ impl Iterator for FilterIter<'_> {
None => return Ok(None),
};

let mut next_header = self.client.get_block_header_info(&next_hash)?;
let mut next_header = self.client.get_block_header_verbose(&next_hash)?;

// In case of a reorg, rewind by fetching headers of previous hashes until we find
// one with enough confirmations.
while next_header.confirmations < 0 {
let prev_hash = next_header
.previous_block_hash
.ok_or(Error::ReorgDepthExceeded)?;
let prev_header = self.client.get_block_header_info(&prev_hash)?;
let prev_header = self.client.get_block_header_verbose(&prev_hash)?;
next_header = prev_header;
}

next_hash = next_header.hash;
let next_height: u32 = next_header.height.try_into()?;
let next_height = next_header.height;

cp = cp.insert(next_height, next_hash);

Expand Down Expand Up @@ -153,13 +154,11 @@ impl Iterator for FilterIter<'_> {
#[derive(Debug)]
pub enum Error {
/// RPC error
Rpc(bitcoincore_rpc::Error),
Rpc(bitcoind_client::Error),
/// `bitcoin::bip158` error
Bip158(bitcoin::bip158::Error),
/// Max reorg depth exceeded.
ReorgDepthExceeded,
/// Error converting an integer
TryFromInt(core::num::TryFromIntError),
}

impl core::fmt::Display for Error {
Expand All @@ -168,30 +167,14 @@ impl core::fmt::Display for Error {
Self::Rpc(e) => write!(f, "{e}"),
Self::Bip158(e) => write!(f, "{e}"),
Self::ReorgDepthExceeded => write!(f, "maximum reorg depth exceeded"),
Self::TryFromInt(e) => write!(f, "{e}"),
}
}
}

impl core::error::Error for Error {}

impl From<bitcoincore_rpc::Error> for Error {
fn from(e: bitcoincore_rpc::Error) -> Self {
impl From<bitcoind_client::Error> for Error {
fn from(e: bitcoind_client::Error) -> Self {
Self::Rpc(e)
}
}

impl From<core::num::TryFromIntError> for Error {
fn from(e: core::num::TryFromIntError) -> Self {
Self::TryFromInt(e)
}
}

/// Whether the RPC error is a "not found" error (code: `-5`).
fn is_not_found(e: &bitcoincore_rpc::Error) -> bool {
matches!(
e,
bitcoincore_rpc::Error::JsonRpc(bitcoincore_rpc::jsonrpc::Error::Rpc(e))
if e.code == -5
)
}
Loading