use crate::INTERNAL_PREFIX;
use crate::{prefixed_hash, prefixed_hash2};
use ergo_chain_types::Digest32;
#[cfg_attr(
feature = "json",
derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr)
)]
#[cfg_attr(feature = "arbitrary", derive(proptest_derive::Arbitrary))]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum NodeSide {
Left = 0,
Right = 1,
}
impl std::convert::TryFrom<u8> for NodeSide {
type Error = &'static str;
fn try_from(side: u8) -> Result<Self, Self::Error> {
match side {
0 => Ok(NodeSide::Left),
1 => Ok(NodeSide::Right),
_ => Err("Side is out of bounds"),
}
}
}
#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "json",
serde(into = "crate::json::LevelNodeJson"),
serde(try_from = "crate::json::LevelNodeJson")
)]
#[cfg_attr(feature = "arbitrary", derive(proptest_derive::Arbitrary))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LevelNode {
pub hash: Option<Digest32>,
pub side: NodeSide,
}
impl LevelNode {
pub fn new(hash: Digest32, side: NodeSide) -> Self {
Self {
hash: Some(hash),
side,
}
}
pub fn empty_node(side: NodeSide) -> Self {
Self { hash: None, side }
}
}
#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "json",
serde(try_from = "crate::json::MerkleProofJson"),
serde(into = "crate::json::MerkleProofJson")
)]
#[derive(Clone, Debug)]
pub struct MerkleProof {
pub(crate) leaf_data: Vec<u8>,
pub(crate) levels: Vec<LevelNode>,
}
impl MerkleProof {
pub fn new(leaf_data: &[u8], levels: &[LevelNode]) -> Self {
MerkleProof {
leaf_data: leaf_data.to_owned(),
levels: levels.to_owned(),
}
}
pub fn valid(&self, expected_root: &[u8]) -> bool {
let leaf_hash = prefixed_hash(0, &self.leaf_data); let hash = self
.levels
.iter()
.fold(leaf_hash, |prev_hash, node| match node {
LevelNode {
hash: Some(hash),
side: NodeSide::Left,
} => prefixed_hash2(INTERNAL_PREFIX, prev_hash.as_ref(), hash.as_ref()), LevelNode {
hash: Some(hash),
side: NodeSide::Right,
} => prefixed_hash2(INTERNAL_PREFIX, hash.as_ref(), prev_hash.as_ref()),
LevelNode { hash: None, .. } => prefixed_hash(INTERNAL_PREFIX, prev_hash.as_ref()),
});
hash.as_ref() == expected_root
}
#[cfg(feature = "json")]
pub fn valid_base16(&self, expected_root: &str) -> Result<bool, base16::DecodeError> {
let expected_root = base16::decode(expected_root)?;
Ok(self.valid(&expected_root))
}
pub fn add_node(&mut self, node: LevelNode) {
self.levels.push(node);
}
pub fn get_leaf_data(&self) -> &[u8] {
&self.leaf_data
}
}
#[cfg(test)]
#[cfg(feature = "json")]
#[allow(clippy::unwrap_used)]
mod test {
use crate::LevelNode;
use crate::MerkleProof;
use crate::NodeSide;
use ergo_chain_types::Digest32;
use std::convert::TryFrom;
#[test]
fn miner_proof() {
let msg_preimage = "01fb9e35f8a73c128b73e8fde5c108228060d68f11a69359ee0fb9bfd84e7ecde6d19957ccbbe75b075b3baf1cac6126b6e80b5770258f4cec29fbde92337faeec74c851610658a40f5ae74aa3a4babd5751bd827a6ccc1fe069468ef487cb90a8c452f6f90ab0b6c818f19b5d17befd85de199d533893a359eb25e7804c8b5d7514d784c8e0e52dabae6e89a9d6ed9c84388b228e7cdee09462488c636a87931d656eb8b40f82a507008ccacbee05000000";
let msg_preimage = base16::decode(msg_preimage).unwrap();
let tx_id = "642c15c62553edd8fd9af9a6f754f3c7a6c03faacd0c9b9d5b7d11052c6c6fe8";
let levels_encoded = "0139b79af823a92aa72ced2c6d9e7f7f4687de5b5af7fab0ad205d3e54bda3f3ae";
let mut levels = base16::decode(levels_encoded).unwrap();
let side: NodeSide = levels.remove(0).try_into().unwrap(); let tx_root = &msg_preimage[65..97];
assert_eq!(levels.len(), 32);
let tx_id = base16::decode(&tx_id).unwrap();
let proof = MerkleProof::new(
&tx_id,
&[LevelNode::new(
Digest32::try_from(&levels[0..32]).unwrap(),
side,
)],
);
assert!(proof.valid(tx_root));
}
#[test]
fn block_proof() {
let json = "{
\"leafData\": \"563b34b96e65788d767a10b0c2ce4a9ef5dcb9f7f7919781624870d56506dc5b\",
\"levels\": [
[\"274d105b42c2da3e03519865470ccef5072d389b153535ca7192fef4abf3b3ed\", 0],
[\"c1887cee0c42318ac04dfa93b8ef6b40c2b53a83b0e111f91a16b0842166e76e\", 0],
[\"58be076cd9ef596a739ec551cbb6b467b95044c05a80a66a7f256d4ebafd787f\", 0]]
}";
let proof: MerkleProof = serde_json::from_str(json).unwrap();
let tx_root =
base16::decode("250063ac1cec3bf56f727f644f49b70515616afa6009857a29b1fe298441e69a")
.unwrap();
assert!(proof.valid(&tx_root));
}
#[test]
fn merkle_proof_genesis_block() {
let json = "{
\"leafData\" : \"4c6282be413c6e300a530618b37790be5f286ded758accc2aebd41554a1be308\",
\"levels\" : [[\"\", 0]]}";
let proof: MerkleProof = serde_json::from_str(json).unwrap();
let tx_root =
base16::decode("93fb06aa44413ff57ac878fda9377207d5db0e78833556b331b4d9727b3153ba")
.unwrap();
assert!(proof.valid(&tx_root));
}
}