From c2f1bf43020d6535e69dc4d05f40f8ed9827e859 Mon Sep 17 00:00:00 2001 From: Danny Willems Date: Fri, 3 Jan 2025 09:26:38 +0100 Subject: [PATCH] o1vm: remove legacy flavor --- o1vm/Cargo.toml | 4 - o1vm/README.md | 1 - o1vm/src/interpreters/keccak/environment.rs | 50 +- o1vm/src/legacy/folding.rs | 657 ----------------- o1vm/src/legacy/main.rs | 366 ---------- o1vm/src/legacy/mod.rs | 37 - o1vm/src/legacy/proof.rs | 108 --- o1vm/src/legacy/tests.rs | 742 -------------------- o1vm/src/legacy/trace.rs | 565 --------------- o1vm/src/lib.rs | 5 - 10 files changed, 12 insertions(+), 2523 deletions(-) delete mode 100644 o1vm/src/legacy/folding.rs delete mode 100644 o1vm/src/legacy/main.rs delete mode 100644 o1vm/src/legacy/mod.rs delete mode 100644 o1vm/src/legacy/proof.rs delete mode 100644 o1vm/src/legacy/tests.rs delete mode 100644 o1vm/src/legacy/trace.rs diff --git a/o1vm/Cargo.toml b/o1vm/Cargo.toml index a91b75d54c..574a112d1b 100644 --- a/o1vm/Cargo.toml +++ b/o1vm/Cargo.toml @@ -12,10 +12,6 @@ license = "Apache-2.0" [lib] path = "src/lib.rs" -[[bin]] -name = "legacy_o1vm" -path = "src/legacy/main.rs" - [[bin]] name = "pickles_o1vm" path = "src/pickles/main.rs" diff --git a/o1vm/README.md b/o1vm/README.md index 9e728c4ad9..ce986a2c61 100644 --- a/o1vm/README.md +++ b/o1vm/README.md @@ -126,7 +126,6 @@ In either case, `run-code.sh` will: Different versions/flavors of the o1vm are available. -- [legacy](./src/legacy/mod.rs) - to be deprecated. - [pickles](./src/pickles/mod.rs) (currently the default) You can select the flavor you want to run with `run-code.sh` by using the diff --git a/o1vm/src/interpreters/keccak/environment.rs b/o1vm/src/interpreters/keccak/environment.rs index 7c8c4d087d..be26b08ce7 100644 --- a/o1vm/src/interpreters/keccak/environment.rs +++ b/o1vm/src/interpreters/keccak/environment.rs @@ -1,25 +1,19 @@ //! This module contains the definition and implementation of the Keccak environment //! including the common functions between the witness and the constraints environments //! for arithmetic, boolean, and column operations. -use crate::{ - interpreters::keccak::{ - column::{ - Absorbs::{self, *}, - KeccakWitness, - Sponges::{self, *}, - Steps, - Steps::*, - PAD_SUFFIX_LEN, - }, - constraints::Env as ConstraintsEnv, - grid_index, - interpreter::KeccakInterpreter, - pad_blocks, standardize, - witness::Env as WitnessEnv, - KeccakColumn, DIM, HASH_BYTELENGTH, QUARTERS, WORDS_IN_HASH, +use crate::interpreters::keccak::{ + column::{ + Absorbs::{self, *}, + KeccakWitness, + Sponges::{self, *}, + Steps, + Steps::*, + PAD_SUFFIX_LEN, }, - lookups::Lookup, - E, + constraints::Env as ConstraintsEnv, + grid_index, pad_blocks, standardize, + witness::Env as WitnessEnv, + KeccakColumn, DIM, HASH_BYTELENGTH, QUARTERS, WORDS_IN_HASH, }; use ark_ff::Field; @@ -437,24 +431,4 @@ impl KeccakEnv { state_g } - - /// Returns the list of constraints used in a specific Keccak step - pub(crate) fn constraints_of(step: Steps) -> Vec> { - let mut env = ConstraintsEnv { - constraints: vec![], - lookups: vec![], - }; - env.constraints(step); - env.constraints - } - - /// Returns the list of lookups used in a specific Keccak step - pub(crate) fn lookups_of(step: Steps) -> Vec>> { - let mut env = ConstraintsEnv { - constraints: vec![], - lookups: vec![], - }; - env.lookups(step); - env.lookups - } } diff --git a/o1vm/src/legacy/folding.rs b/o1vm/src/legacy/folding.rs deleted file mode 100644 index 6062077e94..0000000000 --- a/o1vm/src/legacy/folding.rs +++ /dev/null @@ -1,657 +0,0 @@ -use ark_ec::AffineRepr; -use ark_ff::FftField; -use ark_poly::{Evaluations, Radix2EvaluationDomain}; -use folding::{ - instance_witness::Foldable, Alphas, FoldingConfig, FoldingEnv, Instance, Side, Witness, -}; -use kimchi::circuits::{berkeley_columns::BerkeleyChallengeTerm, gate::CurrOrNext}; -use kimchi_msm::witness::Witness as GenericWitness; -use poly_commitment::commitment::CommitmentCurve; -use std::{array, ops::Index}; -use strum::EnumCount; -use strum_macros::{EnumCount as EnumCountMacro, EnumIter}; - -// Simple type alias as ScalarField/BaseField is often used. Reduce type -// complexity for clippy. -// Should be moved into FoldingConfig, but associated type defaults are unstable -// at the moment. -pub(crate) type ScalarField = <::Curve as AffineRepr>::ScalarField; -pub(crate) type BaseField = <::Curve as AffineRepr>::BaseField; - -// Does not contain alpha because this one should be provided by folding itself -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, EnumIter, EnumCountMacro)] -pub enum Challenge { - Beta, - Gamma, - JointCombiner, -} - -// Needed to transform from expressions to folding expressions -impl From for Challenge { - fn from(chal: BerkeleyChallengeTerm) -> Self { - match chal { - BerkeleyChallengeTerm::Beta => Challenge::Beta, - BerkeleyChallengeTerm::Gamma => Challenge::Gamma, - BerkeleyChallengeTerm::JointCombiner => Challenge::JointCombiner, - BerkeleyChallengeTerm::Alpha => panic!("Alpha not allowed in folding expressions"), - } - } -} - -/// Folding instance containing the commitment to a witness of N columns, -/// challenges for the proof, and the alphas -#[derive(Debug, Clone)] -pub struct FoldingInstance { - /// Commitments to the witness columns, including the dynamic selectors - pub commitments: [G; N], - /// Challenges for the proof. - /// We do use 3 challenges: - /// - β as the evaluation point for the logup argument - /// - j: the joint combiner for vector lookups - /// - γ (set to 0 for now) - pub challenges: [::ScalarField; Challenge::COUNT], - /// Reuses the Alphas defined in the example of folding - pub alphas: Alphas<::ScalarField>, - - /// Blinder used in the polynomial commitment scheme - pub blinder: ::ScalarField, -} - -impl Foldable for FoldingInstance { - fn combine(a: Self, b: Self, challenge: G::ScalarField) -> Self { - FoldingInstance { - commitments: array::from_fn(|i| { - (a.commitments[i] + b.commitments[i].mul(challenge)).into() - }), - challenges: array::from_fn(|i| a.challenges[i] + challenge * b.challenges[i]), - alphas: Alphas::combine(a.alphas, b.alphas, challenge), - blinder: a.blinder + challenge * b.blinder, - } - } -} - -impl Instance for FoldingInstance { - fn to_absorb(&self) -> (Vec<::ScalarField>, Vec) { - // FIXME: check!!!! - let mut scalars = Vec::new(); - let mut points = Vec::new(); - points.extend(self.commitments); - scalars.extend(self.challenges); - scalars.extend(self.alphas.clone().powers()); - (scalars, points) - } - - fn get_alphas(&self) -> &Alphas { - &self.alphas - } - - fn get_blinder(&self) -> ::ScalarField { - self.blinder - } -} - -impl Index for FoldingInstance { - type Output = G::ScalarField; - - fn index(&self, index: Challenge) -> &Self::Output { - match index { - Challenge::Beta => &self.challenges[0], - Challenge::Gamma => &self.challenges[1], - Challenge::JointCombiner => &self.challenges[2], - } - } -} - -/// Includes the data witness columns and also the dynamic selector columns -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct FoldingWitness { - pub witness: GenericWitness>>, -} - -impl Foldable for FoldingWitness { - fn combine(a: Self, b: Self, challenge: F) -> Self { - Self { - witness: GenericWitness::combine(a.witness, b.witness, challenge), - } - } -} - -impl Witness for FoldingWitness {} - -/// Environment for the decomposable folding protocol, for a given number of -/// witness columns and selectors. -pub struct DecomposedFoldingEnvironment< - const N: usize, - const N_REL: usize, - const N_DSEL: usize, - C: FoldingConfig, - Structure, -> { - pub structure: Structure, - /// Commitments to the witness columns, for both sides - pub instances: [FoldingInstance; 2], - /// Corresponds to the omega evaluations, for both sides - pub curr_witnesses: [FoldingWitness>; 2], - /// Corresponds to the zeta*omega evaluations, for both sides - /// This is curr_witness but left shifted by 1 - pub next_witnesses: [FoldingWitness>; 2], -} - -impl< - const N: usize, - const N_REL: usize, - const N_SEL: usize, - C: FoldingConfig, - // FIXME: Clone should not be used. Only a reference should be stored - Structure: Clone, - > - FoldingEnv< - ScalarField, - FoldingInstance, - FoldingWitness>, - C::Column, - Challenge, - C::Selector, - > for DecomposedFoldingEnvironment -where - // Used by col and selector - FoldingWitness>: Index< - C::Column, - Output = Evaluations, Radix2EvaluationDomain>>, - >, - FoldingWitness>: Index< - C::Selector, - Output = Evaluations, Radix2EvaluationDomain>>, - >, -{ - type Structure = Structure; - - fn new( - structure: &Self::Structure, - instances: [&FoldingInstance; 2], - witnesses: [&FoldingWitness>; 2], - ) -> Self { - let curr_witnesses = [witnesses[0].clone(), witnesses[1].clone()]; - let mut next_witnesses = curr_witnesses.clone(); - for side in next_witnesses.iter_mut() { - for col in side.witness.cols.iter_mut() { - col.evals.rotate_left(1); - } - } - DecomposedFoldingEnvironment { - // FIXME: This is a clone, but it should be a reference - structure: structure.clone(), - instances: [instances[0].clone(), instances[1].clone()], - curr_witnesses, - next_witnesses, - } - } - - fn col(&self, col: C::Column, curr_or_next: CurrOrNext, side: Side) -> &[ScalarField] { - let wit = match curr_or_next { - CurrOrNext::Curr => &self.curr_witnesses[side as usize], - CurrOrNext::Next => &self.next_witnesses[side as usize], - }; - // The following is possible because Index is implemented for our circuit witnesses - &wit[col].evals - } - - fn challenge(&self, challenge: Challenge, side: Side) -> ScalarField { - match challenge { - Challenge::Beta => self.instances[side as usize].challenges[0], - Challenge::Gamma => self.instances[side as usize].challenges[1], - Challenge::JointCombiner => self.instances[side as usize].challenges[2], - } - } - - fn selector(&self, s: &C::Selector, side: Side) -> &[ScalarField] { - let witness = &self.curr_witnesses[side as usize]; - &witness[*s].evals - } -} - -pub struct FoldingEnvironment { - /// Structure of the folded circuit - pub structure: Structure, - /// Commitments to the witness columns, for both sides - pub instances: [FoldingInstance; 2], - /// Corresponds to the evaluations at ω, for both sides - pub curr_witnesses: [FoldingWitness>; 2], - /// Corresponds to the evaluations at ζω, for both sides - /// This is curr_witness but left shifted by 1 - pub next_witnesses: [FoldingWitness>; 2], -} - -impl< - const N: usize, - C: FoldingConfig, - // FIXME: Clone should not be used. Only a reference should be stored - Structure: Clone, - > - FoldingEnv< - ScalarField, - FoldingInstance, - FoldingWitness>, - C::Column, - Challenge, - (), - > for FoldingEnvironment -where - // Used by col and selector - FoldingWitness>: Index< - C::Column, - Output = Evaluations, Radix2EvaluationDomain>>, - >, -{ - type Structure = Structure; - - fn new( - structure: &Self::Structure, - instances: [&FoldingInstance; 2], - witnesses: [&FoldingWitness>; 2], - ) -> Self { - let curr_witnesses = [witnesses[0].clone(), witnesses[1].clone()]; - let mut next_witnesses = curr_witnesses.clone(); - for side in next_witnesses.iter_mut() { - for col in side.witness.cols.iter_mut() { - col.evals.rotate_left(1); - } - } - FoldingEnvironment { - // FIXME: This is a clone, but it should be a reference - structure: structure.clone(), - instances: [instances[0].clone(), instances[1].clone()], - curr_witnesses, - next_witnesses, - } - } - - fn col(&self, col: C::Column, curr_or_next: CurrOrNext, side: Side) -> &[ScalarField] { - let wit = match curr_or_next { - CurrOrNext::Curr => &self.curr_witnesses[side as usize], - CurrOrNext::Next => &self.next_witnesses[side as usize], - }; - // The following is possible because Index is implemented for our circuit witnesses - &wit[col].evals - } - - fn challenge(&self, challenge: Challenge, side: Side) -> ScalarField { - match challenge { - Challenge::Beta => self.instances[side as usize].challenges[0], - Challenge::Gamma => self.instances[side as usize].challenges[1], - Challenge::JointCombiner => self.instances[side as usize].challenges[2], - } - } - - fn selector(&self, _s: &(), _side: Side) -> &[ScalarField] { - unimplemented!("Selector not implemented for FoldingEnvironment. No selectors are supposed to be used when there is only one instruction.") - } -} - -pub mod keccak { - use std::ops::Index; - - use ark_poly::{Evaluations, Radix2EvaluationDomain}; - use folding::{ - checker::{Checker, ExtendedProvider, Provider}, - expressions::FoldingColumnTrait, - FoldingConfig, - }; - use kimchi_msm::columns::Column; - use poly_commitment::kzg::PairingSRS; - - use crate::{ - interpreters::keccak::{ - column::{ - ColumnAlias as KeccakColumn, N_ZKVM_KECCAK_COLS, N_ZKVM_KECCAK_REL_COLS, - N_ZKVM_KECCAK_SEL_COLS, - }, - Steps, - }, - legacy::{Curve, Fp, Pairing}, - }; - - use super::{Challenge, DecomposedFoldingEnvironment, FoldingInstance, FoldingWitness}; - - pub type KeccakFoldingEnvironment = DecomposedFoldingEnvironment< - N_ZKVM_KECCAK_COLS, - N_ZKVM_KECCAK_REL_COLS, - N_ZKVM_KECCAK_SEL_COLS, - KeccakConfig, - (), - >; - - pub type KeccakFoldingWitness = FoldingWitness; - pub type KeccakFoldingInstance = FoldingInstance; - - impl Index for KeccakFoldingWitness { - type Output = Evaluations>; - - fn index(&self, index: KeccakColumn) -> &Self::Output { - &self.witness.cols[usize::from(index)] - } - } - - // Implemented for decomposable folding compatibility - impl Index for KeccakFoldingWitness { - type Output = Evaluations>; - - /// Map a selector column to the corresponding witness column. - fn index(&self, index: Steps) -> &Self::Output { - &self.witness.cols[usize::from(index)] - } - } - - // Implementing this so that generic constraints can be used in folding - impl Index for KeccakFoldingWitness { - type Output = Evaluations>; - - /// Map a column alias to the corresponding witness column. - fn index(&self, index: Column) -> &Self::Output { - match index { - Column::Relation(ix) => &self.witness.cols[ix], - // Even if `Column::DynamicSelector(ix)` would correspond to - // `&self.witness.cols[N_ZKVM_KECCAK_REL_COLS + ix]`, the - // current design of constraints should not include the dynamic - // selectors. Instead, folding will add them in the - // `DecomposableFoldingScheme` as extended selector columns, and - // the `selector()` function inside the `FoldingEnv` will return - // the actual witness column values. - _ => panic!("Undesired column type inside expressions"), - } - } - } - - #[derive(Clone, Debug, PartialEq, Eq, Hash)] - pub struct KeccakConfig; - - impl FoldingColumnTrait for KeccakColumn { - fn is_witness(&self) -> bool { - // dynamic selectors KeccakColumn::Selector() count as witnesses - true - } - } - - impl FoldingConfig for KeccakConfig { - type Column = Column; - type Selector = Steps; - type Challenge = Challenge; - type Curve = Curve; - type Srs = PairingSRS; - type Instance = KeccakFoldingInstance; - type Witness = KeccakFoldingWitness; - type Structure = (); - type Env = KeccakFoldingEnvironment; - } - - // IMPLEMENT CHECKER TRAITS - - impl Checker for ExtendedProvider {} - impl Checker for Provider {} -} - -pub mod mips { - use std::ops::Index; - - use ark_poly::{Evaluations, Radix2EvaluationDomain}; - use folding::{expressions::FoldingColumnTrait, FoldingConfig}; - use kimchi_msm::columns::Column; - use poly_commitment::kzg::PairingSRS; - - use crate::{ - interpreters::mips::{ - column::{ColumnAlias as MIPSColumn, N_MIPS_COLS, N_MIPS_REL_COLS, N_MIPS_SEL_COLS}, - Instruction, - }, - legacy::{Curve, Fp, Pairing}, - }; - - use super::{Challenge, DecomposedFoldingEnvironment, FoldingInstance, FoldingWitness}; - - // Decomposable folding compatibility - pub type DecomposableMIPSFoldingEnvironment = DecomposedFoldingEnvironment< - N_MIPS_COLS, - N_MIPS_REL_COLS, - N_MIPS_SEL_COLS, - DecomposableMIPSFoldingConfig, - (), - >; - - pub type MIPSFoldingWitness = FoldingWitness; - pub type MIPSFoldingInstance = FoldingInstance; - - // -- Start indexer implementations - // Implement indexers over columns and selectors to implement an abstract - // folding environment over selectors, see [crate::folding::FoldingEnvironment] - // for more details - - impl Index for FoldingWitness { - type Output = Evaluations>; - - fn index(&self, index: Column) -> &Self::Output { - match index { - Column::Relation(ix) => &self.witness.cols[ix], - _ => panic!("Invalid column type"), - } - } - } - - impl Index for MIPSFoldingWitness { - type Output = Evaluations>; - - fn index(&self, index: MIPSColumn) -> &Self::Output { - &self.witness.cols[usize::from(index)] - } - } - - // Implemented for decomposable folding compatibility - impl Index for MIPSFoldingWitness { - type Output = Evaluations>; - - /// Map a selector column to the corresponding witness column. - fn index(&self, index: Instruction) -> &Self::Output { - &self.witness.cols[usize::from(index)] - } - } - - // Implementing this so that generic constraints can be used in folding - impl Index for MIPSFoldingWitness { - type Output = Evaluations>; - - /// Map a column alias to the corresponding witness column. - fn index(&self, index: Column) -> &Self::Output { - match index { - Column::Relation(ix) => &self.witness.cols[ix], - Column::DynamicSelector(ix) => &self.witness.cols[N_MIPS_REL_COLS + ix], - _ => panic!("Invalid column type"), - } - } - } - // -- End of indexer implementations - - #[derive(Clone, Debug, PartialEq, Eq, Hash)] - pub struct DecomposableMIPSFoldingConfig; - - impl FoldingColumnTrait for MIPSColumn { - fn is_witness(&self) -> bool { - // All MIPS columns are witness columns - true - } - } - - impl FoldingConfig for DecomposableMIPSFoldingConfig { - type Column = Column; - type Selector = Instruction; - type Challenge = Challenge; - type Curve = Curve; - type Srs = PairingSRS; - type Instance = MIPSFoldingInstance; - type Witness = MIPSFoldingWitness; - type Structure = (); - type Env = DecomposableMIPSFoldingEnvironment; - } -} - -#[cfg(test)] -mod tests { - use crate::legacy::{ - folding::{FoldingInstance, FoldingWitness, *}, - Curve, Fp, Pairing, - }; - use ark_poly::{Evaluations, Radix2EvaluationDomain}; - use folding::{ - expressions::{FoldingColumnTrait, FoldingCompatibleExpr, FoldingCompatibleExprInner}, - FoldingConfig, - }; - use kimchi::{ - circuits::expr::{ - ConstantExprInner, ConstantTerm, Constants, Expr, ExprInner, Literal, Variable, - }, - curve::KimchiCurve, - }; - use poly_commitment::kzg::PairingSRS; - use std::ops::Index; - - #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)] - enum TestColumn { - X, - Y, - Z, - } - - #[derive(Clone, Debug, PartialEq, Eq, Hash)] - struct TestConfig; - - type TestWitness = kimchi_msm::witness::Witness<3, T>; - type TestFoldingWitness = FoldingWitness<3, Fp>; - type TestFoldingInstance = FoldingInstance<3, Curve>; - type TestFoldingEnvironment = FoldingEnvironment<3, TestConfig, ()>; - - impl Index for TestFoldingWitness { - type Output = Evaluations>; - - fn index(&self, index: TestColumn) -> &Self::Output { - &self.witness[index] - } - } - - impl FoldingColumnTrait for TestColumn { - fn is_witness(&self) -> bool { - true - } - } - - impl Index for TestWitness { - type Output = T; - fn index(&self, index: TestColumn) -> &Self::Output { - match index { - TestColumn::X => &self.cols[0], - TestColumn::Y => &self.cols[1], - TestColumn::Z => &self.cols[2], - } - } - } - - impl FoldingConfig for TestConfig { - type Column = TestColumn; - type Challenge = Challenge; - type Selector = (); - type Curve = Curve; - type Srs = PairingSRS; - type Instance = TestFoldingInstance; - type Witness = TestFoldingWitness; - type Structure = (); - type Env = TestFoldingEnvironment; - } - - #[test] - fn test_conversion() { - use super::*; - use kimchi::circuits::berkeley_columns::BerkeleyChallengeTerm; - - // Check that the conversion from ChallengeTerm to Challenge works as expected - assert_eq!(Challenge::Beta, BerkeleyChallengeTerm::Beta.into()); - assert_eq!(Challenge::Gamma, BerkeleyChallengeTerm::Gamma.into()); - assert_eq!( - Challenge::JointCombiner, - BerkeleyChallengeTerm::JointCombiner.into() - ); - - // Create my special constants - let constants = Constants { - endo_coefficient: Fp::from(3), - mds: &Curve::sponge_params().mds, - zk_rows: 0, - }; - - // Define variables to be used in larger expressions - let x = Expr::Atom(ExprInner::Cell::< - ConstantExprInner, - TestColumn, - >(Variable { - col: TestColumn::X, - row: CurrOrNext::Curr, - })); - let y = Expr::Atom(ExprInner::Cell::< - ConstantExprInner, - TestColumn, - >(Variable { - col: TestColumn::Y, - row: CurrOrNext::Curr, - })); - let z = Expr::Atom(ExprInner::Cell::< - ConstantExprInner, - TestColumn, - >(Variable { - col: TestColumn::Z, - row: CurrOrNext::Curr, - })); - let endo = Expr::Atom(ExprInner::< - ConstantExprInner, - TestColumn, - >::Constant(ConstantExprInner::Constant( - ConstantTerm::EndoCoefficient, - ))); - - // Define variables with folding expressions - let x_f = - FoldingCompatibleExpr::::Atom(FoldingCompatibleExprInner::Cell(Variable { - col: TestColumn::X, - row: CurrOrNext::Curr, - })); - let y_f = - FoldingCompatibleExpr::::Atom(FoldingCompatibleExprInner::Cell(Variable { - col: TestColumn::Y, - row: CurrOrNext::Curr, - })); - let z_f = - FoldingCompatibleExpr::::Atom(FoldingCompatibleExprInner::Cell(Variable { - col: TestColumn::Z, - row: CurrOrNext::Curr, - })); - - // Check conversion of general expressions - let xyz = x.clone() * y * z; - let xyz_f = FoldingCompatibleExpr::::Mul( - Box::new(FoldingCompatibleExpr::::Mul( - Box::new(x_f.clone()), - Box::new(y_f), - )), - Box::new(z_f), - ); - assert_eq!(FoldingCompatibleExpr::::from(xyz), xyz_f); - - let x_endo = x + endo; - let x_endo_f = FoldingCompatibleExpr::::Add( - Box::new(x_f), - Box::new(FoldingCompatibleExpr::::Atom( - FoldingCompatibleExprInner::Constant(constants.endo_coefficient), - )), - ); - let x_endo_lit = x_endo.as_literal(&constants); - assert_eq!( - FoldingCompatibleExpr::::from(x_endo_lit), - x_endo_f - ); - } -} diff --git a/o1vm/src/legacy/main.rs b/o1vm/src/legacy/main.rs deleted file mode 100644 index 1435d47312..0000000000 --- a/o1vm/src/legacy/main.rs +++ /dev/null @@ -1,366 +0,0 @@ -use ark_ff::UniformRand; -use clap::Parser; -use folding::decomposable_folding::DecomposableFoldingScheme; -use kimchi::o1_utils; -use kimchi_msm::{proof::ProofInputs, prover::prove, verifier::verify, witness::Witness}; -use log::debug; -use o1vm::{ - cannon::{self, Start, State}, - cli, elf_loader, - interpreters::{ - keccak::{ - column::{Steps, N_ZKVM_KECCAK_COLS, N_ZKVM_KECCAK_REL_COLS, N_ZKVM_KECCAK_SEL_COLS}, - environment::KeccakEnv, - }, - mips::{ - column::{N_MIPS_COLS, N_MIPS_REL_COLS, N_MIPS_SEL_COLS, SCRATCH_SIZE}, - constraints as mips_constraints, - interpreter::Instruction, - witness::{self as mips_witness}, - }, - }, - legacy::{ - folding::mips::DecomposableMIPSFoldingConfig, - proof, - trace::{ - keccak::DecomposedKeccakTrace, mips::DecomposedMIPSTrace, DecomposableTracer, Foldable, - Tracer, - }, - BaseSponge, Fp, OpeningProof, ScalarSponge, - }, - lookups::LookupTableIDs, - preimage_oracle::{NullPreImageOracle, PreImageOracle, PreImageOracleT}, - test_preimage_read, -}; -use poly_commitment::SRS as _; -use std::{ - cmp::Ordering, collections::HashMap, fs::File, io::BufReader, path::Path, process::ExitCode, -}; -use strum::IntoEnumIterator; - -/// Domain size shared by the Keccak evaluations, MIPS evaluation and main -/// program. -pub const DOMAIN_SIZE: usize = 1 << 15; - -pub fn cannon_main(args: cli::cannon::RunArgs) { - let configuration: cannon::VmConfiguration = args.vm_cfg.into(); - - let file = - File::open(&configuration.input_state_file).expect("Error opening input state file "); - - let reader = BufReader::new(file); - // Read the JSON contents of the file as an instance of `State`. - let state: State = serde_json::from_reader(reader).expect("Error reading input state file"); - - let meta = &configuration.metadata_file.as_ref().map(|f| { - let meta_file = - File::open(f).unwrap_or_else(|_| panic!("Could not open metadata file {}", f)); - serde_json::from_reader(BufReader::new(meta_file)) - .unwrap_or_else(|_| panic!("Error deserializing metadata file {}", f)) - }); - - // Initialize some data used for statistical computations - let start = Start::create(state.step as usize); - - let domain = kimchi::circuits::domains::EvaluationDomains::::create(DOMAIN_SIZE).unwrap(); - - let mut rng = o1_utils::tests::make_test_rng(None); - - let srs = { - // FIXME: toxic waste is generated in `create`. This is unsafe for prod. - let srs = poly_commitment::kzg::PairingSRS::create(DOMAIN_SIZE); - srs.get_lagrange_basis(domain.d1); - srs - }; - - // Initialize the environments - // The Keccak environment is extracted inside the loop - let mut mips_wit_env = match configuration.host.clone() { - Some(host) => { - let mut po = PreImageOracle::create(host); - let _child = po.start(); - mips_witness::Env::>::create( - cannon::PAGE_SIZE as usize, - state, - Box::new(po), - ) - } - None => { - debug!("No preimage oracle provided 🤞"); - // warning: the null preimage oracle has no data and will crash the program if used - mips_witness::Env::>::create( - cannon::PAGE_SIZE as usize, - state, - Box::new(NullPreImageOracle), - ) - } - }; - - let mut mips_con_env = mips_constraints::Env::::default(); - // The keccak environment is extracted inside the loop - - // Initialize the circuits. Includes pre-folding witnesses. - let mut mips_trace = DecomposedMIPSTrace::new(DOMAIN_SIZE, &mut mips_con_env); - let mut keccak_trace = DecomposedKeccakTrace::new(DOMAIN_SIZE, &mut KeccakEnv::::default()); - - let _mips_folding = { - DecomposableFoldingScheme::::new( - >::folding_constraints(&mips_trace), - vec![], - &srs, - domain.d1, - &(), - ) - }; - - // Initialize folded instances of the sub circuits - let mut mips_folded_instance = HashMap::new(); - for instr in Instruction::iter().flat_map(|x| x.into_iter()) { - mips_folded_instance.insert( - instr, - ProofInputs::::default(), - ); - } - - let mut keccak_folded_instance = HashMap::new(); - for step in Steps::iter().flat_map(|x| x.into_iter()) { - keccak_folded_instance.insert( - step, - ProofInputs::::default(), - ); - } - - while !mips_wit_env.halt { - let instr = mips_wit_env.step(&configuration, meta, &start); - - if let Some(ref mut keccak_env) = mips_wit_env.keccak_env { - // Run all steps of hash - while keccak_env.step.is_some() { - // Get the current standardize step that is being executed - let step = keccak_env.selector(); - // Run the interpreter, which sets the witness columns - keccak_env.step(); - // Add the witness row to the Keccak circuit for this step - keccak_trace.push_row(step, &keccak_env.witness_env.witness.cols); - - // If the witness is full, fold it and reset the pre-folding witness - if keccak_trace.number_of_rows(step) == DOMAIN_SIZE { - // Set to zero all selectors except for the one corresponding to the current instruction - keccak_trace.set_selector_column::(step, DOMAIN_SIZE); - proof::fold::( - domain, - &srs, - keccak_folded_instance.get_mut(&step).unwrap(), - &keccak_trace[step].witness, - ); - keccak_trace.reset(step); - } - } - // When the Keccak interpreter is finished, we can reset the environment - mips_wit_env.keccak_env = None; - } - - // TODO: unify witness of MIPS to include scratch state, instruction counter, and error - for i in 0..N_MIPS_REL_COLS { - match i.cmp(&SCRATCH_SIZE) { - Ordering::Less => mips_trace.trace.get_mut(&instr).unwrap().witness.cols[i] - .push(mips_wit_env.scratch_state[i]), - Ordering::Equal => mips_trace.trace.get_mut(&instr).unwrap().witness.cols[i] - .push(Fp::from(mips_wit_env.instruction_counter)), - Ordering::Greater => { - // TODO: error - mips_trace.trace.get_mut(&instr).unwrap().witness.cols[i] - .push(Fp::rand(&mut rand::rngs::OsRng)) - } - } - } - - if mips_trace.number_of_rows(instr) == DOMAIN_SIZE { - // Set to zero all selectors except for the one corresponding to the current instruction - mips_trace.set_selector_column::(instr, DOMAIN_SIZE); - proof::fold::( - domain, - &srs, - mips_folded_instance.get_mut(&instr).unwrap(), - &mips_trace[instr].witness, - ); - mips_trace.reset(instr); - } - } - - // Pad any possible remaining rows if the execution was not a multiple of the domain size - for instr in Instruction::iter().flat_map(|x| x.into_iter()) { - // Start by padding with the first row - let needs_folding = mips_trace.pad_dummy(instr) != 0; - if needs_folding { - // Then set the selector columns (all of them, none has selectors set) - mips_trace.set_selector_column::(instr, DOMAIN_SIZE); - - // Finally fold instance - proof::fold::( - domain, - &srs, - mips_folded_instance.get_mut(&instr).unwrap(), - &mips_trace[instr].witness, - ); - } - } - for step in Steps::iter().flat_map(|x| x.into_iter()) { - let needs_folding = keccak_trace.pad_dummy(step) != 0; - if needs_folding { - keccak_trace.set_selector_column::(step, DOMAIN_SIZE); - - proof::fold::( - domain, - &srs, - keccak_folded_instance.get_mut(&step).unwrap(), - &keccak_trace[step].witness, - ); - } - } - - { - // MIPS - for instr in Instruction::iter().flat_map(|x| x.into_iter()) { - // Prove only if the instruction was executed - // and if the number of constraints is nonzero (otherwise quotient polynomial cannot be created) - if mips_trace.in_circuit(instr) && !mips_trace[instr].constraints.is_empty() { - debug!("Checking MIPS circuit {:?}", instr); - let mips_result = prove::< - _, - OpeningProof, - BaseSponge, - ScalarSponge, - _, - N_MIPS_COLS, - N_MIPS_REL_COLS, - N_MIPS_SEL_COLS, - 0, - LookupTableIDs, - >( - domain, - &srs, - &mips_trace[instr].constraints, - Box::new([]), - mips_folded_instance[&instr].clone(), - &mut rng, - ); - let mips_proof = mips_result.unwrap(); - debug!("Generated a MIPS {:?} proof:", instr); - let mips_verifies = verify::< - _, - OpeningProof, - BaseSponge, - ScalarSponge, - N_MIPS_COLS, - N_MIPS_REL_COLS, - N_MIPS_SEL_COLS, - 0, - 0, - LookupTableIDs, - >( - domain, - &srs, - &mips_trace[instr].constraints, - Box::new([]), - &mips_proof, - Witness::zero_vec(DOMAIN_SIZE), - ); - if mips_verifies { - debug!("The MIPS {:?} proof verifies\n", instr) - } else { - debug!("The MIPS {:?} proof doesn't verify\n", instr) - } - } - } - } - - { - // KECCAK - // FIXME: when folding is applied, the error term will be created to satisfy the folded witness - for step in Steps::iter().flat_map(|x| x.into_iter()) { - // Prove only if the instruction was executed - if keccak_trace.in_circuit(step) { - debug!("Checking Keccak circuit {:?}", step); - let keccak_result = prove::< - _, - OpeningProof, - BaseSponge, - ScalarSponge, - _, - N_ZKVM_KECCAK_COLS, - N_ZKVM_KECCAK_REL_COLS, - N_ZKVM_KECCAK_SEL_COLS, - 0, - LookupTableIDs, - >( - domain, - &srs, - &keccak_trace[step].constraints, - Box::new([]), - keccak_folded_instance[&step].clone(), - &mut rng, - ); - let keccak_proof = keccak_result.unwrap(); - debug!("Generated a Keccak {:?} proof:", step); - let keccak_verifies = verify::< - _, - OpeningProof, - BaseSponge, - ScalarSponge, - N_ZKVM_KECCAK_COLS, - N_ZKVM_KECCAK_REL_COLS, - N_ZKVM_KECCAK_SEL_COLS, - 0, - 0, - LookupTableIDs, - >( - domain, - &srs, - &keccak_trace[step].constraints, - Box::new([]), - &keccak_proof, - Witness::zero_vec(DOMAIN_SIZE), - ); - if keccak_verifies { - debug!("The Keccak {:?} proof verifies\n", step) - } else { - debug!("The Keccak {:?} proof doesn't verify\n", step) - } - } - } - } - - // TODO: Logic -} - -fn gen_state_json(arg: cli::cannon::GenStateJsonArgs) -> Result<(), String> { - let path = Path::new(&arg.input); - let state = elf_loader::parse_elf(elf_loader::Architecture::Mips, path)?; - let file = File::create(&arg.output).expect("Error creating output state file"); - serde_json::to_writer_pretty(file, &state).expect("Error writing output state file"); - Ok(()) -} - -pub fn main() -> ExitCode { - env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); - let args = cli::Commands::parse(); - match args { - cli::Commands::Cannon(args) => match args { - cli::cannon::Cannon::Run(args) => { - cannon_main(args); - } - cli::cannon::Cannon::TestPreimageRead(args) => { - test_preimage_read::main(args); - } - cli::cannon::Cannon::GenStateJson(args) => { - gen_state_json(args).expect("Error generating state.json"); - } - }, - } - ExitCode::SUCCESS -} diff --git a/o1vm/src/legacy/mod.rs b/o1vm/src/legacy/mod.rs deleted file mode 100644 index d4b9e8027d..0000000000 --- a/o1vm/src/legacy/mod.rs +++ /dev/null @@ -1,37 +0,0 @@ -//! This submodule provides the legacy flavor/interface of the o1vm, and is not -//! supposed to be used anymore. -//! -//! This o1vm flavor was supposed to use the [folding](folding) library defined -//! in [folding](folding), which consists of reducing all constraints to degree -//! 2, in addition to the `ivc` library defined in this monorepo to support long -//! traces. -//! The goal of this flavor was to support the curve `bn254`. For the time -//! being, the project has been stopped in favor of the pickles version defined -//! in [crate::pickles] and we do not aim to provide any support for now. -//! -//! You can still run the legacy flavor by using: -//! -//! ```bash -//! O1VM_FLAVOR=legacy bash run-code.sh -//! ``` - -use ark_ec::bn::Bn; -use mina_poseidon::{ - constants::PlonkSpongeConstantsKimchi, - sponge::{DefaultFqSponge, DefaultFrSponge}, -}; -use poly_commitment::kzg::KZGProof; - -/// Scalar field of BN254 -pub type Fp = ark_bn254::Fr; -/// Elliptic curve group of BN254 -pub type Curve = ark_bn254::G1Affine; -pub type Pairing = ark_bn254::Bn254; -pub type BaseSponge = DefaultFqSponge; -pub type ScalarSponge = DefaultFrSponge; -pub type SpongeParams = PlonkSpongeConstantsKimchi; -pub type OpeningProof = KZGProof>; - -pub mod folding; -pub mod proof; -pub mod trace; diff --git a/o1vm/src/legacy/proof.rs b/o1vm/src/legacy/proof.rs deleted file mode 100644 index 5e352f4332..0000000000 --- a/o1vm/src/legacy/proof.rs +++ /dev/null @@ -1,108 +0,0 @@ -use crate::lookups::LookupTableIDs; -use ark_poly::{Evaluations, Radix2EvaluationDomain as D}; -use kimchi::{circuits::domains::EvaluationDomains, curve::KimchiCurve, plonk_sponge::FrSponge}; -use kimchi_msm::{proof::ProofInputs, witness::Witness}; -use mina_poseidon::{sponge::ScalarChallenge, FqSponge}; -use poly_commitment::{commitment::absorb_commitment, OpenProof, SRS as _}; -use rayon::iter::{ - IndexedParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator, -}; - -/// FIXME: DUMMY FOLD FUNCTION THAT ONLY KEEPS THE LAST INSTANCE -pub fn fold< - const N: usize, - G: KimchiCurve, - OpeningProof: OpenProof, - EFqSponge: Clone + FqSponge, - EFrSponge: FrSponge, ->( - domain: EvaluationDomains, - srs: &OpeningProof::SRS, - accumulator: &mut ProofInputs, - inputs: &Witness>, -) where - >::SRS: std::marker::Sync, -{ - let commitments = { - inputs - .par_iter() - .map(|evals: &Vec| { - let evals = Evaluations::>::from_vec_and_domain( - evals.clone(), - domain.d1, - ); - srs.commit_evaluations_non_hiding(domain.d1, &evals) - }) - .collect::>() - }; - let mut fq_sponge = EFqSponge::new(G::other_curve_sponge_params()); - - commitments.into_iter().for_each(|comm| { - absorb_commitment(&mut fq_sponge, &comm); - }); - - // TODO: fold mvlookups as well - accumulator - .evaluations - .par_iter_mut() - .zip(inputs.par_iter()) - .for_each(|(accumulator, inputs)| { - accumulator - .par_iter_mut() - .zip(inputs.par_iter()) - .for_each(|(accumulator, input)| *accumulator = *input); - }); -} - -#[allow(dead_code)] -/// This function folds the witness of the current circuit with the accumulated Keccak instance -/// with a random combination using a scaling challenge -// FIXME: This will have to be adapted when the folding library is available -fn old_fold< - const N: usize, - G: KimchiCurve, - OpeningProof: OpenProof, - EFqSponge: Clone + FqSponge, ->( - domain: EvaluationDomains, - srs: &OpeningProof::SRS, - accumulator: &mut ProofInputs, - inputs: &Witness>, -) where - >::SRS: std::marker::Sync, -{ - let commitments = { - inputs - .par_iter() - .map(|evals: &Vec| { - let evals = Evaluations::>::from_vec_and_domain( - evals.clone(), - domain.d1, - ); - srs.commit_evaluations_non_hiding(domain.d1, &evals) - }) - .collect::>() - }; - let mut fq_sponge = EFqSponge::new(G::other_curve_sponge_params()); - - commitments.into_iter().for_each(|comm| { - absorb_commitment(&mut fq_sponge, &comm); - }); - - let scaling_challenge = ScalarChallenge(fq_sponge.challenge()); - let (_, endo_r) = G::endos(); - let scaling_challenge = scaling_challenge.to_field(endo_r); - // TODO: fold mvlookups as well - accumulator - .evaluations - .par_iter_mut() - .zip(inputs.par_iter()) - .for_each(|(accumulator, inputs)| { - accumulator - .par_iter_mut() - .zip(inputs.par_iter()) - .for_each(|(accumulator, input)| { - *accumulator = *input + scaling_challenge * *accumulator - }); - }); -} diff --git a/o1vm/src/legacy/tests.rs b/o1vm/src/legacy/tests.rs deleted file mode 100644 index bce2990d86..0000000000 --- a/o1vm/src/legacy/tests.rs +++ /dev/null @@ -1,742 +0,0 @@ -use crate::{ - interpreters::mips::{ - column::{N_MIPS_REL_COLS, SCRATCH_SIZE}, - constraints::Env as CEnv, - interpreter::{debugging::InstructionParts, interpret_itype}, - ITypeInstruction, - }, - legacy::{ - folding::{ - Challenge, DecomposedMIPSTrace, FoldingEnvironment, FoldingInstance, FoldingWitness, - }, - trace::Trace, - }, - BaseSponge, Curve, -}; - -use ark_ff::One; -use ark_poly::{EvaluationDomain as _, Evaluations, Radix2EvaluationDomain as D}; -use folding::{expressions::FoldingCompatibleExpr, Alphas, FoldingConfig, FoldingScheme}; -use itertools::Itertools; -use kimchi::{curve::KimchiCurve, o1_utils}; -use kimchi_msm::{columns::Column, witness::Witness}; -use mina_poseidon::FqSponge; -use poly_commitment::{commitment::absorb_commitment, kzg::PairingSRS, PolyComm, SRS as _}; -use rand::{CryptoRng, Rng, RngCore}; -use rayon::iter::{IntoParallelIterator as _, ParallelIterator as _}; - -pub mod mips { - fn make_random_witness_for_addiu( - domain_size: usize, - rng: &mut RNG, - ) -> Witness> - where - RNG: RngCore + CryptoRng, - { - let mut dummy_env = dummy_env(rng); - let instr = ITypeInstruction::AddImmediateUnsigned; - let a = std::array::from_fn(|_| Vec::with_capacity(domain_size)); - let mut witness = Witness { cols: Box::new(a) }; - // Building trace for AddImmediateUnsigned - (0..domain_size).for_each(|_i| { - // Registers should not conflict because RAMlookup does not support - // reading from/writing into the same register in the same - // instruction - let reg_src = rng.gen_range(0..10); - let reg_dest = rng.gen_range(10..20); - // we simulate always the same instruction, with some random values - // and registers - write_instruction( - &mut dummy_env, - InstructionParts { - op_code: 0b001001, - rs: reg_src, // source register - rt: reg_dest, // destination register - // The rest is the immediate value - rd: rng.gen_range(0..32), - shamt: rng.gen_range(0..32), - funct: rng.gen_range(0..64), - }, - ); - interpret_itype(&mut dummy_env, instr); - - for j in 0..SCRATCH_SIZE { - witness.cols[j].push(dummy_env.scratch_state[j]); - } - witness.cols[SCRATCH_SIZE].push(Fp::from(dummy_env.instruction_counter)); - witness.cols[SCRATCH_SIZE + 1].push(Fp::from(0)); - dummy_env.instruction_counter += 1; - - dummy_env.reset_scratch_state() - }); - // sanity check - witness - .cols - .iter() - .for_each(|x| assert_eq!(x.len(), domain_size)); - witness - } - - fn build_folding_instance( - witness: &FoldingWitness, - fq_sponge: &mut BaseSponge, - domain: D, - srs: &PairingSRS, - ) -> FoldingInstance { - let commitments: Witness> = (&witness.witness) - .into_par_iter() - .map(|w| srs.commit_evaluations_non_hiding(domain, w)) - .collect(); - - // Absorbing commitments - (&commitments) - .into_iter() - .for_each(|c| absorb_commitment(fq_sponge, c)); - - let commitments: [Curve; N_MIPS_REL_COLS] = commitments - .into_iter() - .map(|c| c.elems[0]) - .collect_vec() - .try_into() - .unwrap(); - - let beta = fq_sponge.challenge(); - let gamma = fq_sponge.challenge(); - let joint_combiner = fq_sponge.challenge(); - let alpha = fq_sponge.challenge(); - let challenges = [beta, gamma, joint_combiner]; - let alphas = Alphas::new(alpha); - let blinder = Fp::one(); - - FoldingInstance { - commitments, - challenges, - alphas, - blinder, - } - } - - // Manually change the number of constraints if they are modififed in the - // interpreter - // FIXME: can be moved up, in interpreters::mips, wihthout using the - // DecomposedMIPSTrace - #[test] - fn test_mips_number_constraints() { - let domain_size = 1 << 8; - - // Initialize the environment and run the interpreter - let mut constraints_env = Env:: { - scratch_state_idx: 0, - constraints: Vec::new(), - lookups: Vec::new(), - }; - - // Keep track of the constraints and lookups of the sub-circuits - let mips_circuit = DecomposedMIPSTrace::new(domain_size, &mut constraints_env); - - let assert_num_constraints = |instr: &Instruction, num: usize| { - assert_eq!( - mips_circuit.trace.get(instr).unwrap().constraints.len(), - num - ) - }; - - let mut i = 0; - for instr in Instruction::iter().flat_map(|x| x.into_iter()) { - match instr { - RType(rtype) => match rtype { - JumpRegister | SyscallExitGroup | Sync => assert_num_constraints(&instr, 1), - ShiftLeftLogical - | ShiftRightLogical - | ShiftRightArithmetic - | ShiftLeftLogicalVariable - | ShiftRightLogicalVariable - | ShiftRightArithmeticVariable - | JumpAndLinkRegister - | SyscallReadHint - | MoveFromHi - | MoveFromLo - | MoveToLo - | MoveToHi - | Add - | AddUnsigned - | Sub - | SubUnsigned - | And - | Or - | Xor - | Nor - | SetLessThan - | SetLessThanUnsigned - | MultiplyToRegister - | CountLeadingOnes - | CountLeadingZeros => assert_num_constraints(&instr, 4), - MoveZero | MoveNonZero => assert_num_constraints(&instr, 6), - SyscallReadOther | SyscallWriteHint | SyscallWriteOther | Multiply - | MultiplyUnsigned | Div | DivUnsigned => assert_num_constraints(&instr, 7), - SyscallOther => assert_num_constraints(&instr, 11), - SyscallMmap => assert_num_constraints(&instr, 12), - SyscallFcntl | SyscallReadPreimage => assert_num_constraints(&instr, 23), - // TODO: update SyscallReadPreimage to 31 when using self.equal() - SyscallWritePreimage => assert_num_constraints(&instr, 31), - }, - JType(jtype) => match jtype { - Jump => assert_num_constraints(&instr, 1), - JumpAndLink => assert_num_constraints(&instr, 4), - }, - IType(itype) => match itype { - BranchLeqZero | BranchGtZero | BranchLtZero | BranchGeqZero | Store8 - | Store16 => assert_num_constraints(&instr, 1), - BranchEq | BranchNeq | Store32 => assert_num_constraints(&instr, 3), - AddImmediate - | AddImmediateUnsigned - | SetLessThanImmediate - | SetLessThanImmediateUnsigned - | AndImmediate - | OrImmediate - | XorImmediate - | LoadUpperImmediate - | Load8 - | Load16 - | Load32 - | Load8Unsigned - | Load16Unsigned - | Store32Conditional => assert_num_constraints(&instr, 4), - LoadWordLeft | LoadWordRight | StoreWordLeft | StoreWordRight => { - assert_num_constraints(&instr, 13) - } - }, - } - i += 1; - } - assert_eq!( - i, - RTypeInstruction::COUNT + JTypeInstruction::COUNT + ITypeInstruction::COUNT - ); - } - - #[test] - fn test_folding_mips_addiu_constraint() { - let mut fq_sponge: BaseSponge = FqSponge::new(Curve::other_curve_sponge_params()); - let mut rng = o1_utils::tests::make_test_rng(None); - - let domain_size = 1 << 3; - let domain: D = D::::new(domain_size).unwrap(); - - let srs = PairingSRS::::create(domain_size); - srs.get_lagrange_basis(domain); - - // Generating constraints - let constraints = { - // Initialize the environment and run the interpreter - let mut constraints_env = CEnv::::default(); - interpret_itype(&mut constraints_env, ITypeInstruction::AddImmediateUnsigned); - constraints_env.constraints - }; - // We have 3 constraints here. We can select only one. - // println!("Nb of constraints: {:?}", constraints.len()); - // - // You can select one of the constraints if you want to fold only one - // - // constraints - // .iter() - // .for_each(|constraint| - // println!("Degree: {:?}", constraint.degree(1, 0))); - // - // Selecting the first constraint for testing - // let constraints - // = vec![constraints.first().unwrap().clone()]; - - let witness_one = make_random_witness_for_addiu(domain_size, &mut rng); - let witness_two = make_random_witness_for_addiu(domain_size, &mut rng); - // FIXME: run PlonK here to check the it is satisfied. - - // Now, we will fold and use the folding scheme to only prove the - // aggregation instead of the individual instances - - #[derive(Clone, Debug, PartialEq, Eq, Hash)] - struct MIPSFoldingConfig; - - let trace_one: Trace = Trace { - domain_size, - // FIXME: do not use clone - witness: witness_one.clone(), - constraints: constraints.clone(), - lookups: vec![], - }; - - impl FoldingConfig for MIPSFoldingConfig { - type Column = Column; - type Selector = (); - type Challenge = Challenge; - type Curve = Curve; - type Srs = PairingSRS; - type Instance = FoldingInstance; - type Witness = FoldingWitness; - // The structure must a provable Trace. Here we use a single - // instruction trace - type Structure = Trace; - type Env = FoldingEnvironment< - N_MIPS_REL_COLS, - MIPSFoldingConfig, - Trace, - >; - } - - let folding_witness_one: FoldingWitness = { - let witness_one = (&witness_one) - .into_par_iter() - .map(|w| Evaluations::from_vec_and_domain(w.to_vec(), domain)) - .collect(); - FoldingWitness { - witness: witness_one, - } - }; - - let folding_witness_two: FoldingWitness = { - let witness_two = (&witness_two) - .into_par_iter() - .map(|w| Evaluations::from_vec_and_domain(w.to_vec(), domain)) - .collect(); - FoldingWitness { - witness: witness_two, - } - }; - - let folding_instance_one = - build_folding_instance(&folding_witness_one, &mut fq_sponge, domain, &srs); - let folding_instance_two = - build_folding_instance(&folding_witness_two, &mut fq_sponge, domain, &srs); - - let folding_compat_constraints: Vec> = constraints - .iter() - .map(|x| FoldingCompatibleExpr::from(x.clone())) - .collect::>(); - - let (folding_scheme, _) = FoldingScheme::::new( - folding_compat_constraints, - &srs, - domain, - &trace_one, - ); - - let one = (folding_instance_one, folding_witness_one); - let two = (folding_instance_two, folding_witness_two); - let (_relaxed_instance, _relatex_witness) = folding_scheme - .fold_instance_witness_pair(one, two, &mut fq_sponge) - .pair(); - - // FIXME: add IVC - } -} - -pub mod keccak { - fn create_trace_all_steps(domain_size: usize, rng: &mut StdRng) -> DecomposedKeccakTrace { - let mut trace = >>::new( - domain_size, - &mut KeccakEnv::::default(), - ); - { - // 1 block preimages for Sponge(Absorb(Only)), Round(0), and Sponge(Squeeze) - for _ in 0..domain_size { - // random 1-block preimages - let bytelength = rng.gen_range(0..RATE_IN_BYTES); - let preimage: Vec = (0..bytelength).map(|_| rng.gen()).collect(); - // Initialize the environment and run the interpreter - let mut keccak_env = KeccakEnv::::new(0, &preimage); - while keccak_env.step.is_some() { - let step = keccak_env.step.unwrap(); - // Create the relation witness columns - keccak_env.step(); - match step { - Sponge(Absorb(Only)) | Round(0) | Sponge(Squeeze) => { - // Add the witness row to the circuit - trace.push_row(step, &keccak_env.witness_env.witness.cols); - } - _ => {} - } - } - } - // Check there is no need for padding because we reached domain_size rows for these selectors - assert!(trace.is_full(Sponge(Absorb(Only)))); - assert!(trace.is_full(Round(0))); - assert!(trace.is_full(Sponge(Squeeze))); - - // Add the columns of the selectors to the circuit - trace.set_selector_column::(Sponge(Absorb(Only)), domain_size); - trace.set_selector_column::(Round(0), domain_size); - trace.set_selector_column::(Sponge(Squeeze), domain_size); - } - { - // 3 block preimages for Sponge(Absorb(First)), Sponge(Absorb(Middle)), and Sponge(Absorb(Last)) - for _ in 0..domain_size { - // random 3-block preimages - let bytelength = rng.gen_range(2 * RATE_IN_BYTES..3 * RATE_IN_BYTES); - let preimage: Vec = (0..bytelength).map(|_| rng.gen()).collect(); - // Initialize the environment and run the interpreter - let mut keccak_env = KeccakEnv::::new(0, &preimage); - while keccak_env.step.is_some() { - let step = keccak_env.step.unwrap(); - // Create the relation witness columns - keccak_env.step(); - match step { - Sponge(Absorb(First)) | Sponge(Absorb(Middle)) | Sponge(Absorb(Last)) => { - // Add the witness row to the circuit - trace.push_row(step, &keccak_env.witness_env.witness.cols); - } - _ => {} - } - } - } - // Check there is no need for padding because we reached domain_size rows for these selectors - assert!(trace.is_full(Sponge(Absorb(First)))); - assert!(trace.is_full(Sponge(Absorb(Middle)))); - assert!(trace.is_full(Sponge(Absorb(Last)))); - - // Add the columns of the selectors to the circuit - trace.set_selector_column::(Sponge(Absorb(First)), domain_size); - trace - .set_selector_column::(Sponge(Absorb(Middle)), domain_size); - trace.set_selector_column::(Sponge(Absorb(Last)), domain_size); - trace - } - } - - // Prover/Verifier test includidng the Keccak constraints - #[test] - fn test_keccak_prover_constraints() { - // guaranteed to have at least 30MB of stack - stacker::grow(30 * 1024 * 1024, || { - let mut rng = o1_utils::tests::make_test_rng(None); - let domain_size = 1 << 8; - - // Generate 3 blocks of preimage data - let bytelength = rng.gen_range(2 * RATE_IN_BYTES..RATE_IN_BYTES * 3); - let preimage: Vec = (0..bytelength).map(|_| rng.gen()).collect(); - - // Initialize the environment and run the interpreter - let mut keccak_env = KeccakEnv::::new(0, &preimage); - - // Keep track of the constraints and lookups of the sub-circuits - let mut keccak_circuit = - >>::new( - domain_size, - &mut keccak_env, - ); - - while keccak_env.step.is_some() { - let step = keccak_env.selector(); - - // Run the interpreter, which sets the witness columns - keccak_env.step(); - - // Add the witness row to the circuit - keccak_circuit.push_row(step, &keccak_env.witness_env.witness.cols); - } - keccak_circuit.pad_witnesses(); - - for step in Steps::iter().flat_map(|x| x.into_iter()) { - if keccak_circuit.in_circuit(step) { - test_completeness_generic_no_lookups::< - N_ZKVM_KECCAK_COLS, - N_ZKVM_KECCAK_REL_COLS, - N_ZKVM_KECCAK_SEL_COLS, - 0, - _, - >( - keccak_circuit[step].constraints.clone(), - Box::new([]), - keccak_circuit[step].witness.clone(), - domain_size, - &mut rng, - ); - } - } - }); - } - - fn dummy_constraints() -> BTreeMap>> { - Steps::iter() - .flat_map(|x| x.into_iter()) - .map(|step| { - ( - step, - vec![FoldingCompatibleExpr::::Atom( - FoldingCompatibleExprInner::Constant(Fp::zero()), - )], - ) - }) - .collect() - } - - // (Instance, Witness) - type KeccakFoldingSide = ( - ::Instance, - ::Witness, - ); - - // (Step, Left, Right) - type KeccakFoldingPair = (Steps, KeccakFoldingSide, KeccakFoldingSide); - - type KeccakDefaultFqSponge = DefaultFqSponge; - - #[test] - fn heavy_test_keccak_folding() { - use crate::{keccak::folding::KeccakConfig, trace::Foldable, Curve}; - use ark_poly::{EvaluationDomain, Radix2EvaluationDomain as D}; - use folding::{checker::Checker, expressions::FoldingCompatibleExpr}; - use kimchi::curve::KimchiCurve; - use mina_poseidon::FqSponge; - use poly_commitment::kzg::PairingSRS; - - // guaranteed to have at least 30MB of stack - stacker::grow(30 * 1024 * 1024, || { - let mut rng = o1_utils::tests::make_test_rng(None); - let domain_size = 1 << 6; - - let domain = D::::new(domain_size).unwrap(); - let srs = PairingSRS::::create(domain_size); - srs.get_lagrange_basis(domain); - - // Create sponge - let mut fq_sponge = BaseSponge::new(Curve::other_curve_sponge_params()); - - // Create two instances for each selector to be folded - let keccak_trace: [DecomposedKeccakTrace; 2] = - array::from_fn(|_| create_trace_all_steps(domain_size, &mut rng)); - let trace = keccak_trace[0].clone(); - - // Store all constraints indexed by Step - let constraints = >::folding_constraints(&trace); - - // DEFINITIONS OF FUNCTIONS FOR TESTING PURPOSES - - let check_instance_satisfy_constraints = - |constraints: &[FoldingCompatibleExpr], side: &KeccakFoldingSide| { - let (instance, witness) = side; - let checker = Provider::new(instance.clone(), witness.clone()); - constraints.iter().for_each(|c| { - checker.check(c, domain); - }); - }; - - let check_folding = - |left: &KeccakFoldingSide, - right: &KeccakFoldingSide, - constraints: &[FoldingCompatibleExpr], - fq_sponge: &mut KeccakDefaultFqSponge| { - // Create the folding scheme ignoring selectors - let (scheme, final_constraint) = - FoldingScheme::::new(constraints.to_vec(), &srs, domain, &()); - - // Fold both sides and check the constraints ignoring the selector columns - let fout = - scheme.fold_instance_witness_pair(left.clone(), right.clone(), fq_sponge); - - // We should always have 0 as the degree of the constraints, - // without selectors, they are never higher than 2 in Keccak. - assert_eq!(scheme.get_number_of_additional_columns(), 0); - - let checker = ExtendedProvider::new(fout.folded_instance, fout.folded_witness); - checker.check(&final_constraint, domain); - }; - - let check_decomposable_folding_pair = - |step: Option, - left: &KeccakFoldingSide, - right: &KeccakFoldingSide, - scheme: &DecomposableFoldingScheme, - final_constraint: &FoldingCompatibleExpr, - quadri_cols: Option, - fq_sponge: &mut KeccakDefaultFqSponge| { - let fout = scheme.fold_instance_witness_pair( - left.clone(), - right.clone(), - step, - fq_sponge, - ); - - let extra_cols = scheme.get_number_of_additional_columns(); - if let Some(quadri_cols) = quadri_cols { - assert!(extra_cols == quadri_cols); - } - - // Check the constraints on the folded circuit applying selectors - let checker = ExtendedProvider::::new( - fout.folded_instance, - fout.folded_witness, - ); - checker.check(final_constraint, domain); - }; - - let check_decomposable_folding = - |pair: &KeccakFoldingPair, - constraints: BTreeMap>>, - common_constraints: Vec>, - quadri_cols: Option, - fq_sponge: &mut KeccakDefaultFqSponge| { - let (step, left, right) = pair; - let (dec_scheme, dec_final_constraint) = - DecomposableFoldingScheme::::new( - constraints, - common_constraints, - &srs, - domain, - &(), - ); - // Subcase A: Check the folded circuit of decomposable folding ignoring selectors (None) - check_decomposable_folding_pair( - None, - left, - right, - &dec_scheme, - &dec_final_constraint, - quadri_cols, - fq_sponge, - ); - // Subcase B: Check the folded circuit of decomposable folding applying selectors (Some) - check_decomposable_folding_pair( - Some(*step), - left, - right, - &dec_scheme, - &dec_final_constraint, - quadri_cols, - fq_sponge, - ); - }; - - let check_decomposable_folding_mix = - |steps: (Steps, Steps), fq_sponge: &mut KeccakDefaultFqSponge| { - let (dec_scheme, dec_final_constraint) = - DecomposableFoldingScheme::::new( - constraints.clone(), - vec![], - &srs, - domain, - &(), - ); - let left = { - let fout = dec_scheme.fold_instance_witness_pair( - keccak_trace[0].to_folding_pair(steps.0, fq_sponge, domain, &srs), - keccak_trace[1].to_folding_pair(steps.0, fq_sponge, domain, &srs), - Some(steps.0), - fq_sponge, - ); - let checker = ExtendedProvider::::new( - fout.folded_instance, - fout.folded_witness, - ); - (checker.instance, checker.witness) - }; - let right = { - let fout = dec_scheme.fold_instance_witness_pair( - keccak_trace[0].to_folding_pair(steps.1, fq_sponge, domain, &srs), - keccak_trace[1].to_folding_pair(steps.1, fq_sponge, domain, &srs), - Some(steps.1), - fq_sponge, - ); - let checker = ExtendedProvider::::new( - fout.folded_instance, - fout.folded_witness, - ); - (checker.instance, checker.witness) - }; - let fout = dec_scheme.fold_instance_witness_pair(left, right, None, fq_sponge); - let checker = ExtendedProvider::new(fout.folded_instance, fout.folded_witness); - checker.check(&dec_final_constraint, domain); - }; - - // HERE STARTS THE TESTING - - // Sanity checks that the number of constraints are as expected for each step - assert_eq!(constraints[&Sponge(Absorb(First))].len(), 332); - assert_eq!(constraints[&Sponge(Absorb(Middle))].len(), 232); - assert_eq!(constraints[&Sponge(Absorb(Last))].len(), 374); - assert_eq!(constraints[&Sponge(Absorb(Only))].len(), 474); - assert_eq!(constraints[&Sponge(Squeeze)].len(), 16); - assert_eq!(constraints[&Round(0)].len(), 389); - - // Total number of Keccak constraints of degree higher than 2 (should be 0) - let total_deg_higher_2 = - Steps::iter() - .flat_map(|x| x.into_iter()) - .fold(0, |acc, step| { - acc + trace[step] - .constraints - .iter() - .filter(|c| c.degree(1, 0) > 2) - .count() - }); - assert_eq!(total_deg_higher_2, 0); - - // Check folding constraints of individual steps ignoring selectors - for step in Steps::iter().flat_map(|x| x.into_iter()) { - // BTreeMap with constraints of this step - let mut step_constraints = BTreeMap::new(); - step_constraints.insert(step, constraints[&step].clone()); - - // Create sides for folding - let left = keccak_trace[0].to_folding_pair(step, &mut fq_sponge, domain, &srs); - let right = keccak_trace[1].to_folding_pair(step, &mut fq_sponge, domain, &srs); - - // CASE 0: Check instances satisfy the constraints, without folding them - check_instance_satisfy_constraints(&constraints[&step], &left); - check_instance_satisfy_constraints(&constraints[&step], &right); - - // CASE 1: Check constraints on folded circuit ignoring selectors with `FoldingScheme` - check_folding(&left, &right, &constraints[&step], &mut fq_sponge); - - // CASE 2: Check that `DecomposableFoldingScheme` works when passing the dummy zero constraint - // to each step, and an empty list of common constraints. - let pair = (step, left, right); - check_decomposable_folding( - &pair, - dummy_constraints(), - vec![], - Some(0), - &mut fq_sponge, - ); - - // CASE 3: Using a separate `DecomposableFoldingScheme` for each step, check each step - // constraints using a dummy BTreeMap of `vec[0]` per-step constraints and - // common constraints set to each selector's constraints. - check_decomposable_folding( - &pair, - dummy_constraints(), - step_constraints[&step].clone(), - Some(0), - &mut fq_sponge, - ); - - // CASE 4: Using the same `DecomposableFoldingScheme` for all steps, initialized with a real - // BTreeMap of only the current step, and common constraints set to `vec[]`, check - // the folded circuit - check_decomposable_folding( - &pair, - step_constraints.clone(), - vec![], - None, - &mut fq_sponge, - ); - - // CASE 5: Using the same `DecomposableFoldingScheme` for all steps, initialized with a real - // BTreeMap of constraints per-step, and common constraints set to `vec[]`, check - // the folded circuit - check_decomposable_folding( - &pair, - constraints.clone(), - vec![], - Some(151), - &mut fq_sponge, - ); - } - // CASE 6: Fold mixed steps together and check the final constraints - check_decomposable_folding_mix((Sponge(Absorb(First)), Round(0)), &mut fq_sponge); - }); - } -} diff --git a/o1vm/src/legacy/trace.rs b/o1vm/src/legacy/trace.rs deleted file mode 100644 index f5c34acd10..0000000000 --- a/o1vm/src/legacy/trace.rs +++ /dev/null @@ -1,565 +0,0 @@ -//! This module defines structures and traits to build and manipulate traces. -//! A trace is a collection of data points that represent the execution of a -//! program. -//! Some trace can be seen as "decomposable" in the sense that they can be -//! divided into sub-traces that share the same columns, and sub-traces can be -//! selected using "selectors". - -use crate::{ - legacy::{ - folding::{BaseField, FoldingInstance, FoldingWitness, ScalarField}, - Curve, Pairing, - }, - lookups::Lookup, - E, -}; -use ark_ff::{One, Zero}; -use ark_poly::{Evaluations, Radix2EvaluationDomain as D}; -use folding::{expressions::FoldingCompatibleExpr, Alphas, FoldingConfig}; -use itertools::Itertools; -use kimchi::circuits::berkeley_columns::BerkeleyChallengeTerm; -use kimchi_msm::{columns::Column, witness::Witness}; -use mina_poseidon::sponge::FqSponge; -use poly_commitment::{commitment::absorb_commitment, PolyComm, SRS as _}; -use rayon::{iter::ParallelIterator, prelude::IntoParallelIterator}; -use std::{collections::BTreeMap, ops::Index}; - -/// Implement a trace for a single instruction. -// TODO: we should use the generic traits defined in [kimchi_msm]. -// For now, we want to have this to be able to test the folding library for a -// single instruction. -// It is not recommended to use this in production and it should not be -// maintained in the long term. -#[derive(Clone)] -pub struct Trace { - pub domain_size: usize, - pub witness: Witness>>, - pub constraints: Vec>>, - pub lookups: Vec>>>, -} - -/// Struct representing a circuit execution trace which is decomposable in -/// individual sub-circuits sharing the same columns. -/// It is parameterized by -/// - `N`: the total number of columns (constant), it must equal `N_REL + N_DSEL` -/// - `N_REL`: the number of relation columns (constant), -/// - `N_DSEL`: the number of dynamic selector columns (constant), -/// - `Selector`: an enum representing the different gate behaviours, -/// - `F`: the type of the witness data. -#[derive(Clone)] -pub struct DecomposedTrace { - /// The domain size of the circuit (should coincide with that of the traces) - pub domain_size: usize, - /// The traces are indexed by the selector - /// Inside the witness of the trace for a given selector, - /// - the last N_SEL columns represent the selector columns - /// and only the one for `Selector` should be all ones (the rest of selector columns should be all zeros) - pub trace: BTreeMap>, -} - -// Implementation of [Index] using `C::Selector`` as the index for [DecomposedTrace] to access the trace directly. -impl Index for DecomposedTrace { - type Output = Trace; - - fn index(&self, index: C::Selector) -> &Self::Output { - &self.trace[&index] - } -} - -impl DecomposedTrace -where - usize: From<::Selector>, -{ - /// Returns the number of rows that have been instantiated for the given - /// selector. - /// It is important that the column used is a relation column because - /// selector columns are only instantiated at the very end, so their length - /// could be zero most times. - /// That is the reason that relation columns are located first. - pub fn number_of_rows(&self, opcode: C::Selector) -> usize { - self[opcode].witness.cols[0].len() - } - - /// Returns a boolean indicating whether the witness for the given selector - /// was ever found in the circuit or not. - pub fn in_circuit(&self, opcode: C::Selector) -> bool { - self.number_of_rows(opcode) != 0 - } - - /// Returns whether the witness for the given selector has achieved a number - /// of rows that is equal to the domain size. - pub fn is_full(&self, opcode: C::Selector) -> bool { - self.domain_size == self.number_of_rows(opcode) - } - - /// Resets the witness after folding - pub fn reset(&mut self, opcode: C::Selector) { - (self.trace.get_mut(&opcode).unwrap().witness.cols.as_mut()) - .iter_mut() - .for_each(Vec::clear); - } - - /// Sets the selector column to all ones, and the rest to all zeros - pub fn set_selector_column( - &mut self, - selector: C::Selector, - number_of_rows: usize, - ) { - (N_REL..N).for_each(|i| { - if i == usize::from(selector) { - self.trace.get_mut(&selector).unwrap().witness.cols[i] - .extend((0..number_of_rows).map(|_| ScalarField::::one())) - } else { - self.trace.get_mut(&selector).unwrap().witness.cols[i] - .extend((0..number_of_rows).map(|_| ScalarField::::zero())) - } - }); - } -} - -/// The trait [Foldable] describes structures that can be folded. -/// For that, it requires to be able to implement a way to return a folding -/// instance and a folding witness. -/// It is specialized for the [DecomposedTrace] struct for now and is expected -/// to fold individual instructions, selected with a specific `C::Selector`. -pub trait Foldable { - /// Returns the witness for the given selector as a folding witness and - /// folding instance pair. - /// Note that this function will also absorb all commitments to the columns - /// to coin challenges appropriately. - fn to_folding_pair( - &self, - selector: C::Selector, - fq_sponge: &mut Sponge, - domain: D>, - srs: &poly_commitment::kzg::PairingSRS, - ) -> ( - FoldingInstance, - FoldingWitness>, - ); - - /// Returns a map of constraints that are compatible with folding for each selector - fn folding_constraints(&self) -> BTreeMap>>; -} - -/// Implement the trait Foldable for the structure [DecomposedTrace] -impl, Sponge> - Foldable for DecomposedTrace -where - C::Selector: Into, - Sponge: FqSponge, C::Curve, ScalarField>, - ::Challenge: From, -{ - fn to_folding_pair( - &self, - selector: C::Selector, - fq_sponge: &mut Sponge, - domain: D>, - srs: &poly_commitment::kzg::PairingSRS, - ) -> ( - FoldingInstance, - FoldingWitness>, - ) { - let folding_witness = FoldingWitness { - witness: (&self[selector].witness) - .into_par_iter() - .map(|w| Evaluations::from_vec_and_domain(w.to_vec(), domain)) - .collect(), - }; - - let commitments: Witness> = (&folding_witness.witness) - .into_par_iter() - .map(|w| srs.commit_evaluations_non_hiding(domain, w)) - .collect(); - - // Absorbing commitments - (&commitments) - .into_iter() - .for_each(|c| absorb_commitment(fq_sponge, c)); - - let commitments: [C::Curve; N] = commitments - .into_iter() - .map(|c| c.get_first_chunk()) - .collect_vec() - .try_into() - .unwrap(); - - let beta = fq_sponge.challenge(); - let gamma = fq_sponge.challenge(); - let joint_combiner = fq_sponge.challenge(); - let alpha = fq_sponge.challenge(); - let challenges = [beta, gamma, joint_combiner]; - let alphas = Alphas::new(alpha); - let blinder = ScalarField::::one(); - let instance = FoldingInstance { - commitments, - challenges, - alphas, - blinder, - }; - - (instance, folding_witness) - } - - fn folding_constraints(&self) -> BTreeMap>> { - self.trace - .iter() - .map(|(k, instr)| { - ( - *k, - instr - .constraints - .iter() - .map(|x| FoldingCompatibleExpr::from(x.clone())) - .collect(), - ) - }) - .collect() - } -} - -/// Tracer builds traces for some program executions. -/// The constant type `N_REL` is defined as the maximum number of relation -/// columns the trace can use per row. -/// The type `C` encodes the folding configuration, from which the selector, -/// which encodes the information of the kind of information the trace encodes, -/// and scalar field are derived. Examples of selectors are: -/// - For Keccak, `Step` encodes the row being performed at a time: round, -/// squeeze, padding, etc... -/// - For MIPS, `Instruction` encodes the CPU instruction being executed: add, -/// sub, load, store, etc... -pub trait Tracer { - type Selector; - - /// Initialize a new trace with the given domain size, selector, and environment. - fn init(domain_size: usize, selector: C::Selector, env: &mut Env) -> Self; - - /// Add a witness row to the circuit (only for relation columns) - fn push_row(&mut self, selector: Self::Selector, row: &[ScalarField; N_REL]); - - /// Pad the rows of one opcode with the given row until - /// reaching the domain size if needed. - /// Returns the number of rows that were added. - /// It does not add selector columns. - fn pad_with_row(&mut self, selector: Self::Selector, row: &[ScalarField; N_REL]) -> usize; - - /// Pads the rows of one opcode with zero rows until - /// reaching the domain size if needed. - /// Returns the number of rows that were added. - /// It does not add selector columns. - fn pad_with_zeros(&mut self, selector: Self::Selector) -> usize; - - /// Pad the rows of one opcode with the first row until - /// reaching the domain size if needed. - /// It only tries to pad witnesses which are non empty. - /// Returns the number of rows that were added. - /// It does not add selector columns. - /// - Use `None` for single traces - /// - Use `Some(selector)` for multi traces - fn pad_dummy(&mut self, selector: Self::Selector) -> usize; -} - -/// DecomposableTracer builds traces for some program executions. -/// The constant type `N_REL` is defined as the maximum number of relation -/// columns the trace can use per row. -/// The type `C` encodes the folding configuration, from which the selector, -/// and scalar field are derived. Examples of selectors are: -/// - For Keccak, `Step` encodes the row being performed at a time: round, -/// squeeze, padding, etc... -/// - For MIPS, `Instruction` encodes the CPU instruction being executed: add, -/// sub, load, store, etc... -pub trait DecomposableTracer { - /// Create a new decomposable trace with the given domain size, and environment. - fn new(domain_size: usize, env: &mut Env) -> Self; - - /// Pads the rows of the witnesses until reaching the domain size using the first - /// row repeatedly. It does not add selector columns. - fn pad_witnesses(&mut self); -} - -/// Generic implementation of the [Tracer] trait for the [DecomposedTrace] struct. -/// It requires the [DecomposedTrace] to implement the [DecomposableTracer] trait, -/// and the [Trace] struct to implement the [Tracer] trait with Selector set to (), -/// and `usize` to implement the [From] trait with `C::Selector`. -impl Tracer - for DecomposedTrace -where - DecomposedTrace: DecomposableTracer, - Trace: Tracer, - usize: From<::Selector>, -{ - type Selector = C::Selector; - - fn init(domain_size: usize, _selector: C::Selector, env: &mut Env) -> Self { - >::new(domain_size, env) - } - - fn push_row(&mut self, selector: Self::Selector, row: &[ScalarField; N_REL]) { - self.trace.get_mut(&selector).unwrap().push_row((), row); - } - - fn pad_with_row(&mut self, selector: Self::Selector, row: &[ScalarField; N_REL]) -> usize { - // We only want to pad non-empty witnesses. - if !self.in_circuit(selector) { - 0 - } else { - self.trace.get_mut(&selector).unwrap().pad_with_row((), row) - } - } - - fn pad_with_zeros(&mut self, selector: Self::Selector) -> usize { - // We only want to pad non-empty witnesses. - if !self.in_circuit(selector) { - 0 - } else { - self.trace.get_mut(&selector).unwrap().pad_with_zeros(()) - } - } - - fn pad_dummy(&mut self, selector: Self::Selector) -> usize { - // We only want to pad non-empty witnesses. - if !self.in_circuit(selector) { - 0 - } else { - self.trace.get_mut(&selector).unwrap().pad_dummy(()) - } - } -} - -pub mod keccak { - use std::{array, collections::BTreeMap}; - - use ark_ff::Zero; - use kimchi_msm::witness::Witness; - use strum::IntoEnumIterator; - - use crate::{ - interpreters::keccak::{ - column::{Steps, N_ZKVM_KECCAK_COLS, N_ZKVM_KECCAK_REL_COLS}, - environment::KeccakEnv, - standardize, - }, - legacy::{ - folding::{keccak::KeccakConfig, ScalarField}, - trace::{DecomposableTracer, DecomposedTrace, Trace, Tracer}, - }, - }; - - /// A Keccak instruction trace - pub type KeccakTrace = Trace; - /// The Keccak circuit trace - pub type DecomposedKeccakTrace = DecomposedTrace; - - impl DecomposableTracer>> for DecomposedKeccakTrace { - fn new(domain_size: usize, env: &mut KeccakEnv>) -> Self { - let mut circuit = Self { - domain_size, - trace: BTreeMap::new(), - }; - for step in Steps::iter().flat_map(|step| step.into_iter()) { - circuit - .trace - .insert(step, KeccakTrace::init(domain_size, step, env)); - } - circuit - } - - fn pad_witnesses(&mut self) { - for opcode in Steps::iter().flat_map(|opcode| opcode.into_iter()) { - if self.in_circuit(opcode) { - self.trace.get_mut(&opcode).unwrap().pad_dummy(()); - } - } - } - } - - impl Tracer>> - for KeccakTrace - { - type Selector = (); - - fn init( - domain_size: usize, - selector: Steps, - _env: &mut KeccakEnv>, - ) -> Self { - // Make sure we are using the same round number to refer to round steps - let step = standardize(selector); - Self { - domain_size, - witness: Witness { - cols: Box::new(std::array::from_fn(|_| Vec::with_capacity(domain_size))), - }, - constraints: KeccakEnv::constraints_of(step), - lookups: KeccakEnv::lookups_of(step), - } - } - - fn push_row( - &mut self, - _selector: Self::Selector, - row: &[ScalarField; N_ZKVM_KECCAK_REL_COLS], - ) { - for (i, value) in row.iter().enumerate() { - if self.witness.cols[i].len() < self.witness.cols[i].capacity() { - self.witness.cols[i].push(*value); - } - } - } - - fn pad_with_row( - &mut self, - _selector: Self::Selector, - row: &[ScalarField; N_ZKVM_KECCAK_REL_COLS], - ) -> usize { - let len = self.witness.cols[0].len(); - assert!(len <= self.domain_size); - let rows_to_add = self.domain_size - len; - // When we reach the domain size, we don't need to pad anymore. - for _ in 0..rows_to_add { - self.push_row((), row); - } - rows_to_add - } - - fn pad_with_zeros(&mut self, _selector: Self::Selector) -> usize { - let len = self.witness.cols[0].len(); - assert!(len <= self.domain_size); - let rows_to_add = self.domain_size - len; - // When we reach the domain size, we don't need to pad anymore. - for col in self.witness.cols.iter_mut() { - col.extend((0..rows_to_add).map(|_| ScalarField::::zero())); - } - rows_to_add - } - - fn pad_dummy(&mut self, _selector: Self::Selector) -> usize { - // We keep track of the first row of the non-empty witness, which is a real step witness. - let row = array::from_fn(|i| self.witness.cols[i][0]); - self.pad_with_row(_selector, &row) - } - } -} - -pub mod mips { - use crate::{ - interpreters::mips::{ - column::{N_MIPS_COLS, N_MIPS_REL_COLS}, - constraints::Env, - interpreter::{interpret_instruction, Instruction, InterpreterEnv}, - }, - legacy::{ - folding::{mips::DecomposableMIPSFoldingConfig, ScalarField}, - trace::{DecomposableTracer, DecomposedTrace, Trace, Tracer}, - }, - }; - use ark_ff::Zero; - use kimchi_msm::witness::Witness; - use std::{array, collections::BTreeMap}; - use strum::IntoEnumIterator; - - /// The MIPS instruction trace - pub type MIPSTrace = Trace; - /// The MIPS circuit trace - pub type DecomposedMIPSTrace = DecomposedTrace; - - impl DecomposableTracer>> for DecomposedMIPSTrace { - fn new( - domain_size: usize, - env: &mut Env>, - ) -> Self { - let mut circuit = Self { - domain_size, - trace: BTreeMap::new(), - }; - for instr in Instruction::iter().flat_map(|step| step.into_iter()) { - circuit - .trace - .insert(instr, ::init(domain_size, instr, env)); - } - circuit - } - - fn pad_witnesses(&mut self) { - for opcode in Instruction::iter().flat_map(|opcode| opcode.into_iter()) { - self.trace.get_mut(&opcode).unwrap().pad_dummy(()); - } - } - } - - impl - Tracer< - N_MIPS_REL_COLS, - DecomposableMIPSFoldingConfig, - Env>, - > for MIPSTrace - { - type Selector = (); - - fn init( - domain_size: usize, - instr: Instruction, - env: &mut Env>, - ) -> Self { - interpret_instruction(env, instr); - - let trace = Self { - domain_size, - witness: Witness { - cols: Box::new(std::array::from_fn(|_| Vec::with_capacity(domain_size))), - }, - constraints: env.get_constraints(), - lookups: env.get_lookups(), - }; - // Clear for the next instruction - env.reset(); - trace - } - - fn push_row( - &mut self, - _selector: Self::Selector, - row: &[ScalarField; N_MIPS_REL_COLS], - ) { - for (i, value) in row.iter().enumerate() { - if self.witness.cols[i].len() < self.witness.cols[i].capacity() { - self.witness.cols[i].push(*value); - } - } - } - - fn pad_with_row( - &mut self, - _selector: Self::Selector, - row: &[ScalarField; N_MIPS_REL_COLS], - ) -> usize { - let len = self.witness.cols[0].len(); - assert!(len <= self.domain_size); - let rows_to_add = self.domain_size - len; - // When we reach the domain size, we don't need to pad anymore. - for _ in 0..rows_to_add { - self.push_row(_selector, row); - } - rows_to_add - } - - fn pad_with_zeros(&mut self, _selector: Self::Selector) -> usize { - let len = self.witness.cols[0].len(); - assert!(len <= self.domain_size); - let rows_to_add = self.domain_size - len; - // When we reach the domain size, we don't need to pad anymore. - for col in self.witness.cols.iter_mut() { - col.extend( - (0..rows_to_add).map(|_| ScalarField::::zero()), - ); - } - rows_to_add - } - - fn pad_dummy(&mut self, _selector: Self::Selector) -> usize { - // We keep track of the first row of the non-empty witness, which is a real step witness. - let row = array::from_fn(|i| self.witness.cols[i][0]); - self.pad_with_row(_selector, &row) - } - } -} diff --git a/o1vm/src/lib.rs b/o1vm/src/lib.rs index 335442a57f..e5ddbe84a8 100644 --- a/o1vm/src/lib.rs +++ b/o1vm/src/lib.rs @@ -8,11 +8,6 @@ pub mod elf_loader; pub mod interpreters; -/// Legacy implementation of the recursive proof composition. -/// It does use the folding and ivc libraries defined in this monorepo, and aims -/// to be compatible with Ethereum natively, using the curve bn254. -pub mod legacy; - /// Pickles flavor of the o1vm. pub mod pickles;