Skip to content

Commit 1c64149

Browse files
chore(state): improve docs for chain module
1 parent 4a6fa23 commit 1c64149

File tree

1 file changed

+228
-5
lines changed

1 file changed

+228
-5
lines changed

src/state/chain.rs

Lines changed: 228 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
//! Chain traversal and querying interface for a specific chain.
2+
//!
3+
//! The [`Chain`] represents a specific chain, providing methods to query
4+
//! [`BlockTreeEntry`] by height, check block membership, and iterate
5+
//! through the chain from genesis to tip.
6+
17
use std::marker::PhantomData;
28

39
use libbitcoinkernel_sys::{
@@ -16,6 +22,29 @@ use crate::{
1622
use super::ChainstateManager;
1723

1824
/// Iterator for traversing blocks sequentially from genesis to tip.
25+
///
26+
/// This iterator yields [`BlockTreeEntry`] items for each block in the
27+
/// chain, starting from the genesis block (height 0) and continuing
28+
/// through to the chain tip.
29+
///
30+
/// # Lifetime
31+
/// The iterator is tied to the lifetime of the [`Chain`] it was created from,
32+
/// which in turn is tied to the [`ChainstateManager`]. The iterator becomes
33+
/// invalid when either is dropped.
34+
///
35+
/// # Example
36+
/// ```no_run
37+
/// # use bitcoinkernel::{ContextBuilder, ChainstateManager, ChainType, KernelError};
38+
/// # let context = ContextBuilder::new().chain_type(ChainType::Regtest).build()?;
39+
/// # let chainman = ChainstateManager::builder(&context, "/data", "/blocks")?.build()?;
40+
/// let chain = chainman.active_chain();
41+
///
42+
/// // Iterate through all blocks
43+
/// for entry in chain.iter() {
44+
/// println!("Block {} at height {}", entry.block_hash(), entry.height());
45+
/// }
46+
/// # Ok::<(), KernelError>(())
47+
/// ```
1948
pub struct ChainIterator<'a> {
2049
chain: Chain<'a>,
2150
current_height: usize,
@@ -33,33 +62,144 @@ impl<'a> ChainIterator<'a> {
3362
impl<'a> Iterator for ChainIterator<'a> {
3463
type Item = BlockTreeEntry<'a>;
3564

65+
/// Returns the next block in the chain.
66+
///
67+
/// Yields blocks sequentially from genesis (height 0) to the current tip.
68+
/// Returns `None` when all blocks have been iterated.
3669
fn next(&mut self) -> Option<Self::Item> {
3770
let height = self.current_height;
3871
self.current_height += 1;
3972
self.chain.at_height(height)
4073
}
4174
}
4275

43-
/// Represents a chain instance for querying and traversal.
76+
/// Represents a specific chain for querying and traversal.
77+
///
78+
/// The [`Chain`] allows retrieving block tree entries by height, checking if
79+
/// blocks are part of the chain, and iterating through all blocks from
80+
/// genesis to tip.
81+
///
82+
/// # Lifetime
83+
/// The [`Chain`] is tied to the lifetime of the [`ChainstateManager`] that created
84+
/// it. It becomes invalid when the manager is dropped.
85+
///
86+
/// # Thread Safety
87+
/// [`Chain`] is `Copy` and can be safely shared across threads.
88+
///
89+
/// # Examples
90+
/// ```no_run
91+
/// use bitcoinkernel::{ChainstateManager, ChainType, ContextBuilder, KernelError};
92+
///
93+
/// # let context = ContextBuilder::new()
94+
/// # .chain_type(ChainType::Regtest)
95+
/// # .build()?;
96+
/// #
97+
/// # let chainman = ChainstateManager::builder(&context, "/data", "/blocks")?.build()?;
98+
/// let chain = chainman.active_chain();
99+
///
100+
/// // Get the current tip
101+
/// let tip = chain.tip();
102+
/// println!("Chain height: {}", chain.height());
103+
/// println!("Tip hash: {}", tip.block_hash());
104+
///
105+
/// // Get the genesis block
106+
/// let genesis = chain.genesis();
107+
/// println!("Genesis hash: {}", genesis.block_hash());
108+
///
109+
/// // Query a specific height
110+
/// if let Some(block_index_100) = chain.at_height(100) {
111+
/// println!("Block 100: {}", block_index_100.block_hash());
112+
/// }
113+
/// # Ok::<(), KernelError>(())
114+
/// ```
44115
pub struct Chain<'a> {
45116
inner: *const btck_Chain,
46117
marker: PhantomData<&'a ChainstateManager>,
47118
}
48119

49120
impl<'a> Chain<'a> {
50-
/// Returns the tip (highest block) of the active chain.
121+
/// Returns the tip (highest block) of the chain.
122+
///
123+
/// The tip represents the most recent block in the chain.
124+
///
125+
/// # Returns
126+
/// A [`BlockTreeEntry`] for the chain's tip block.
127+
///
128+
/// # Example
129+
/// ```no_run
130+
/// use bitcoinkernel::{ChainstateManager, ChainType, ContextBuilder, KernelError};
131+
///
132+
/// # let context = ContextBuilder::new().chain_type(ChainType::Regtest).build()?;
133+
/// # let chainman = ChainstateManager::builder(&context, "/data", "/blocks")?.build()?;
134+
/// let chain = chainman.active_chain();
135+
/// let tip = chain.tip();
136+
///
137+
/// println!("Current chain height: {}", chain.height());
138+
/// println!("Tip block hash: {}", tip.block_hash());
139+
/// # Ok::<(), KernelError>(())
140+
/// ```
51141
pub fn tip(&self) -> BlockTreeEntry<'a> {
52142
let ptr = unsafe { btck_chain_get_tip(self.inner) };
53143
unsafe { BlockTreeEntry::from_ptr(ptr) }
54144
}
55145

56146
/// Returns the genesis block (height 0) of the chain.
147+
///
148+
/// The genesis block is the first block in the chain and is hardcoded
149+
/// in the protocol.
150+
///
151+
/// # Returns
152+
/// A [`BlockTreeEntry`] for the genesis block at height 0.
153+
///
154+
/// # Example
155+
/// ```no_run
156+
/// use bitcoinkernel::{ChainstateManager, ChainType, ContextBuilder, KernelError};
157+
///
158+
/// # let context = ContextBuilder::new().chain_type(ChainType::Regtest).build()?;
159+
/// # let chainman = ChainstateManager::builder(&context, "/data", "/blocks")?.build()?;
160+
/// let chain = chainman.active_chain();
161+
/// let genesis = chain.genesis();
162+
///
163+
/// println!("Genesis block hash: {}", genesis.block_hash());
164+
/// assert_eq!(genesis.height(), 0);
165+
/// # Ok::<(), KernelError>(())
166+
/// ```
57167
pub fn genesis(&self) -> BlockTreeEntry<'a> {
58168
let ptr = unsafe { btck_chain_get_genesis(self.inner) };
59169
unsafe { BlockTreeEntry::from_ptr(ptr) }
60170
}
61171

62-
/// Returns the block at the specified height, if it exists.
172+
/// Returns the block tree entry at the specified height, if it exists.
173+
///
174+
/// Retrieves the block entry for a specific height in the chain.
175+
/// Height is zero-indexed, with the genesis block at height 0.
176+
///
177+
/// # Arguments
178+
/// * `height` - The block height to query (0 = genesis block)
179+
///
180+
/// # Returns
181+
/// * `Some(`[`BlockTreeEntry`]`)` - If a block exists at the specified height
182+
/// * `None` - If the height exceeds the current chain tip
183+
///
184+
/// # Example
185+
/// ```no_run
186+
/// use bitcoinkernel::{ChainstateManager, ChainType, ContextBuilder, KernelError};
187+
///
188+
/// # let context = ContextBuilder::new().chain_type(ChainType::Regtest).build()?;
189+
/// # let chainman = ChainstateManager::builder(&context, "/data", "/blocks")?.build()?;
190+
/// let chain = chainman.active_chain();
191+
///
192+
/// // Get block at height 1000
193+
/// if let Some(block_index) = chain.at_height(1000) {
194+
/// println!("Block 1000: {}", block_index.block_hash());
195+
/// } else {
196+
/// println!("Chain height is less than 1000");
197+
/// }
198+
///
199+
/// // Genesis block is always present
200+
/// let genesis = chain.at_height(0).expect("Genesis must exist");
201+
/// # Ok::<(), KernelError>(())
202+
/// ```
63203
pub fn at_height(&self, height: usize) -> Option<BlockTreeEntry<'a>> {
64204
let tip_height = self.height();
65205
if height > tip_height as usize {
@@ -74,17 +214,100 @@ impl<'a> Chain<'a> {
74214
Some(unsafe { BlockTreeEntry::from_ptr(ptr) })
75215
}
76216

77-
/// Checks if the given block entry is part of the active chain.
217+
/// Checks if the given block entry is part of the chain.
218+
///
219+
/// Determines whether a block is in the chain.
220+
///
221+
/// # Arguments
222+
/// * `entry` - The [`BlockTreeEntry`] to check for membership
223+
///
224+
/// # Returns
225+
/// * `true` - If the block is part of the chain
226+
/// * `false` - If the block is not in the chain (e.g., a stale block)
227+
///
228+
/// # Example
229+
/// ```no_run
230+
/// use bitcoinkernel::{BlockHash, ChainstateManager, ChainType, ContextBuilder, KernelError};
231+
///
232+
/// # let context = ContextBuilder::new().chain_type(ChainType::Regtest).build()?;
233+
/// # let chainman = ChainstateManager::builder(&context, "/data", "/blocks")?.build()?;
234+
/// # let block_hash = BlockHash::from([0u8; 32]);
235+
/// let chain = chainman.active_chain();
236+
///
237+
/// if let Some(entry) = chainman.get_block_tree_entry(&block_hash) {
238+
/// if chain.contains(&entry) {
239+
/// println!("Block is in the active chain");
240+
/// } else {
241+
/// println!("Block is stale");
242+
/// }
243+
/// }
244+
/// # Ok::<(), KernelError>(())
245+
/// ```
78246
pub fn contains(&self, entry: &BlockTreeEntry<'a>) -> bool {
79247
let result = unsafe { btck_chain_contains(self.inner, entry.as_ptr()) };
80248
c_helpers::present(result)
81249
}
82250

83-
/// Returns an iterator over all blocks from genesis to tip.
251+
/// Returns an iterator over all block tree entries from genesis to tip.
252+
///
253+
/// Creates a [`ChainIterator`] that yields [`BlockTreeEntry`] items
254+
/// for each block in the chain, starting from the genesis block (height 0) and
255+
/// continuing sequentially to the current tip.
256+
///
257+
/// # Returns
258+
/// A [`ChainIterator`] that traverses the entire chain.
259+
///
260+
/// # Example
261+
/// ```no_run
262+
/// use bitcoinkernel::{ChainstateManager, ChainType, ContextBuilder, KernelError};
263+
///
264+
/// # let context = ContextBuilder::new().chain_type(ChainType::Regtest).build()?;
265+
/// # let chainman = ChainstateManager::builder(&context, "/data", "/blocks")?.build()?;
266+
/// let chain = chainman.active_chain();
267+
///
268+
/// // Iterate through all blocks
269+
/// for entry in chain.iter() {
270+
/// println!("Block {} at height {}",
271+
/// entry.block_hash(),
272+
/// entry.height());
273+
/// }
274+
///
275+
/// // Or with enumerate for explicit height tracking
276+
/// for (height, entry) in chain.iter().enumerate() {
277+
/// println!("Height {}: {}", height, entry.block_hash());
278+
/// }
279+
///
280+
/// // Use iterator adapters
281+
/// let recent_blocks: Vec<_> = chain.iter()
282+
/// .take(10)
283+
/// .collect();
284+
/// # Ok::<(), KernelError>(())
285+
/// ```
84286
pub fn iter(&self) -> ChainIterator<'a> {
85287
ChainIterator::new(*self)
86288
}
87289

290+
/// Returns the height of the chain tip.
291+
///
292+
/// The height is the zero-based index of the tip block in the chain,
293+
/// where the genesis block has height 0. This is equivalent to
294+
/// calling `chain.tip().height()`.
295+
///
296+
/// # Returns
297+
/// The height of the chain's tip block as an `i32`.
298+
///
299+
/// # Example
300+
/// ```no_run
301+
/// use bitcoinkernel::{ChainstateManager, ChainType, ContextBuilder, KernelError};
302+
///
303+
/// # let context = ContextBuilder::new().chain_type(ChainType::Regtest).build()?;
304+
/// # let chainman = ChainstateManager::builder(&context, "/data", "/blocks")?.build()?;
305+
/// let chain = chainman.active_chain();
306+
/// let height = chain.height();
307+
///
308+
/// println!("Chain has {} blocks (0 to {})", height + 1, height);
309+
/// # Ok::<(), KernelError>(())
310+
/// ```
88311
pub fn height(&self) -> i32 {
89312
unsafe { btck_chain_get_height(self.inner) }
90313
}

0 commit comments

Comments
 (0)