use crate::CommitmentProtocol;
#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error, From)]
#[display(doc_comments)]
pub enum EmbedVerifyError<E: std::error::Error> {
CommitmentMismatch,
#[from]
InvalidMessage(E),
InvalidProof,
ProofMismatch,
}
pub trait VerifyEq {
fn verify_eq(&self, other: &Self) -> bool;
}
impl<T> VerifyEq for T
where T: Eq
{
fn verify_eq(&self, other: &Self) -> bool { self == other }
}
pub trait EmbedCommitProof<Msg, Container, Protocol>
where
Self: Sized + VerifyEq,
Container: EmbedCommitVerify<Msg, Protocol>,
Protocol: CommitmentProtocol,
{
fn restore_original_container(
&self,
commit_container: &Container,
) -> Result<Container, EmbedVerifyError<Container::CommitError>>;
}
pub trait EmbedCommitVerify<Msg, Protocol>
where
Self: Sized,
Protocol: CommitmentProtocol,
{
type Proof: EmbedCommitProof<Msg, Self, Protocol>;
type CommitError: std::error::Error;
fn embed_commit(&mut self, msg: &Msg) -> Result<Self::Proof, Self::CommitError>;
fn verify(
&self,
msg: &Msg,
proof: &Self::Proof,
) -> Result<(), EmbedVerifyError<Self::CommitError>>
where
Self: VerifyEq,
Self::Proof: VerifyEq,
{
let mut container_prime = proof.restore_original_container(self)?;
let proof_prime = container_prime.embed_commit(msg)?;
if !proof_prime.verify_eq(proof) {
return Err(EmbedVerifyError::InvalidProof);
}
if !self.verify_eq(&container_prime) {
return Err(EmbedVerifyError::CommitmentMismatch);
}
Ok(())
}
#[doc(hidden)]
fn _phantom(_: Protocol) {
unimplemented!("EmbedCommitVerify::_phantom is a marker method that must not be used")
}
}
#[cfg(test)]
pub(crate) mod test_helpers {
#![allow(missing_docs)]
#![cfg_attr(coverage_nightly, coverage(off))]
use core::fmt::Debug;
use core::hash::Hash;
use std::collections::HashSet;
use super::*;
use crate::{ConvolveCommit, ConvolveCommitProof};
pub enum TestProtocol {}
impl CommitmentProtocol for TestProtocol {}
pub const SUPPLEMENT: [u8; 32] = [0xFFu8; 32];
pub fn embed_commit_verify_suite<Msg, Container>(messages: Vec<Msg>, container: Container)
where
Msg: AsRef<[u8]> + Eq + Clone,
Container: EmbedCommitVerify<Msg, TestProtocol> + Eq + Hash + Debug + Clone,
Container::Proof: Clone,
{
messages.iter().fold(
HashSet::<Container>::with_capacity(messages.len()),
|mut acc, msg| {
let mut commitment = container.clone();
let proof = commitment.embed_commit(msg).unwrap();
(1..10).for_each(|_| {
let mut commitment_prime = container.clone();
commitment_prime.embed_commit(msg).unwrap();
assert_eq!(commitment_prime, commitment);
});
assert!(commitment.clone().verify(msg, &proof).is_ok());
messages.iter().for_each(|m| {
assert_eq!(commitment.clone().verify(m, &proof).is_ok(), m == msg);
});
acc.iter().for_each(|cmt| {
assert!(cmt.clone().verify(msg, &proof).is_err());
});
assert!(acc.insert(commitment));
acc
},
);
}
pub fn convolve_commit_verify_suite<Msg, Source>(messages: Vec<Msg>, container: Source)
where
Msg: AsRef<[u8]> + Eq + Clone,
Source: ConvolveCommit<Msg, [u8; 32], TestProtocol> + VerifyEq + Eq + Hash + Debug + Clone,
Source::Commitment: Clone + Debug + Hash + VerifyEq + Eq,
[u8; 32]: ConvolveCommitProof<Msg, Source, TestProtocol, Suppl = [u8; 32]>,
{
messages.iter().fold(
HashSet::<Source::Commitment>::with_capacity(messages.len()),
|mut acc, msg| {
let (commitment, _) = container.convolve_commit(&SUPPLEMENT, msg).unwrap();
(1..10).for_each(|_| {
let (commitment_prime, _) =
container.convolve_commit(&SUPPLEMENT, msg).unwrap();
assert_eq!(commitment_prime, commitment);
});
assert!(SUPPLEMENT.verify(msg, &commitment).is_ok());
messages.iter().for_each(|m| {
assert_eq!(SUPPLEMENT.verify(m, &commitment).is_ok(), m == msg);
});
acc.iter().for_each(|commitment| {
assert!(SUPPLEMENT.verify(msg, commitment).is_err());
});
assert!(acc.insert(commitment));
acc
},
);
}
}
#[cfg(test)]
mod test {
#![cfg_attr(coverage_nightly, coverage(off))]
use core::fmt::Debug;
use amplify::confinement::{SmallBlob, SmallVec, U32};
use sha2::Sha256;
use super::test_helpers::*;
use super::*;
use crate::digest::DigestExt;
use crate::test_helpers::gen_messages;
use crate::{ConvolveCommit, ConvolveCommitProof};
#[derive(Clone, PartialEq, Eq, Debug, Hash, Error, Display)]
#[display("error")]
struct Error;
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
struct DummyVec(SmallBlob);
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
struct DummyProof(SmallBlob);
impl<T> EmbedCommitProof<T, DummyVec, TestProtocol> for DummyProof
where T: AsRef<[u8]> + Clone
{
fn restore_original_container(
&self,
_: &DummyVec,
) -> Result<DummyVec, EmbedVerifyError<Error>> {
Ok(DummyVec(self.0.clone()))
}
}
impl<T> EmbedCommitVerify<T, TestProtocol> for DummyVec
where T: AsRef<[u8]> + Clone
{
type Proof = DummyProof;
type CommitError = Error;
fn embed_commit(&mut self, msg: &T) -> Result<Self::Proof, Self::CommitError> {
let proof = self.0.clone();
let result = &mut self.0;
result.extend(msg.as_ref().iter().copied()).unwrap();
Ok(DummyProof(proof))
}
}
impl<T> ConvolveCommit<T, [u8; 32], TestProtocol> for DummyVec
where T: AsRef<[u8]> + Clone
{
type Commitment = [u8; 32];
type CommitError = Error;
fn convolve_commit(
&self,
supplement: &[u8; 32],
msg: &T,
) -> Result<(Self::Commitment, [u8; 32]), Self::CommitError> {
let mut engine = Sha256::default();
engine.input_raw(supplement);
engine.input_with_len::<U32>(msg.as_ref());
Ok((engine.finish(), *supplement))
}
}
impl<T> ConvolveCommitProof<T, DummyVec, TestProtocol> for [u8; 32]
where T: AsRef<[u8]> + Clone
{
type Suppl = [u8; 32];
fn restore_original(&self, _: &[u8; 32]) -> DummyVec { DummyVec(default!()) }
fn extract_supplement(&self) -> &Self::Suppl { self }
}
#[test]
fn test_embed_commit() {
embed_commit_verify_suite::<SmallVec<u8>, DummyVec>(gen_messages(), DummyVec(default!()));
}
#[test]
fn test_convolve_commit() {
convolve_commit_verify_suite::<SmallVec<u8>, DummyVec>(
gen_messages(),
DummyVec(small_vec![0xC0; 15]),
);
}
}