1use std::{
2 cell::RefCell,
3 collections::BTreeMap,
4 ops::{Deref, DerefMut},
5 rc::Rc,
6};
7
8use anyhow::{anyhow, Result};
9use arbitrary::Arbitrary;
10
11use crate::{
12 common::crypto::hash::Hash,
13 storage::mkvs::{marshal::Marshal, tree::*},
14};
15
16const PROOF_ENTRY_FULL: u8 = 0x01;
18const PROOF_ENTRY_HASH: u8 = 0x02;
20
21const MIN_PROOF_VERSION: u16 = 0;
23const MAX_PROOF_VERSION: u16 = 1;
24
25#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode, Arbitrary)]
27#[cbor(transparent)]
28pub struct RawProofEntry(pub Vec<u8>);
29
30impl AsRef<[u8]> for RawProofEntry {
31 fn as_ref(&self) -> &[u8] {
32 &self.0
33 }
34}
35
36impl Deref for RawProofEntry {
37 type Target = Vec<u8>;
38
39 fn deref(&self) -> &Self::Target {
40 &self.0
41 }
42}
43
44impl DerefMut for RawProofEntry {
45 fn deref_mut(&mut self) -> &mut Self::Target {
46 &mut self.0
47 }
48}
49
50impl From<RawProofEntry> for Vec<u8> {
51 fn from(val: RawProofEntry) -> Self {
52 val.0
53 }
54}
55
56impl From<Vec<u8>> for RawProofEntry {
57 fn from(v: Vec<u8>) -> RawProofEntry {
58 RawProofEntry(v)
59 }
60}
61
62#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
64pub struct Proof {
65 #[cbor(optional)]
79 pub v: u16,
80 pub untrusted_root: Hash,
84 pub entries: Vec<Option<RawProofEntry>>,
86}
87
88struct ProofNode {
89 serialized: Vec<u8>,
90 children: Vec<Hash>,
91}
92
93pub struct ProofBuilder {
95 proof_version: u16,
96 root: Hash,
97 included: BTreeMap<Hash, ProofNode>,
98}
99
100impl ProofBuilder {
101 pub fn new(root: Hash) -> Self {
103 Self::new_with_version(root, MAX_PROOF_VERSION).unwrap()
104 }
105
106 pub fn new_with_version(root: Hash, proof_version: u16) -> Result<Self> {
108 if !(MIN_PROOF_VERSION..=MAX_PROOF_VERSION).contains(&proof_version) {
109 return Err(anyhow!(
110 "proof builder: unsupported proof version: {}",
111 proof_version
112 ));
113 }
114
115 Ok(Self {
116 proof_version,
117 root,
118 included: BTreeMap::new(),
119 })
120 }
121
122 pub fn include(&mut self, node: &NodeBox) {
128 if !node.is_clean() {
129 panic!("proof builder: node is not clean");
130 }
131
132 let nh = node.get_hash();
134 if self.included.contains_key(&nh) {
135 return;
136 }
137 let mut pn = ProofNode {
138 serialized: node
139 .compact_marshal_binary(self.proof_version)
140 .expect("marshaling node in proof"),
141 children: vec![],
142 };
143
144 if let NodeBox::Internal(nd) = node {
146 fn get_child_hash(nd: &Rc<RefCell<NodePointer>>) -> Hash {
147 nd.borrow()
148 .node
149 .as_ref()
150 .map(|n| n.borrow().get_hash())
151 .unwrap_or_else(Hash::empty_hash)
152 }
153
154 if self.proof_version == 1 {
155 pn.children.push(get_child_hash(&nd.leaf_node));
157 }
158 pn.children.push(get_child_hash(&nd.left));
159 pn.children.push(get_child_hash(&nd.right));
160 }
161
162 self.included.insert(nh, pn);
163 }
164
165 pub fn build(&self) -> Proof {
167 let mut proof = Proof {
168 v: self.proof_version,
169 untrusted_root: self.root,
170 entries: vec![],
171 };
172 self._build(&mut proof, &self.root);
173
174 proof
175 }
176
177 fn _build(&self, p: &mut Proof, h: &Hash) {
178 if h.is_empty() {
179 p.entries.push(None);
181 return;
182 }
183
184 match self.included.get(h) {
185 None => {
186 let mut data = Vec::with_capacity(h.as_ref().len() + 1);
188 data.push(PROOF_ENTRY_HASH);
189 data.extend_from_slice(h.as_ref());
190
191 p.entries.push(Some(RawProofEntry(data)));
192 }
193 Some(pn) => {
194 let mut data = Vec::with_capacity(pn.serialized.len() + 1);
196 data.push(PROOF_ENTRY_FULL);
197 data.extend_from_slice(&pn.serialized);
198
199 p.entries.push(Some(RawProofEntry(data)));
200
201 for ch in pn.children.iter() {
203 self._build(p, ch);
204 }
205 }
206 }
207 }
208}
209
210pub struct ProofVerifier;
212
213impl ProofVerifier {
214 pub fn verify_proof(&self, root: Hash, proof: &Proof) -> Result<NodePtrRef> {
217 if !(MIN_PROOF_VERSION..=MAX_PROOF_VERSION).contains(&proof.v) {
219 return Err(anyhow!("verifier: unsupported proof version: {}", proof.v));
220 }
221
222 if proof.untrusted_root != root {
225 return Err(anyhow!(
226 "verifier: got proof for unexpected root (expected: {:?} got {:?})",
227 root,
228 proof.untrusted_root,
229 ));
230 }
231 if proof.entries.is_empty() {
232 return Err(anyhow!("verifier: empty proof"));
233 }
234
235 let (idx, root_node) = Self::_verify_proof(proof, 0)?;
236 if idx != proof.entries.len() {
239 return Err(anyhow!("verifier: unused entries in proof"));
240 }
241 let root_hash = root_node.borrow().hash;
242 if root_hash != root {
243 return Err(anyhow!(
244 "verifier: bad root (expected: {:?} got {:?})",
245 root,
246 root_hash,
247 ));
248 }
249
250 Ok(root_node)
251 }
252
253 fn _verify_proof(proof: &Proof, idx: usize) -> Result<(usize, NodePtrRef)> {
254 if idx >= proof.entries.len() {
255 return Err(anyhow!("verifier: malformed proof"));
256 }
257 let entry = match &proof.entries[idx] {
258 Some(entry) => entry.as_ref(),
259 None => return Ok((idx + 1, NodePointer::null_ptr())),
260 };
261 if entry.is_empty() {
262 return Err(anyhow!("verifier: malformed proof"));
263 }
264
265 match entry[0] {
266 PROOF_ENTRY_FULL => {
267 let mut node = NodeBox::default();
269 node.unmarshal_binary(&entry[1..])?;
270
271 let mut pos = idx + 1;
273 if let NodeBox::Internal(ref mut nd) = node {
274 match proof.v {
275 0 => {
276 }
278 1 => {
279 (pos, nd.leaf_node) = Self::_verify_proof(proof, pos)?;
281 }
282 _ => {
283 panic!("unsupported proof version: {:?}", proof.v)
285 }
286 }
287
288 (pos, nd.left) = Self::_verify_proof(proof, pos)?;
290 (pos, nd.right) = Self::_verify_proof(proof, pos)?;
292
293 nd.update_hash();
295 }
296
297 Ok((pos, NodePointer::from_node(node)))
298 }
299 PROOF_ENTRY_HASH => {
300 let entry = &entry[1..];
302 if entry.len() != Hash::len() {
303 return Err(anyhow!("verifier: malformed hash entry"));
304 }
305
306 Ok((idx + 1, NodePointer::hash_ptr(entry.into())))
307 }
308 entry_type => Err(anyhow!(
309 "verifier: unexpected entry in proof ({:?})",
310 entry_type
311 )),
312 }
313 }
314}
315
316#[cfg(test)]
317mod test {
318 use base64::prelude::*;
319 use rustc_hex::ToHex;
320
321 use crate::storage::mkvs::{cache::Cache, sync::NoopReadSyncer};
322
323 use super::*;
324
325 #[test]
326 fn test_proof() {
327 let test_vector_proof = BASE64_STANDARD.decode(
331 "omdlbnRyaWVzhUoBASQAa2V5IDACRgEBAQAAAlghAsFltYRhD4dAwHOdOmEigY1r02pJH6InhiibKlh9neYlWCECpsJnkjOnIgc4+\
332yfvpsqCcIYHh5eld1hNMWTT7arAfHFYIQLhNTLWRbks1RBf52ulnlOTO+7D5EZNMYFzTx8U46sCnm51bnRydXN0ZWRfcm9vdFggWeZ8L9wIuOEN0Iu2\
333uO/mFPzJZey4liX5fxf4fwcQRhM=",
334 ).unwrap();
335
336 let test_vector_root_hash =
337 "59e67c2fdc08b8e10dd08bb6b8efe614fcc965ecb89625f97f17f87f07104613";
338
339 let proof: Proof =
341 cbor::from_slice(&test_vector_proof).expect("V0 proof should deserialize");
342 assert_eq!(proof.v, 0, "proof version should be 0");
343 let root_hash = Hash::from(test_vector_root_hash);
344
345 assert_eq!(
347 test_vector_proof,
348 cbor::to_vec(proof.clone()),
349 "proof should round-trip"
350 );
351
352 let pv = ProofVerifier;
354 pv.verify_proof(root_hash, &proof)
355 .expect("verify proof should not fail with a valid proof");
356
357 let empty_proof = Proof::default();
361 let result = pv.verify_proof(root_hash, &empty_proof);
362 assert!(
363 result.is_err(),
364 "verify proof should fail with an empty proof"
365 );
366
367 let bogus_hash = Hash::digest_bytes(b"i am a bogus hash");
369 let result = pv.verify_proof(bogus_hash, &proof);
370 assert!(
371 result.is_err(),
372 "verify proof should fail with a proof for a different root"
373 );
374
375 let mut corrupted = proof.clone();
377 corrupted.entries[4].as_mut().unwrap()[10] = 0x00;
378 let result = pv.verify_proof(root_hash, &corrupted);
379 assert!(
380 result.is_err(),
381 "verify proof should fail with invalid proof"
382 );
383
384 let mut corrupted = proof.clone();
386 corrupted.entries[0].as_mut().unwrap().truncate(3);
387 let result = pv.verify_proof(root_hash, &corrupted);
388 assert!(
389 result.is_err(),
390 "verify proof should fail with invalid proof"
391 );
392
393 let mut corrupted = proof.clone();
395 corrupted.entries[2].as_mut().unwrap().truncate(3);
396 let result = pv.verify_proof(root_hash, &corrupted);
397 assert!(
398 result.is_err(),
399 "verify proof should fail with invalid proof"
400 );
401
402 let mut corrupted = proof.clone();
404 corrupted.entries[3].as_mut().unwrap()[0] = 0xaa;
405 let result = pv.verify_proof(root_hash, &corrupted);
406 assert!(
407 result.is_err(),
408 "verify proof should fail with invalid proof"
409 );
410
411 let mut corrupted = proof.clone();
413 corrupted.entries.truncate(3);
414 let result = pv.verify_proof(root_hash, &corrupted);
415 assert!(
416 result.is_err(),
417 "verify proof should fail with invalid proof"
418 );
419
420 let test_vector_proof = BASE64_STANDARD.decode(
422 "o2F2AWdlbnRyaWVzh0oBASQAa2V5IDAC9kYBAQEAAAL2WCECwWW1hGEPh0DAc506YSKBjWvTakkfoieGKJsqWH2d5iVYIQKmwmeSM6ciBzj7J+\
423 +myoJwhgeHl6V3WE0xZNPtqsB8cVghAuE1MtZFuSzVEF/na6WeU5M77sPkRk0xgXNPHxTjqwKebnVudHJ1c3RlZF9yb290WCBZ5nwv3Ai44Q3Qi7a47+\
424 YU/Mll7LiWJfl/F/h/BxBGEw=="
425 ).unwrap();
426
427 let proof: Proof =
429 cbor::from_slice(&test_vector_proof).expect("V1 proof should deserialize");
430 assert_eq!(proof.v, 1, "proof version should be 1");
431 let root_hash = Hash::from(test_vector_root_hash);
432
433 assert_eq!(
435 test_vector_proof,
436 cbor::to_vec(proof.clone()),
437 "proof should round-trip"
438 );
439
440 let pv = ProofVerifier;
442 pv.verify_proof(root_hash, &proof)
443 .expect("verify proof should not fail with a valid proof");
444 }
445
446 #[test]
447 fn test_proof_builder_v1() {
448 let mut tree = Tree::builder()
452 .with_root(Root {
453 hash: Hash::empty_hash(),
454 ..Default::default()
455 })
456 .build(Box::new(NoopReadSyncer));
457 for i in 0..11 {
458 let k = format!("key {}", i).into_bytes();
459 let v = format!("value {}", i).into_bytes();
460 tree.insert(&k, &v).expect("insert");
461 }
462 let roothash = tree.commit(Default::default(), 1).expect("commit");
463
464 let mut pb = ProofBuilder::new(roothash);
465
466 let proof = pb.build();
468 assert_eq!(
469 BASE64_STANDARD.encode(cbor::to_vec(proof)),
470 "o2F2AWdlbnRyaWVzgVghAqlAud7XYhorEEl8hG9G3Hd4OXl5VR1xvuLAepMZ5qpFbnVudHJ1c3RlZF9yb290WCCpQLne12IaKxBJfIRvRtx3eDl5eVUdcb7iwHqTGeaqRQ=="
471 );
472
473 let root_ptr = tree.cache.borrow().get_pending_root();
475 let root_node = root_ptr.borrow().get_node();
476 pb.include(&*root_node.borrow());
477 let proof = pb.build();
479 assert_eq!(
480 BASE64_STANDARD.encode(cbor::to_vec(proof)),
481 "o2F2AWdlbnRyaWVzhEoBASQAa2V5IDAC9lghAhQ6RgqFtADx+B6VKE0CVRrfDHmwgZwU3ewsj4gswWv+WCEC4TUy1kW5LNUQX+drpZ5Tkzvuw+RGTTGBc08fFOOrAp5udW50cnVzdGVkX3Jvb3RYIKlAud7XYhorEEl8hG9G3Hd4OXl5VR1xvuLAepMZ5qpF",
482 );
483
484 let root_left = noderef_as!(root_node, Internal).left.borrow().get_node();
486 pb.include(&*root_left.borrow());
487 let proof = pb.build();
489 assert_eq!(
490 BASE64_STANDARD.encode(cbor::to_vec(proof)),
491 "o2F2AWdlbnRyaWVzh0oBASQAa2V5IDAC9kYBAQEAAAL2WCEC+OrSSPCCo9/QXQt7IsunAp+eUqMndKBCu0NcGHx4HrhYIQKmwmeSM6ciBzj7J++myoJwhgeHl6V3WE0xZNPtqsB8cVghAuE1MtZFuSzVEF/na6WeU5M77sPkRk0xgXNPHxTjqwKebnVudHJ1c3RlZF9yb290WCCpQLne12IaKxBJfIRvRtx3eDl5eVUdcb7iwHqTGeaqRQ==",
492 );
493
494 let root_left2 = noderef_as!(root_left, Internal).left.borrow().get_node();
496 pb.include(&*root_left2.borrow());
497
498 let root_left3: Rc<RefCell<NodeBox>> =
500 noderef_as!(root_left2, Internal).left.borrow().get_node();
501 pb.include(&*root_left3.borrow());
502
503 let root_left3_right = noderef_as!(root_left3, Internal).right.borrow().get_node();
505 pb.include(&*root_left3_right.borrow());
506
507 let bottom = noderef_as!(root_left3_right, Internal)
509 .left
510 .borrow()
511 .get_node();
512 pb.include(&*bottom.borrow());
513 let proof = pb.build();
515 assert_eq!(
516 BASE64_STANDARD.encode(cbor::to_vec(proof)),
517 "o2F2AWdlbnRyaWVzkEoBASQAa2V5IDAC9kYBAQEAAAL2RgEBAQAAAvZGAQEBAAAC9lghAlF8/rp9QOAd1qSchhUxDtVkpmnze6sjz5IfFhdOuaypRgEBAQCAAlghAldMzQwgHh/Ecm+rF+i31AnOgFYBipBAlcx5Tf5l4yW+VgEABgBrZXkgMTAIAAAAdmFsdWUgMTD2WCECDnYjQhsAp5fD+gf0W5YYFY6CnGURrEETtJvJp+ijH4xYIQKmwmeSM6ciBzj7J++myoJwhgeHl6V3WE0xZNPtqsB8cVghAuE1MtZFuSzVEF/na6WeU5M77sPkRk0xgXNPHxTjqwKebnVudHJ1c3RlZF9yb290WCCpQLne12IaKxBJfIRvRtx3eDl5eVUdcb7iwHqTGeaqRQ==",
518 );
519 }
520
521 #[test]
522 fn test_proof_extra_nodes() {
523 let test_vector_proof = BASE64_STANDARD.decode(
525 "omdlbnRyaWVzhUoBASQAa2V5IDACRgEBAQAAAlghAsFltYRhD4dAwHOdOmEigY1r02pJH6InhiibKlh9neYlWCECpsJnkjOnIgc4+\
526 yfvpsqCcIYHh5eld1hNMWTT7arAfHFYIQLhNTLWRbks1RBf52ulnlOTO+7D5EZNMYFzTx8U46sCnm51bnRydXN0ZWRfcm9vdFggWeZ8L9wIuOEN0Iu2\
527 uO/mFPzJZey4liX5fxf4fwcQRhM=",
528 ).unwrap();
529 let test_vector_root_hash =
530 "59e67c2fdc08b8e10dd08bb6b8efe614fcc965ecb89625f97f17f87f07104613";
531
532 let mut proof: Proof =
534 cbor::from_slice(&test_vector_proof).expect("proof should deserialize");
535 let root_hash = Hash::from(test_vector_root_hash);
536
537 let pv = ProofVerifier;
539 pv.verify_proof(root_hash, &proof)
540 .expect("verify proof should not fail with a valid proof");
541
542 proof.entries.push(proof.entries[0].clone());
544
545 pv.verify_proof(root_hash, &proof)
546 .expect_err("proof with extra data should fail to validate");
547
548 let test_vector_proof = BASE64_STANDARD.decode(
550 "o2F2AWdlbnRyaWVzh0oBASQAa2V5IDAC9kYBAQEAAAL2WCECwWW1hGEPh0DAc506YSKBjWvTakkfoieGKJsqWH2d5iVYIQKmwmeSM6ciBzj7J+\
551 +myoJwhgeHl6V3WE0xZNPtqsB8cVghAuE1MtZFuSzVEF/na6WeU5M77sPkRk0xgXNPHxTjqwKebnVudHJ1c3RlZF9yb290WCBZ5nwv3Ai44Q3Qi7a47+\
552 YU/Mll7LiWJfl/F/h/BxBGEw=="
553 ).unwrap();
554
555 let mut proof: Proof =
557 cbor::from_slice(&test_vector_proof).expect("V1 proof should deserialize");
558 assert_eq!(proof.v, 1, "proof version should be 1");
559 let root_hash = Hash::from(test_vector_root_hash);
560
561 let pv = ProofVerifier;
563 pv.verify_proof(root_hash, &proof)
564 .expect("verify proof should not fail with a valid proof");
565
566 proof.entries.push(proof.entries[0].clone());
568 pv.verify_proof(root_hash, &proof)
569 .expect_err("verify proof should fail with an invalid proof");
570 }
571
572 #[test]
573 fn test_proof_builder_v0() {
574 let mut tree = Tree::builder()
578 .with_root(Root {
579 hash: Hash::empty_hash(),
580 ..Default::default()
581 })
582 .build(Box::new(NoopReadSyncer));
583 for i in 0..10 {
584 let k = format!("key {}", i).into_bytes();
585 let v = format!("value {}", i).into_bytes();
586 tree.insert(&k, &v).expect("insert");
587 }
588 let roothash = tree.commit(Default::default(), 1).expect("commit");
589
590 assert_eq!(
592 roothash.0.to_hex::<String>(),
593 "59e67c2fdc08b8e10dd08bb6b8efe614fcc965ecb89625f97f17f87f07104613",
594 );
595
596 let mut pb = ProofBuilder::new_with_version(roothash, 0).expect("new proof builder v0");
598 let root_only_proof = pb.build();
599 assert_eq!(
600 BASE64_STANDARD.encode(cbor::to_vec(root_only_proof)),
601 "omdlbnRyaWVzgVghAlnmfC/cCLjhDdCLtrjv5hT8yWXsuJYl+X8X+H8HEEYTbnVudHJ1c3RlZF9yb290WCBZ5nwv3Ai44Q3Qi7a47+YU/Mll7LiWJfl/F/h/BxBGEw==",
602 );
603
604 let root_ptr = tree.cache.borrow().get_pending_root();
606 let root_node = root_ptr.borrow().get_node();
607 pb.include(&*root_node.borrow());
608 pb.include(
610 &*noderef_as!(root_node, Internal)
611 .left
612 .borrow()
613 .get_node()
614 .borrow(),
615 );
616 let test_proof = pb.build();
618 assert_eq!(
619 BASE64_STANDARD.encode(cbor::to_vec(test_proof)),
620 "omdlbnRyaWVzhUoBASQAa2V5IDACRgEBAQAAAlghAsFltYRhD4dAwHOdOmEigY1r02pJH6InhiibKlh9neYlWCECpsJnkjOnIgc4+yfvpsqCcIYHh5eld1hNMWTT7arAfHFYIQLhNTLWRbks1RBf52ulnlOTO+7D5EZNMYFzTx8U46sCnm51bnRydXN0ZWRfcm9vdFggWeZ8L9wIuOEN0Iu2uO/mFPzJZey4liX5fxf4fwcQRhM=",
621 );
622 }
623
624 #[test]
625 fn test_tree_proofs() {
626 let mut tree = Tree::builder()
630 .with_root(Root {
631 hash: Hash::empty_hash(),
632 ..Default::default()
633 })
634 .build(Box::new(NoopReadSyncer));
635 let mut keys = vec![];
636 for i in 0..11 {
637 let k = format!("key {}", i).into_bytes();
638 let v = format!("value {}", i).into_bytes();
639 tree.insert(&k, &v).expect("insert");
640 keys.push(k);
641 }
642 let roothash = tree.commit(Default::default(), 1).expect("commit");
643 assert_eq!(
645 roothash.0.to_hex::<String>(),
646 "a940b9ded7621a2b10497c846f46dc7778397979551d71bee2c07a9319e6aa45",
647 );
648
649 for tc in vec![
650 (
653 1,
655 vec![
656 "o2F2AWdlbnRyaWVzjUoBASQAa2V5IDAC9kYBAQEAAAL2RgEBAQAAAvZGAQEBAAAC9lQBAAUAa2V5IDAHAAAAdmFsdWUgMFghAlO8PtYkGTEg304b/z/cv4oH6+BRaJ88layf7VgIl3xTWCECDnYjQhsAp5fD+gf0W5YYFY6CnGURrEETtJvJp+ijH4xYIQKmwmeSM6ciBzj7J++myoJwhgeHl6V3WE0xZNPtqsB8cVghAuE1MtZFuSzVEF/na6WeU5M77sPkRk0xgXNPHxTjqwKebnVudHJ1c3RlZF9yb290WCCpQLne12IaKxBJfIRvRtx3eDl5eVUdcb7iwHqTGeaqRQ==",
658 "o2F2AWdlbnRyaWVzkEoBASQAa2V5IDAC9kYBAQEAAAL2RgEBAQAAAvZGAQEBAAAC9lghAlF8/rp9QOAd1qSchhUxDtVkpmnze6sjz5IfFhdOuaypRgEBAQCAAlQBAAUAa2V5IDEHAAAAdmFsdWUgMVghAm2EG0dH85+yEl5rdT67+D59/gbjHB9qDtnCcv0kkuje9lghAg52I0IbAKeXw/oH9FuWGBWOgpxlEaxBE7Sbyafoox+MWCECpsJnkjOnIgc4+yfvpsqCcIYHh5eld1hNMWTT7arAfHFYIQLhNTLWRbks1RBf52ulnlOTO+7D5EZNMYFzTx8U46sCnm51bnRydXN0ZWRfcm9vdFggqUC53tdiGisQSXyEb0bcd3g5eXlVHXG+4sB6kxnmqkU=",
660 "o2F2AWdlbnRyaWVzjUoBASQAa2V5IDAC9kYBAQEAAAL2RgEBAQAAAvZYIQLnu/nMm00WQo9ZxRbRFM/hVtoTov4Phs3vIQ/6jS/29kYBAQEAgAL2VAEABQBrZXkgMgcAAAB2YWx1ZSAyWCECJtIdvBvSs2Vh4Z1ghY3zvHvK8JsoBmt3+dBpRCNdA/xYIQKmwmeSM6ciBzj7J++myoJwhgeHl6V3WE0xZNPtqsB8cVghAuE1MtZFuSzVEF/na6WeU5M77sPkRk0xgXNPHxTjqwKebnVudHJ1c3RlZF9yb290WCCpQLne12IaKxBJfIRvRtx3eDl5eVUdcb7iwHqTGeaqRQ==",
662 "o2F2AWdlbnRyaWVzjUoBASQAa2V5IDAC9kYBAQEAAAL2RgEBAQAAAvZYIQLnu/nMm00WQo9ZxRbRFM/hVtoTov4Phs3vIQ/6jS/29kYBAQEAgAL2WCECoYj1TutUHeB1K0anT1hRts8AKOw8AfEtn963XMVxV1xUAQAFAGtleSAzBwAAAHZhbHVlIDNYIQKmwmeSM6ciBzj7J++myoJwhgeHl6V3WE0xZNPtqsB8cVghAuE1MtZFuSzVEF/na6WeU5M77sPkRk0xgXNPHxTjqwKebnVudHJ1c3RlZF9yb290WCCpQLne12IaKxBJfIRvRtx3eDl5eVUdcb7iwHqTGeaqRQ==",
664 "o2F2AWdlbnRyaWVzjUoBASQAa2V5IDAC9kYBAQEAAAL2WCEC+OrSSPCCo9/QXQt7IsunAp+eUqMndKBCu0NcGHx4HrhGAQEBAIAC9kYBAQEAAAL2VAEABQBrZXkgNAcAAAB2YWx1ZSA0WCEC7uES0HThvwiREsS6OKGDOodNGn7WGC3BTBJSLBIsJf1YIQK31gqPbwp33M7yKb2VSo7eB8p4jZZmYGnJ7njeFBGHtVghAuE1MtZFuSzVEF/na6WeU5M77sPkRk0xgXNPHxTjqwKebnVudHJ1c3RlZF9yb290WCCpQLne12IaKxBJfIRvRtx3eDl5eVUdcb7iwHqTGeaqRQ==",
666 "o2F2AWdlbnRyaWVzjUoBASQAa2V5IDAC9kYBAQEAAAL2WCEC+OrSSPCCo9/QXQt7IsunAp+eUqMndKBCu0NcGHx4HrhGAQEBAIAC9kYBAQEAAAL2WCECxgplEp2jBopznMqoBJZ1ezCE7IO2Bb5CqLypC+IiEhpUAQAFAGtleSA1BwAAAHZhbHVlIDVYIQK31gqPbwp33M7yKb2VSo7eB8p4jZZmYGnJ7njeFBGHtVghAuE1MtZFuSzVEF/na6WeU5M77sPkRk0xgXNPHxTjqwKebnVudHJ1c3RlZF9yb290WCCpQLne12IaKxBJfIRvRtx3eDl5eVUdcb7iwHqTGeaqRQ==",
668 "o2F2AWdlbnRyaWVzjUoBASQAa2V5IDAC9kYBAQEAAAL2WCEC+OrSSPCCo9/QXQt7IsunAp+eUqMndKBCu0NcGHx4HrhGAQEBAIAC9lghAivLwJbWkwZ8nROaPHGxpfthiG8vqyPbvzhkEEX793dIRgEBAQCAAvZUAQAFAGtleSA2BwAAAHZhbHVlIDZYIQK96CvAaM4vOReqLe+AoO6KajYFZiUsAyvSi8rEgClU7FghAuE1MtZFuSzVEF/na6WeU5M77sPkRk0xgXNPHxTjqwKebnVudHJ1c3RlZF9yb290WCCpQLne12IaKxBJfIRvRtx3eDl5eVUdcb7iwHqTGeaqRQ==",
670 "o2F2AWdlbnRyaWVzjUoBASQAa2V5IDAC9kYBAQEAAAL2WCEC+OrSSPCCo9/QXQt7IsunAp+eUqMndKBCu0NcGHx4HrhGAQEBAIAC9lghAivLwJbWkwZ8nROaPHGxpfthiG8vqyPbvzhkEEX793dIRgEBAQCAAvZYIQICPF9slSfLMzCKpIGUKFWpXWq5dxfVdfe6wfGW6xjer1QBAAUAa2V5IDcHAAAAdmFsdWUgN1ghAuE1MtZFuSzVEF/na6WeU5M77sPkRk0xgXNPHxTjqwKebnVudHJ1c3RlZF9yb290WCCpQLne12IaKxBJfIRvRtx3eDl5eVUdcb7iwHqTGeaqRQ==",
672 "o2F2AWdlbnRyaWVzh0oBASQAa2V5IDAC9lghAhQ6RgqFtADx+B6VKE0CVRrfDHmwgZwU3ewsj4gswWv+RgEBAwCAAvZUAQAFAGtleSA4BwAAAHZhbHVlIDhYIQINczJ806eMr+T/iGsJmo8/JQFXZtjy1/k+as0V9FdfVG51bnRydXN0ZWRfcm9vdFggqUC53tdiGisQSXyEb0bcd3g5eXlVHXG+4sB6kxnmqkU=",
674 "o2F2AWdlbnRyaWVzh0oBASQAa2V5IDAC9lghAhQ6RgqFtADx+B6VKE0CVRrfDHmwgZwU3ewsj4gswWv+RgEBAwCAAvZYIQIwwW7eyXCi2yXyFCzFD9U+Ssy1gwSwiskBQfk+9KCUA1QBAAUAa2V5IDkHAAAAdmFsdWUgOW51bnRydXN0ZWRfcm9vdFggqUC53tdiGisQSXyEb0bcd3g5eXlVHXG+4sB6kxnmqkU=",
676 "o2F2AWdlbnRyaWVzkEoBASQAa2V5IDAC9kYBAQEAAAL2RgEBAQAAAvZGAQEBAAAC9lghAlF8/rp9QOAd1qSchhUxDtVkpmnze6sjz5IfFhdOuaypRgEBAQCAAlghAldMzQwgHh/Ecm+rF+i31AnOgFYBipBAlcx5Tf5l4yW+VgEABgBrZXkgMTAIAAAAdmFsdWUgMTD2WCECDnYjQhsAp5fD+gf0W5YYFY6CnGURrEETtJvJp+ijH4xYIQKmwmeSM6ciBzj7J++myoJwhgeHl6V3WE0xZNPtqsB8cVghAuE1MtZFuSzVEF/na6WeU5M77sPkRk0xgXNPHxTjqwKebnVudHJ1c3RlZF9yb290WCCpQLne12IaKxBJfIRvRtx3eDl5eVUdcb7iwHqTGeaqRQ==",
678 ],
679 ),
680 ]{
681 for (i, k) in tc.1.iter().enumerate() {
683 let proof = tree.get_proof(&keys[i]).expect("get proof works").expect("proof exists");
684 assert_eq!(
685 BASE64_STANDARD.encode(cbor::to_vec(proof.clone())),
686 *k,
687 "expected proof for keys[{}]",
688 i
689 );
690 let pv = ProofVerifier;
692 pv.verify_proof(roothash, &proof)
693 .expect("verify proof should not fail with a valid proof");
694 }
695 };
696 }
697}