pub trait CommitVerify<M>
where
Self: Eq + Sized,
{
fn commit(msg: &M) -> Self;
#[inline]
fn verify(&self, msg: &M) -> bool {
Self::commit(msg) == *self
}
}
pub trait TryCommitVerify<M>
where
Self: Eq + Sized,
{
type Error: std::error::Error;
fn try_commit(msg: &M) -> Result<Self, Self::Error>;
#[inline]
fn try_verify(&self, msg: &M) -> Result<bool, Self::Error> {
Ok(Self::try_commit(msg)? == *self)
}
}
pub trait EmbedCommitVerify<M>
where
Self: Sized + Eq,
{
type Container: Clone;
type Error: std::error::Error;
fn embed_commit(
container: &mut Self::Container,
msg: &M,
) -> Result<Self, Self::Error>;
#[inline]
fn verify(
&self,
container: &Self::Container,
msg: &M,
) -> Result<bool, Self::Error> {
let mut container = container.clone();
Ok(match Self::embed_commit(&mut container, msg) {
Ok(commitment) => commitment == *self,
Err(_) => false,
})
}
}
#[cfg(test)]
pub mod test_helpers {
use super::*;
use bitcoin_hashes::hex::FromHex;
use core::fmt::Debug;
use core::hash::Hash;
use std::collections::HashSet;
pub fn gen_messages() -> Vec<Vec<u8>> {
vec![
b"".to_vec(),
b"\x00".to_vec(),
b"test".to_vec(),
b"test*".to_vec(),
Vec::from_hex("deadbeef").unwrap(),
Vec::from_hex("deadbeef00").unwrap(),
Vec::from_hex("00deadbeef").unwrap(),
b"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798".to_vec(),
Vec::from_hex("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")
.unwrap(),
Vec::from_hex("02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9")
.unwrap(),
]
}
pub fn commit_verify_suite<MSG, CMT>(messages: Vec<MSG>)
where
MSG: AsRef<[u8]> + Eq,
CMT: CommitVerify<MSG> + Eq + Hash + Debug,
{
messages.iter().fold(
HashSet::<CMT>::with_capacity(messages.len()),
|mut acc, msg| {
let commitment = CMT::commit(msg);
(1..10).for_each(|_| {
assert_eq!(CMT::commit(msg), commitment);
});
assert!(commitment.verify(msg));
messages.iter().for_each(|m| {
assert_eq!(commitment.verify(m), m == msg);
});
acc.iter().for_each(|cmt| {
assert!(!cmt.verify(msg));
});
assert!(acc.insert(commitment));
acc
},
);
}
pub fn embed_commit_verify_suite<MSG, CMT>(
messages: Vec<MSG>,
container: &mut CMT::Container,
) where
MSG: AsRef<[u8]> + Eq,
CMT: EmbedCommitVerify<MSG> + Eq + Hash + Debug,
{
messages.iter().fold(
HashSet::<CMT>::with_capacity(messages.len()),
|mut acc, msg| {
let commitment = CMT::embed_commit(container, msg).unwrap();
(1..10).for_each(|_| {
assert_eq!(
CMT::embed_commit(container, msg).unwrap(),
commitment
);
});
assert!(commitment.verify(container, msg).unwrap());
messages.iter().for_each(|m| {
assert_eq!(
commitment.verify(container, m).unwrap(),
m == msg
);
});
acc.iter().for_each(|cmt| {
assert!(!cmt.verify(container, msg).unwrap());
});
assert!(acc.insert(commitment));
acc
},
);
}
}
#[cfg(test)]
mod test {
use super::test_helpers::*;
use super::*;
use bitcoin_hashes::sha256d;
use core::fmt::Debug;
use core::hash::Hash;
#[derive(Debug, Display, Error)]
#[display(Debug)]
struct Error;
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
struct DummyHashCommitment(sha256d::Hash);
impl<T> CommitVerify<T> for DummyHashCommitment
where
T: AsRef<[u8]>,
{
fn commit(msg: &T) -> Self {
Self(bitcoin_hashes::Hash::hash(msg.as_ref()))
}
}
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
struct DummyVec(Vec<u8>);
impl<T> EmbedCommitVerify<T> for DummyVec
where
T: AsRef<[u8]>,
{
type Container = DummyVec;
type Error = Error;
fn embed_commit(
container: &mut Self::Container,
msg: &T,
) -> Result<Self, Self::Error> {
let mut result = container.0.clone();
result.extend(msg.as_ref());
Ok(DummyVec(result))
}
}
#[test]
fn test_commit_verify() {
commit_verify_suite::<Vec<u8>, DummyHashCommitment>(gen_messages());
}
#[test]
fn test_embed_commit() {
embed_commit_verify_suite::<Vec<u8>, DummyVec>(
gen_messages(),
&mut DummyVec(vec![]),
);
}
}