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+
17use std:: marker:: PhantomData ;
28
39use libbitcoinkernel_sys:: {
@@ -16,6 +22,29 @@ use crate::{
1622use 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+ /// ```
1948pub struct ChainIterator < ' a > {
2049 chain : Chain < ' a > ,
2150 current_height : usize ,
@@ -33,33 +62,144 @@ impl<'a> ChainIterator<'a> {
3362impl < ' 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+ /// ```
44115pub struct Chain < ' a > {
45116 inner : * const btck_Chain ,
46117 marker : PhantomData < & ' a ChainstateManager > ,
47118}
48119
49120impl < ' 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