Skip to content
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
20 changes: 20 additions & 0 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,26 @@ impl Client {
self.call("getblock", &[json!(block_hash), json!(1)])?;
block_info.into_model().map_err(Error::GetBlockVerboseOne)
}

/// Retrieves the prune height of the `bitcoind` instance this client is connected to.
///
/// # Returns
///
/// The prune height of the `bitcoind` instance, as an `Option<u32>`.
pub fn get_prune_height(&self) -> Result<Option<u32>, Error> {
let res = {
let res: v30::GetBlockchainInfo = self.call("getblockchaininfo", &[])?;
res.into_model().map_err(Error::GetBlockchainInfo)?
};

if res.pruned {
Ok(Some(
res.prune_height.expect("pruned=true implies a pruneheight"),
))
} else {
Ok(None)
}
}
}

#[cfg(test)]
Expand Down
20 changes: 20 additions & 0 deletions src/client/v28.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,24 @@ impl Client {
self.call("getblock", &[json!(block_hash), json!(1)])?;
block_info.into_model().map_err(Error::GetBlockVerboseOne)
}

/// Retrieves the prune height of the `bitcoind` instance this client is connected to.
///
/// # Returns
///
/// The prune height of the `bitcoind` instance, as an `Option<u32>`.
pub fn get_prune_height(&self) -> Result<Option<u32>, Error> {
let res = {
let res: v28::GetBlockchainInfo = self.call("getblockchaininfo", &[])?;
res.into_model().map_err(Error::GetBlockchainInfo)?
};

if res.pruned {
Ok(Some(
res.prune_height.expect("pruned=true implies a pruneheight"),
))
} else {
Ok(None)
}
}
}
10 changes: 9 additions & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
use bitcoin::{consensus::encode::FromHexError, hex::HexToArrayError};
#[cfg(feature = "28_0")]
use corepc_types::v17::{GetBlockHeaderVerboseError, GetBlockVerboseOneError};
#[cfg(feature = "28_0")]
use corepc_types::v19::GetBlockchainInfoError;
#[cfg(not(feature = "28_0"))]
use corepc_types::v30::{GetBlockHeaderVerboseError, GetBlockVerboseOneError};
use corepc_types::v30::{
GetBlockHeaderVerboseError, GetBlockVerboseOneError, GetBlockchainInfoError,
};
use corepc_types::{bitcoin, v30::GetBlockFilterError};
use jsonrpc::serde_json;
use std::{fmt, io, num::TryFromIntError};
Expand All @@ -27,6 +31,9 @@ pub enum Error {
/// Error modeling [`GetBlockFilter`](corepc_types::model::GetBlockFilter)
GetBlockFilter(GetBlockFilterError),

/// Error modeling [`GetBlockchainInfo`](corepc_types::model::GetBlockchainInfo).
GetBlockchainInfo(GetBlockchainInfoError),

/// Invalid or corrupted cookie file.
InvalidCookieFile,

Expand Down Expand Up @@ -56,6 +63,7 @@ impl fmt::Display for Error {
Error::GetBlockVerboseOne(e) => write!(f, "block verbose error: {e}"),
Error::GetBlockHeaderVerbose(e) => write!(f, "block header verbose error: {e}"),
Error::GetBlockFilter(e) => write!(f, "block filter error: {e}"),
Error::GetBlockchainInfo(e) => write!(f, "getblockchaininfo error: {e}"),
Error::InvalidCookieFile => write!(f, "invalid or missing cookie file"),
Error::InvalidUrl(e) => write!(f, "invalid RPC URL: {e}"),
Error::HexToArray(e) => write!(f, "hash parsing error: {e}"),
Expand Down
132 changes: 83 additions & 49 deletions tests/test_rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//! These tests require a running Bitcoin Core node in regtest mode. To setup refer to [`corepc_node`].

use bdk_bitcoind_client::{Auth, Client, Error};
use corepc_node::Conf;
use corepc_types::bitcoin::{Amount, BlockHash, Txid};
use std::str::FromStr;

Expand All @@ -12,7 +13,8 @@ use testenv::TestEnv;

#[test]
fn test_invalid_credentials() {
let env = TestEnv::setup().unwrap();
let env = TestEnv::new();

let client = Client::with_auth(
&env.node.rpc_url(),
Auth::UserPass("wrong".to_string(), "credentials".to_string()),
Expand All @@ -28,7 +30,7 @@ fn test_invalid_credentials() {
fn test_client_with_custom_transport() {
use jsonrpc::http::bitreq_http::Builder;

let env = TestEnv::setup().unwrap();
let env = TestEnv::new();

let rpc_url = env.node.rpc_url();
let cookie = env
Expand All @@ -54,7 +56,7 @@ fn test_client_with_custom_transport() {

#[test]
fn test_get_block_count() {
let env = TestEnv::setup().unwrap();
let env = TestEnv::new();

let block_count = env
.client
Expand All @@ -66,7 +68,7 @@ fn test_get_block_count() {

#[test]
fn test_get_block_hash() {
let env = TestEnv::setup().unwrap();
let env = TestEnv::new();

let _genesis_hash = env
.client
Expand All @@ -76,21 +78,22 @@ fn test_get_block_hash() {

#[test]
fn test_get_block_hash_for_current_height() {
let TestEnv {
client,
node: _node,
} = TestEnv::setup().unwrap();
let env = TestEnv::new();

let block_count = client.get_block_count().expect("failed to get block count");
let block_count = env
.client
.get_block_count()
.expect("failed to get block count");

let _block_hash = client
let _block_hash = env
.client
.get_block_hash(block_count)
.expect("failed to get block hash");
}

#[test]
fn test_get_block_hash_invalid_height() {
let env = TestEnv::setup().unwrap();
let env = TestEnv::new();

let result = env.client.get_block_hash(999_999_999);

Expand All @@ -99,17 +102,19 @@ fn test_get_block_hash_invalid_height() {

#[test]
fn test_get_best_block_hash() {
let TestEnv {
client,
node: _node,
} = TestEnv::setup().unwrap();
let env = TestEnv::new();

let best_block_hash = client
let best_block_hash = env
.client
.get_best_block_hash()
.expect("failed to get best block hash");

let block_count = client.get_block_count().expect("failed to get block count");
let block_hash = client
let block_count = env
.client
.get_block_count()
.expect("failed to get block count");
let block_hash = env
.client
.get_block_hash(block_count)
.expect("failed to get block hash");

Expand All @@ -118,16 +123,15 @@ fn test_get_best_block_hash() {

#[test]
fn test_get_block() {
let TestEnv {
client,
node: _node,
} = TestEnv::setup().unwrap();
let env = TestEnv::new();

let genesis_hash = client
let genesis_hash = env
.client
.get_block_hash(0)
.expect("failed to get genesis hash");

let block = client
let block = env
.client
.get_block(&genesis_hash)
.expect("failed to get block");

Expand All @@ -137,7 +141,7 @@ fn test_get_block() {

#[test]
fn test_get_block_after_mining() {
let env = TestEnv::setup().unwrap();
let env = TestEnv::new();

let hashes = env.mine_blocks(1, None).expect("failed to mine block");
let block_hash = hashes[0];
Expand All @@ -153,7 +157,7 @@ fn test_get_block_after_mining() {

#[test]
fn test_get_block_verbose() {
let env = TestEnv::setup().unwrap();
let env = TestEnv::new();

let hashes = env.mine_blocks(1, None).expect("failed to mine block");
let block_hash = hashes[0];
Expand All @@ -169,7 +173,7 @@ fn test_get_block_verbose() {

#[test]
fn test_get_block_invalid_hash() {
let env = TestEnv::setup().unwrap();
let env = TestEnv::new();

let invalid_hash =
BlockHash::from_str("0000000000000000000000000000000000000000000000000000000000000000")
Expand All @@ -182,16 +186,15 @@ fn test_get_block_invalid_hash() {

#[test]
fn test_get_block_header() {
let TestEnv {
client,
node: _node,
} = TestEnv::setup().unwrap();
let env = TestEnv::new();

let genesis_hash = client
let genesis_hash = env
.client
.get_block_hash(0)
.expect("failed to get genesis hash");

let header = client
let header = env
.client
.get_block_header(&genesis_hash)
.expect("failed to get block header");

Expand All @@ -200,16 +203,15 @@ fn test_get_block_header() {

#[test]
fn test_get_block_header_verbose() {
let TestEnv {
client,
node: _node,
} = TestEnv::setup().unwrap();
let env = TestEnv::new();

let genesis_hash = client
let genesis_hash = env
.client
.get_block_hash(0)
.expect("failed to get genesis hash");

let header = client
let header = env
.client
.get_block_header_verbose(&genesis_hash)
.expect("failed to get block header verbose");

Expand All @@ -218,7 +220,7 @@ fn test_get_block_header_verbose() {

#[test]
fn test_get_raw_mempool_empty() {
let env = TestEnv::setup().unwrap();
let env = TestEnv::new();

let _hashes = env.mine_blocks(1, None).expect("failed to mine block");

Expand All @@ -231,7 +233,7 @@ fn test_get_raw_mempool_empty() {

#[test]
fn test_get_raw_mempool_with_transaction() {
let env = TestEnv::setup().unwrap();
let env = TestEnv::new();

let _hashes = env.mine_blocks(101, None).expect("failed to mine block");

Expand All @@ -251,7 +253,7 @@ fn test_get_raw_mempool_with_transaction() {

#[test]
fn test_get_raw_transaction() {
let env = TestEnv::setup().unwrap();
let env = TestEnv::new();

let _hashes = env.mine_blocks(1, None).expect("failed to mine block");

Expand Down Expand Up @@ -279,7 +281,7 @@ fn test_get_raw_transaction() {

#[test]
fn test_get_raw_transaction_invalid_txid() {
let env = TestEnv::setup().unwrap();
let env = TestEnv::new();

let fake_txid =
Txid::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap();
Expand All @@ -291,18 +293,50 @@ fn test_get_raw_transaction_invalid_txid() {

#[test]
fn test_get_block_filter() {
let TestEnv {
client,
node: _node,
} = TestEnv::setup().unwrap();
let env = TestEnv::new();

let genesis_hash = client
let genesis_hash = env
.client
.get_block_hash(0)
.expect("failed to get genesis hash");

let result = client
let result = env
.client
.get_block_filter(&genesis_hash)
.expect("failed to get block filter");

assert!(!result.filter.is_empty());
}

#[test]
fn test_get_prune_height() {
// Spawn an unpruned node.
let env_unpruned = TestEnv::new();

// Assert that `getblockchaininfo.pruned` is `None`.
let unpruned_res = env_unpruned.client.get_prune_height().unwrap();
assert_eq!(unpruned_res, None);

// Spawn a node with manual pruning enabled.
let mut node_config = Conf::default();
node_config.args.push("-prune=1");
node_config.args.push("-fastprune");
let env_pruned = TestEnv::new_with_config(&node_config);

// Mine 1000 blocks.
let block_count = 1000;
let _hashes = env_pruned.mine_blocks(block_count as usize, None);

// Assert that `getblockchaininfo.pruned` is `Some(0)`.
let pruned_res = env_pruned.client.get_prune_height().unwrap();
assert_eq!(pruned_res, Some(0));

// Prune the last 2 blocks.
let _ = env_pruned.corepc_client.prune_blockchain(block_count - 2);

// Assert that `getblockchaininfo.prunedheight` is > 0.
// Note: it's not possible to assert on a specific block height since Bitcoin Core
// prunes at the block file level (`blkXXXX.dat`), and not at block height level.
let pruned_res = env_pruned.client.get_prune_height().unwrap();
assert!(pruned_res > Some(0));
}
Loading
Loading