risc0_binfmt/
sys_state.rs

1// Copyright 2025 RISC Zero, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15extern crate alloc;
16
17use alloc::{collections::VecDeque, vec::Vec};
18use core::fmt;
19
20use borsh::{BorshDeserialize, BorshSerialize};
21use risc0_zkp::core::{digest::Digest, hash::sha::Sha256};
22use serde::{Deserialize, Serialize};
23
24use crate::{tagged_struct, Digestible};
25
26/// Represents the public state of a segment, needed for continuations and
27/// receipt verification.
28#[derive(Clone, Serialize, Deserialize, PartialEq, BorshSerialize, BorshDeserialize)]
29pub struct SystemState {
30    /// The program counter.
31    pub pc: u32,
32
33    /// The root hash of a merkle tree which confirms the
34    /// integrity of the memory image.
35    pub merkle_root: Digest,
36}
37
38impl SystemState {
39    /// Decode a [SystemState] struct from the given vector of u32 words. This encoding is used by
40    /// the zkVM circuit to write is final memory and PC state to the public information in the
41    /// seal.
42    pub fn decode(flat: &mut VecDeque<u32>) -> Result<Self, DecodeError> {
43        Ok(Self {
44            pc: read_u32_bytes(flat)?,
45            merkle_root: read_sha_halfs(flat)?,
46        })
47    }
48
49    /// Encode a [SystemState] struct to the given vector of u32 words. This encoding is used by
50    /// the zkVM circuit to write is final memory and PC state to the public information in the
51    /// seal.
52    pub fn encode(&self, flat: &mut Vec<u32>) {
53        write_u32_bytes(flat, self.pc);
54        write_sha_halfs(flat, &self.merkle_root);
55    }
56}
57
58impl fmt::Debug for SystemState {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        f.debug_struct("SystemState")
61            .field("pc", &format_args!("0x{:08x}", self.pc))
62            .field("merkle_root", &self.merkle_root)
63            .finish()
64    }
65}
66
67impl Eq for SystemState {}
68
69impl Digestible for SystemState {
70    /// Hash the [crate::SystemState] to get a digest of the struct.
71    fn digest<S: Sha256>(&self) -> Digest {
72        tagged_struct::<S>("risc0.SystemState", &[self.merkle_root], &[self.pc])
73    }
74}
75
76/// Read a SHA-256 digest as a series of half-wrods (i.e. 16-bit values).
77pub fn read_sha_halfs(flat: &mut VecDeque<u32>) -> Result<Digest, DecodeError> {
78    let mut bytes = Vec::<u8>::new();
79    if flat.len() < 16 {
80        return Err(DecodeError::EndOfStream);
81    }
82    for half in flat.drain(0..16) {
83        bytes.push((half & 0xff).try_into().unwrap());
84        bytes.push(
85            (half >> 8)
86                .try_into()
87                .map_err(|_| DecodeError::OutOfRange)?,
88        );
89    }
90    Ok(bytes.try_into().unwrap())
91}
92
93fn read_u32_bytes(flat: &mut VecDeque<u32>) -> Result<u32, DecodeError> {
94    if flat.len() < 4 {
95        return Err(DecodeError::EndOfStream);
96    }
97    Ok(u32::from_le_bytes(
98        flat.drain(0..4)
99            .map(|x| x as u8)
100            .collect::<Vec<u8>>()
101            .try_into()
102            .unwrap(),
103    ))
104}
105
106/// Write a SHA-256 digest as a series of half-wrods (i.e. 16-bit values).
107pub fn write_sha_halfs(flat: &mut Vec<u32>, digest: &Digest) {
108    for x in digest.as_words() {
109        flat.push(*x & 0xffff);
110        flat.push(*x >> 16);
111    }
112}
113
114fn write_u32_bytes(flat: &mut Vec<u32>, word: u32) {
115    for x in word.to_le_bytes() {
116        flat.push(x as u32);
117    }
118}
119
120/// Error returned when a decoding failure occurs.
121#[derive(Debug, Copy, Clone)]
122pub enum DecodeError {
123    /// End of stream was reached when more data was expected.
124    EndOfStream,
125    /// Value in the stream is outside the expected range.
126    OutOfRange,
127}
128
129impl fmt::Display for DecodeError {
130    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131        match self {
132            Self::EndOfStream => write!(f, "end of stream reached when more data was expected"),
133            Self::OutOfRange => write!(f, "value outside of expected range"),
134        }
135    }
136}
137
138#[cfg(feature = "std")]
139impl std::error::Error for DecodeError {}