use cosmwasm_schema::cw_serde;
use cosmwasm_std::StdError;
use hex::FromHexError;
use sha2::Digest;
use std::convert::TryInto;
use thiserror::Error;
#[cw_serde]
pub struct Merkle {
root: String,
}
pub type Proof = Vec<String>;
#[derive(Error, Debug, PartialEq)]
pub enum Error {
#[error("{0}")]
Std(#[from] StdError),
#[error("{0}")]
Hex(#[from] FromHexError),
#[error("Wrong length")]
WrongLength {},
#[error("Verification failed")]
VerificationFailed {},
}
impl Merkle {
pub fn new(root: String) -> Result<Self, Error> {
let mut root_buf: [u8; 32] = [0; 32];
hex::decode_to_slice(&root, &mut root_buf)?;
Ok(Self { root })
}
pub fn verify(&self, proof: Proof, input: String) -> Result<(), Error> {
let hash = sha2::Sha256::digest(input.as_bytes())
.as_slice()
.try_into()
.map_err(|_| Error::WrongLength {})?;
let hash = proof.into_iter().try_fold(hash, |hash, p| {
let mut proof_buf = [0; 32];
hex::decode_to_slice(p, &mut proof_buf)?;
let mut hashes = [hash, proof_buf];
hashes.sort_unstable();
sha2::Sha256::digest(hashes.concat())
.as_slice()
.try_into()
.map_err(|_| Error::WrongLength {})
})?;
let mut root_buf: [u8; 32] = [0; 32];
hex::decode_to_slice(self.root.clone(), &mut root_buf)?;
if root_buf != hash {
return Err(Error::VerificationFailed {});
}
Ok(())
}
}