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_zkp::{
29 adapter::CircuitInfo as _,
30 core::{digest::Digest, hash::poseidon2::Poseidon2HashSuite},
31 layout::Tree,
32 verify::VerificationError,
33};
34use serde::{Deserialize, Serialize};
35
36use self::zirgen::circuit::{Val, LAYOUT_GLOBAL};
37
38pub use self::zirgen::CircuitImpl;
39
40pub const RV32IM_SEAL_VERSION: u32 = 1;
41
42pub const MAX_INSN_CYCLES: usize = 25_000;
44
45pub const MAX_INSN_CYCLES_LOWER_PO2: usize = 2_000;
47
48pub fn verify(seal: &[u32]) -> Result<(), VerificationError> {
49 tracing::debug!("verify");
50
51 let check_code_fn = |_: u32, _: &Digest| Ok(());
53
54 if seal[0] != RV32IM_SEAL_VERSION {
55 return Err(VerificationError::ReceiptFormatError);
56 }
57
58 let seal = &seal[1..];
59
60 let hash_suite = Poseidon2HashSuite::new_suite();
61 risc0_zkp::verify::verify(&CircuitImpl, &hash_suite, seal, check_code_fn)
62}
63
64#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq)]
65pub struct HighLowU16(pub u16, pub u16);
66
67impl From<HighLowU16> for u32 {
68 fn from(x: HighLowU16) -> Self {
69 ((x.0 as u32) << 16) | (x.1 as u32)
70 }
71}
72
73impl From<u32> for HighLowU16 {
74 fn from(x: u32) -> Self {
75 Self((x >> 16) as u16, (x & 0xffff) as u16)
76 }
77}
78
79#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq)]
80pub struct TerminateState {
81 pub a0: HighLowU16,
82 pub a1: HighLowU16,
83}
84
85#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
86pub struct Rv32imV2Claim {
87 pub pre_state: Digest,
88 pub post_state: Digest,
89 pub input: Digest,
90 pub output: Option<Digest>,
91 pub terminate_state: Option<TerminateState>,
92 pub shutdown_cycle: Option<u32>,
93}
94
95impl Rv32imV2Claim {
96 pub fn decode(seal: &[u32]) -> Result<Rv32imV2Claim> {
97 ensure!(seal[0] == RV32IM_SEAL_VERSION, "seal version mismatch");
98 let seal = &seal[1..];
99
100 let io: &[Val] = bytemuck::checked::cast_slice(&seal[..CircuitImpl::OUTPUT_SIZE]);
101 let global = Tree::new(io, LAYOUT_GLOBAL);
102
103 let pre_state = global.map(|c| c.state_in).get_digest_from_shorts()?;
104 let post_state = global.map(|c| c.state_out).get_digest_from_shorts()?;
105 let input = global.map(|c| c.input).get_digest_from_shorts()?;
106 let output = global.map(|c| c.output).get_digest_from_shorts()?;
107 let is_terminate = global.map(|c| c.is_terminate).get_u32_from_elem()?;
108 let term_a0_high = global.map(|c| c.term_a0high).get_u32_from_elem()?;
109 let term_a0_low = global.map(|c| c.term_a0low).get_u32_from_elem()?;
110 let term_a1_high = global.map(|c| c.term_a1high).get_u32_from_elem()?;
111 let term_a1_low = global.map(|c| c.term_a1low).get_u32_from_elem()?;
112 let shutdown_cycle = global.map(|c| c.shutdown_cycle).get_u32_from_elem()?;
113
114 fn try_as_u16(x: u32) -> Result<u16> {
115 x.try_into()
116 .map_err(|err: TryFromIntError| anyhow!("{err}"))
117 }
118
119 let terminate_state = if is_terminate == 1 {
120 Some(TerminateState {
121 a0: HighLowU16(try_as_u16(term_a0_high)?, try_as_u16(term_a0_low)?),
122 a1: HighLowU16(try_as_u16(term_a1_high)?, try_as_u16(term_a1_low)?),
123 })
124 } else {
125 None
126 };
127
128 let output = if is_terminate == 1 {
129 Some(output)
130 } else {
131 None
132 };
133
134 Ok(Rv32imV2Claim {
135 pre_state,
136 post_state,
137 input,
138 output,
139 terminate_state,
140 shutdown_cycle: Some(shutdown_cycle),
141 })
142 }
143}