Skip to main content

hotmint_storage/
evidence_store.rs

1use std::collections::HashSet;
2
3use hotmint_consensus::evidence_store::EvidenceStore;
4use hotmint_types::evidence::EquivocationProof;
5use hotmint_types::validator::ValidatorId;
6use hotmint_types::view::ViewNumber;
7
8/// In-memory evidence store backed by a `Vec` and a committed-set.
9pub struct MemoryEvidenceStore {
10    proofs: Vec<EquivocationProof>,
11    committed: HashSet<(ViewNumber, ValidatorId)>,
12}
13
14impl MemoryEvidenceStore {
15    pub fn new() -> Self {
16        Self {
17            proofs: Vec::new(),
18            committed: HashSet::new(),
19        }
20    }
21}
22
23impl Default for MemoryEvidenceStore {
24    fn default() -> Self {
25        Self::new()
26    }
27}
28
29impl EvidenceStore for MemoryEvidenceStore {
30    fn put_evidence(&mut self, proof: EquivocationProof) {
31        // Deduplicate: skip if we already have evidence for this (view, validator).
32        let dominated = self
33            .proofs
34            .iter()
35            .any(|p| p.view == proof.view && p.validator == proof.validator);
36        if !dominated {
37            self.proofs.push(proof);
38        }
39    }
40
41    fn get_pending(&self) -> Vec<EquivocationProof> {
42        self.proofs
43            .iter()
44            .filter(|p| !self.committed.contains(&(p.view, p.validator)))
45            .cloned()
46            .collect()
47    }
48
49    fn mark_committed(&mut self, view: ViewNumber, validator: ValidatorId) {
50        self.committed.insert((view, validator));
51    }
52
53    fn all(&self) -> Vec<EquivocationProof> {
54        self.proofs.clone()
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61    use hotmint_types::block::BlockHash;
62    use hotmint_types::crypto::Signature;
63    use hotmint_types::vote::VoteType;
64
65    fn dummy_proof(view: u64, validator: u64) -> EquivocationProof {
66        EquivocationProof {
67            validator: ValidatorId(validator),
68            view: ViewNumber(view),
69            vote_type: VoteType::Vote,
70            epoch: Default::default(),
71            block_hash_a: BlockHash::GENESIS,
72            signature_a: Signature(vec![1]),
73            block_hash_b: BlockHash::GENESIS,
74            signature_b: Signature(vec![2]),
75        }
76    }
77
78    #[test]
79    fn put_and_get_pending() {
80        let mut store = MemoryEvidenceStore::new();
81        store.put_evidence(dummy_proof(1, 0));
82        store.put_evidence(dummy_proof(2, 1));
83
84        assert_eq!(store.get_pending().len(), 2);
85        assert_eq!(store.all().len(), 2);
86    }
87
88    #[test]
89    fn mark_committed_filters_pending() {
90        let mut store = MemoryEvidenceStore::new();
91        store.put_evidence(dummy_proof(1, 0));
92        store.put_evidence(dummy_proof(2, 1));
93        store.mark_committed(ViewNumber(1), ValidatorId(0));
94
95        let pending = store.get_pending();
96        assert_eq!(pending.len(), 1);
97        assert_eq!(pending[0].view, ViewNumber(2));
98        // all() still returns everything
99        assert_eq!(store.all().len(), 2);
100    }
101
102    #[test]
103    fn deduplication() {
104        let mut store = MemoryEvidenceStore::new();
105        store.put_evidence(dummy_proof(1, 0));
106        store.put_evidence(dummy_proof(1, 0)); // duplicate
107        assert_eq!(store.all().len(), 1);
108    }
109}