hotmint_storage/
block_store.rs1use hotmint_consensus::store::BlockStore;
2use hotmint_types::{Block, BlockHash, Height, QuorumCertificate};
3use ruc::*;
4use std::path::Path;
5use tracing::debug;
6use vsdb::MapxOrd;
7
8const META_FILE: &str = "block_store.meta";
10
11pub struct VsdbBlockStore {
13 by_hash: MapxOrd<[u8; 32], Block>,
14 by_height: MapxOrd<u64, [u8; 32]>,
15 commit_qcs: MapxOrd<u64, QuorumCertificate>,
16}
17
18impl VsdbBlockStore {
19 pub fn open(data_dir: &Path) -> Result<Self> {
27 let meta_path = data_dir.join(META_FILE);
28 if meta_path.exists() {
29 let bytes = std::fs::read(&meta_path).c(d!("read block_store.meta"))?;
30 if bytes.len() != 24 {
31 return Err(eg!(
32 "corrupt block_store.meta: expected 24 bytes, got {}",
33 bytes.len()
34 ));
35 }
36 let by_hash_id = u64::from_le_bytes(bytes[0..8].try_into().unwrap());
37 let by_height_id = u64::from_le_bytes(bytes[8..16].try_into().unwrap());
38 let commit_qcs_id = u64::from_le_bytes(bytes[16..24].try_into().unwrap());
39 Ok(Self {
40 by_hash: MapxOrd::from_meta(by_hash_id).c(d!("restore by_hash"))?,
41 by_height: MapxOrd::from_meta(by_height_id).c(d!("restore by_height"))?,
42 commit_qcs: MapxOrd::from_meta(commit_qcs_id).c(d!("restore commit_qcs"))?,
43 })
44 } else {
45 let by_hash: MapxOrd<[u8; 32], Block> = MapxOrd::new();
46 let by_height: MapxOrd<u64, [u8; 32]> = MapxOrd::new();
47 let commit_qcs: MapxOrd<u64, QuorumCertificate> = MapxOrd::new();
48
49 let by_hash_id = by_hash.save_meta().c(d!())?;
50 let by_height_id = by_height.save_meta().c(d!())?;
51 let commit_qcs_id = commit_qcs.save_meta().c(d!())?;
52
53 let mut meta = [0u8; 24];
54 meta[0..8].copy_from_slice(&by_hash_id.to_le_bytes());
55 meta[8..16].copy_from_slice(&by_height_id.to_le_bytes());
56 meta[16..24].copy_from_slice(&commit_qcs_id.to_le_bytes());
57 std::fs::write(&meta_path, meta).c(d!("write block_store.meta"))?;
58
59 let mut store = Self {
60 by_hash,
61 by_height,
62 commit_qcs,
63 };
64 store.put_block(Block::genesis());
65 Ok(store)
66 }
67 }
68
69 pub fn new() -> Self {
72 let mut store = Self {
73 by_hash: MapxOrd::new(),
74 by_height: MapxOrd::new(),
75 commit_qcs: MapxOrd::new(),
76 };
77 store.put_block(Block::genesis());
78 store
79 }
80
81 pub fn contains(&self, hash: &BlockHash) -> bool {
82 self.by_hash.contains_key(&hash.0)
83 }
84
85 pub fn flush(&self) {
86 vsdb::vsdb_flush();
87 }
88}
89
90impl Default for VsdbBlockStore {
91 fn default() -> Self {
92 Self::new()
93 }
94}
95
96impl BlockStore for VsdbBlockStore {
97 fn put_block(&mut self, block: Block) {
98 debug!(height = block.height.as_u64(), hash = %block.hash, "storing block to vsdb");
99 self.by_height.insert(&block.height.as_u64(), &block.hash.0);
100 self.by_hash.insert(&block.hash.0, &block);
101 }
102
103 fn get_block(&self, hash: &BlockHash) -> Option<Block> {
104 self.by_hash.get(&hash.0)
105 }
106
107 fn get_block_by_height(&self, h: Height) -> Option<Block> {
108 self.by_height
109 .get(&h.as_u64())
110 .and_then(|hash_bytes| self.by_hash.get(&hash_bytes))
111 }
112
113 fn get_blocks_in_range(&self, from: Height, to: Height) -> Vec<Block> {
114 self.by_height
115 .range(from.as_u64()..=to.as_u64())
116 .filter_map(|(_, hash_bytes)| self.by_hash.get(&hash_bytes))
117 .collect()
118 }
119
120 fn tip_height(&self) -> Height {
121 self.by_height
122 .last()
123 .map(|(h, _)| Height(h))
124 .unwrap_or(Height::GENESIS)
125 }
126
127 fn put_commit_qc(&mut self, height: Height, qc: QuorumCertificate) {
128 self.commit_qcs.insert(&height.as_u64(), &qc);
129 }
130
131 fn get_commit_qc(&self, height: Height) -> Option<QuorumCertificate> {
132 self.commit_qcs.get(&height.as_u64())
133 }
134
135 fn flush(&self) {
136 vsdb::vsdb_flush();
137 }
138}