mithril_persistence/database/record/
block_range_root.rs1use sqlite::Row;
2
3use mithril_common::crypto_helper::MKTreeNode;
4use mithril_common::entities::{BlockNumber, BlockRange};
5
6use crate::database::Hydrator;
7use crate::sqlite::{HydrationError, Projection, SqLiteEntity};
8
9#[derive(Debug, PartialEq, Clone)]
11pub struct BlockRangeRootRecord {
12 pub range: BlockRange,
14 pub merkle_root: MKTreeNode,
16}
17
18impl From<(BlockRange, MKTreeNode)> for BlockRangeRootRecord {
19 fn from(value: (BlockRange, MKTreeNode)) -> Self {
20 Self {
21 range: value.0,
22 merkle_root: value.1,
23 }
24 }
25}
26
27impl From<BlockRangeRootRecord> for (BlockRange, MKTreeNode) {
28 fn from(value: BlockRangeRootRecord) -> Self {
29 (value.range, value.merkle_root)
30 }
31}
32
33impl SqLiteEntity for BlockRangeRootRecord {
34 fn hydrate(row: Row) -> Result<Self, HydrationError>
35 where
36 Self: Sized,
37 {
38 let start = Hydrator::try_to_u64("block_range.start", row.read::<i64, _>(0))?;
39 let end = Hydrator::try_to_u64("block_range.end", row.read::<i64, _>(1))?;
40 let range = BlockRange::from_block_number(BlockNumber(start));
41 let merkle_root = row.read::<&str, _>(2);
42
43 if range.start != start || range.end != end {
44 return Err(HydrationError::InvalidData(format!(
45 "Invalid block range: start={start}, end={end}, expected_start={}, expected_end={}",
46 range.start, range.end
47 )));
48 }
49
50 Ok(Self {
51 range,
52 merkle_root: MKTreeNode::from_hex(merkle_root)
53 .map_err(|e| HydrationError::InvalidData(
54 format!(
55 "Field block_range.merkle_root (value={merkle_root}) is incompatible with hex representation. Error = {e}")
56 )
57 )?,
58 })
59 }
60
61 fn get_projection() -> Projection {
62 Projection::from(&[
63 ("start", "{:block_range_root:}.start", "int"),
64 ("end", "{:block_range_root:}.end", "int"),
65 ("merkle_root", "{:block_range_root:}.merkle_root", "text"),
66 ])
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use sqlite::Connection;
73
74 use super::*;
75
76 fn select_block_range_from_db(start: BlockNumber, end: BlockNumber, merkle_root: &str) -> Row {
77 let conn = Connection::open(":memory:").unwrap();
78 let query = format!("SELECT {start}, {end}, '{merkle_root}'");
79 let mut statement = conn.prepare(query).unwrap();
80 statement.iter().next().unwrap().unwrap()
81 }
82
83 #[test]
84 fn hydrate_succeed_if_valid_block_range_in_row() {
85 let row = select_block_range_from_db(BlockNumber(0), BlockRange::LENGTH, "AAAA");
88 let res = BlockRangeRootRecord::hydrate(row).expect("Expected hydrate to succeed");
89
90 assert_eq!(
91 res,
92 BlockRangeRootRecord {
93 range: BlockRange::from_block_number(BlockNumber(0)),
94 merkle_root: MKTreeNode::from_hex("AAAA").unwrap(),
95 }
96 );
97 }
98
99 #[test]
100 fn hydrate_fail_if_invalid_block_range_in_row() {
101 for invalid_row in [
102 select_block_range_from_db(BlockNumber(1), BlockRange::LENGTH, "AAAA"),
104 select_block_range_from_db(BlockNumber(0), BlockRange::LENGTH - 1, "AAAA"),
106 select_block_range_from_db(BlockNumber(0), BlockRange::LENGTH * 4, "AAAA"),
108 ] {
109 let res =
110 BlockRangeRootRecord::hydrate(invalid_row).expect_err("Expected hydrate to fail");
111
112 assert!(
113 format!("{res:?}").contains("Invalid block range"),
114 "Expected 'Invalid block range' error, got {:?}",
115 res
116 );
117 }
118 }
119}