sp1_recursion_core/runtime/
mod.rs

1pub mod instruction;
2mod memory;
3mod opcode;
4mod program;
5mod record;
6
7// Avoid triggering annoying branch of thiserror derive macro.
8use backtrace::Backtrace as Trace;
9use instruction::HintAddCurveInstr;
10pub use instruction::Instruction;
11use instruction::{FieldEltType, HintBitsInstr, HintExt2FeltsInstr, HintInstr, PrintInstr};
12use itertools::Itertools;
13use memory::*;
14pub use opcode::*;
15use p3_field::AbstractExtensionField;
16use p3_field::{AbstractField, ExtensionField, PrimeField32};
17use p3_maybe_rayon::prelude::*;
18use p3_poseidon2::{Poseidon2, Poseidon2ExternalMatrixGeneral};
19use p3_symmetric::{CryptographicPermutation, Permutation};
20use p3_util::reverse_bits_len;
21pub use program::*;
22pub use record::*;
23use sp1_stark::septic_curve::SepticCurve;
24use sp1_stark::septic_extension::SepticExtension;
25use sp1_stark::MachineRecord;
26use std::{
27    array,
28    borrow::Borrow,
29    collections::VecDeque,
30    fmt::Debug,
31    io::{stdout, Write},
32    iter::zip,
33    marker::PhantomData,
34    sync::{Arc, Mutex},
35};
36use thiserror::Error;
37
38use crate::air::{Block, RECURSIVE_PROOF_NUM_PV_ELTS};
39
40/// TODO expand glob import once things are organized enough
41use crate::*;
42
43/// The heap pointer address.
44pub const HEAP_PTR: i32 = -4;
45pub const STACK_SIZE: usize = 1 << 24;
46pub const HEAP_START_ADDRESS: usize = STACK_SIZE + 4;
47pub const MEMORY_SIZE: usize = 1 << 28;
48
49/// The width of the Poseidon2 permutation.
50pub const PERMUTATION_WIDTH: usize = 16;
51pub const POSEIDON2_SBOX_DEGREE: u64 = 7;
52pub const HASH_RATE: usize = 8;
53
54/// The current verifier implementation assumes that we are using a 256-bit hash with 32-bit
55/// elements.
56pub const DIGEST_SIZE: usize = 8;
57
58pub const NUM_BITS: usize = 31;
59
60pub const D: usize = 4;
61
62type Perm<F, Diffusion> = Poseidon2<
63    F,
64    Poseidon2ExternalMatrixGeneral,
65    Diffusion,
66    PERMUTATION_WIDTH,
67    POSEIDON2_SBOX_DEGREE,
68>;
69
70/// TODO fully document.
71/// Taken from [`sp1_recursion_core::runtime::Runtime`].
72/// Many missing things (compared to the old `Runtime`) will need to be implemented.
73pub struct Runtime<'a, F: PrimeField32, EF: ExtensionField<F>, Diffusion> {
74    pub timestamp: usize,
75
76    pub nb_poseidons: usize,
77
78    pub nb_wide_poseidons: usize,
79
80    pub nb_bit_decompositions: usize,
81
82    pub nb_ext_ops: usize,
83
84    pub nb_base_ops: usize,
85
86    pub nb_memory_ops: usize,
87
88    pub nb_branch_ops: usize,
89
90    pub nb_select: usize,
91
92    pub nb_exp_reverse_bits: usize,
93
94    pub nb_fri_fold: usize,
95
96    pub nb_batch_fri: usize,
97
98    pub nb_print_f: usize,
99
100    pub nb_print_e: usize,
101
102    /// The program.
103    pub program: Arc<RecursionProgram<F>>,
104
105    /// Memory. From canonical usize of an Address to a MemoryEntry.
106    pub memory: MemVec<F>,
107
108    /// The execution record.
109    pub record: ExecutionRecord<F>,
110
111    pub witness_stream: VecDeque<Block<F>>,
112
113    /// The stream that print statements write to.
114    pub debug_stdout: Box<dyn Write + Send + 'a>,
115
116    /// Entries for dealing with the Poseidon2 hash state.
117    perm: Option<Perm<F, Diffusion>>,
118
119    _marker_ef: PhantomData<EF>,
120
121    _marker_diffusion: PhantomData<Diffusion>,
122}
123
124#[derive(Error, Debug)]
125pub enum RuntimeError<F: Debug, EF: Debug> {
126    #[error(
127        "attempted to perform base field division {in1:?}/{in2:?}\n\
128        \tin instruction {instr:#?}\n\
129        \tnearest backtrace:\n{trace:#?}"
130    )]
131    DivFOutOfDomain { in1: F, in2: F, instr: BaseAluInstr<F>, trace: Option<Trace> },
132    #[error(
133        "attempted to perform extension field division {in1:?}/{in2:?}\n\
134        \tin instruction {instr:#?}\n\
135        \tnearest backtrace:\n{trace:#?}"
136    )]
137    DivEOutOfDomain { in1: EF, in2: EF, instr: ExtAluInstr<F>, trace: Option<Trace> },
138    #[error("failed to print to `debug_stdout`: {0}")]
139    DebugPrint(#[from] std::io::Error),
140    #[error("attempted to read from empty witness stream")]
141    EmptyWitnessStream,
142}
143
144impl<F: PrimeField32, EF: ExtensionField<F>, Diffusion> Runtime<'_, F, EF, Diffusion>
145where
146    Poseidon2<
147        F,
148        Poseidon2ExternalMatrixGeneral,
149        Diffusion,
150        PERMUTATION_WIDTH,
151        POSEIDON2_SBOX_DEGREE,
152    >: CryptographicPermutation<[F; PERMUTATION_WIDTH]>,
153{
154    pub fn new(
155        program: Arc<RecursionProgram<F>>,
156        perm: Poseidon2<
157            F,
158            Poseidon2ExternalMatrixGeneral,
159            Diffusion,
160            PERMUTATION_WIDTH,
161            POSEIDON2_SBOX_DEGREE,
162        >,
163    ) -> Self {
164        let record = ExecutionRecord::<F> { program: program.clone(), ..Default::default() };
165        let memory = MemVec::with_capacity(program.total_memory);
166        Self {
167            timestamp: 0,
168            nb_poseidons: 0,
169            nb_wide_poseidons: 0,
170            nb_bit_decompositions: 0,
171            nb_select: 0,
172            nb_exp_reverse_bits: 0,
173            nb_ext_ops: 0,
174            nb_base_ops: 0,
175            nb_memory_ops: 0,
176            nb_branch_ops: 0,
177            nb_fri_fold: 0,
178            nb_batch_fri: 0,
179            nb_print_f: 0,
180            nb_print_e: 0,
181            program,
182            memory,
183            record,
184            witness_stream: VecDeque::new(),
185            debug_stdout: Box::new(stdout()),
186            perm: Some(perm),
187            _marker_ef: PhantomData,
188            _marker_diffusion: PhantomData,
189        }
190    }
191
192    pub fn print_stats(&self) {
193        if tracing::event_enabled!(tracing::Level::DEBUG) {
194            let mut stats = self.record.stats().into_iter().collect::<Vec<_>>();
195            stats.sort_unstable();
196            tracing::debug!("total events: {}", stats.iter().map(|(_, v)| *v).sum::<usize>());
197            for (k, v) in stats {
198                tracing::debug!("  {k}: {v}");
199            }
200        }
201    }
202
203    /// # Safety
204    ///
205    /// Safety is guaranteed if calls to this function (with the given `env` argument) obey the
206    /// happens-before relation defined in the documentation of [`RecursionProgram::new_unchecked`].
207    ///
208    /// This function makes use of interior mutability of `env` via `UnsafeCell`.
209    /// All of this function's unsafety stems from the `instruction` argument that indicates'
210    /// whether/how to read and write from the memory in `env`. There must be a strict
211    /// happens-before relation where reads happen before writes, and memory read from must be
212    /// initialized.
213    unsafe fn execute_one(
214        state: &mut ExecState<F, Diffusion>,
215        witness_stream: Option<&mut VecDeque<Block<F>>>,
216        instruction: Instruction<F>,
217    ) -> Result<(), RuntimeError<F, EF>> {
218        let ExecEnv { memory, perm, debug_stdout } = state.env;
219        let record = &mut state.record;
220        match instruction {
221            Instruction::BaseAlu(instr @ BaseAluInstr { opcode, mult: _, addrs }) => {
222                let in1 = memory.mr_unchecked(addrs.in1).val[0];
223                let in2 = memory.mr_unchecked(addrs.in2).val[0];
224                // Do the computation.
225                let out = match opcode {
226                    BaseAluOpcode::AddF => in1 + in2,
227                    BaseAluOpcode::SubF => in1 - in2,
228                    BaseAluOpcode::MulF => in1 * in2,
229                    BaseAluOpcode::DivF => match in1.try_div(in2) {
230                        Some(x) => x,
231                        None => {
232                            // Check for division exceptions and error. Note that 0/0 is defined
233                            // to be 1.
234                            if in1.is_zero() {
235                                AbstractField::one()
236                            } else {
237                                return Err(RuntimeError::DivFOutOfDomain {
238                                    in1,
239                                    in2,
240                                    instr,
241                                    trace: state.resolve_trace().cloned(),
242                                });
243                            }
244                        }
245                    },
246                };
247                memory.mw_unchecked(addrs.out, Block::from(out));
248                record.base_alu_events.push(BaseAluEvent { out, in1, in2 });
249            }
250            Instruction::ExtAlu(instr @ ExtAluInstr { opcode, mult: _, addrs }) => {
251                let in1 = memory.mr_unchecked(addrs.in1).val;
252                let in2 = memory.mr_unchecked(addrs.in2).val;
253                // Do the computation.
254                let in1_ef = EF::from_base_slice(&in1.0);
255                let in2_ef = EF::from_base_slice(&in2.0);
256                let out_ef = match opcode {
257                    ExtAluOpcode::AddE => in1_ef + in2_ef,
258                    ExtAluOpcode::SubE => in1_ef - in2_ef,
259                    ExtAluOpcode::MulE => in1_ef * in2_ef,
260                    ExtAluOpcode::DivE => match in1_ef.try_div(in2_ef) {
261                        Some(x) => x,
262                        None => {
263                            // Check for division exceptions and error. Note that 0/0 is defined
264                            // to be 1.
265                            if in1_ef.is_zero() {
266                                AbstractField::one()
267                            } else {
268                                return Err(RuntimeError::DivEOutOfDomain {
269                                    in1: in1_ef,
270                                    in2: in2_ef,
271                                    instr,
272                                    trace: state.resolve_trace().cloned(),
273                                });
274                            }
275                        }
276                    },
277                };
278                let out = Block::from(out_ef.as_base_slice());
279                memory.mw_unchecked(addrs.out, out);
280                record.ext_alu_events.push(ExtAluEvent { out, in1, in2 });
281            }
282            Instruction::Mem(MemInstr {
283                addrs: MemIo { inner: addr },
284                vals: MemIo { inner: val },
285                mult: _,
286                kind,
287            }) => {
288                match kind {
289                    MemAccessKind::Read => {
290                        let mem_entry = memory.mr_unchecked(addr);
291                        assert_eq!(
292                            mem_entry.val, val,
293                            "stored memory value should be the specified value"
294                        );
295                    }
296                    MemAccessKind::Write => memory.mw_unchecked(addr, val),
297                }
298                record.mem_const_count += 1;
299            }
300            Instruction::Poseidon2(instr) => {
301                let Poseidon2Instr { addrs: Poseidon2Io { input, output }, mults: _ } = *instr;
302                let in_vals = std::array::from_fn(|i| memory.mr_unchecked(input[i]).val[0]);
303                let perm_output = perm.permute(in_vals);
304
305                perm_output.iter().zip(output).for_each(|(&val, addr)| {
306                    memory.mw_unchecked(addr, Block::from(val));
307                });
308                record
309                    .poseidon2_events
310                    .push(Poseidon2Event { input: in_vals, output: perm_output });
311            }
312            Instruction::Select(SelectInstr {
313                addrs: SelectIo { bit, out1, out2, in1, in2 },
314                mult1: _,
315                mult2: _,
316            }) => {
317                let bit = memory.mr_unchecked(bit).val[0];
318                let in1 = memory.mr_unchecked(in1).val[0];
319                let in2 = memory.mr_unchecked(in2).val[0];
320                let out1_val = bit * in2 + (F::one() - bit) * in1;
321                let out2_val = bit * in1 + (F::one() - bit) * in2;
322                memory.mw_unchecked(out1, Block::from(out1_val));
323                memory.mw_unchecked(out2, Block::from(out2_val));
324                record.select_events.push(SelectEvent {
325                    bit,
326                    out1: out1_val,
327                    out2: out2_val,
328                    in1,
329                    in2,
330                })
331            }
332            Instruction::ExpReverseBitsLen(ExpReverseBitsInstr {
333                addrs: ExpReverseBitsIo { base, exp, result },
334                mult: _,
335            }) => {
336                let base_val = memory.mr_unchecked(base).val[0];
337                let exp_bits: Vec<_> =
338                    exp.iter().map(|bit| memory.mr_unchecked(*bit).val[0]).collect();
339                let exp_val = exp_bits
340                    .iter()
341                    .enumerate()
342                    .fold(0, |acc, (i, &val)| acc + val.as_canonical_u32() * (1 << i));
343                let out =
344                    base_val.exp_u64(reverse_bits_len(exp_val as usize, exp_bits.len()) as u64);
345                memory.mw_unchecked(result, Block::from(out));
346                record.exp_reverse_bits_len_events.push(ExpReverseBitsEvent {
347                    result: out,
348                    base: base_val,
349                    exp: exp_bits,
350                });
351            }
352            Instruction::HintBits(HintBitsInstr { output_addrs_mults, input_addr }) => {
353                let num = memory.mr_unchecked(input_addr).val[0].as_canonical_u32();
354                // Decompose the num into LE bits.
355                let bits = (0..output_addrs_mults.len())
356                    .map(|i| Block::from(F::from_canonical_u32((num >> i) & 1)))
357                    .collect::<Vec<_>>();
358                // Write the bits to the array at dst.
359                for (bit, (addr, _mult)) in bits.into_iter().zip(output_addrs_mults) {
360                    memory.mw_unchecked(addr, bit);
361                    record.mem_var_events.push(MemEvent { inner: bit });
362                }
363            }
364            Instruction::HintAddCurve(instr) => {
365                let HintAddCurveInstr {
366                    output_x_addrs_mults,
367                    output_y_addrs_mults,
368                    input1_x_addrs,
369                    input1_y_addrs,
370                    input2_x_addrs,
371                    input2_y_addrs,
372                } = *instr;
373                let input1_x = SepticExtension::<F>::from_base_fn(|i| {
374                    memory.mr_unchecked(input1_x_addrs[i]).val[0]
375                });
376                let input1_y = SepticExtension::<F>::from_base_fn(|i| {
377                    memory.mr_unchecked(input1_y_addrs[i]).val[0]
378                });
379                let input2_x = SepticExtension::<F>::from_base_fn(|i| {
380                    memory.mr_unchecked(input2_x_addrs[i]).val[0]
381                });
382                let input2_y = SepticExtension::<F>::from_base_fn(|i| {
383                    memory.mr_unchecked(input2_y_addrs[i]).val[0]
384                });
385                let point1 = SepticCurve { x: input1_x, y: input1_y };
386                let point2 = SepticCurve { x: input2_x, y: input2_y };
387                let output = point1.add_incomplete(point2);
388
389                for (val, (addr, _mult)) in
390                    output.x.0.into_iter().zip(output_x_addrs_mults.into_iter())
391                {
392                    memory.mw_unchecked(addr, Block::from(val));
393                    record.mem_var_events.push(MemEvent { inner: Block::from(val) });
394                }
395                for (val, (addr, _mult)) in
396                    output.y.0.into_iter().zip(output_y_addrs_mults.into_iter())
397                {
398                    memory.mw_unchecked(addr, Block::from(val));
399                    record.mem_var_events.push(MemEvent { inner: Block::from(val) });
400                }
401            }
402            Instruction::FriFold(instr) => {
403                let FriFoldInstr {
404                    base_single_addrs,
405                    ext_single_addrs,
406                    ext_vec_addrs,
407                    alpha_pow_mults: _,
408                    ro_mults: _,
409                } = *instr;
410                let x = memory.mr_unchecked(base_single_addrs.x).val[0];
411                let z = memory.mr_unchecked(ext_single_addrs.z).val;
412                let z: EF = z.ext();
413                let alpha = memory.mr_unchecked(ext_single_addrs.alpha).val;
414                let alpha: EF = alpha.ext();
415                let mat_opening = ext_vec_addrs
416                    .mat_opening
417                    .iter()
418                    .map(|addr| memory.mr_unchecked(*addr).val)
419                    .collect_vec();
420                let ps_at_z = ext_vec_addrs
421                    .ps_at_z
422                    .iter()
423                    .map(|addr| memory.mr_unchecked(*addr).val)
424                    .collect_vec();
425
426                for m in 0..ps_at_z.len() {
427                    // let m = F::from_canonical_u32(m);
428                    // Get the opening values.
429                    let p_at_x = mat_opening[m];
430                    let p_at_x: EF = p_at_x.ext();
431                    let p_at_z = ps_at_z[m];
432                    let p_at_z: EF = p_at_z.ext();
433
434                    // Calculate the quotient and update the values
435                    let quotient = (-p_at_z + p_at_x) / (-z + x);
436
437                    // First we peek to get the current value.
438                    let alpha_pow: EF =
439                        memory.mr_unchecked(ext_vec_addrs.alpha_pow_input[m]).val.ext();
440
441                    let ro: EF = memory.mr_unchecked(ext_vec_addrs.ro_input[m]).val.ext();
442
443                    let new_ro = ro + alpha_pow * quotient;
444                    let new_alpha_pow = alpha_pow * alpha;
445
446                    memory.mw_unchecked(
447                        ext_vec_addrs.ro_output[m],
448                        Block::from(new_ro.as_base_slice()),
449                    );
450
451                    memory.mw_unchecked(
452                        ext_vec_addrs.alpha_pow_output[m],
453                        Block::from(new_alpha_pow.as_base_slice()),
454                    );
455
456                    record.fri_fold_events.push(FriFoldEvent {
457                        base_single: FriFoldBaseIo { x },
458                        ext_single: FriFoldExtSingleIo {
459                            z: Block::from(z.as_base_slice()),
460                            alpha: Block::from(alpha.as_base_slice()),
461                        },
462                        ext_vec: FriFoldExtVecIo {
463                            mat_opening: Block::from(p_at_x.as_base_slice()),
464                            ps_at_z: Block::from(p_at_z.as_base_slice()),
465                            alpha_pow_input: Block::from(alpha_pow.as_base_slice()),
466                            ro_input: Block::from(ro.as_base_slice()),
467                            alpha_pow_output: Block::from(new_alpha_pow.as_base_slice()),
468                            ro_output: Block::from(new_ro.as_base_slice()),
469                        },
470                    });
471                }
472            }
473            Instruction::BatchFRI(instr) => {
474                let BatchFRIInstr { base_vec_addrs, ext_single_addrs, ext_vec_addrs, acc_mult: _ } =
475                    *instr;
476
477                let mut acc = EF::zero();
478                let p_at_xs = base_vec_addrs
479                    .p_at_x
480                    .iter()
481                    .map(|addr| memory.mr_unchecked(*addr).val[0])
482                    .collect_vec();
483                let p_at_zs = ext_vec_addrs
484                    .p_at_z
485                    .iter()
486                    .map(|addr| memory.mr_unchecked(*addr).val.ext::<EF>())
487                    .collect_vec();
488                let alpha_pows: Vec<_> = ext_vec_addrs
489                    .alpha_pow
490                    .iter()
491                    .map(|addr| memory.mr_unchecked(*addr).val.ext::<EF>())
492                    .collect_vec();
493
494                for m in 0..p_at_zs.len() {
495                    acc += alpha_pows[m] * (p_at_zs[m] - EF::from_base(p_at_xs[m]));
496                    record.batch_fri_events.push(BatchFRIEvent {
497                        base_vec: BatchFRIBaseVecIo { p_at_x: p_at_xs[m] },
498                        ext_single: BatchFRIExtSingleIo { acc: Block::from(acc.as_base_slice()) },
499                        ext_vec: BatchFRIExtVecIo {
500                            p_at_z: Block::from(p_at_zs[m].as_base_slice()),
501                            alpha_pow: Block::from(alpha_pows[m].as_base_slice()),
502                        },
503                    });
504                }
505
506                memory.mw_unchecked(ext_single_addrs.acc, Block::from(acc.as_base_slice()));
507            }
508            Instruction::CommitPublicValues(instr) => {
509                let pv_addrs = instr.pv_addrs.as_array();
510                let pv_values: [F; RECURSIVE_PROOF_NUM_PV_ELTS] =
511                    array::from_fn(|i| memory.mr_unchecked(pv_addrs[i]).val[0]);
512                record.public_values = *pv_values.as_slice().borrow();
513                record
514                    .commit_pv_hash_events
515                    .push(CommitPublicValuesEvent { public_values: record.public_values });
516            }
517
518            Instruction::Print(PrintInstr { field_elt_type, addr }) => match field_elt_type {
519                FieldEltType::Base => {
520                    let f = memory.mr_unchecked(addr).val[0];
521                    writeln!(debug_stdout.lock().unwrap(), "PRINTF={f}")
522                }
523                FieldEltType::Extension => {
524                    let ef = memory.mr_unchecked(addr).val;
525                    writeln!(debug_stdout.lock().unwrap(), "PRINTEF={ef:?}")
526                }
527            }
528            .map_err(RuntimeError::DebugPrint)?,
529            Instruction::HintExt2Felts(HintExt2FeltsInstr { output_addrs_mults, input_addr }) => {
530                let fs = memory.mr_unchecked(input_addr).val;
531                // Write the bits to the array at dst.
532                for (f, (addr, _mult)) in fs.into_iter().zip(output_addrs_mults) {
533                    let felt = Block::from(f);
534                    memory.mw_unchecked(addr, felt);
535                    record.mem_var_events.push(MemEvent { inner: felt });
536                }
537            }
538            Instruction::Hint(HintInstr { output_addrs_mults }) => {
539                let witness_stream =
540                    witness_stream.expect("hint should be called outside parallel contexts");
541                // Check that enough Blocks can be read, so `drain` does not panic.
542                if witness_stream.len() < output_addrs_mults.len() {
543                    return Err(RuntimeError::EmptyWitnessStream);
544                }
545                let witness = witness_stream.drain(0..output_addrs_mults.len());
546                for ((addr, _mult), val) in zip(output_addrs_mults, witness) {
547                    // Inline [`Self::mw`] to mutably borrow multiple fields of `self`.
548                    memory.mw_unchecked(addr, val);
549                    record.mem_var_events.push(MemEvent { inner: val });
550                }
551            }
552            #[cfg(feature = "debug")]
553            Instruction::DebugBacktrace(backtrace) => {
554                state.last_trace = Some(backtrace);
555            }
556        }
557
558        Ok(())
559    }
560
561    /// # Safety
562    ///
563    /// This function makes the same safety assumptions as [`RecursionProgram::new_unchecked`].
564    unsafe fn execute_raw(
565        env: &ExecEnv<F, Diffusion>,
566        program: &RawProgram<Instruction<F>>,
567        root_program: &Arc<RecursionProgram<F>>,
568        mut witness_stream: Option<&mut VecDeque<Block<F>>>,
569    ) -> Result<ExecutionRecord<F>, RuntimeError<F, EF>> {
570        let fresh_record =
571            || ExecutionRecord { program: Arc::clone(root_program), ..Default::default() };
572
573        let mut state = ExecState {
574            env: env.clone(),
575            record: fresh_record(),
576            #[cfg(feature = "debug")]
577            last_trace: None,
578        };
579
580        for block in &program.seq_blocks {
581            match block {
582                SeqBlock::Basic(basic_block) => {
583                    for instruction in &basic_block.instrs {
584                        unsafe {
585                            Self::execute_one(
586                                &mut state,
587                                witness_stream.as_deref_mut(),
588                                instruction.clone(),
589                            )
590                        }?;
591                    }
592                }
593                SeqBlock::Parallel(vec) => {
594                    state.record.append(
595                        &mut vec
596                            .par_iter()
597                            .map(|subprogram| {
598                                // Witness stream may not be called inside parallel contexts to
599                                // avoid nondeterminism.
600                                Self::execute_raw(env, subprogram, root_program, None)
601                            })
602                            .try_reduce(fresh_record, |mut record, mut res| {
603                                record.append(&mut res);
604                                Ok(record)
605                            })?,
606                    );
607                }
608            }
609        }
610        Ok(state.record)
611    }
612
613    /// Run the program.
614    pub fn run(&mut self) -> Result<(), RuntimeError<F, EF>> {
615        let record = unsafe {
616            Self::execute_raw(
617                &ExecEnv {
618                    memory: &self.memory,
619                    perm: self.perm.as_ref().unwrap(),
620                    debug_stdout: &Mutex::new(&mut self.debug_stdout),
621                },
622                &self.program.inner,
623                &self.program,
624                Some(&mut self.witness_stream),
625            )
626        }?;
627
628        self.record = record;
629
630        Ok(())
631    }
632}
633
634struct ExecState<'a, 'b, F, Diffusion> {
635    pub env: ExecEnv<'a, 'b, F, Diffusion>,
636    pub record: ExecutionRecord<F>,
637    #[cfg(feature = "debug")]
638    pub last_trace: Option<Trace>,
639}
640
641impl<F, Diffusion> ExecState<'_, '_, F, Diffusion> {
642    fn resolve_trace(&mut self) -> Option<&mut Trace> {
643        cfg_if::cfg_if! {
644            if #[cfg(feature = "debug")] {
645                // False positive.
646                #[allow(clippy::manual_inspect)]
647                self.last_trace.as_mut().map(|trace| {
648                    trace.resolve();
649                    trace
650                })
651            } else {
652                None
653            }
654        }
655    }
656}
657
658impl<'a, 'b, F, Diffusion> Clone for ExecState<'a, 'b, F, Diffusion>
659where
660    ExecEnv<'a, 'b, F, Diffusion>: Clone,
661    ExecutionRecord<F>: Clone,
662{
663    fn clone(&self) -> Self {
664        let Self {
665            env,
666            record,
667            #[cfg(feature = "debug")]
668            last_trace,
669        } = self;
670        Self {
671            env: env.clone(),
672            record: record.clone(),
673            #[cfg(feature = "debug")]
674            last_trace: last_trace.clone(),
675        }
676    }
677
678    fn clone_from(&mut self, source: &Self) {
679        let Self {
680            env,
681            record,
682            #[cfg(feature = "debug")]
683            last_trace,
684        } = self;
685        env.clone_from(&source.env);
686        record.clone_from(&source.record);
687        #[cfg(feature = "debug")]
688        last_trace.clone_from(&source.last_trace);
689    }
690}
691
692struct ExecEnv<'a, 'b, F, Diffusion> {
693    pub memory: &'a MemVec<F>,
694    pub perm: &'a Perm<F, Diffusion>,
695    pub debug_stdout: &'a Mutex<dyn Write + Send + 'b>,
696}
697
698impl<F, Diffusion> Clone for ExecEnv<'_, '_, F, Diffusion> {
699    fn clone(&self) -> Self {
700        let Self { memory, perm, debug_stdout } = self;
701        Self { memory, perm, debug_stdout }
702    }
703
704    fn clone_from(&mut self, source: &Self) {
705        let Self { memory, perm, debug_stdout } = self;
706        memory.clone_from(&source.memory);
707        perm.clone_from(&source.perm);
708        debug_stdout.clone_from(&source.debug_stdout);
709    }
710}