diff --git a/indy-data-types/src/merkle_tree/mod.rs b/indy-data-types/src/merkle_tree/mod.rs index 44fde87..2126d6f 100644 --- a/indy-data-types/src/merkle_tree/mod.rs +++ b/indy-data-types/src/merkle_tree/mod.rs @@ -3,9 +3,10 @@ use indy_utils::hash::{TreeHash, SHA256::DigestType as Hash}; use self::tree::{Tree, TreeLeafData}; use crate::ValidationError; -mod merkletree; pub use self::merkletree::MerkleTree; +pub use self::proof::Positioned; +mod merkletree; mod proof; mod tree; @@ -199,6 +200,25 @@ impl MerkleTree { } Ok(()) } + + pub fn check_inclusion_proof( + root_hash: &[u8], + leaf_value: &TreeLeafData, + path: &[Positioned], + ) -> Result { + let mut check_hash = Hash::hash_leaf(leaf_value)?; + for node in path { + match node { + Positioned::Left(data) => { + check_hash = Hash::hash_nodes(data, &check_hash)?; + } + Positioned::Right(data) => { + check_hash = Hash::hash_nodes(&check_hash, data)?; + } + } + } + Ok(check_hash == root_hash) + } } #[cfg(test)] @@ -443,4 +463,27 @@ mod tests { .consistency_proof(&full_root_hash, 8, &proofs_for_8) .unwrap()); } + + #[test] + fn check_inclusion_proof_works() { + let nodes = [ + (true, "Gf9aBhHCtBpTYbJXQWnt1DU8q33hwi6nN4f3NhnsBgMZ"), + (false, "68TGAdRjeQ29eNcuFYhsX5uLakGQLgKMKp5wSyPzt9Nq"), + (true, "25KLEkkyCEPSBj4qMFE3AcH87mFocyJEuPJ5xzPGwDgz"), + ]; + let path: Vec>> = nodes + .iter() + .map(|(side, val)| { + let val = base58::decode(val).unwrap(); + if *side { + Positioned::Right(val) + } else { + Positioned::Left(val) + } + }) + .collect(); + let root_hash = base58::decode("CrA5sqYe3ruf2uY7d8re7ePmyHqptHqANtMZcfZd4BvK").unwrap(); + let leaf_value = b"\x81\xa13\xa13".to_vec(); // {"3":"3"} serialized via rmp + assert!(MerkleTree::check_inclusion_proof(&root_hash, &leaf_value, &path).unwrap()); + } } diff --git a/indy-data-types/src/merkle_tree/proof.rs b/indy-data-types/src/merkle_tree/proof.rs index ed798f9..84afa3a 100644 --- a/indy-data-types/src/merkle_tree/proof.rs +++ b/indy-data-types/src/merkle_tree/proof.rs @@ -46,13 +46,13 @@ impl Proof { Some(Positioned::Left(ref hash)) => { let combined = Hash::hash_nodes(hash, &sub.node_hash)?; - let hashes_match = combined.to_vec().as_slice() == lemma.node_hash.as_slice(); + let hashes_match = combined == lemma.node_hash; Ok(hashes_match && self.validate_lemma(sub)?) } Some(Positioned::Right(ref hash)) => { let combined = Hash::hash_nodes(&sub.node_hash, hash)?; - let hashes_match = combined.to_vec().as_slice() == lemma.node_hash.as_slice(); + let hashes_match = combined == lemma.node_hash; Ok(hashes_match && self.validate_lemma(sub)?) } },