risc0_circuit_rv32im/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
16
17#[cfg(feature = "execute")]
18pub mod execute;
19#[cfg(feature = "prove")]
20pub mod prove;
21pub mod trace;
22mod zirgen;
23
24use core::num::TryFromIntError;
25
26use anyhow::{anyhow, ensure, Result};
27use derive_more::Debug;
28use risc0_binfmt::PovwNonce;
29use risc0_zkp::{
30 adapter::CircuitInfo as _,
31 core::{digest::Digest, hash::poseidon2::Poseidon2HashSuite},
32 layout::Tree,
33 verify::VerificationError,
34};
35use serde::{Deserialize, Serialize};
36
37use self::zirgen::circuit::{Val, LAYOUT_GLOBAL};
38
39pub use self::zirgen::CircuitImpl;
40
41pub const RV32IM_SEAL_VERSION: u32 = 2;
43
44pub const MAX_INSN_CYCLES: usize = 25_000;
46
47pub const MAX_INSN_CYCLES_LOWER_PO2: usize = 2_000;
49
50pub fn verify(seal: &[u32]) -> Result<(), VerificationError> {
51 tracing::debug!("verify");
52
53 let check_code_fn = |_: u32, _: &Digest| Ok(());
55
56 if seal[0] != RV32IM_SEAL_VERSION {
57 return Err(VerificationError::ReceiptFormatError);
58 }
59
60 let seal = &seal[1..];
61
62 let hash_suite = Poseidon2HashSuite::new_suite();
63 risc0_zkp::verify::verify(&CircuitImpl, &hash_suite, seal, check_code_fn)
64}
65
66#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq)]
67pub struct HighLowU16(pub u16, pub u16);
68
69impl From<HighLowU16> for u32 {
70 fn from(x: HighLowU16) -> Self {
71 ((x.0 as u32) << 16) | (x.1 as u32)
72 }
73}
74
75impl From<u32> for HighLowU16 {
76 fn from(x: u32) -> Self {
77 Self((x >> 16) as u16, (x & 0xffff) as u16)
78 }
79}
80
81#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq)]
82pub struct TerminateState {
83 pub a0: HighLowU16,
84 pub a1: HighLowU16,
85}
86
87#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
88pub struct Rv32imV2Claim {
89 pub pre_state: Digest,
90 pub post_state: Digest,
91 pub input: Digest,
92 pub output: Option<Digest>,
93 pub terminate_state: Option<TerminateState>,
94 pub shutdown_cycle: Option<u32>,
95}
96
97impl Rv32imV2Claim {
98 pub fn decode(segment_seal: &[u32]) -> Result<Rv32imV2Claim> {
99 ensure!(
100 segment_seal[0] == RV32IM_SEAL_VERSION,
101 "seal version mismatch"
102 );
103 let segment_seal = &segment_seal[1..];
104
105 let io: &[Val] = bytemuck::checked::cast_slice(&segment_seal[..CircuitImpl::OUTPUT_SIZE]);
106 let global = Tree::new(io, LAYOUT_GLOBAL);
107
108 let pre_state = global.map(|c| c.state_in).get_digest_from_shorts()?;
111 let post_state = global.map(|c| c.state_out).get_digest_from_shorts()?;
112 let input = global.map(|c| c.input).get_digest_from_shorts()?;
113 let output = global.map(|c| c.output).get_digest_from_shorts()?;
114 let is_terminate = global.map(|c| c.is_terminate).get_u32_from_elem()?;
115 let term_a0_high = global.map(|c| c.term_a0high).get_u32_from_elem()?;
116 let term_a0_low = global.map(|c| c.term_a0low).get_u32_from_elem()?;
117 let term_a1_high = global.map(|c| c.term_a1high).get_u32_from_elem()?;
118 let term_a1_low = global.map(|c| c.term_a1low).get_u32_from_elem()?;
119 let shutdown_cycle = global.map(|c| c.shutdown_cycle).get_u32_from_elem()?;
120
121 fn try_as_u16(x: u32) -> Result<u16> {
122 x.try_into()
123 .map_err(|err: TryFromIntError| anyhow!("{err}"))
124 }
125
126 let terminate_state = if is_terminate == 1 {
127 Some(TerminateState {
128 a0: HighLowU16(try_as_u16(term_a0_high)?, try_as_u16(term_a0_low)?),
129 a1: HighLowU16(try_as_u16(term_a1_high)?, try_as_u16(term_a1_low)?),
130 })
131 } else {
132 None
133 };
134
135 let output = if is_terminate == 1 {
136 Some(output)
137 } else {
138 None
139 };
140
141 Ok(Rv32imV2Claim {
142 pre_state,
143 post_state,
144 input,
145 output,
146 terminate_state,
147 shutdown_cycle: Some(shutdown_cycle),
148 })
149 }
150}
151
152pub fn decode_povw_nonce(segment_seal: &[u32]) -> Result<PovwNonce> {
154 ensure!(
155 segment_seal[0] == RV32IM_SEAL_VERSION,
156 "seal version mismatch"
157 );
158 let segment_seal = &segment_seal[1..];
159
160 let io: &[Val] = bytemuck::checked::cast_slice(&segment_seal[..CircuitImpl::OUTPUT_SIZE]);
161 let global = Tree::new(io, LAYOUT_GLOBAL);
162
163 let povw_nonce_shorts_vec = global.map(|c| c.povw_nonce).get_shorts()?;
164 let povw_nonce_shorts_arr = povw_nonce_shorts_vec
165 .try_into()
166 .map_err(|_| anyhow!("povw nonce global has unexpected length"))?;
167 Ok(PovwNonce::from_u16s(povw_nonce_shorts_arr))
168}