use crate::entities::{IntoMKTreeNode, MkSetProof};
use crate::messages::{MkSetProofMessagePart, VerifyProofsV2Error};
pub(crate) struct ProofMessageVerifier<T, U> {
subject: &'static str,
hash_extractor: fn(&U) -> String,
_phantom: std::marker::PhantomData<T>,
}
impl<T, U> ProofMessageVerifier<T, U>
where
T: Clone,
U: IntoMKTreeNode + Clone + From<T>,
{
pub fn new(subject: &'static str, hash_extractor: fn(&U) -> String) -> Self {
Self {
subject,
hash_extractor,
_phantom: std::marker::PhantomData,
}
}
fn proof_message_into_entity(
&self,
message: &MkSetProofMessagePart<T>,
) -> Result<MkSetProof<U>, VerifyProofsV2Error> {
message
.clone()
.try_into()
.map_err(|e| VerifyProofsV2Error::MalformedData(self.subject, e))
}
pub fn verify(
&self,
proof_message: &MkSetProofMessagePart<T>,
) -> Result<String, VerifyProofsV2Error> {
let certified_item = self.proof_message_into_entity(proof_message)?;
certified_item
.verify()
.map_err(|e| VerifyProofsV2Error::InvalidSetProof {
subject: self.subject,
hashes: certified_item.items.iter().map(self.hash_extractor).collect(),
source: e,
})?;
Ok(certified_item.merkle_root())
}
}
#[cfg(test)]
mod tests {
use crate::crypto_helper::{
MKMap, MKMapNode, MKTree, MKTreeStoreInMemory, ProtocolKey, ProtocolMkProof,
};
use crate::entities::BlockRange;
use crate::test::entities_extensions::BlockRangeTestExtension;
use super::*;
fn mk_map_proof_for(items: &[&str]) -> ProtocolMkProof {
let mk_map: MKMap<_, MKMapNode<BlockRange, MKTreeStoreInMemory>, MKTreeStoreInMemory> =
MKMap::new(&[(BlockRange::new(0, 100), MKTree::new(items).unwrap().into())]).unwrap();
ProtocolKey::new(mk_map.compute_proof(items).unwrap())
}
#[test]
fn verify_malformed_proofs_fail() {
let proof_message = MkSetProofMessagePart::<&str> {
items: vec![],
proof: "invalid".to_string(),
};
let error = ProofMessageVerifier::<_, &str>::new("subject", |i| i.to_string())
.verify(&proof_message)
.expect_err("Malformed txs proofs should fail to verify itself");
assert!(
matches!(error, VerifyProofsV2Error::MalformedData(..)),
"Expected 'MalformedData' error but got '{error:?}'"
);
}
#[test]
fn verify_valid_proofs() {
let items = vec!["item1", "item2", "item3"];
let proof = mk_map_proof_for(&items);
let txs_proofs = MkSetProofMessagePart::<&str> {
items: items.clone(),
proof: proof.to_bytes_hex().unwrap(),
};
let merkle_root = ProofMessageVerifier::<&str, &str>::new("subject", |i| i.to_string())
.verify(&txs_proofs)
.expect("Valid proof should verify itself");
assert_eq!(proof.compute_root().to_hex(), merkle_root);
}
#[test]
fn verify_proof_with_items_not_included_in_its_merkle_tree_fail() {
let txs_proofs = MkSetProofMessagePart::<&str> {
items: vec!["invalid"],
proof: mk_map_proof_for(&["item1"]).to_bytes_hex().unwrap(),
};
let error = ProofMessageVerifier::<&str, &str>::new("subject", |i| i.to_string())
.verify(&txs_proofs)
.expect_err(
"Proofs with items not included in its merkle tree should fail to verify itself",
);
assert!(
matches!(error, VerifyProofsV2Error::InvalidSetProof { .. }),
"Expected 'InvalidSetProof' error but got '{error:?}'"
);
}
}