99//! - [variable]: Variant for values of variable size.
1010
1111use crate :: {
12- merkle:: Graftable ,
12+ merkle:: { hasher :: Standard as StandardHasher , Graftable } ,
1313 qmdb:: {
14- any:: { ordered:: Update , ValueEncoding } ,
14+ any:: {
15+ ordered:: { self , Operation , Update } ,
16+ ValueEncoding ,
17+ } ,
1518 current:: proof:: OperationProof ,
1619 operation:: Key ,
1720 } ,
1821} ;
1922use bytes:: { Buf , BufMut } ;
20- use commonware_codec:: { EncodeSize , Read , ReadExt as _, Write } ;
21- use commonware_cryptography:: Digest ;
23+ use commonware_codec:: { Codec , EncodeSize , Read , ReadExt as _, Write } ;
24+ use commonware_cryptography:: { Digest , Hasher } ;
2225
2326pub mod db;
2427pub mod fixed;
@@ -46,6 +49,43 @@ pub enum ExclusionProof<F: Graftable, K: Key, V: ValueEncoding, D: Digest, const
4649 Commit ( OperationProof < F , D , N > , Option < V :: Value > ) ,
4750}
4851
52+ impl < F : Graftable , K : Key , V : ValueEncoding , D : Digest , const N : usize >
53+ ExclusionProof < F , K , V , D , N >
54+ where
55+ Operation < F , K , V > : Codec ,
56+ {
57+ /// Return true if this proof authenticates that `key` is not active at `root`.
58+ pub fn verify_key < H > ( & self , hasher : & StandardHasher < H > , key : & K , root : & D ) -> bool
59+ where
60+ H : Hasher < Digest = D > ,
61+ {
62+ let ( op_proof, operation) = match self {
63+ Self :: KeyValue ( op_proof, update) => {
64+ if update. key == * key {
65+ // The provided `key` is in the DB if it matches the start of the span.
66+ return false ;
67+ }
68+ if !ordered:: span_contains ( & update. key , & update. next_key , key) {
69+ // If the key is not within the span, then this proof cannot prove its
70+ // exclusion.
71+ return false ;
72+ }
73+
74+ ( op_proof, Operation :: Update ( update. clone ( ) ) )
75+ }
76+ Self :: Commit ( op_proof, metadata) => {
77+ // Handle the case where the proof shows the db is empty, hence any key is proven
78+ // excluded. For the db to be empty, the floor must equal the commit operation's
79+ // location.
80+ let floor = op_proof. loc ;
81+ ( op_proof, Operation :: CommitFloor ( metadata. clone ( ) , floor) )
82+ }
83+ } ;
84+
85+ op_proof. verify ( hasher, operation, root)
86+ }
87+ }
88+
4989const KEY_VALUE_CONTEXT : u8 = 0 ;
5090const COMMIT_CONTEXT : u8 = 1 ;
5191
@@ -587,6 +627,14 @@ pub mod tests {
587627 assert ! ( TestDb :: <F , C , V >:: verify_key_value_proof(
588628 & hasher, key, value, & proof, & root
589629 ) ) ;
630+ let update = Update {
631+ key,
632+ value,
633+ next_key : proof. next_key ,
634+ } ;
635+ assert ! ( proof. verify_update( & hasher, & update, & root) ) ;
636+ assert ! ( proof. verify_operation( & hasher, & Operation :: Update ( update. clone( ) ) , & root) ) ;
637+ assert ! ( !proof. verify_operation( & hasher, & Operation :: Delete ( key) , & root) ) ;
590638 // Proof should fail against the wrong value. Use hash instead of fill to ensure
591639 // the value differs from any key/value created by TestKey::from_seed (which uses
592640 // fill patterns).
@@ -614,6 +662,9 @@ pub mod tests {
614662 assert ! ( !TestDb :: <F , C , V >:: verify_key_value_proof(
615663 & hasher, key, value, & bad_proof, & root,
616664 ) ) ;
665+ let mut bad_update = update;
666+ bad_update. next_key = wrong_key;
667+ assert ! ( !proof. verify_update( & hasher, & bad_update, & root) ) ;
617668 }
618669
619670 db. destroy ( ) . await . unwrap ( ) ;
@@ -705,6 +756,7 @@ pub mod tests {
705756 & empty_proof,
706757 & empty_root,
707758 ) ) ;
759+ assert ! ( empty_proof. verify_key( & hasher, & key_exists_1, & empty_root) ) ;
708760
709761 // Add `key_exists_1` and test exclusion proving over the single-key database case.
710762 let v1 = Sha256 :: fill ( 0xA1 ) ;
@@ -737,6 +789,7 @@ pub mod tests {
737789 & proof,
738790 & root,
739791 ) ) ;
792+ assert ! ( proof. verify_key( & hasher, & greater_key, & root) ) ;
740793 assert ! ( TestDb :: <F , C , V >:: verify_exclusion_proof(
741794 & hasher,
742795 & lesser_key,
@@ -750,6 +803,7 @@ pub mod tests {
750803 & proof,
751804 & root,
752805 ) ) ;
806+ assert ! ( !proof. verify_key( & hasher, & key_exists_1, & root) ) ;
753807
754808 // Add a second key and test exclusion proving over the two-key database case.
755809 let key_exists_2 = Sha256 :: fill ( 0x30 ) ;
@@ -811,6 +865,7 @@ pub mod tests {
811865 & proof,
812866 & root,
813867 ) ) ;
868+ assert ! ( proof. verify_key( & hasher, & middle_key, & root) ) ;
814869 assert ! ( !TestDb :: <F , C , V >:: verify_exclusion_proof(
815870 & hasher,
816871 & key_exists_2,
0 commit comments