Skip to content

Commit e598091

Browse files
committed
feat(node): BlockTreeContext adapter implementing DeploymentContext over BlockTree
Op: extend
1 parent bff6ad8 commit e598091

2 files changed

Lines changed: 104 additions & 0 deletions

File tree

crates/node/src/bip9_context.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
//! Adapter implementing `bitcoin_rs_consensus::DeploymentContext`
2+
//! over `bitcoin_rs_chain::BlockTree`.
3+
//!
4+
//! The consensus crate stays storage-agnostic via the
5+
//! `DeploymentContext` trait; this adapter is the node-crate-side
6+
//! implementation that lets `bitcoin_rs_consensus::compute_state` query
7+
//! historical block versions + MTPs against the in-memory block tree.
8+
9+
use bitcoin_rs_chain::{BlockTree, node::NodeId};
10+
use bitcoin_rs_consensus::DeploymentContext;
11+
12+
/// Read-only adapter over a `BlockTree` rooted at a chosen tip.
13+
///
14+
/// All lookups walk backward from `start_tip_id` via parent pointers.
15+
/// Callers typically pass the current chain tip as `start_tip_id` so
16+
/// the adapter answers queries against the active chain.
17+
pub struct BlockTreeContext<'a> {
18+
tree: &'a BlockTree,
19+
start_tip_id: NodeId,
20+
}
21+
22+
impl<'a> BlockTreeContext<'a> {
23+
/// Constructs an adapter anchored at `start_tip_id` within `tree`.
24+
#[must_use]
25+
pub const fn new(tree: &'a BlockTree, start_tip_id: NodeId) -> Self {
26+
Self { tree, start_tip_id }
27+
}
28+
}
29+
30+
impl DeploymentContext for BlockTreeContext<'_> {
31+
fn block_version(&self, height: u32) -> Option<i32> {
32+
let node_id = self.tree.node_at_height_from(self.start_tip_id, height)?;
33+
let node = self.tree.node(node_id).ok()?;
34+
Some(node.header.version.to_consensus())
35+
}
36+
37+
fn median_time_past(&self, height: u32, window: usize) -> Option<u32> {
38+
let node_id = self.tree.node_at_height_from(self.start_tip_id, height)?;
39+
self.tree.median_time_past_at(node_id, window)
40+
}
41+
}
42+
43+
#[cfg(test)]
44+
mod tests {
45+
use bitcoin::block::{Header, Version};
46+
use bitcoin::hashes::Hash as _;
47+
use bitcoin::{BlockHash, CompactTarget, TxMerkleNode};
48+
use bitcoin_rs_chain::{BlockTree, node::NodeStatus};
49+
use bitcoin_rs_consensus::DeploymentContext;
50+
51+
use super::BlockTreeContext;
52+
53+
fn synthetic_header(prev_blockhash: BlockHash, time: u32) -> Header {
54+
Header {
55+
version: Version::ONE,
56+
prev_blockhash,
57+
merkle_root: TxMerkleNode::all_zeros(),
58+
time,
59+
bits: CompactTarget::from_consensus(0x207f_ffff),
60+
nonce: 0,
61+
}
62+
}
63+
64+
#[test]
65+
fn block_version_returns_header_version_at_height() -> Result<(), Box<dyn std::error::Error>> {
66+
let mut tree = BlockTree::new();
67+
let header_0 = synthetic_header(BlockHash::all_zeros(), 1_000_000);
68+
let header_0_hash = header_0.block_hash();
69+
tree.insert_header(header_0, NodeStatus::HeaderValid)?;
70+
let header_1 = synthetic_header(header_0_hash, 1_000_600);
71+
let tip = tree.insert_header(header_1, NodeStatus::HeaderValid)?;
72+
let ctx = BlockTreeContext::new(&tree, tip);
73+
74+
assert_eq!(ctx.block_version(0), Some(1));
75+
assert_eq!(ctx.block_version(1), Some(1));
76+
assert_eq!(ctx.block_version(99), None);
77+
Ok(())
78+
}
79+
80+
#[test]
81+
fn median_time_past_returns_window_median() -> Result<(), Box<dyn std::error::Error>> {
82+
let mut tree = BlockTree::new();
83+
let mut prev = BlockHash::all_zeros();
84+
let mut tip = None;
85+
for i in 0..11_u32 {
86+
let header = synthetic_header(prev, 1_000_000 + i * 600);
87+
prev = header.block_hash();
88+
tip = Some(tree.insert_header(header, NodeStatus::HeaderValid)?);
89+
}
90+
let Some(tip) = tip else {
91+
panic!("chain has 11 blocks should yield a tip");
92+
};
93+
let ctx = BlockTreeContext::new(&tree, tip);
94+
let Some(mtp) = ctx.median_time_past(10, 11) else {
95+
panic!("chain has 11 blocks should yield a median time past");
96+
};
97+
98+
assert_eq!(mtp, 1_003_000);
99+
Ok(())
100+
}
101+
}

crates/node/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ extern crate alloc;
1010

1111
/// Block-apply pipeline executed by `NodeState::apply_block` and `BlockSync::tick`.
1212
pub mod apply;
13+
/// BIP9 deployment-state adapter over `BlockTree`.
14+
pub mod bip9_context;
1315
/// Bitcoin Core configuration compatibility.
1416
pub mod bitcoin_conf_compat;
1517
/// Layered node configuration.
@@ -37,6 +39,7 @@ pub mod sync;
3739
/// UTXO view adapter for consensus transaction checks.
3840
pub mod utxo_view;
3941

42+
pub use bip9_context::BlockTreeContext;
4043
pub use bitcoin_rs_primitives::Network;
4144
pub use config::{Auth, Config};
4245
pub use run::run;

0 commit comments

Comments
 (0)