1use serde::{Deserialize, Serialize};
4
5use crate::{DocumentId, HashAlgorithm, Hasher};
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12#[serde(rename_all = "camelCase")]
13pub struct BlockProof {
14 pub index: usize,
16
17 pub path: Vec<(DocumentId, bool)>,
19
20 pub root_hash: DocumentId,
22
23 pub algorithm: HashAlgorithm,
25}
26
27impl BlockProof {
28 #[must_use]
38 pub fn verify(&self, block_hash: &DocumentId) -> bool {
39 let mut current = block_hash.clone();
40
41 for (sibling, is_right) in &self.path {
42 let combined = if *is_right {
43 format!("{}{}", current.hex_digest(), sibling.hex_digest())
45 } else {
46 format!("{}{}", sibling.hex_digest(), current.hex_digest())
48 };
49
50 current = Hasher::hash(self.algorithm, combined.as_bytes());
51 }
52
53 current == self.root_hash
54 }
55
56 #[must_use]
58 pub fn verify_detailed(&self, block_hash: &DocumentId) -> ProofVerification {
59 let valid = self.verify(block_hash);
60 ProofVerification {
61 valid,
62 index: self.index,
63 root_hash: self.root_hash.clone(),
64 error: if valid {
65 None
66 } else {
67 Some("Computed root hash does not match expected".to_string())
68 },
69 }
70 }
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
75pub struct ProofVerification {
76 pub valid: bool,
78
79 pub index: usize,
81
82 pub root_hash: DocumentId,
84
85 pub error: Option<String>,
87}
88
89impl ProofVerification {
90 #[must_use]
92 pub fn is_valid(&self) -> bool {
93 self.valid
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 use crate::provenance::MerkleTree;
101
102 #[test]
103 fn test_proof_verification() {
104 let items = vec!["block0", "block1", "block2", "block3"];
105 let tree = MerkleTree::from_items(&items, HashAlgorithm::Sha256).unwrap();
106
107 let proof = tree.prove(1).unwrap();
109
110 let block_hash = Hasher::hash(HashAlgorithm::Sha256, b"block1");
112
113 assert!(proof.verify(&block_hash));
115 }
116
117 #[test]
118 fn test_proof_fails_wrong_block() {
119 let items = vec!["block0", "block1", "block2", "block3"];
120 let tree = MerkleTree::from_items(&items, HashAlgorithm::Sha256).unwrap();
121
122 let proof = tree.prove(1).unwrap();
123
124 let wrong_hash = Hasher::hash(HashAlgorithm::Sha256, b"wrong_block");
126 assert!(!proof.verify(&wrong_hash));
127 }
128
129 #[test]
130 fn test_proof_verification_detailed() {
131 let items = vec!["a", "b", "c", "d"];
132 let tree = MerkleTree::from_items(&items, HashAlgorithm::Sha256).unwrap();
133
134 let proof = tree.prove(2).unwrap();
135 let block_hash = Hasher::hash(HashAlgorithm::Sha256, b"c");
136
137 let result = proof.verify_detailed(&block_hash);
138 assert!(result.is_valid());
139 assert_eq!(result.index, 2);
140 assert!(result.error.is_none());
141 }
142
143 #[test]
144 fn test_proof_serialization() {
145 let items = vec!["x", "y"];
146 let tree = MerkleTree::from_items(&items, HashAlgorithm::Sha256).unwrap();
147
148 let proof = tree.prove(0).unwrap();
149
150 let json = serde_json::to_string_pretty(&proof).unwrap();
151 assert!(json.contains("\"index\": 0"));
152
153 let deserialized: BlockProof = serde_json::from_str(&json).unwrap();
154 assert_eq!(deserialized.index, proof.index);
155 }
156
157 #[test]
158 fn test_all_blocks_verifiable() {
159 let items: Vec<&str> = vec!["0", "1", "2", "3", "4", "5", "6", "7"];
160 let tree = MerkleTree::from_items(&items, HashAlgorithm::Sha256).unwrap();
161
162 for (i, item) in items.iter().enumerate() {
163 let proof = tree.prove(i).unwrap();
164 let block_hash = Hasher::hash(HashAlgorithm::Sha256, item.as_bytes());
165 assert!(proof.verify(&block_hash), "Proof failed for block {i}");
166 }
167 }
168
169 #[test]
170 fn test_proof_verification_detailed_failure() {
171 let items = vec!["a", "b", "c", "d"];
172 let tree = MerkleTree::from_items(&items, HashAlgorithm::Sha256).unwrap();
173
174 let proof = tree.prove(2).unwrap();
175 let wrong_hash = Hasher::hash(HashAlgorithm::Sha256, b"wrong");
176
177 let result = proof.verify_detailed(&wrong_hash);
178 assert!(!result.is_valid());
179 assert!(!result.valid);
180 assert_eq!(result.index, 2);
181 assert!(result.error.is_some());
182 assert!(result.error.unwrap().contains("does not match"));
183 }
184
185 #[test]
186 fn test_block_proof_fields() {
187 let items = vec!["x", "y", "z"];
188 let tree = MerkleTree::from_items(&items, HashAlgorithm::Sha256).unwrap();
189
190 let proof = tree.prove(1).unwrap();
191
192 assert_eq!(proof.index, 1);
193 assert_eq!(proof.algorithm, HashAlgorithm::Sha256);
194 assert_eq!(proof.root_hash, *tree.root_hash());
195 assert!(!proof.path.is_empty());
196 }
197
198 #[test]
199 fn test_single_item_proof() {
200 let items = vec!["only"];
201 let tree = MerkleTree::from_items(&items, HashAlgorithm::Sha256).unwrap();
202
203 let proof = tree.prove(0).unwrap();
204 let block_hash = Hasher::hash(HashAlgorithm::Sha256, b"only");
205
206 assert!(proof.verify(&block_hash));
208 }
209
210 #[test]
211 fn test_proof_verification_struct() {
212 let verification = ProofVerification {
213 valid: true,
214 index: 5,
215 root_hash: Hasher::hash(HashAlgorithm::Sha256, b"root"),
216 error: None,
217 };
218
219 assert!(verification.is_valid());
220 assert_eq!(verification.index, 5);
221 assert!(verification.error.is_none());
222 }
223
224 #[test]
225 fn test_proof_path_direction() {
226 let items = vec!["a", "b", "c", "d"];
228 let tree = MerkleTree::from_items(&items, HashAlgorithm::Sha256).unwrap();
229
230 let proof0 = tree.prove(0).unwrap();
232 let proof3 = tree.prove(3).unwrap();
234
235 assert_eq!(proof0.path.len(), proof3.path.len());
237
238 let hash0 = Hasher::hash(HashAlgorithm::Sha256, b"a");
240 let hash3 = Hasher::hash(HashAlgorithm::Sha256, b"d");
241 assert!(proof0.verify(&hash0));
242 assert!(proof3.verify(&hash3));
243 }
244
245 #[test]
246 fn test_proof_power_of_two_tree() {
247 let items: Vec<&str> = vec!["a", "b", "c", "d"];
249 let tree = MerkleTree::from_items(&items, HashAlgorithm::Sha256).unwrap();
250
251 for (i, item) in items.iter().enumerate() {
253 let proof = tree.prove(i).unwrap();
254 let hash = Hasher::hash(HashAlgorithm::Sha256, item.as_bytes());
255 assert!(proof.verify(&hash), "Failed for index {i}");
256 }
257 }
258
259 #[test]
260 fn test_proof_sixteen_items() {
261 let items: Vec<String> = (0..16).map(|i| format!("item{i}")).collect();
263 let tree = MerkleTree::from_items(&items, HashAlgorithm::Sha256).unwrap();
264
265 for (i, item) in items.iter().enumerate() {
266 let proof = tree.prove(i).unwrap();
267 let hash = Hasher::hash(HashAlgorithm::Sha256, item.as_bytes());
268 assert!(proof.verify(&hash), "Failed for index {i}");
269 }
270 }
271}