extern crate alloc;
use alloc::vec::Vec;
use core::{borrow::Borrow, mem::size_of};
use risc0_zkp::core::{
digest::{Digest, DIGEST_BYTES},
hash::sha::Sha256,
};
pub trait Digestible {
fn digest<S: Sha256>(&self) -> Digest;
}
impl Digestible for [u8] {
fn digest<S: Sha256>(&self) -> Digest {
*S::hash_bytes(self)
}
}
impl Digestible for Vec<u8> {
fn digest<S: Sha256>(&self) -> Digest {
*S::hash_bytes(self)
}
}
impl<D: Digestible> Digestible for [D] {
fn digest<S: Sha256>(&self) -> Digest {
self.iter().rfold(Digest::ZERO, |accum, item| {
*S::hash_bytes(&[accum.as_bytes(), item.digest::<S>().as_bytes()].concat())
})
}
}
impl<T: Digestible> Digestible for Option<T> {
fn digest<S: Sha256>(&self) -> Digest {
match self {
Some(val) => val.digest::<S>(),
None => Digest::ZERO,
}
}
}
pub fn tagged_struct<S: Sha256>(tag: &str, down: &[impl Borrow<Digest>], data: &[u32]) -> Digest {
let tag_digest: Digest = *S::hash_bytes(tag.as_bytes());
#[allow(clippy::manual_slice_size_calculation)]
let mut all = Vec::<u8>::with_capacity(
DIGEST_BYTES * (down.len() + 1) + size_of::<u32>() * data.len() + size_of::<u16>(),
);
all.extend_from_slice(tag_digest.as_bytes());
for digest in down {
all.extend_from_slice(digest.borrow().as_ref());
}
for word in data.iter().copied() {
all.extend_from_slice(&word.to_le_bytes());
}
let down_count: u16 = down
.len()
.try_into()
.expect("struct defined with more than 2^16 fields");
all.extend_from_slice(&down_count.to_le_bytes());
*S::hash_bytes(&all)
}
pub fn tagged_iter<S: Sha256>(
tag: &str,
iter: impl DoubleEndedIterator<Item = impl Borrow<Digest>>,
) -> Digest {
iter.rfold(Digest::ZERO, |list_digest, elem| {
tagged_list_cons::<S>(tag, elem.borrow(), &list_digest)
})
}
pub fn tagged_list<S: Sha256>(tag: &str, list: &[impl Borrow<Digest>]) -> Digest {
tagged_iter::<S>(tag, list.iter().map(|x| x.borrow()))
}
pub fn tagged_list_cons<S: Sha256>(tag: &str, head: &Digest, tail: &Digest) -> Digest {
tagged_struct::<S>(tag, &[head, tail], &[])
}
#[cfg(test)]
mod tests {
use risc0_zkp::core::hash::sha::cpu;
use super::{tagged_struct, Digest};
#[test]
fn test_tagged_struct() {
let digest1 = tagged_struct::<cpu::Impl>("foo", &Vec::<Digest>::new(), &[1, 2013265920, 3]);
let digest2 = tagged_struct::<cpu::Impl>("bar", &[digest1, digest1], &[2013265920, 5]);
let digest3 = tagged_struct::<cpu::Impl>(
"baz",
&[digest1, digest2, digest1],
&[6, 7, 2013265920, 9, 10],
);
println!("digest = {digest3:?}");
assert_eq!(
digest3.to_string(),
"9ff20cc6d365efa2af09181772f49013d05cdee6da896851614cae23aa5dd442"
);
}
}