risc0_zkvm/host/server/prove/
dev_mode.rs1use std::time::Duration;
16
17use anyhow::{ensure, Context, Result};
18use risc0_zkp::core::digest::Digest;
19use serde::{Deserialize, Deserializer, Serialize};
20
21use crate::{
22 claim::{
23 receipt::{exit_code_from_terminate_state, UnionClaim},
24 Unknown,
25 },
26 host::{
27 prove_info::ProveInfo,
28 server::{exec::executor::ExecutorImpl, session::null_callback},
29 },
30 receipt::{FakeReceipt, InnerReceipt, SegmentReceipt, SuccinctReceipt},
31 recursion::MerkleProof,
32 ExecutorEnv, MaybePruned, PreflightResults, ProverOpts, ProverServer, Receipt, ReceiptClaim,
33 Segment, Session, VerifierContext, WorkClaim,
34};
35
36const ERR_DEV_MODE_DISABLED: &str =
37 "zkVM: dev mode is disabled. Unset RISC0_DEV_MODE environment variable to produce valid proofs";
38
39#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
41pub struct DevModeDelay {
42 #[serde(deserialize_with = "duration_secs")]
44 pub prove_segment_core: Duration,
45
46 #[serde(deserialize_with = "duration_secs")]
48 pub segment_preflight: Duration,
49
50 #[serde(deserialize_with = "duration_secs")]
52 pub prove_keccak: Duration,
53
54 #[serde(deserialize_with = "duration_secs")]
56 pub lift: Duration,
57
58 #[serde(deserialize_with = "duration_secs")]
60 pub join: Duration,
61
62 #[serde(deserialize_with = "duration_secs")]
64 pub union: Duration,
65
66 #[serde(deserialize_with = "duration_secs")]
68 pub resolve: Duration,
69}
70
71#[non_exhaustive]
91pub struct DevModeProver {
92 delay: Option<DevModeDelay>,
93}
94
95macro_rules! ensure_dev_mode_allowed {
97 () => {
98 ensure!(
99 cfg!(not(feature = "disable-dev-mode")),
100 ERR_DEV_MODE_DISABLED
101 );
102 };
103}
104
105impl DevModeProver {
106 pub fn new() -> Self {
108 Self { delay: None }
109 }
110
111 pub fn with_delay(delay: DevModeDelay) -> Self {
113 Self { delay: Some(delay) }
114 }
115}
116
117impl Default for DevModeProver {
118 fn default() -> Self {
119 Self::new()
120 }
121}
122
123impl ProverServer for DevModeProver {
124 fn prove(&self, env: ExecutorEnv<'_>, elf: &[u8]) -> Result<ProveInfo> {
125 let ctx = VerifierContext::default().with_dev_mode(true);
126 self.prove_with_ctx(env, &ctx, elf)
127 }
128
129 fn prove_session(&self, ctx: &VerifierContext, session: &Session) -> Result<ProveInfo> {
130 eprintln!(
131 "WARNING: Proving in dev mode does not generate a valid receipt. \
132 Receipts generated from this process are invalid and should never be used in production."
133 );
134
135 ensure!(ctx.dev_mode(), ERR_DEV_MODE_DISABLED);
136 ensure_dev_mode_allowed!();
137
138 let session_claim = session
139 .claim()
140 .context("failed to compute claim for session")?;
141 let receipt = Receipt::new(
142 FakeReceipt::new(session_claim.clone()).into(),
143 session.journal.clone().unwrap_or_default().bytes,
144 );
145 let work_receipt = session.work().map(|work| {
146 FakeReceipt::new(WorkClaim {
147 claim: session_claim.into(),
148 work: work.into(),
149 })
150 .into()
151 });
152
153 Ok(ProveInfo {
154 receipt,
155 work_receipt,
156 stats: session.stats(),
157 })
158 }
159
160 fn prove_with_ctx(
162 &self,
163 env: ExecutorEnv<'_>,
164 ctx: &VerifierContext,
165 elf: &[u8],
166 ) -> Result<ProveInfo> {
167 let session = ExecutorImpl::from_elf(env, elf)
168 .unwrap()
169 .run_with_callback(null_callback)?;
170 self.prove_session(ctx, &session)
171 }
172
173 fn segment_preflight(&self, segment: &Segment) -> Result<PreflightResults> {
174 ensure_dev_mode_allowed!();
175
176 if let Some(ref delay) = self.delay {
177 std::thread::sleep(delay.segment_preflight);
178 }
179
180 Ok(PreflightResults {
181 inner: Default::default(),
182 terminate_state: segment.inner.claim.terminate_state,
183 output: segment.output.clone(),
184 segment_index: segment.index,
185 })
186 }
187
188 fn prove_segment_core(
189 &self,
190 ctx: &VerifierContext,
191 preflight_results: PreflightResults,
192 ) -> Result<SegmentReceipt> {
193 ensure!(ctx.dev_mode(), ERR_DEV_MODE_DISABLED);
194 ensure_dev_mode_allowed!();
195
196 if let Some(ref delay) = self.delay {
197 std::thread::sleep(delay.prove_segment_core);
198 }
199
200 let exit_code = exit_code_from_terminate_state(&preflight_results.terminate_state)?;
201 Ok(SegmentReceipt {
202 seal: Vec::new(),
203 index: preflight_results.segment_index,
204 hashfn: "fake".into(),
205 verifier_parameters: Digest::ZERO,
206 claim: ReceiptClaim {
207 pre: MaybePruned::Pruned(Digest::ZERO),
208 post: MaybePruned::Pruned(Digest::ZERO),
209 exit_code,
210 input: MaybePruned::Pruned(Digest::ZERO),
211 output: MaybePruned::Pruned(Digest::ZERO),
212 },
213 })
214 }
215
216 fn prove_keccak(
217 &self,
218 _request: &crate::ProveKeccakRequest,
219 ) -> Result<SuccinctReceipt<Unknown>> {
220 ensure_dev_mode_allowed!();
221
222 if let Some(ref delay) = self.delay {
223 std::thread::sleep(delay.prove_keccak);
224 }
225
226 Ok(fake_succinct_receipt())
227 }
228
229 fn lift(&self, _receipt: &SegmentReceipt) -> Result<SuccinctReceipt<ReceiptClaim>> {
230 fake_recursion(self.delay.map(|d| d.lift))
231 }
232
233 fn lift_povw(
234 &self,
235 _receipt: &SegmentReceipt,
236 ) -> Result<SuccinctReceipt<WorkClaim<ReceiptClaim>>> {
237 fake_recursion(self.delay.map(|d| d.lift))
238 }
239
240 fn join(
241 &self,
242 _a: &SuccinctReceipt<ReceiptClaim>,
243 _b: &SuccinctReceipt<ReceiptClaim>,
244 ) -> Result<SuccinctReceipt<ReceiptClaim>> {
245 fake_recursion(self.delay.map(|d| d.join))
246 }
247
248 fn join_povw(
249 &self,
250 _a: &SuccinctReceipt<WorkClaim<ReceiptClaim>>,
251 _b: &SuccinctReceipt<WorkClaim<ReceiptClaim>>,
252 ) -> Result<SuccinctReceipt<WorkClaim<ReceiptClaim>>> {
253 fake_recursion(self.delay.map(|d| d.join))
254 }
255
256 fn join_unwrap_povw(
257 &self,
258 _a: &SuccinctReceipt<WorkClaim<ReceiptClaim>>,
259 _b: &SuccinctReceipt<WorkClaim<ReceiptClaim>>,
260 ) -> Result<SuccinctReceipt<ReceiptClaim>> {
261 fake_recursion(self.delay.map(|d| d.join))
262 }
263
264 fn resolve(
265 &self,
266 _conditional: &SuccinctReceipt<ReceiptClaim>,
267 _assumption: &SuccinctReceipt<Unknown>,
268 ) -> Result<SuccinctReceipt<ReceiptClaim>> {
269 fake_recursion(self.delay.map(|d| d.resolve))
270 }
271
272 fn resolve_povw(
273 &self,
274 _conditional: &SuccinctReceipt<WorkClaim<ReceiptClaim>>,
275 _assumption: &SuccinctReceipt<Unknown>,
276 ) -> Result<SuccinctReceipt<WorkClaim<ReceiptClaim>>> {
277 fake_recursion(self.delay.map(|d| d.resolve))
278 }
279
280 fn resolve_unwrap_povw(
281 &self,
282 _conditional: &SuccinctReceipt<WorkClaim<ReceiptClaim>>,
283 _assumption: &SuccinctReceipt<Unknown>,
284 ) -> Result<SuccinctReceipt<ReceiptClaim>> {
285 fake_recursion(self.delay.map(|d| d.resolve))
286 }
287
288 fn union(
289 &self,
290 _a: &SuccinctReceipt<Unknown>,
291 _b: &SuccinctReceipt<Unknown>,
292 ) -> Result<SuccinctReceipt<UnionClaim>> {
293 fake_recursion(self.delay.map(|d| d.union))
294 }
295
296 fn unwrap_povw(
297 &self,
298 _a: &SuccinctReceipt<WorkClaim<ReceiptClaim>>,
299 ) -> Result<SuccinctReceipt<ReceiptClaim>> {
300 fake_recursion(None)
302 }
303
304 fn identity_p254(
305 &self,
306 _a: &SuccinctReceipt<ReceiptClaim>,
307 ) -> Result<SuccinctReceipt<ReceiptClaim>> {
308 fake_recursion(None)
310 }
311
312 fn compress(&self, opts: &ProverOpts, receipt: &Receipt) -> Result<Receipt> {
313 ensure!(opts.dev_mode(), ERR_DEV_MODE_DISABLED);
314 ensure_dev_mode_allowed!();
315
316 Ok(Receipt::new(
317 InnerReceipt::Fake(FakeReceipt {
318 claim: receipt.claim()?,
319 }),
320 receipt.journal.bytes.clone(),
321 ))
322 }
323}
324
325fn fake_recursion<Claim>(delay: Option<Duration>) -> Result<SuccinctReceipt<Claim>> {
328 ensure_dev_mode_allowed!();
329
330 if let Some(delay) = delay {
331 std::thread::sleep(delay);
332 }
333
334 Ok(fake_succinct_receipt())
335}
336
337fn fake_succinct_receipt<Claim>() -> SuccinctReceipt<Claim> {
338 SuccinctReceipt {
339 seal: vec![],
340 control_id: Digest::ZERO,
341 claim: MaybePruned::Pruned(Digest::ZERO),
342 hashfn: "fake".into(),
343 verifier_parameters: Digest::ZERO,
344 control_inclusion_proof: MerkleProof {
345 index: 0,
346 digests: vec![],
347 },
348 }
349}
350
351fn duration_secs<'de, D>(deserializer: D) -> Result<Duration, D::Error>
352where
353 D: Deserializer<'de>,
354{
355 let secs = f64::deserialize(deserializer)?;
356 Ok(Duration::from_secs_f64(secs))
357}