use crate::node::{is_null_node_ref, Node, NodeRef};
use crate::proof::ProofHash;
use crate::tally::{tally_node, TallyList};
use crate::utilstring::to_hex;
use crate::{Validation, VoteReference};
#[cfg(feature = "with-serde")]
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::fmt;
#[derive(PartialEq, Clone, Eq, Hash, Copy)]
#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
pub struct NodeHash(pub [u8; 32]);
pub const NULL_HASH: NodeHash = NodeHash([0x0; 32]);
pub const ERROR_HASH: NodeHash = NodeHash([0xff; 32]);
impl NodeHash {
pub fn as_slice(&self) -> &[u8] {
&self.0
}
pub fn as_array(&self) -> &[u8; 32] {
&self.0
}
}
impl From<ProofHash> for NodeHash {
fn from(h: ProofHash) -> Self {
NodeHash(h)
}
}
impl fmt::Display for NodeHash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NodeHash {{ {} }}", to_hex(self.as_slice()).unwrap())
}
}
impl fmt::Debug for NodeHash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NodeHash {{ {} }}", to_hex(self.as_slice()).unwrap())
}
}
pub fn hash_leaf(vote_reference: &VoteReference, vote: &[u32]) -> NodeHash {
let mut hasher = Sha256::new();
hasher.update(vote_reference);
vote.iter()
.map(|t| t.to_be_bytes())
.for_each(|b| hasher.update(b));
NodeHash(
hasher
.finalize()
.as_slice()
.try_into()
.expect("Expected hash to be 32 bytes"),
)
}
pub fn hash_node(node: &Node, v: &Validation) -> Result<(NodeHash, Option<TallyList>), String> {
if let Some(h) = &node.hash {
return Ok(h.clone());
}
let mut hasher = Sha256::new();
if let Some((vote_reference, _)) = node.vote {
assert!(node.left.is_none());
assert!(node.right.is_none());
hasher.update(vote_reference);
}
let tally = tally_node(node, v)?.ok_or_else(|| "Expected node to provide tally".to_string())?;
tally
.iter()
.map(|t| t.to_be_bytes())
.for_each(|b| hasher.update(b));
if let Some((h, _)) = hash_node_ref(&node.left, v)? {
hasher.update(h.as_slice());
}
if let Some((h, _)) = hash_node_ref(&node.right, v)? {
assert!(node.left.is_some());
hasher.update(h.as_slice());
}
Ok((
NodeHash(
hasher
.finalize()
.as_slice()
.try_into()
.expect("Expected hash to be 32 bytes"),
),
Some(tally),
))
}
pub fn hash_node_ref(
node: &NodeRef,
v: &Validation,
) -> Result<Option<(NodeHash, Option<TallyList>)>, String> {
if is_null_node_ref(node) {
return Ok(Some((NULL_HASH, None)));
}
match node {
Some(n) => Ok(Some(hash_node(n, v)?)),
None => Ok(None),
}
}