Skip to content

Commit

Permalink
wip: Add bitcoincore_rpc support
Browse files Browse the repository at this point in the history
  • Loading branch information
Ademan committed Jan 14, 2024
1 parent 290e22b commit a7438d9
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 19 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ repository = "https://github.com/bitcoindevkit/bdk-reserves"
[dependencies]
bdk = { version = "0.28", default-features = false }
bitcoinconsensus = "0.19.0-3"
bitcoincore-rpc = { version = "0.16", optional = true }
electrum-client = { version = "0.12", optional = true }
esplora-client = { version = "0.4", default-features = false, optional = true }
log = "^0.4"

[features]
electrum = ["electrum-client", "bdk/electrum"]
use-esplora-blocking = ["esplora-client/blocking", "bdk/use-esplora-blocking"]
rpc = ["bitcoincore-rpc", "bdk/rpc"]

[dev-dependencies]
rstest = "^0.11"
Expand Down
78 changes: 78 additions & 0 deletions src/txout_set.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use bdk::bitcoin::{OutPoint, TxOut};
#[cfg(any(feature = "rpc"))]
use bdk::bitcoin::Script;
#[cfg(any(feature = "electrum", feature = "use-esplora-blocking"))]
use bdk::bitcoin::{Transaction, Txid};
use bdk::database::BatchDatabase;
use bdk::wallet::Wallet;

#[cfg(feature = "rpc")]
use bitcoincore_rpc::{Client as RpcClient, RpcApi};

#[cfg(feature = "electrum")]
use electrum_client::{Client as ElectrumClient, ElectrumApi};

Expand Down Expand Up @@ -200,6 +205,79 @@ where
}
}

#[cfg(feature = "rpc")]
pub struct RpcAtHeight<'a> {
client: &'a RpcClient,
maximum_txout_height: Option<u32>,
}

#[cfg(feature = "rpc")]
impl TxOutSet for RpcClient {
type Error = bitcoincore_rpc::Error;

fn get_prevouts<'a, I, T>(&self, outpoints: I) -> Result<T, Self::Error>
where
I: IntoIterator<Item = &'a OutPoint>,
T: FromIterator<Option<TxOut>>,
{
let rpc_at_height = RpcAtHeight { client: self, maximum_txout_height: None };

rpc_at_height.get_prevouts(outpoints)
}
}

#[cfg(feature = "rpc")]
impl<'a> MaxHeightTxOutQuery<'a> for RpcClient {
type Target = RpcAtHeight<'a>;

fn txout_set_confirmed_by_height(&'a self, height: u32) -> Self::Target {
RpcAtHeight { client: self, maximum_txout_height: Some(height) }
}
}

#[cfg(feature = "rpc")]
impl<'a> TxOutSet for RpcAtHeight<'a> {
type Error = bitcoincore_rpc::Error;

fn get_prevouts<'b, I, T>(&self, outpoints: I) -> Result<T, Self::Error>
where
I: IntoIterator<Item = &'b OutPoint>,
T: FromIterator<Option<TxOut>>,
{
let outpoints: Vec<_> = outpoints.into_iter().collect();

let iter = outpoints.iter().map(|outpoint| {
let prevout = match self.client.get_tx_out(&outpoint.txid, outpoint.vout, Some(false))? {
Some(prevout) => prevout,
None => {
return Ok(None);
}
};

if let Some(maximum_txout_height) = self.maximum_txout_height {
let blockchain_tip_info = self.client.get_block_header_info(&prevout.bestblock)?;

let block_height = blockchain_tip_info.height - (prevout.confirmations as usize) + 1;

if block_height > (maximum_txout_height as usize) {
return Ok(None);
}
};

let output_script_pubkey: Script = prevout.script_pub_key.hex.into();

let txout = TxOut {
script_pubkey: output_script_pubkey.clone(),
value: prevout.value.to_sat(),
};

Ok(Some(txout))
});

Result::<T, Self::Error>::from_iter(iter)
}
}

#[cfg(feature = "electrum")]
pub struct ElectrumAtHeight<'a> {
client: &'a ElectrumClient,
Expand Down
15 changes: 15 additions & 0 deletions tests/regtestenv.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use bdk::bitcoin::Network;
use bdk::blockchain::{electrum::ElectrumBlockchain, Blockchain};
#[cfg(feature = "rpc")]
use bdk::blockchain::{rpc::Auth as RpcAuth, RpcConfig};
use bdk::database::memory::MemoryDatabase;
use bdk::electrum_client::Client;
use bdk::wallet::{AddressIndex, SyncOptions, Wallet};
Expand Down Expand Up @@ -38,6 +41,18 @@ impl RegTestEnv {
RegTestEnv { bitcoind, electrsd }
}

#[allow(dead_code)]
#[cfg(feature = "rpc")]
pub fn bitcoind_conf(&self, wallet: String) -> RpcConfig {
RpcConfig {
url: self.bitcoind.rpc_url(),
auth: RpcAuth::Cookie { file: self.bitcoind.params.cookie_file.clone() },
network: Network::Regtest,
wallet_name: wallet,
sync_params: None,
}
}

/// returns the URL where an electrum client can connect to the embedded electrum server
pub fn electrum_url(&self) -> &str {
&self.electrsd.electrum_url
Expand Down
57 changes: 38 additions & 19 deletions tests/txout_set.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,45 @@
#[cfg(any(feature = "electrum", feature = "use-esplora-blocking"))]
#[cfg(any(feature = "electrum", feature = "rpc", feature = "use-esplora-blocking"))]
mod regtestenv;

#[cfg(any(feature = "electrum", feature = "use-esplora-blocking"))]
#[cfg(any(feature = "electrum", feature = "rpc", feature = "use-esplora-blocking"))]
use bdk::bitcoin::Network;
#[cfg(feature = "electrum")]
use bdk::blockchain::electrum::ElectrumBlockchain;
#[cfg(feature = "use-esplora-blocking")]
use bdk::blockchain::esplora::EsploraBlockchain;
#[cfg(any(feature = "electrum", feature = "use-esplora-blocking"))]
#[cfg(feature = "rpc")]
use bdk::blockchain::rpc::RpcBlockchain;
#[cfg(feature = "rpc")]
use bdk::blockchain::ConfigurableBlockchain;
#[cfg(any(feature = "electrum", feature = "rpc", feature = "use-esplora-blocking"))]
use bdk::blockchain::{Blockchain, GetHeight};
#[cfg(any(feature = "electrum", feature = "use-esplora-blocking"))]
#[cfg(any(feature = "electrum", feature = "rpc", feature = "use-esplora-blocking"))]
use bdk::database::memory::MemoryDatabase;
#[cfg(any(feature = "electrum", feature = "use-esplora-blocking"))]
#[cfg(any(feature = "electrum", feature = "rpc", feature = "use-esplora-blocking"))]
use bdk::wallet::{SyncOptions, Wallet};
#[cfg(any(feature = "electrum", feature = "use-esplora-blocking"))]
#[cfg(any(feature = "electrum", feature = "rpc", feature = "use-esplora-blocking"))]
use bdk::Error;
#[cfg(any(feature = "electrum", feature = "use-esplora-blocking"))]
#[cfg(any(feature = "electrum", feature = "rpc", feature = "use-esplora-blocking"))]
use bdk::SignOptions;
#[cfg(any(feature = "electrum", feature = "use-esplora-blocking"))]
#[cfg(any(feature = "electrum", feature = "rpc", feature = "use-esplora-blocking"))]
use bdk_reserves::reserves::*;
#[cfg(any(feature = "electrum", feature = "use-esplora-blocking"))]
#[cfg(any(feature = "electrum", feature = "rpc", feature = "use-esplora-blocking"))]
use electrsd::bitcoind::bitcoincore_rpc::bitcoin::Address;

#[cfg(feature = "rpc")]
use bitcoincore_rpc::Client as RpcClient;

#[cfg(feature = "electrum")]
use electrum_client::Client as ElectrumClient;

#[cfg(feature = "use-esplora-blocking")]
use esplora_client::{BlockingClient as EsploraClient, Builder};
#[cfg(any(feature = "electrum", feature = "use-esplora-blocking"))]
#[cfg(any(feature = "electrum", feature = "rpc", feature = "use-esplora-blocking"))]
use regtestenv::RegTestEnv;
#[cfg(any(feature = "electrum", feature = "use-esplora-blocking"))]
#[cfg(any(feature = "electrum", feature = "rpc", feature = "use-esplora-blocking"))]
use std::str::FromStr;

#[cfg(any(feature = "electrum", feature = "use-esplora-blocking"))]
#[cfg(any(feature = "electrum", feature = "rpc", feature = "use-esplora-blocking"))]
fn construct_wallet(desc: &str, network: Network) -> Result<Wallet<MemoryDatabase>, Error> {
let wallet = Wallet::new(desc, None, network, MemoryDatabase::default())?;

Expand Down Expand Up @@ -73,7 +80,7 @@ where
let spendable = proof
.verify_reserve_proof(message, txouts_point_in_time)
.unwrap();
assert_eq!(spendable, old_balance.confirmed);
assert_eq!(spendable, dbg!(old_balance.confirmed));

proof
.verify_reserve_proof(message, blockchain.txout_set_at_tip())
Expand Down Expand Up @@ -116,7 +123,7 @@ where
.expect_err("expect proof utxos to be spent at tip");
}

#[cfg(feature = "electrum")]
#[cfg(any(feature = "electrum", feature = "rpc"))]
fn confirmed_by<B, C>(regtestenv: RegTestEnv, blockchain: B)
where
B: Blockchain + GetHeight + std::ops::Deref<Target = C>,
Expand Down Expand Up @@ -145,14 +152,14 @@ where
let proof = psbt;
assert!(finalized);

let txouts = blockchain.txout_set_confirmed_by_height(old_height);

let spendable = proof.verify_reserve_proof(message, txouts).unwrap();
assert_eq!(spendable, old_balance.confirmed);

let spendable = proof
.verify_reserve_proof(message, blockchain.txout_set_at_tip())
.unwrap();
assert_eq!(spendable, dbg!(old_balance.confirmed));

let txouts = blockchain.txout_set_confirmed_by_height(old_height);

let spendable = proof.verify_reserve_proof(message, txouts).unwrap();
assert_eq!(spendable, old_balance.confirmed);

const MY_FOREIGN_ADDR: &str = "mpSFfNURcFTz2yJxBzRY9NhnozxeJ2AUC8";
Expand Down Expand Up @@ -202,6 +209,18 @@ fn test_electrum_confirmed_by() {
confirmed_by::<ElectrumBlockchain, ElectrumClient>(regtestenv, blockchain);
}

#[test]
#[cfg(feature = "rpc")]
fn test_bitcoincore_rpc_confirmed_by() {
let regtestenv = RegTestEnv::new();

let config = regtestenv.bitcoind_conf("".to_string());

let blockchain = RpcBlockchain::from_config(&config).unwrap();

confirmed_by::<RpcBlockchain, RpcClient>(regtestenv, blockchain);
}

#[test]
#[cfg(feature = "use-esplora-blocking")]
fn test_esplora_point_in_time() {
Expand Down

0 comments on commit a7438d9

Please sign in to comment.