Skip to main content

chainindex_core/
cursor.rs

1//! Indexer cursor — tracks the current position in the chain.
2
3use serde::{Deserialize, Serialize};
4
5/// The indexer's current position in the chain.
6///
7/// The cursor knows:
8/// - Which block was last successfully processed
9/// - The confirmation depth (how many blocks behind head we consider "confirmed")
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct Cursor {
12    /// Last confirmed block number that was processed.
13    pub block_number: u64,
14    /// Last confirmed block hash.
15    pub block_hash: String,
16    /// Minimum number of confirmations before processing a block.
17    pub confirmation_depth: u64,
18}
19
20impl Cursor {
21    /// Create a new cursor at the given starting position.
22    pub fn new(block_number: u64, block_hash: impl Into<String>, confirmation_depth: u64) -> Self {
23        Self {
24            block_number,
25            block_hash: block_hash.into(),
26            confirmation_depth,
27        }
28    }
29
30    /// Advance the cursor to a new confirmed block.
31    pub fn advance(&mut self, block_number: u64, block_hash: impl Into<String>) {
32        self.block_number = block_number;
33        self.block_hash = block_hash.into();
34    }
35
36    /// Returns `true` if `head_number` is far enough ahead for `target` to be confirmed.
37    pub fn is_confirmed(&self, target: u64, head_number: u64) -> bool {
38        head_number.saturating_sub(target) >= self.confirmation_depth
39    }
40
41    /// Returns the next block to process (cursor + 1).
42    pub fn next_block(&self) -> u64 {
43        self.block_number + 1
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn cursor_advance() {
53        let mut cursor = Cursor::new(100, "0xaaa", 12);
54        cursor.advance(101, "0xbbb");
55        assert_eq!(cursor.block_number, 101);
56        assert_eq!(cursor.block_hash, "0xbbb");
57    }
58
59    #[test]
60    fn cursor_confirmation_depth() {
61        let cursor = Cursor::new(100, "0xaaa", 12);
62        assert!(cursor.is_confirmed(100, 112)); // 112 - 100 = 12 ≥ 12
63        assert!(!cursor.is_confirmed(100, 111)); // 111 - 100 = 11 < 12
64    }
65
66    #[test]
67    fn cursor_next_block() {
68        let cursor = Cursor::new(500, "0x123", 6);
69        assert_eq!(cursor.next_block(), 501);
70    }
71}