Skip to content

Commit 7b26c34

Browse files
committed
feat(client): implement Client::get_prune_height
Implement `Client::get_prune_height`, returning: - `None`, if `bitcoind` is not pruned, - `Some(u32)` with the prune height, if `bitcoind` is pruned, This information is sourced from `getblockchaininfo`.
1 parent f3d49e1 commit 7b26c34

File tree

4 files changed

+81
-1
lines changed

4 files changed

+81
-1
lines changed

src/client.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,25 @@ impl Client {
263263
self.call("getblock", &[json!(block_hash), json!(1)])?;
264264
block_info.into_model().map_err(Error::GetBlockVerboseOne)
265265
}
266+
267+
/// Retrives the prune height of the `bitcoind` instance this client is connected to.
268+
///
269+
/// # Returns
270+
///
271+
/// The prune height of the `bitcoind` instance, as an `Option<u32>`.
272+
pub fn get_prune_height(&self) -> Result<Option<u32>, Error> {
273+
let res = {
274+
let res: v30::GetBlockchainInfo = self.call("getblockchaininfo", &[])?;
275+
res.into_model().map_err(Error::GetBlockchainInfo)?
276+
};
277+
278+
match res.pruned {
279+
true => Ok(Some(
280+
res.prune_height.expect("pruned=true implies a pruneheight"),
281+
)),
282+
false => Ok(None),
283+
}
284+
}
266285
}
267286

268287
#[cfg(test)]

src/client/v28.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,23 @@ impl Client {
4444
self.call("getblock", &[json!(block_hash), json!(1)])?;
4545
block_info.into_model().map_err(Error::GetBlockVerboseOne)
4646
}
47+
48+
/// Retrives the prune height of the `bitcoind` instance this client is connected to.
49+
///
50+
/// # Returns
51+
///
52+
/// The prune height of the `bitcoind` instance, as an `Option<u32>`.
53+
pub fn get_prune_height(&self) -> Result<Option<u32>, Error> {
54+
let res = {
55+
let res: v28::GetBlockchainInfo = self.call("getblockchaininfo", &[])?;
56+
res.into_model().map_err(Error::GetBlockchainInfo)?
57+
};
58+
59+
match res.pruned {
60+
true => Ok(Some(
61+
res.prune_height.expect("pruned=true implies a pruneheight"),
62+
)),
63+
false => Ok(None),
64+
}
65+
}
4766
}

src/error.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33
use bitcoin::{consensus::encode::FromHexError, hex::HexToArrayError};
44
#[cfg(feature = "28_0")]
55
use corepc_types::v17::{GetBlockHeaderVerboseError, GetBlockVerboseOneError};
6+
#[cfg(feature = "28_0")]
7+
use corepc_types::v19::GetBlockchainInfoError;
68
#[cfg(not(feature = "28_0"))]
7-
use corepc_types::v30::{GetBlockHeaderVerboseError, GetBlockVerboseOneError};
9+
use corepc_types::v30::{
10+
GetBlockHeaderVerboseError, GetBlockVerboseOneError, GetBlockchainInfoError,
11+
};
812
use corepc_types::{bitcoin, v30::GetBlockFilterError};
913
use jsonrpc::serde_json;
1014
use std::{fmt, io, num::TryFromIntError};
@@ -27,6 +31,9 @@ pub enum Error {
2731
/// Error modeling [`GetBlockFilter`](corepc_types::model::GetBlockFilter)
2832
GetBlockFilter(GetBlockFilterError),
2933

34+
/// Error modeling [`GetBlockchainInfo`](corepc_types::model::GetBlockchainInfo).
35+
GetBlockchainInfo(GetBlockchainInfoError),
36+
3037
/// Invalid or corrupted cookie file.
3138
InvalidCookieFile,
3239

@@ -56,6 +63,7 @@ impl fmt::Display for Error {
5663
Error::GetBlockVerboseOne(e) => write!(f, "block verbose error: {e}"),
5764
Error::GetBlockHeaderVerbose(e) => write!(f, "block header verbose error: {e}"),
5865
Error::GetBlockFilter(e) => write!(f, "block filter error: {e}"),
66+
Error::GetBlockchainInfo(e) => write!(f, "getblockchaininfo error: {e}"),
5967
Error::InvalidCookieFile => write!(f, "invalid or missing cookie file"),
6068
Error::InvalidUrl(e) => write!(f, "invalid RPC URL: {e}"),
6169
Error::HexToArray(e) => write!(f, "hash parsing error: {e}"),

tests/test_rpc_client.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//! These tests require a running Bitcoin Core node in regtest mode. To setup refer to [`corepc_node`].
44
55
use bdk_bitcoind_client::{Auth, Client, Error};
6+
use corepc_node::Conf;
67
use corepc_types::bitcoin::{Amount, BlockHash, Txid};
78
use std::str::FromStr;
89

@@ -306,3 +307,36 @@ fn test_get_block_filter() {
306307

307308
assert!(!result.filter.is_empty());
308309
}
310+
311+
#[test]
312+
fn test_get_prune_height() {
313+
// Spawn an unpruned node.
314+
let env_unpruned = TestEnv::new();
315+
316+
// Assert that `getblockchaininfo.pruned` is `None`.
317+
let unpruned_res = env_unpruned.client.get_prune_height().unwrap();
318+
assert_eq!(unpruned_res, None);
319+
320+
// Spawn a node with manual pruning enabled.
321+
let mut node_config = Conf::default();
322+
node_config.args.push("-prune=1");
323+
node_config.args.push("-fastprune");
324+
let env_pruned = TestEnv::new_with_config(&node_config);
325+
326+
// Mine 1000 blocks.
327+
let block_count = 1000;
328+
let _hashes = env_pruned.mine_blocks(block_count as usize, None);
329+
330+
// Assert that `getblockchaininfo.pruned` is `Some(0)`.
331+
let pruned_res = env_pruned.client.get_prune_height().unwrap();
332+
assert_eq!(pruned_res, Some(0));
333+
334+
// Prune the last 2 blocks.
335+
let _ = env_pruned.corepc_client.prune_blockchain(block_count - 2);
336+
337+
// Assert that `getblockchaininfo.pruned` is > 0.
338+
// We can't assert on a specific block height because Bitcoin Core prunes
339+
// at the block file level (`blkXXXX.dat`), and not at block height level.
340+
let pruned_res = env_pruned.client.get_prune_height().unwrap();
341+
assert!(pruned_res.unwrap() > 0);
342+
}

0 commit comments

Comments
 (0)