Skip to main content

hotmint_storage/
consensus_state.rs

1use hotmint_types::{Epoch, Height, QuorumCertificate, ViewNumber};
2use serde::{Deserialize, Serialize};
3use vsdb::MapxOrd;
4
5/// Key constants for the consensus state KV store
6const KEY_CURRENT_VIEW: u64 = 1;
7const KEY_LOCKED_QC: u64 = 2;
8const KEY_HIGHEST_QC: u64 = 3;
9const KEY_LAST_COMMITTED_HEIGHT: u64 = 4;
10const KEY_CURRENT_EPOCH: u64 = 5;
11
12/// Persisted consensus state fields (serialized as a single blob per key)
13#[derive(Debug, Clone, Serialize, Deserialize)]
14enum StateValue {
15    View(ViewNumber),
16    Height(Height),
17    Qc(QuorumCertificate),
18    Epoch(Epoch),
19}
20
21/// Persistent consensus state store backed by vsdb
22pub struct PersistentConsensusState {
23    store: MapxOrd<u64, StateValue>,
24}
25
26impl PersistentConsensusState {
27    pub fn new() -> Self {
28        Self {
29            store: MapxOrd::new(),
30        }
31    }
32
33    pub fn save_current_view(&mut self, view: ViewNumber) {
34        self.store
35            .insert(&KEY_CURRENT_VIEW, &StateValue::View(view));
36    }
37
38    pub fn load_current_view(&self) -> Option<ViewNumber> {
39        self.store.get(&KEY_CURRENT_VIEW).and_then(|v| match v {
40            StateValue::View(view) => Some(view),
41            _ => None,
42        })
43    }
44
45    pub fn save_locked_qc(&mut self, qc: &QuorumCertificate) {
46        self.store
47            .insert(&KEY_LOCKED_QC, &StateValue::Qc(qc.clone()));
48    }
49
50    pub fn load_locked_qc(&self) -> Option<QuorumCertificate> {
51        self.store.get(&KEY_LOCKED_QC).and_then(|v| match v {
52            StateValue::Qc(qc) => Some(qc),
53            _ => None,
54        })
55    }
56
57    pub fn save_highest_qc(&mut self, qc: &QuorumCertificate) {
58        self.store
59            .insert(&KEY_HIGHEST_QC, &StateValue::Qc(qc.clone()));
60    }
61
62    pub fn load_highest_qc(&self) -> Option<QuorumCertificate> {
63        self.store.get(&KEY_HIGHEST_QC).and_then(|v| match v {
64            StateValue::Qc(qc) => Some(qc),
65            _ => None,
66        })
67    }
68
69    pub fn save_last_committed_height(&mut self, height: Height) {
70        self.store
71            .insert(&KEY_LAST_COMMITTED_HEIGHT, &StateValue::Height(height));
72    }
73
74    pub fn load_last_committed_height(&self) -> Option<Height> {
75        self.store
76            .get(&KEY_LAST_COMMITTED_HEIGHT)
77            .and_then(|v| match v {
78                StateValue::Height(h) => Some(h),
79                _ => None,
80            })
81    }
82
83    pub fn save_current_epoch(&mut self, epoch: &Epoch) {
84        self.store
85            .insert(&KEY_CURRENT_EPOCH, &StateValue::Epoch(epoch.clone()));
86    }
87
88    pub fn load_current_epoch(&self) -> Option<Epoch> {
89        self.store.get(&KEY_CURRENT_EPOCH).and_then(|v| match v {
90            StateValue::Epoch(e) => Some(e),
91            _ => None,
92        })
93    }
94
95    pub fn flush(&self) {
96        vsdb::vsdb_flush();
97    }
98}
99
100impl Default for PersistentConsensusState {
101    fn default() -> Self {
102        Self::new()
103    }
104}