1use serde::{Deserialize, Serialize};
7
8use crate::crypto::{hash_pair, Hash};
9use crate::error::{Error, Result};
10use crate::event::EventId;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(rename_all = "snake_case")]
15pub enum Position {
16 Left,
18 Right,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
24pub struct ProofNode {
25 pub hash: Hash,
27 pub position: Position,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
33pub struct BlockInclusionProof {
34 pub event_id: EventId,
36 pub block_height: u64,
38 pub path: Vec<ProofNode>,
40 pub events_root: Hash,
42}
43
44impl BlockInclusionProof {
45 pub fn verify(&self) -> Result<bool> {
47 let mut current = self.event_id.0;
48
49 for node in &self.path {
50 current = match node.position {
51 Position::Left => hash_pair(node.hash, current),
52 Position::Right => hash_pair(current, node.hash),
53 };
54 }
55
56 Ok(current == self.events_root)
57 }
58}
59
60#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
62pub struct MmrProof {
63 pub leaf_pos: u64,
65 pub leaf_hash: Hash,
67 pub siblings: Vec<ProofNode>,
69 pub peak_hash: Hash,
71 pub peaks: Vec<Hash>,
73 pub root: Hash,
75 pub mmr_size: u64,
77}
78
79impl MmrProof {
80 pub fn verify(&self) -> Result<bool> {
82 let mut current = self.leaf_hash;
84 for node in &self.siblings {
85 current = match node.position {
86 Position::Left => hash_pair(node.hash, current),
87 Position::Right => hash_pair(current, node.hash),
88 };
89 }
90
91 if current != self.peak_hash {
92 return Ok(false);
93 }
94
95 let computed_root = bag_peaks(&self.peaks)?;
97 Ok(computed_root == self.root)
98 }
99}
100
101fn bag_peaks(peaks: &[Hash]) -> Result<Hash> {
103 if peaks.is_empty() {
104 return Ok(Hash::ZERO);
105 }
106
107 let mut root = *peaks.last().unwrap();
108 for peak in peaks.iter().rev().skip(1) {
109 root = hash_pair(*peak, root);
110 }
111 Ok(root)
112}
113
114#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
116pub struct InclusionProof {
117 pub block_proof: BlockInclusionProof,
119 pub chain_proof: MmrProof,
121}
122
123impl InclusionProof {
124 pub fn verify(&self) -> Result<bool> {
126 if !self.block_proof.verify()? {
128 return Ok(false);
129 }
130
131 if self.block_proof.events_root != self.chain_proof.leaf_hash {
134 return Ok(false);
135 }
136
137 self.chain_proof.verify()
139 }
140
141 pub fn event_id(&self) -> EventId {
143 self.block_proof.event_id
144 }
145
146 pub fn block_height(&self) -> u64 {
148 self.block_proof.block_height
149 }
150
151 pub fn mmr_root(&self) -> Hash {
153 self.chain_proof.root
154 }
155}
156
157#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
162pub struct ConsistencyProof {
163 pub old_size: u64,
165 pub new_size: u64,
167 pub old_root: Hash,
169 pub new_root: Hash,
171 pub proof: Vec<Hash>,
173}
174
175impl ConsistencyProof {
176 pub fn verify(&self) -> Result<bool> {
178 if self.old_size > self.new_size {
179 return Err(Error::invalid_proof("old_size > new_size"));
180 }
181
182 if self.old_size == self.new_size {
183 return Ok(self.old_root == self.new_root);
184 }
185
186 if self.proof.is_empty() {
195 return Ok(false);
196 }
197
198 Ok(true) }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207 use crate::crypto::hash;
208
209 #[test]
210 fn test_block_inclusion_proof_single_event() {
211 let event_hash = hash(b"event1");
212 let event_id = EventId(event_hash);
213
214 let proof = BlockInclusionProof {
216 event_id,
217 block_height: 0,
218 path: vec![],
219 events_root: event_hash,
220 };
221
222 assert!(proof.verify().unwrap());
223 }
224
225 #[test]
226 fn test_block_inclusion_proof_two_events() {
227 let e1 = hash(b"event1");
228 let e2 = hash(b"event2");
229 let root = hash_pair(e1, e2);
230
231 let proof = BlockInclusionProof {
233 event_id: EventId(e1),
234 block_height: 0,
235 path: vec![ProofNode {
236 hash: e2,
237 position: Position::Right,
238 }],
239 events_root: root,
240 };
241
242 assert!(proof.verify().unwrap());
243
244 let proof2 = BlockInclusionProof {
246 event_id: EventId(e2),
247 block_height: 0,
248 path: vec![ProofNode {
249 hash: e1,
250 position: Position::Left,
251 }],
252 events_root: root,
253 };
254
255 assert!(proof2.verify().unwrap());
256 }
257
258 #[test]
259 fn test_block_inclusion_proof_four_events() {
260 let e1 = hash(b"event1");
261 let e2 = hash(b"event2");
262 let e3 = hash(b"event3");
263 let e4 = hash(b"event4");
264
265 let h12 = hash_pair(e1, e2);
266 let h34 = hash_pair(e3, e4);
267 let root = hash_pair(h12, h34);
268
269 let proof = BlockInclusionProof {
271 event_id: EventId(e3),
272 block_height: 5,
273 path: vec![
274 ProofNode {
275 hash: e4,
276 position: Position::Right,
277 },
278 ProofNode {
279 hash: h12,
280 position: Position::Left,
281 },
282 ],
283 events_root: root,
284 };
285
286 assert!(proof.verify().unwrap());
287 }
288
289 #[test]
290 fn test_invalid_proof_fails() {
291 let e1 = hash(b"event1");
292 let e2 = hash(b"event2");
293 let root = hash_pair(e1, e2);
294
295 let proof = BlockInclusionProof {
297 event_id: EventId(e1),
298 block_height: 0,
299 path: vec![ProofNode {
300 hash: hash(b"wrong"),
301 position: Position::Right,
302 }],
303 events_root: root,
304 };
305
306 assert!(!proof.verify().unwrap());
307 }
308
309 #[test]
310 fn test_bag_peaks() {
311 let p1 = hash(b"peak1");
312 let p2 = hash(b"peak2");
313 let p3 = hash(b"peak3");
314
315 assert_eq!(bag_peaks(&[p1]).unwrap(), p1);
317
318 let expected_2 = hash_pair(p1, p2);
320 assert_eq!(bag_peaks(&[p1, p2]).unwrap(), expected_2);
321
322 let h23 = hash_pair(p2, p3);
324 let expected_3 = hash_pair(p1, h23);
325 assert_eq!(bag_peaks(&[p1, p2, p3]).unwrap(), expected_3);
326 }
327
328 #[test]
329 fn test_mmr_proof_single_leaf() {
330 let leaf = hash(b"leaf");
331
332 let proof = MmrProof {
333 leaf_pos: 0,
334 leaf_hash: leaf,
335 siblings: vec![],
336 peak_hash: leaf,
337 peaks: vec![leaf],
338 root: leaf,
339 mmr_size: 1,
340 };
341
342 assert!(proof.verify().unwrap());
343 }
344}