1use cosmwasm_schema::cw_serde;
2use cosmwasm_std::StdError;
3use hex::FromHexError;
4use sha2::Digest;
5use std::convert::TryInto;
6use thiserror::Error;
7
8#[cw_serde]
9pub struct Merkle {
10 root: String,
11}
12
13pub type Proof = Vec<String>;
14
15#[derive(Error, Debug, PartialEq)]
16pub enum Error {
17 #[error("{0}")]
18 Std(#[from] StdError),
19
20 #[error("{0}")]
21 Hex(#[from] FromHexError),
22
23 #[error("Wrong length")]
24 WrongLength {},
25
26 #[error("Verification failed")]
27 VerificationFailed {},
28}
29
30impl Merkle {
31 pub fn new(root: String) -> Result<Self, Error> {
32 let mut root_buf: [u8; 32] = [0; 32];
33 hex::decode_to_slice(&root, &mut root_buf)?;
34
35 Ok(Self { root })
36 }
37
38 pub fn verify(&self, proof: Proof, input: String) -> Result<(), Error> {
39 let hash = sha2::Sha256::digest(input.as_bytes())
40 .as_slice()
41 .try_into()
42 .map_err(|_| Error::WrongLength {})?;
43
44 let hash = proof.into_iter().try_fold(hash, |hash, p| {
45 let mut proof_buf = [0; 32];
46 hex::decode_to_slice(p, &mut proof_buf)?;
47 let mut hashes = [hash, proof_buf];
48 hashes.sort_unstable();
49 sha2::Sha256::digest(hashes.concat())
50 .as_slice()
51 .try_into()
52 .map_err(|_| Error::WrongLength {})
53 })?;
54
55 let mut root_buf: [u8; 32] = [0; 32];
56 hex::decode_to_slice(self.root.clone(), &mut root_buf)?;
57 if root_buf != hash {
58 return Err(Error::VerificationFailed {});
59 }
60 Ok(())
61 }
62}