extern crate alloc;
use alloc::{collections::VecDeque, vec::Vec};
use risc0_zkp::core::{
digest::Digest,
hash::sha::{cpu::Impl, Sha256},
};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct SystemState {
pub pc: u32,
pub merkle_root: Digest,
}
impl SystemState {
pub fn decode(flat: &mut VecDeque<u32>) -> Self {
Self {
pc: read_u32_bytes(flat),
merkle_root: read_sha_halfs(flat),
}
}
pub fn encode(&self, flat: &mut Vec<u32>) {
write_u32_bytes(flat, self.pc);
write_sha_halfs(flat, &self.merkle_root);
}
pub fn digest(&self) -> Digest {
tagged_struct("risc0.SystemState", &[self.merkle_root], &[self.pc])
}
}
pub fn tagged_struct(tag: &str, down: &[Digest], data: &[u32]) -> Digest {
let tag_digest: Digest = *Impl::hash_bytes(tag.as_bytes());
let mut all = Vec::<u8>::new();
all.extend_from_slice(tag_digest.as_bytes());
for digest in down {
all.extend_from_slice(digest.as_ref());
}
for word in data.iter().copied() {
all.extend_from_slice(&word.to_le_bytes());
}
let down_count: u16 = down.len().try_into().unwrap();
all.extend_from_slice(&down_count.to_le_bytes());
*Impl::hash_bytes(&all)
}
pub fn read_sha_halfs(flat: &mut VecDeque<u32>) -> Digest {
let mut bytes = Vec::<u8>::new();
for half in flat.drain(0..16) {
bytes.push((half & 0xff).try_into().unwrap());
bytes.push((half >> 8).try_into().unwrap());
}
bytes.try_into().unwrap()
}
fn read_u32_bytes(flat: &mut VecDeque<u32>) -> u32 {
u32::from_le_bytes(
flat.drain(0..4)
.map(|x| x as u8)
.collect::<Vec<u8>>()
.try_into()
.unwrap(),
)
}
pub fn write_sha_halfs(flat: &mut Vec<u32>, digest: &Digest) {
for x in digest.as_words() {
flat.push(*x & 0xffff);
flat.push(*x >> 16);
}
}
fn write_u32_bytes(flat: &mut Vec<u32>, word: u32) {
for x in word.to_le_bytes() {
flat.push(x as u32);
}
}
#[cfg(test)]
mod tests {
use super::tagged_struct;
#[test]
fn test_tagged_struct() {
let digest1 = tagged_struct("foo", &[], &[1, 2013265920, 3]);
let digest2 = tagged_struct("bar", &[digest1, digest1], &[2013265920, 5]);
let digest3 = tagged_struct(
"baz",
&[digest1, digest2, digest1],
&[6, 7, 2013265920, 9, 10],
);
println!("digest = {:?}", digest3);
assert_eq!(
digest3.to_string(),
"9ff20cc6d365efa2af09181772f49013d05cdee6da896851614cae23aa5dd442"
);
}
}