From 8d5b2abbbf7e12f85dfc25eda57628cc8fd5320d Mon Sep 17 00:00:00 2001 From: cpunkzzz Date: Wed, 28 Sep 2022 20:34:53 -0400 Subject: [PATCH 1/2] enable miden back --- miden | 2 +- recursive/methods/guest/src/bin/recursive.rs | 264 +++++++++++++++---- recursive/starter/src/main.rs | 201 +++++++------- recursive/utils/src/inputs.rs | 21 +- 4 files changed, 313 insertions(+), 175 deletions(-) diff --git a/miden b/miden index f23336a..3f47ebd 160000 --- a/miden +++ b/miden @@ -1 +1 @@ -Subproject commit f23336a959c1c6b5b830385616bbcf542081737a +Subproject commit 3f47ebdcf7d08a90cc38cde902b7e852bd9193ce diff --git a/recursive/methods/guest/src/bin/recursive.rs b/recursive/methods/guest/src/bin/recursive.rs index bed80b4..72d8361 100644 --- a/recursive/methods/guest/src/bin/recursive.rs +++ b/recursive/methods/guest/src/bin/recursive.rs @@ -2,22 +2,22 @@ #![no_std] extern crate alloc; +use alloc::format; use alloc::vec::Vec; use anyhow::{anyhow, Result}; -use miden_air::ProcessorAir; +use miden_air::{FieldElement, ProcessorAir}; use risc0_zkvm_guest::{env, sha}; -use rkyv::{option::ArchivedOption, Archive, Deserialize}; -use utils::inputs::{AirInput, RiscInput}; -use winter_air::{ - proof::{Commitments, Context, OodFrame, Queries, StarkProof}, - Air, AuxTraceRandElements, ConstraintCompositionCoefficients, EvaluationFrame, -}; +use rkyv::Deserialize; +use utils::inputs::{MidenAirInput, MidenRiscInput}; +use winter_air::{Air, AuxTraceRandElements, ConstraintCompositionCoefficients}; +use winter_crypto::ElementHasher; use winter_crypto::{ hashers::{Sha2_256, ShaHasherT}, - ByteDigest, RandomCoin, + RandomCoin, }; -use winter_math::fields::f64::BaseElement; -use winter_verifier::evaluate_constraints; +use winter_math::fields::f64::{BaseElement, INV_NONDET}; +use winter_utils::Serializable; +use winter_verifier::{evaluate_constraints, DeepComposer, FriVerifier, VerifierChannel}; risc0_zkvm_guest::entry!(main); @@ -29,85 +29,118 @@ impl ShaHasherT for GuestSha2 { } } +type E = BaseElement; +type H = Sha2_256; +type C = VerifierChannel; +type VerfierAIR = ProcessorAir; + pub fn aux_trace_segments( - risc_input: & as Archive>::Archived, - public_coin: &mut RandomCoin>, - air: &ProcessorAir, -) -> Result> { - let first_digest = ByteDigest::new(risc_input.trace_commitments[0]); - public_coin.reseed(first_digest); - let mut aux_trace_rand_elements = AuxTraceRandElements::::new(); - for (i, commitment) in risc_input.trace_commitments.iter().skip(1).enumerate() { + verifier_channel: &C, + public_coin: &mut RandomCoin>, + air: &VerfierAIR, +) -> Result> { + let mut aux_trace_rand_elements = AuxTraceRandElements::::new(); + for (i, commitment) in verifier_channel + .read_trace_commitments() + .iter() + .skip(1) + .enumerate() + { let rand_elements = air .get_aux_trace_segment_random_elements(i, public_coin) .map_err(|_| anyhow!("Random coin error"))?; aux_trace_rand_elements.add_segment_elements(rand_elements); - let c = ByteDigest::new(*commitment); - public_coin.reseed(c); + public_coin.reseed(*commitment); } Ok(aux_trace_rand_elements) } pub fn get_constraint_coffs( - public_coin: &mut RandomCoin>, - air: &ProcessorAir, -) -> Result> { + public_coin: &mut RandomCoin>, + air: &VerfierAIR, +) -> Result> { let constraint_coeffs = air .get_constraint_composition_coefficients(public_coin) .map_err(|_| anyhow!("Random coin error"))?; Ok(constraint_coeffs) } -pub fn main() { +pub fn init_public_coin_seed( + public_coin_seed: &mut Vec, + result: S, + context: &[u8], +) { + result.write_into(public_coin_seed); + public_coin_seed.extend(context); +} + +pub fn run_main_logic() -> Result<()> { + // Deserialize public inputs let aux_input: &[u8] = env::read_aux_input(); - let air_input: AirInput = env::read(); - let air = ProcessorAir::new( + let pub_inputs = unsafe { rkyv::archived_root::>(&aux_input[..]) }; + + let mut verifier_channel: C = pub_inputs + .verifier_channel + .deserialize(&mut rkyv::Infallible) + .unwrap(); + + for (a, inv_a) in pub_inputs.inv_nondet.iter() { + let a_copy: BaseElement = a.deserialize(&mut rkyv::Infallible).unwrap(); + let inv_a_copy: BaseElement = inv_a.deserialize(&mut rkyv::Infallible).unwrap(); + INV_NONDET.lock().insert(a_copy, inv_a_copy); + } + + // Extract context + let context = pub_inputs.context.as_slice(); + + // Extract Fibonacci AIR + let air_input: MidenAirInput = env::read(); + let air = VerfierAIR::new( air_input.trace_info, - air_input.public_inputs, + air_input.public_inputs.clone(), air_input.proof_options, ); - let risc_input = unsafe { rkyv::archived_root::>(&aux_input[..]) }; - let public_coin_seed = Vec::new(); - let mut public_coin: RandomCoin> = - RandomCoin::new(&public_coin_seed); + // build a seed for the public coin; the initial seed is the hash of public inputs and proof + // context, but as the protocol progresses, the coin will be reseeded with the info received + // from the prover + let mut public_coin_seed = Vec::new(); + init_public_coin_seed(&mut public_coin_seed, air_input.public_inputs, context); + + let mut public_coin: RandomCoin> = RandomCoin::new(&public_coin_seed); + + // reseed the coin with the commitment to the main trace segment + public_coin.reseed(verifier_channel.read_trace_commitments()[0]); + // process auxiliary trace segments (if any), to build a set of random elements for each segment - let aux_trace_rand_elements = - aux_trace_segments(&risc_input, &mut public_coin, &air).expect("aux trace segments failed"); + let aux_trace_rand_elements = aux_trace_segments(&verifier_channel, &mut public_coin, &air) + .expect("aux trace segments failed"); + // build random coefficients for the composition polynomial let constraint_coeffs = get_constraint_coffs(&mut public_coin, &air).expect("constraint_coeffs_error"); - let constraint_commitment = ByteDigest::new(risc_input.constraint_commitment); - public_coin.reseed(constraint_commitment); + // env::log(&format!("constraint coeffs: {:?}", &constraint_coeffs)); + + // 2 ----- constraint commitment -------------------------------------------------------------- + // let constraint_commitment = ByteDigest::new(pub_inputs.constraint_commitment); + // env::log(&format!("constraint commitment")); + public_coin.reseed(verifier_channel.read_constraint_commitment()); let z = public_coin - .draw::() + .draw::() .map_err(|_| anyhow!("Random coin error")) .expect("constraint_commitment"); - // TODO remove redundant copy - let ood_main_trace_frame: EvaluationFrame = EvaluationFrame::from_rows( - risc_input - .ood_main_trace_frame - .current - .deserialize(&mut rkyv::Infallible) - .unwrap(), - risc_input - .ood_main_trace_frame - .next - .deserialize(&mut rkyv::Infallible) - .unwrap(), - ); + // 3 ----- OOD consistency check -------------------------------------------------------------- + // make sure that evaluations obtained by evaluating constraints over the out-of-domain frame + // are consistent with the evaluations of composition polynomial columns sent by the prover - let ood_aux_trace_frame: Option> = - match &risc_input.ood_aux_trace_frame { - ArchivedOption::None => None, - ArchivedOption::Some(row) => Some(EvaluationFrame::from_rows( - row.current.deserialize(&mut rkyv::Infallible).unwrap(), - row.next.deserialize(&mut rkyv::Infallible).unwrap(), - )), - }; + // read the out-of-domain trace frames (the main trace frame and auxiliary trace frame, if + // provided) sent by the prover and evaluate constraints over them; also, reseed the public + // coin with the OOD frames received from the prover. - evaluate_constraints( + // env::log(&format!("ood_frame")); + let (ood_main_trace_frame, ood_aux_trace_frame) = verifier_channel.read_ood_trace_frame(); + let ood_constraint_evaluation_1 = evaluate_constraints( &air, constraint_coeffs, &ood_main_trace_frame, @@ -115,4 +148,119 @@ pub fn main() { aux_trace_rand_elements, z, ); + + // env::log(&format!("reseed ood_frame")); + if let Some(ref aux_trace_frame) = ood_aux_trace_frame { + // when the trace contains auxiliary segments, append auxiliary trace elements at the + // end of main trace elements for both current and next rows in the frame. this is + // needed to be consistent with how the prover writes OOD frame into the channel. + + let mut current = ood_main_trace_frame.current().to_vec(); + current.extend_from_slice(aux_trace_frame.current()); + public_coin.reseed(H::hash_elements(¤t)); + + let mut next = ood_main_trace_frame.next().to_vec(); + next.extend_from_slice(aux_trace_frame.next()); + public_coin.reseed(H::hash_elements(&next)); + } else { + public_coin.reseed(H::hash_elements(ood_main_trace_frame.current())); + public_coin.reseed(H::hash_elements(ood_main_trace_frame.next())); + } + + // // read evaluations of composition polynomial columns sent by the prover, and reduce them into + // // a single value by computing sum(z^i * value_i), where value_i is the evaluation of the ith + // // column polynomial at z^m, where m is the total number of column polynomials; also, reseed + // // the public coin with the OOD constraint evaluations received from the prover. + // env::log(&format!("ood_constraint_evaluation_2")); + let ood_constraint_evaluations = verifier_channel.read_ood_constraint_evaluations(); + let ood_constraint_evaluation_2 = ood_constraint_evaluations + .iter() + .enumerate() + .fold(E::ZERO, |result, (i, &value)| { + result + z.exp((i as u32).into()) * value + }); + public_coin.reseed(H::hash_elements(&ood_constraint_evaluations)); + + // finally, make sure the values are the same + if ood_constraint_evaluation_1 != ood_constraint_evaluation_2 { + panic!("Inconsistent OOD constraint evaluations"); + } + + // // 4 ----- FRI commitments -------------------------------------------------------------------- + // // draw coefficients for computing DEEP composition polynomial from the public coin; in the + // // interactive version of the protocol, the verifier sends these coefficients to the prover + // // and the prover uses them to compute the DEEP composition polynomial. the prover, then + // // applies FRI protocol to the evaluations of the DEEP composition polynomial. + let deep_coefficients = air + .get_deep_composition_coefficients::(&mut public_coin) + .map_err(|msg| anyhow!(msg)) + .unwrap(); + + // instantiates a FRI verifier with the FRI layer commitments read from the channel. From the + // verifier's perspective, this is equivalent to executing the commit phase of the FRI protocol. + // The verifier uses these commitments to update the public coin and draw random points alpha + // from them; in the interactive version of the protocol, the verifier sends these alphas to + // the prover, and the prover uses them to compute and commit to the subsequent FRI layers. + let fri_verifier: FriVerifier = FriVerifier::new( + &mut verifier_channel, + &mut public_coin, + air.options().to_fri_options(), + air.trace_poly_degree(), + ) + .expect("fri verifier init failed"); + + // 5 ----- trace and constraint queries ------------------------------------------------------- + // read proof-of-work nonce sent by the prover and update the public coin with it + public_coin.reseed_with_int(verifier_channel.read_pow_nonce()); + + // make sure the proof-of-work specified by the grinding factor is satisfied + if public_coin.leading_zeros() < air.options().grinding_factor() { + panic!("QuerySeedProofOfWorkVerificationFailed"); + } + + // draw pseudo-random query positions for the LDE domain from the public coin; in the + // interactive version of the protocol, the verifier sends these query positions to the prover, + // and the prover responds with decommitments against these positions for trace and constraint + // composition polynomial evaluations. + let query_positions = public_coin + .draw_integers(air.options().num_queries(), air.lde_domain_size()) + .map_err(|_| anyhow!("random coin error"))?; + // read evaluations of trace and constraint composition polynomials at the queried positions; + // this also checks that the read values are valid against trace and constraint commitments + let (queried_main_trace_states, queried_aux_trace_states) = verifier_channel + .read_queried_trace_states(&query_positions) + .map_err(|e| anyhow!("read_queried_trace_states, e = {}", e))?; + let queried_constraint_evaluations = verifier_channel + .read_constraint_evaluations(&query_positions) + .map_err(|e| anyhow!("read_constraint_evaluations, e = {}", e))?; + + // 6 ----- DEEP composition ------------------------------------------------------------------- + // compute evaluations of the DEEP composition polynomial at the queried positions + let composer = DeepComposer::new(&air, &query_positions, z, deep_coefficients); + let t_composition = composer.compose_trace_columns( + queried_main_trace_states, + queried_aux_trace_states, + ood_main_trace_frame, + ood_aux_trace_frame, + ); + let c_composition = composer + .compose_constraint_evaluations(queried_constraint_evaluations, ood_constraint_evaluations); + let deep_evaluations = composer.combine_compositions(t_composition, c_composition); + + // 7 ----- Verify low-degree proof ------------------------------------------------------------- + // make sure that evaluations of the DEEP composition polynomial we computed in the previous + // step are in fact evaluations of a polynomial of degree equal to trace polynomial degree + fri_verifier + .verify(&mut verifier_channel, &deep_evaluations, &query_positions) + .map_err(|e| anyhow!("fri verifier failed, e = {}", e))?; + Ok(()) +} + +pub fn main() { + match run_main_logic() { + Ok(_) => {} + Err(e) => { + env::log(&format!("error: {}", e)); + } + } } diff --git a/recursive/starter/src/main.rs b/recursive/starter/src/main.rs index 1ac26c3..4d63d4b 100644 --- a/recursive/starter/src/main.rs +++ b/recursive/starter/src/main.rs @@ -2,124 +2,119 @@ use anyhow::{anyhow, Result}; use methods::{EXP_ID, EXP_PATH, RECURSIVE_ID, RECURSIVE_PATH, SHA3_ID, SHA3_PATH}; use miden::{Program, ProofOptions}; use miden_air::{Felt, FieldElement, ProcessorAir, PublicInputs}; +use miden_core::utils::Serializable; use risc0_zkvm::host::Prover; use risc0_zkvm::serde::{from_slice, to_vec}; use sha3::{Digest, Sha3_256}; -use utils::inputs::{AirInput, RiscInput}; +use utils::inputs::{MidenAirInput, MidenRiscInput}; use winter_air::proof::{Commitments, Context, OodFrame, Queries, StarkProof}; use winter_air::Air; use winter_crypto::hashers::DefaultSha2; use winter_crypto::hashers::Sha2_256; -use winter_math::fields::f64::BaseElement; +use winter_math::fields::f64::{BaseElement, INV_NONDET}; use winter_verifier::VerifierChannel; pub mod fib_winter; pub mod fibonacci; fn main() -> Result<()> { - fib_winter::fib_winter()?; - // recursive_miden()?; + // fib_winter::fib_winter()?; + recursive_miden()?; // sha3(); // exp(); Ok(()) } -//#[allow(dead_code)] -//fn recursive_miden() -> Result<()> { -// println!("============================================================"); -// -// let proof_options = get_proof_options(); -// -// // instantiate and prepare the example -// let example = fibonacci::get_example(1024); -// -// let fibonacci::Example { -// program, -// inputs, -// num_outputs, -// pub_inputs, -// expected_result, -// } = example; -// println!("--------------------------------"); -// -// // execute the program and generate the proof of execution -// let (outputs, proof) = miden::prove(&program, &inputs, num_outputs, &proof_options).unwrap(); -// println!("--------------------------------"); -// println!("Trace length: {}", proof.context.trace_length()); -// println!("Trace queries length: {}", proof.trace_queries.len()); -// println!("Program output: {:?}", outputs); -// assert_eq!( -// expected_result, outputs, -// "Program result was computed incorrectly" -// ); -// -// let (mut verifier_channel, air_input) = -// get_verifier_channel(&proof, &outputs, &pub_inputs, program)?; -// let trace_commitments: Vec<[u8; 32]> = verifier_channel -// .read_trace_commitments() -// .into_iter() -// .map(|x| x.get_raw()) -// .collect(); -// let constraint_commitment = verifier_channel.read_constraint_commitment().get_raw(); -// let (ood_main_trace_frame, ood_aux_trace_frame) = verifier_channel.read_ood_trace_frame(); -// println!("ood size is: {}", ood_main_trace_frame.current.len()); -// -// let risc_inputs = RiscInput { -// trace_commitments, -// constraint_commitment, -// ood_main_trace_frame, -// ood_aux_trace_frame, -// }; -// -// let mut prover = Prover::new(&std::fs::read(RECURSIVE_PATH).unwrap(), RECURSIVE_ID).unwrap(); -// let trace_commitments_to_send = rkyv::to_bytes::<_, 256>(&risc_inputs).unwrap(); -// prover.add_input_u8_slice_aux(&trace_commitments_to_send); -// prover.add_input(to_vec(&air_input)?.as_slice())?; -// let receipt = prover.run().unwrap(); -// receipt.verify(RECURSIVE_ID).unwrap(); -// Ok(()) -//} -// -//fn get_verifier_channel( -// proof: &StarkProof, -// outputs: &Vec, -// inputs: &Vec, -// program: Program, -//) -> Result<( -// VerifierChannel>, -// AirInput, -//)> { -// let mut stack_input_felts: Vec = Vec::with_capacity(inputs.len()); -// for &input in inputs.iter().rev() { -// stack_input_felts.push( -// input -// .try_into() -// .map_err(|_| anyhow!("cannot map input into felts"))?, -// ); -// } -// -// let mut stack_output_felts: Vec = Vec::with_capacity(outputs.len()); -// for &output in outputs.iter() { -// stack_output_felts.push( -// output -// .try_into() -// .map_err(|_| anyhow!("cannot map output into felts"))?, -// ); -// } -// -// let pub_inputs = PublicInputs::new(program.hash(), stack_input_felts, stack_output_felts); -// let air_input = AirInput { -// trace_info: proof.get_trace_info(), -// public_inputs: pub_inputs.clone(), -// proof_options: proof.options().clone(), -// }; -// let air = ProcessorAir::new(proof.get_trace_info(), pub_inputs, proof.options().clone()); -// Ok(( -// (VerifierChannel::new::(&air, proof.clone()).map_err(|msg| anyhow!(msg))?), -// air_input, -// )) -//} -// +#[allow(dead_code)] +fn recursive_miden() -> Result<()> { + println!("============================================================"); + + let proof_options = get_proof_options(); + + // instantiate and prepare the example + let example = fibonacci::get_example(1024); + + let fibonacci::Example { + program, + inputs, + num_outputs, + pub_inputs, + expected_result, + } = example; + println!("--------------------------------"); + + // execute the program and generate the proof of execution + let (outputs, proof) = miden::prove(&program, &inputs, num_outputs, &proof_options).unwrap(); + println!("--------------------------------"); + println!("Trace length: {}", proof.context.trace_length()); + println!("Trace queries length: {}", proof.trace_queries.len()); + println!("Program output: {:?}", outputs); + assert_eq!( + expected_result, outputs, + "Program result was computed incorrectly" + ); + + let mut proof_context = Vec::new(); + proof.context.write_into(&mut proof_context); + + let (verifier_channel, air_input) = + get_verifier_channel(&proof, &outputs, &pub_inputs, program)?; + + let risc_inputs = MidenRiscInput { + context: proof_context, + verifier_channel, + inv_nondet: INV_NONDET.lock().clone().into_iter().collect(), + }; + + let mut prover = Prover::new(&std::fs::read(RECURSIVE_PATH).unwrap(), RECURSIVE_ID).unwrap(); + let miden_risc_inputs = rkyv::to_bytes::<_, 256>(&risc_inputs).unwrap(); + prover.add_input_u8_slice_aux(&miden_risc_inputs); + prover.add_input(to_vec(&air_input)?.as_slice())?; + let receipt = prover.run().unwrap(); + receipt.verify(RECURSIVE_ID).unwrap(); + Ok(()) +} + +fn get_verifier_channel( + proof: &StarkProof, + outputs: &Vec, + inputs: &Vec, + program: Program, +) -> Result<( + VerifierChannel>, + MidenAirInput, +)> { + let mut stack_input_felts: Vec = Vec::with_capacity(inputs.len()); + for &input in inputs.iter().rev() { + stack_input_felts.push( + input + .try_into() + .map_err(|_| anyhow!("cannot map input into felts"))?, + ); + } + + let mut stack_output_felts: Vec = Vec::with_capacity(outputs.len()); + for &output in outputs.iter() { + stack_output_felts.push( + output + .try_into() + .map_err(|_| anyhow!("cannot map output into felts"))?, + ); + } + + let pub_inputs = PublicInputs::new(program.hash(), stack_input_felts, stack_output_felts); + let air_input = MidenAirInput { + trace_info: proof.get_trace_info(), + public_inputs: pub_inputs.clone(), + proof_options: proof.options().clone(), + }; + let air = ProcessorAir::new(proof.get_trace_info(), pub_inputs, proof.options().clone()); + Ok(( + (VerifierChannel::new::(&air, proof.clone()).map_err(|msg| anyhow!(msg))?), + air_input, + )) +} + //#[allow(dead_code)] //fn sha3() { // let mut prover = Prover::new(&std::fs::read(SHA3_PATH).unwrap(), SHA3_ID).unwrap(); @@ -154,6 +149,6 @@ fn main() -> Result<()> { // assert_eq!(result, b.exp(exp)); //} -//pub fn get_proof_options() -> ProofOptions { -// ProofOptions::with_sha2() -//} +pub fn get_proof_options() -> ProofOptions { + ProofOptions::with_sha2() +} diff --git a/recursive/utils/src/inputs.rs b/recursive/utils/src/inputs.rs index 028ef38..f3a388c 100644 --- a/recursive/utils/src/inputs.rs +++ b/recursive/utils/src/inputs.rs @@ -1,24 +1,19 @@ -use std::collections::BTreeMap; - -use miden_air::{Felt, FieldElement, PublicInputs}; +use miden_air::{FieldElement, PublicInputs}; use rkyv::{Archive, Deserialize, Serialize}; use serde::{Deserialize as sDeserialize, Serialize as sSerialize}; -use winter_air::{EvaluationFrame, ProofOptions, TraceInfo}; +use winter_air::{ProofOptions, TraceInfo}; use winter_prover::crypto::ElementHasher; use winter_verifier::VerifierChannel; -#[derive(Archive, Deserialize, Serialize, Debug, PartialEq)] -#[archive(compare(PartialEq))] -#[derive(Clone, Eq)] -pub struct RiscInput { - pub trace_commitments: Vec<[u8; 32]>, - pub constraint_commitment: [u8; 32], - pub ood_main_trace_frame: EvaluationFrame, - pub ood_aux_trace_frame: Option>, +#[derive(Archive, Deserialize, Serialize)] +pub struct MidenRiscInput> { + pub context: Vec, + pub verifier_channel: VerifierChannel, + pub inv_nondet: Vec<(E, E)>, } #[derive(sSerialize, sDeserialize, Debug)] -pub struct AirInput { +pub struct MidenAirInput { pub trace_info: TraceInfo, pub public_inputs: PublicInputs, pub proof_options: ProofOptions, From f1abb26ac9f7b39caab621fe72a02837c24610c2 Mon Sep 17 00:00:00 2001 From: cpunkzzz Date: Thu, 29 Sep 2022 16:39:59 -0400 Subject: [PATCH 2/2] Add nondet --- recursive/methods/build.rs | 12 +++++++++++- recursive/starter/src/main.rs | 5 ++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/recursive/methods/build.rs b/recursive/methods/build.rs index 08a8a4e..ca2e202 100644 --- a/recursive/methods/build.rs +++ b/recursive/methods/build.rs @@ -1,3 +1,13 @@ +use std::collections::HashMap; + fn main() { - risc0_build::embed_methods(); + let options_map = HashMap::from([( + "methods-guest", + risc0_build::GuestOptions { + // change to 16 to run miden fully (will increase compile time) + code_limit: 12, + features: vec![], + }, + )]); + risc0_build::embed_methods_with_options(options_map); } diff --git a/recursive/starter/src/main.rs b/recursive/starter/src/main.rs index 4d63d4b..c926bde 100644 --- a/recursive/starter/src/main.rs +++ b/recursive/starter/src/main.rs @@ -58,7 +58,10 @@ fn recursive_miden() -> Result<()> { proof.context.write_into(&mut proof_context); let (verifier_channel, air_input) = - get_verifier_channel(&proof, &outputs, &pub_inputs, program)?; + get_verifier_channel(&proof, &outputs, &pub_inputs, program.clone())?; + + // run verify in order to generate nondet inv inputs + miden::verify(program.hash().clone(), &pub_inputs[..], &outputs[..], proof).unwrap(); let risc_inputs = MidenRiscInput { context: proof_context,