diff --git a/Cargo.lock b/Cargo.lock index e5851fa15..8528d2275 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -692,48 +692,6 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -[[package]] -name = "gkr" -version = "0.1.0" -dependencies = [ - "ark-std", - "cfg-if", - "criterion", - "crossbeam-channel", - "ff", - "ff_ext", - "goldilocks", - "itertools 0.13.0", - "multilinear_extensions", - "pprof", - "rayon", - "serde", - "serde_json", - "simple-frontend", - "sumcheck", - "tiny-keccak", - "tracing", - "tracing-flame", - "tracing-subscriber", - "transcript", -] - -[[package]] -name = "gkr-graph" -version = "0.1.0" -dependencies = [ - "ark-std", - "ff", - "ff_ext", - "gkr", - "goldilocks", - "itertools 0.13.0", - "multilinear_extensions", - "simple-frontend", - "sumcheck", - "transcript", -] - [[package]] name = "glob" version = "0.3.1" @@ -1653,64 +1611,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "simple-frontend" -version = "0.1.0" -dependencies = [ - "ff", - "ff_ext", - "goldilocks", - "itertools 0.13.0", - "serde", -] - -[[package]] -name = "singer" -version = "0.1.0" -dependencies = [ - "ark-std", - "cfg-if", - "criterion", - "ff", - "ff_ext", - "gkr", - "gkr-graph", - "goldilocks", - "itertools 0.13.0", - "multilinear_extensions", - "paste", - "pprof", - "rayon", - "serde", - "simple-frontend", - "singer-utils", - "strum", - "strum_macros", - "tracing", - "tracing-flame", - "tracing-subscriber", - "transcript", -] - -[[package]] -name = "singer-utils" -version = "0.1.0" -dependencies = [ - "ark-std", - "ff", - "ff_ext", - "gkr", - "gkr-graph", - "goldilocks", - "itertools 0.13.0", - "multilinear_extensions", - "simple-frontend", - "strum", - "strum_macros", - "sumcheck", - "transcript", -] - [[package]] name = "smallvec" version = "1.13.2" diff --git a/Cargo.toml b/Cargo.toml index aea908372..d01a8fb0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,13 +4,8 @@ members = [ "ceno_emul", "examples-builder", "ceno_rt", - "gkr", - "gkr-graph", "mpcs", "multilinear_extensions", - "simple-frontend", - "singer", - "singer-utils", "sumcheck", "transcript", "ceno_zkvm", @@ -44,7 +39,6 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" strum = "0.26" strum_macros = "0.26" -subtle = "2.6" tracing = { version = "0.1", features = [ "attributes", ] } diff --git a/gkr-graph/Cargo.toml b/gkr-graph/Cargo.toml deleted file mode 100644 index a8dfbf8d7..000000000 --- a/gkr-graph/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -edition.workspace = true -license.workspace = true -name = "gkr-graph" -version.workspace = true - -[dependencies] -ark-std.workspace = true -ff.workspace = true -ff_ext = { path = "../ff_ext" } -gkr = { path = "../gkr", features = ["parallel"] } -goldilocks.workspace = true -itertools.workspace = true -multilinear_extensions = { version = "0", path = "../multilinear_extensions" } -simple-frontend = { version = "0", path = "../simple-frontend" } -sumcheck = { version = "0", path = "../sumcheck" } -transcript = { version = "0", path = "../transcript" } diff --git a/gkr-graph/examples/series_connection_alt.rs b/gkr-graph/examples/series_connection_alt.rs deleted file mode 100644 index 002872425..000000000 --- a/gkr-graph/examples/series_connection_alt.rs +++ /dev/null @@ -1,278 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; -use gkr::{ - structs::{Circuit, PointAndEval}, - util::ceil_log2, -}; -use gkr_graph::{ - error::GKRGraphError, - structs::{ - CircuitGraphAuxInfo, CircuitGraphBuilder, IOPProverState, IOPVerifierState, NodeOutputType, - PredType, TargetEvaluations, - }, -}; -use goldilocks::{Goldilocks, GoldilocksExt2}; -use multilinear_extensions::mle::DenseMultilinearExtension; -use simple_frontend::structs::{ChallengeId, CircuitBuilder, MixedCell}; -use std::sync::Arc; -use transcript::Transcript; - -fn construct_input( - input_size: usize, - challenge: ChallengeId, -) -> Arc> { - let mut circuit_builder = CircuitBuilder::::default(); - let (_, inputs) = circuit_builder.create_witness_in(input_size); - let (_, lookup_inputs) = circuit_builder.create_ext_witness_out(input_size); - - for (i, input) in inputs.iter().enumerate() { - // denominator = (input + challenge) - circuit_builder.rlc(&lookup_inputs[i], &[*input], challenge); - } - circuit_builder.configure(); - Arc::new(Circuit::new(&circuit_builder)) -} - -/// Construct a selector for n_instances and each instance contains `num` -/// items. `num` must be a power of 2. -pub(crate) fn construct_prefix_selector( - n_instances: usize, - num: usize, -) -> Arc> { - assert_eq!(num, num.next_power_of_two()); - let mut circuit_builder = CircuitBuilder::::default(); - let _ = circuit_builder.create_constant_in(n_instances * num, 1); - circuit_builder.configure(); - Arc::new(Circuit::new(&circuit_builder)) -} - -/// Construct a circuit to compute the inverse sum of two extension field -/// elements. -/// Wire in 0: 2 extension field elements. -/// Wire in 1: 2-bit selector. -/// output layer: the denominator and the numerator. -pub(crate) fn construct_inv_sum() -> Arc> { - let mut circuit_builder = CircuitBuilder::::default(); - let (_input_id, input) = circuit_builder.create_ext_witness_in(2); - let (_cond_id, cond) = circuit_builder.create_witness_in(2); - let (_, output) = circuit_builder.create_ext_witness_out(2); - // selector denominator 1 or input[0] or input[0] * input[1] - let den_mul = circuit_builder.create_ext_cell(); - circuit_builder.mul2_ext(&den_mul, &input[0], &input[1], E::BaseField::ONE); - let tmp = circuit_builder.create_ext_cell(); - circuit_builder.sel_mixed_and_ext( - &tmp, - &MixedCell::Constant(E::BaseField::ONE), - &input[0], - cond[0], - ); - circuit_builder.sel_ext(&output[0], &tmp, &den_mul, cond[1]); - - // select the numerator 0 or 1 or input[0] + input[1] - let den_add = circuit_builder.create_ext_cell(); - circuit_builder.add_ext(&den_add, &input[0], E::BaseField::ONE); - circuit_builder.add_ext(&den_add, &input[0], E::BaseField::ONE); - circuit_builder.sel_mixed_and_ext(&output[1], &cond[0].into(), &den_add, cond[1]); - - circuit_builder.configure(); - Arc::new(Circuit::new(&circuit_builder)) -} - -/// Construct a circuit to compute the sum of two fractions. The -/// denominators and numerators are on the extension field -/// Wire in 0: denominators, 2 extension field elements. -/// Wire in 1: numerators, 2 extensuin field elements. -/// Wire out 0: the denominator. -/// Wire out 1: the numerator. -pub(crate) fn construct_frac_sum_inner() -> Arc> { - let mut circuit_builder = CircuitBuilder::::default(); - let (_, input) = circuit_builder.create_ext_witness_in(4); - let (_, output) = circuit_builder.create_ext_witness_out(2); - // denominator - circuit_builder.mul2_ext( - &output[0], // output_den - &input[0], // input_den[0] - &input[2], // input_den[1] - E::BaseField::ONE, - ); - - // numerator - circuit_builder.mul2_ext( - &output[1], // output_num - &input[0], // input_den[0] - &input[3], // input_num[1] - E::BaseField::ONE, - ); - circuit_builder.mul2_ext( - &output[1], // output_num - &input[2], // input_den[1] - &input[1], // input_num[0] - E::BaseField::ONE, - ); - - circuit_builder.configure(); - Arc::new(Circuit::new(&circuit_builder)) -} - -fn main() -> Result<(), GKRGraphError> { - // ================== - // Construct circuits - // ================== - - let challenge_no = 0; - let input_size = 4; - let input_circuit = construct_input::(input_size, challenge_no); - let prefix_selector = construct_prefix_selector::(1, input_size); - let inv_sum_circuit = construct_inv_sum::(); - let frac_sum_circuit = construct_frac_sum_inner::(); - - // ================== - // Witness generation (only source) - // ================== - - let input_circuit_wires_in = vec![ - Goldilocks::from(2u64), - Goldilocks::from(2u64), - Goldilocks::from(4u64), - Goldilocks::from(16u64), - ]; - - // ================== - // Graph construction - // ================== - - let mut prover_graph_builder = CircuitGraphBuilder::::default(); - let mut verifier_graph_builder = CircuitGraphBuilder::::default(); - let mut prover_transcript = Transcript::::new(b"test"); - let challenge = vec![ - prover_transcript - .get_and_append_challenge(b"lookup challenge") - .elements, - ]; - - let mut add_node_and_witness = |label: &'static str, - circuit: &Arc>, - preds: Vec, - challenges: Vec<_>, - sources: Vec>, - num_instances: usize| - -> Result { - let prover_node_id = prover_graph_builder.add_node_with_witness( - label, - circuit, - preds.clone(), - challenges, - sources, - num_instances, - )?; - let verifier_node_id = verifier_graph_builder.add_node(label, circuit, preds)?; - assert_eq!(prover_node_id, verifier_node_id); - Ok(prover_node_id) - }; - - let input = add_node_and_witness( - "input", - &input_circuit, - vec![PredType::Source], - challenge, - vec![DenseMultilinearExtension::from_evaluations_vec( - ceil_log2(input_circuit_wires_in.len()), - input_circuit_wires_in.clone(), - )], - 1, - )?; - let selector = add_node_and_witness("selector", &prefix_selector, vec![], vec![], vec![], 1)?; - - let mut round_input_size = input_size.next_power_of_two(); - let inv_sum = add_node_and_witness( - "inv_sum", - &inv_sum_circuit, - vec![ - PredType::PredWire(NodeOutputType::WireOut(input, 0)), - PredType::PredWire(NodeOutputType::OutputLayer(selector)), - ], - vec![], - vec![DenseMultilinearExtension::default(); 2], - round_input_size >> 1, - )?; - round_input_size >>= 1; - let mut frac_sum_input = NodeOutputType::WireOut(inv_sum, 0); - while round_input_size > 1 { - frac_sum_input = NodeOutputType::WireOut( - add_node_and_witness( - "frac_sum", - &frac_sum_circuit, - vec![PredType::PredWire(frac_sum_input)], - vec![], - vec![DenseMultilinearExtension::default(); 1], - round_input_size >> 1, - )?, - 0, - ); - round_input_size >>= 1; - } - - let (prover_graph, circuit_witness) = prover_graph_builder.finalize_graph_and_witness(); - let verifier_graph = verifier_graph_builder.finalize_graph(); - let aux_info = CircuitGraphAuxInfo { - instance_num_vars: circuit_witness - .node_witnesses - .iter() - .map(|witness| witness.instance_num_vars()) - .collect(), - }; - - // ================= - // Proofs generation - // ================= - let output_point = vec![ - prover_transcript - .get_and_append_challenge(b"output point") - .elements, - prover_transcript - .get_and_append_challenge(b"output point") - .elements, - ]; - let output_eval = circuit_witness - .node_witnesses - .last() - .unwrap() - .output_layer_witness_ref() - .evaluate(&output_point); - let proof = IOPProverState::prove( - &prover_graph, - &circuit_witness, - &TargetEvaluations(vec![PointAndEval::new(output_point, output_eval)]), - &mut prover_transcript, - 1, - )?; - - // ============= - // Verify proofs - // ============= - - let mut verifier_transcript = Transcript::::new(b"test"); - let challenge = [verifier_transcript - .get_and_append_challenge(b"lookup challenge") - .elements]; - - let output_point = vec![ - verifier_transcript - .get_and_append_challenge(b"output point") - .elements, - verifier_transcript - .get_and_append_challenge(b"output point") - .elements, - ]; - - IOPVerifierState::verify( - &verifier_graph, - &challenge, - &TargetEvaluations(vec![PointAndEval::new(output_point, output_eval)]), - proof, - &aux_info, - &mut verifier_transcript, - )?; - - Ok(()) -} diff --git a/gkr-graph/src/circuit_builder.rs b/gkr-graph/src/circuit_builder.rs deleted file mode 100644 index a505b83cc..000000000 --- a/gkr-graph/src/circuit_builder.rs +++ /dev/null @@ -1,33 +0,0 @@ -use ff_ext::ExtensionField; -use gkr::structs::{Point, PointAndEval}; -use itertools::Itertools; - -use crate::structs::{CircuitGraph, CircuitGraphWitness, NodeOutputType, TargetEvaluations}; - -impl CircuitGraph { - pub fn target_evals( - &self, - witness: &CircuitGraphWitness, - point: &Point, - ) -> TargetEvaluations { - // println!("targets: {:?}, point: {:?}", self.targets, point); - let target_evals = self - .targets - .iter() - .map(|target| { - let poly = match target { - NodeOutputType::OutputLayer(node_id) => { - witness.node_witnesses[*node_id].output_layer_witness_ref() - } - NodeOutputType::WireOut(node_id, wit_id) => { - &witness.node_witnesses[*node_id].witness_out_ref()[*wit_id as usize] - } - }; - // println!("target: {:?}, poly.num_vars: {:?}", target, poly.num_vars); - let p = point[..poly.num_vars()].to_vec(); - PointAndEval::new_from_ref(&p, &poly.evaluate(&p)) - }) - .collect_vec(); - TargetEvaluations(target_evals) - } -} diff --git a/gkr-graph/src/circuit_graph_builder.rs b/gkr-graph/src/circuit_graph_builder.rs deleted file mode 100644 index e73ad4f11..000000000 --- a/gkr-graph/src/circuit_graph_builder.rs +++ /dev/null @@ -1,235 +0,0 @@ -use std::{collections::BTreeSet, sync::Arc}; - -use ark_std::Zero; -use ff_ext::ExtensionField; -use gkr::structs::{Circuit, CircuitWitness}; -use itertools::{Itertools, chain, izip}; -use multilinear_extensions::{ - mle::DenseMultilinearExtension, virtual_poly_v2::ArcMultilinearExtension, -}; -use simple_frontend::structs::WitnessId; - -use crate::{ - error::GKRGraphError, - structs::{ - CircuitGraph, CircuitGraphBuilder, CircuitGraphWitness, CircuitNode, NodeInputType, - NodeOutputType, PredType, - }, -}; - -impl<'a, E: ExtensionField> CircuitGraphBuilder<'a, E> { - /// Add a new node indicating the predecessors and generate the witness. - /// Return the index of the new node. sources has the same number as the - /// input witnesses. If some witness is not source, then the corresponding - /// entry in sources is default. - pub fn add_node_with_witness( - &mut self, - label: &'static str, - circuit: &Arc>, - preds: Vec, - challenges: Vec, - sources: Vec>, - num_instances: usize, - ) -> Result { - let id = self.graph.nodes.len(); - // println!( - // "id: {}, label: {}, num_instances: {}, preds: {:?}", - // id, label, num_instances, preds - // ); - - assert_eq!(preds.len(), circuit.n_witness_in); - assert!(num_instances.is_power_of_two()); - assert_eq!(sources.len(), circuit.n_witness_in); - assert!( - sources - .iter() - .all(|source| source.evaluations.len() % num_instances == 0), - "node_id: {}, num_instances: {}, sources_num_instances: {:?}", - id, - num_instances, - sources - .iter() - .map(|source| source.evaluations.len()) - .collect_vec() - ); - - let mut witness = CircuitWitness::new(circuit, challenges); - let wits_in = izip!(preds.iter(), sources.into_iter()) - .map(|(pred, source)| match pred { - PredType::Source => source.into(), - PredType::PredWire(out) | PredType::PredWireDup(out) => { - let (id, out) = match out { - NodeOutputType::OutputLayer(id) => ( - *id, - self.witness.node_witnesses[*id] - .output_layer_witness_ref() - .clone(), - ), - NodeOutputType::WireOut(id, wit_id) => ( - *id, - self.witness.node_witnesses[*id].witness_out_ref()[*wit_id as usize] - .clone(), - ), - }; - let old_num_instances = self.witness.node_witnesses[id].n_instances(); - let new_instances: ArcMultilinearExtension<'a, E> = match pred { - PredType::PredWire(_) => out, - PredType::PredWireDup(_) => { - let num_dups = num_instances / old_num_instances; - let new: ArcMultilinearExtension = - out.dup(old_num_instances, num_dups).into(); - new - } - _ => unreachable!(), - }; - new_instances - } - }) - .collect_vec(); - - witness.set_instances(circuit, wits_in, num_instances); - self.witness.node_witnesses.push(Arc::new(witness)); - - self.graph.nodes.push(CircuitNode { - id, - label, - circuit: circuit.clone(), - preds, - }); - - Ok(id) - } - - /// Add a new node indicating the predecessors. Return the index of the new - /// node. - pub fn add_node( - &mut self, - label: &'static str, - circuit: &Arc>, - preds: Vec, - ) -> Result { - let id = self.graph.nodes.len(); - - self.graph.nodes.push(CircuitNode { - id, - label, - circuit: circuit.clone(), - preds, - }); - - Ok(id) - } - - /// Collect the information of `self.sources` and `self.targets`. - pub fn finalize_graph_and_witness(mut self) -> (CircuitGraph, CircuitGraphWitness<'a, E>) { - // Generate all possible graph output - let outs = self - .graph - .nodes - .iter() - .enumerate() - .flat_map(|(id, node)| { - chain![ - (0..node.circuit.n_witness_out) - .map(move |wire_id| NodeOutputType::WireOut(id, wire_id as WitnessId)), - node.circuit - .n_witness_out - .is_zero() - .then_some(NodeOutputType::OutputLayer(id)) - ] - }) - .collect::>(); - // Collect all assigned source into `sources`, - // and remove assigned `PredWire*` from possible outs - let (sources, targets) = self.graph.nodes.iter().enumerate().fold( - (BTreeSet::new(), outs), - |(mut sources, mut targets), (id, node)| { - for (wire_id, pred) in node.preds.iter().enumerate() { - match pred { - PredType::Source => { - sources.insert(NodeInputType::WireIn(id, wire_id as WitnessId)); - } - PredType::PredWire(out) => { - targets.remove(out); - } - PredType::PredWireDup(out) => { - targets.remove(out); - } - } - } - - (sources, targets) - }, - ); - self.graph.sources = sources.into_iter().collect(); - self.graph.targets = targets.into_iter().collect(); - - (self.graph, self.witness) - } - - pub fn finalize_graph(self) -> CircuitGraph { - let (graph, _) = self.finalize_graph_and_witness(); - graph - } - - /// Collect the information of `self.sources` and `self.targets`. - pub fn finalize_graph_and_witness_with_targets( - mut self, - targets: &[NodeOutputType], - ) -> (CircuitGraph, CircuitGraphWitness<'a, E>) { - // Generate all possible graph output - let outs = self - .graph - .nodes - .iter() - .enumerate() - .flat_map(|(id, node)| { - chain![ - (0..node.circuit.n_witness_out) - .map(move |wire_id| NodeOutputType::WireOut(id, wire_id as WitnessId)), - node.circuit - .n_witness_out - .is_zero() - .then_some(NodeOutputType::OutputLayer(id)) - ] - }) - .collect::>(); - // Collect all assigned source into `sources`, - // and remove assigned `PredWire*` from possible outs - let (sources, expected_target) = self.graph.nodes.iter().enumerate().fold( - (BTreeSet::new(), outs), - |(mut sources, mut targets), (id, node)| { - for (wire_id, pred) in node.preds.iter().enumerate() { - match pred { - PredType::Source => { - sources.insert(NodeInputType::WireIn(id, wire_id as WitnessId)); - } - PredType::PredWire(out) => { - targets.remove(out); - } - PredType::PredWireDup(out) => { - targets.remove(out); - } - } - } - - (sources, targets) - }, - ); - - assert_eq!( - expected_target, - targets.iter().cloned().collect::>() - ); - - self.graph.sources = sources.into_iter().collect(); - self.graph.targets = targets.to_vec(); - - (self.graph, self.witness) - } - - pub fn finalize_graph_with_targets(self, targets: &[NodeOutputType]) -> CircuitGraph { - let (graph, _) = self.finalize_graph_and_witness_with_targets(targets); - graph - } -} diff --git a/gkr-graph/src/error.rs b/gkr-graph/src/error.rs deleted file mode 100644 index 53b381e27..000000000 --- a/gkr-graph/src/error.rs +++ /dev/null @@ -1,14 +0,0 @@ -use gkr::error::GKRError; - -#[derive(Debug)] -pub enum GKRGraphError { - GKRError(GKRError), - GraphCircuitError, - VerifyError, -} - -impl From for GKRGraphError { - fn from(error: GKRError) -> Self { - Self::GKRError(error) - } -} diff --git a/gkr-graph/src/lib.rs b/gkr-graph/src/lib.rs deleted file mode 100644 index fe0c979a4..000000000 --- a/gkr-graph/src/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod circuit_builder; -mod circuit_graph_builder; -pub mod error; -mod prover; -pub mod structs; -mod verifier; diff --git a/gkr-graph/src/prover.rs b/gkr-graph/src/prover.rs deleted file mode 100644 index 8cc05e407..000000000 --- a/gkr-graph/src/prover.rs +++ /dev/null @@ -1,161 +0,0 @@ -use crate::{ - error::GKRGraphError, - structs::{ - CircuitGraph, CircuitGraphWitness, GKRProverState, IOPProof, IOPProverState, - NodeOutputType, PredType, TargetEvaluations, - }, -}; -use ff_ext::ExtensionField; -use gkr::structs::PointAndEval; -use itertools::{Itertools, izip}; -use std::mem; -use transcript::Transcript; - -impl IOPProverState { - pub fn prove( - circuit: &CircuitGraph, - circuit_witness: &CircuitGraphWitness, - target_evals: &TargetEvaluations, - transcript: &mut Transcript, - expected_max_thread_id: usize, - ) -> Result, GKRGraphError> { - assert_eq!(target_evals.0.len(), circuit.targets.len()); - assert_eq!(circuit_witness.node_witnesses.len(), circuit.nodes.len()); - - let mut output_evals = vec![vec![]; circuit.nodes.len()]; - let mut wit_out_evals = circuit - .nodes - .iter() - .map(|node| vec![PointAndEval::default(); node.circuit.n_witness_out]) - .collect_vec(); - izip!(&circuit.targets, &target_evals.0).for_each(|(target, eval)| match target { - NodeOutputType::OutputLayer(id) => output_evals[*id].push(eval.clone()), - NodeOutputType::WireOut(id, wire_out_id) => { - wit_out_evals[*id][*wire_out_id as usize] = eval.clone() - } - }); - - let gkr_proofs = izip!(&circuit.nodes, &circuit_witness.node_witnesses) - .rev() - .map(|(node, witness)| { - let max_thread_id = witness.n_instances().min(expected_max_thread_id); - - // sanity check for witness poly evaluation - if cfg!(debug_assertions) { - - // TODO figure out a way to do sanity check on output_evals - // it doens't work for now because output evaluation - // might only take partial range of output layer witness - // assert!(output_evals[node.id].len() <= 1); - // if !output_evals[node.id].is_empty() { - // debug_assert_eq!( - // witness - // .output_layer_witness_ref() - // .instances - // .as_slice() - // .original_mle() - // .evaluate(&point_and_eval.point), - // point_and_eval.eval, - // "node_id {} output eval failed", - // node.id, - // ); - // } - - for (witness_id, point_and_eval) in wit_out_evals[node.id].iter().enumerate() { - let mle = &witness.witness_out_ref()[witness_id]; - debug_assert_eq!( - mle.evaluate(&point_and_eval.point), - point_and_eval.eval, - "node_id {} output eval failed", - node.id, - ); - } - } - let (proof, input_claim) = GKRProverState::prove_parallel( - &node.circuit, - witness, - mem::take(&mut output_evals[node.id]), - mem::take(&mut wit_out_evals[node.id]), - max_thread_id, - transcript, - ); - - // println!( - // "Proving node {}, label {}, num_instances:{}, took {}s", - // node.id, - // node.label, - // witness.instance_num_vars(), - // timer.elapsed().as_secs_f64() - // ); - - izip!(&node.preds, &input_claim.point_and_evals) - .enumerate() - .for_each(|(wire_id, (pred_type, point_and_eval))| match pred_type { - PredType::Source => { - // sanity check for input poly evaluation - if cfg!(debug_assertions) { - let input_layer_poly = &witness.witness_in_ref()[wire_id]; - debug_assert_eq!( - input_layer_poly.evaluate(&point_and_eval.point), - point_and_eval.eval, - "mismatch at node.id {:?} wire_id {:?}, input_claim.point_and_evals.point {:?}, node.preds {:?}", - node.id, - wire_id, - input_claim.point_and_evals[0].point, - node.preds - ); - } - } - PredType::PredWire(pred_out) | PredType::PredWireDup(pred_out) => { - let point = match pred_type { - PredType::PredWire(_) => point_and_eval.point.clone(), - PredType::PredWireDup(out) => { - let pred_node_id = match out { - NodeOutputType::OutputLayer(id) => id, - NodeOutputType::WireOut(id, _) => id, - }; - // Suppose the new point is - // [single_instance_slice || - // new_instance_index_slice]. The old point - // is [single_instance_slices || - // new_instance_index_slices[(instance_num_vars - // - pred_instance_num_vars)..]] - let pred_instance_num_vars = circuit_witness.node_witnesses - [*pred_node_id] - .instance_num_vars(); - let instance_num_vars = witness.instance_num_vars(); - let num_vars = point_and_eval.point.len() - instance_num_vars; - [ - point_and_eval.point[..num_vars].to_vec(), - point_and_eval.point[num_vars - + (instance_num_vars - pred_instance_num_vars)..] - .to_vec(), - ] - .concat() - } - _ => unreachable!(), - }; - match pred_out { - NodeOutputType::OutputLayer(id) => { - output_evals[*id] - .push(PointAndEval::new_from_ref(&point, &point_and_eval.eval)) - }, - NodeOutputType::WireOut(id, wire_id) => { - let evals = &mut wit_out_evals[*id][*wire_id as usize]; - assert!( - evals.point.is_empty() && evals.eval.is_zero_vartime(), - "unimplemented", - ); - *evals = PointAndEval::new(point, point_and_eval.eval); - } - } - } - }); - - proof - }) - .collect(); - - Ok(IOPProof { gkr_proofs }) - } -} diff --git a/gkr-graph/src/structs.rs b/gkr-graph/src/structs.rs deleted file mode 100644 index 4e206ee2a..000000000 --- a/gkr-graph/src/structs.rs +++ /dev/null @@ -1,81 +0,0 @@ -use ff_ext::ExtensionField; -use gkr::structs::{Circuit, CircuitWitness, PointAndEval}; -use simple_frontend::structs::WitnessId; -use std::{marker::PhantomData, sync::Arc}; - -pub(crate) type GKRProverState = gkr::structs::IOPProverState; -pub(crate) type GKRVerifierState = gkr::structs::IOPVerifierState; -pub(crate) type GKRProof = gkr::structs::IOPProof; - -/// Corresponds to the `output_evals` and `wires_out_evals` in gkr -/// `prove_parallel`. -pub struct IOPProverState { - marker: PhantomData, -} - -pub struct IOPProof { - pub(crate) gkr_proofs: Vec>, -} - -pub struct IOPVerifierState { - marker: PhantomData, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub(crate) enum NodeInputType { - WireIn(usize, WitnessId), -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum NodeOutputType { - OutputLayer(usize), - WireOut(usize, WitnessId), -} - -/// The predecessor of a node can be a source or a wire. If it is a wire, it can -/// be one wire_out instance connected to one wire_in instance, or one wire_out -/// connected to multiple wire_in instances. -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum PredType { - Source, - PredWire(NodeOutputType), - PredWireDup(NodeOutputType), -} - -#[derive(Clone, Debug)] -pub struct CircuitNode { - pub(crate) id: usize, - // Note: only for debug output. - #[allow(dead_code)] - pub(crate) label: &'static str, - pub(crate) circuit: Arc>, - // Where does each wire in come from. - pub(crate) preds: Vec, -} - -#[derive(Clone, Debug, Default)] -pub struct CircuitGraph { - pub(crate) nodes: Vec>, - pub(crate) targets: Vec, - pub(crate) sources: Vec, -} - -#[derive(Default)] -pub struct CircuitGraphWitness<'a, E: ExtensionField> { - pub node_witnesses: Vec>>, -} - -#[derive(Default)] -pub struct CircuitGraphBuilder<'a, E: ExtensionField> { - pub(crate) graph: CircuitGraph, - pub(crate) witness: CircuitGraphWitness<'a, E>, -} - -#[derive(Clone, Debug, Default)] -pub struct CircuitGraphAuxInfo { - pub instance_num_vars: Vec, -} - -/// Evaluations corresponds to the circuit targets. -#[derive(Clone, Debug)] -pub struct TargetEvaluations(pub Vec>); diff --git a/gkr-graph/src/verifier.rs b/gkr-graph/src/verifier.rs deleted file mode 100644 index ea4d9b8fc..000000000 --- a/gkr-graph/src/verifier.rs +++ /dev/null @@ -1,109 +0,0 @@ -use ff_ext::ExtensionField; -use gkr::structs::PointAndEval; -use itertools::{Itertools, izip}; -use std::mem; -use transcript::Transcript; - -use crate::{ - error::GKRGraphError, - structs::{ - CircuitGraph, CircuitGraphAuxInfo, GKRVerifierState, IOPProof, IOPVerifierState, - NodeOutputType, PredType, TargetEvaluations, - }, -}; - -impl IOPVerifierState { - pub fn verify( - circuit: &CircuitGraph, - challenges: &[E], - target_evals: &TargetEvaluations, - proof: IOPProof, - aux_info: &CircuitGraphAuxInfo, - transcript: &mut Transcript, - ) -> Result<(), GKRGraphError> { - assert_eq!(target_evals.0.len(), circuit.targets.len()); - - let mut output_evals = vec![vec![]; circuit.nodes.len()]; - let mut wit_out_evals = circuit - .nodes - .iter() - .map(|node| vec![PointAndEval::default(); node.circuit.n_witness_out]) - .collect_vec(); - izip!(&circuit.targets, &target_evals.0).for_each(|(target, eval)| match target { - NodeOutputType::OutputLayer(id) => output_evals[*id].push(eval.clone()), - NodeOutputType::WireOut(id, wire_out_id) => { - wit_out_evals[*id][*wire_out_id as usize] = eval.clone() - } - }); - - for ((node, instance_num_vars), proof) in izip!( - izip!(&circuit.nodes, &aux_info.instance_num_vars,).rev(), - proof.gkr_proofs - ) { - let input_claim = GKRVerifierState::verify_parallel( - &node.circuit, - challenges, - mem::take(&mut output_evals[node.id]), - mem::take(&mut wit_out_evals[node.id]), - proof, - *instance_num_vars, - transcript, - )?; - - let new_instance_num_vars = aux_info.instance_num_vars[node.id]; - - izip!(&node.preds, input_claim.point_and_evals).for_each( - |(pred_type, point_and_eval)| { - match pred_type { - PredType::Source => { - // TODO: collect `(proof.point.clone(), *eval)` as `TargetEvaluations` - // for later PCS open? - } - PredType::PredWire(pred_out) | PredType::PredWireDup(pred_out) => { - let point = match pred_type { - PredType::PredWire(_) => point_and_eval.point.clone(), - PredType::PredWireDup(out) => { - let node_id = match out { - NodeOutputType::OutputLayer(id) => *id, - NodeOutputType::WireOut(id, _) => *id, - }; - // Suppose the new point is - // [single_instance_slice || - // new_instance_index_slice]. The old point - // is [single_instance_slices || - // new_instance_index_slices[(new_instance_num_vars - // - old_instance_num_vars)..]] - let old_instance_num_vars = aux_info.instance_num_vars[node_id]; - let num_vars = - point_and_eval.point.len() - new_instance_num_vars; - [ - point_and_eval.point[..num_vars].to_vec(), - point_and_eval.point[num_vars - + (new_instance_num_vars - old_instance_num_vars)..] - .to_vec(), - ] - .concat() - } - _ => unreachable!(), - }; - match pred_out { - NodeOutputType::OutputLayer(id) => output_evals[*id] - .push(PointAndEval::new_from_ref(&point, &point_and_eval.eval)), - NodeOutputType::WireOut(id, wire_id) => { - let evals = &mut wit_out_evals[*id][*wire_id as usize]; - assert!( - evals.point.is_empty() && evals.eval.is_zero_vartime(), - "unimplemented", - ); - *evals = PointAndEval::new(point, point_and_eval.eval); - } - } - } - } - }, - ); - } - - Ok(()) - } -} diff --git a/gkr/Cargo.toml b/gkr/Cargo.toml deleted file mode 100644 index fab3ec6b8..000000000 --- a/gkr/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -edition.workspace = true -license.workspace = true -name = "gkr" -version.workspace = true - -[dependencies] -ark-std.workspace = true -ff.workspace = true -goldilocks.workspace = true - -crossbeam-channel.workspace = true -ff_ext = { path = "../ff_ext" } -itertools.workspace = true -multilinear_extensions = { path = "../multilinear_extensions" } -rayon.workspace = true -serde.workspace = true -serde_json.workspace = true -simple-frontend = { path = "../simple-frontend" } -sumcheck = { path = "../sumcheck" } -tracing.workspace = true -tracing-flame.workspace = true -tracing-subscriber.workspace = true -transcript = { path = "../transcript" } - -[dev-dependencies] -cfg-if.workspace = true -criterion.workspace = true -pprof.workspace = true -tiny-keccak = { version = "2.0", features = ["keccak"] } - -[features] -default = [] -flamegraph = ["pprof/flamegraph", "pprof/criterion"] -non_pow2_rayon_thread = ["sumcheck/non_pow2_rayon_thread"] -parallel = [] -unsafe = [] - -[[bench]] -harness = false -name = "keccak256" diff --git a/gkr/Makefile.toml b/gkr/Makefile.toml deleted file mode 100644 index f75ac8205..000000000 --- a/gkr/Makefile.toml +++ /dev/null @@ -1,17 +0,0 @@ -[env] -CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true -CORE = { script = ["nproc"] } -RAYON_NUM_THREADS = "${CORE}" - -[tasks.gkr_bench] -args = ["bench", "--bench", "keccak256", "--features", "flamegraph", "--features", "parallel", "--package", "gkr"] -command = "cargo" - -[tasks.gkr_example] -args = ["run", "--package", "gkr", "--release", "--features", "parallel", "--example", "keccak256"] -command = "cargo" - -[tasks.gkr_example_flamegraph] -args = ["run", "--package", "gkr", "--release", "--features", "parallel", "--example", "keccak256"] -command = "cargo" -env = { "RUST_LOG" = "debug", "RAYON_NUM_THREADS" = "1" } diff --git a/gkr/benches/README.md b/gkr/benches/README.md deleted file mode 100644 index 78139207b..000000000 --- a/gkr/benches/README.md +++ /dev/null @@ -1,24 +0,0 @@ -benchmark -======= - -rust benchmark powered by [criterion.rs](https://bheisler.github.io/criterion.rs/book/criterion_rs.html) - -- [x] keccak256 - - - -### command -benchmark and save as `bashline` -``` -cargo bench --bench --features parallel --package gkr -- --save-baseline baseline -``` - -comparing with `baseline` -``` -cargo bench --bench --features parallel [--features ...] --package gkr -- --baseline baseline -``` - -flamegraph -``` -cargo bench --bench --features parallel --features flamegraph --package gkr -- --profile-time -``` diff --git a/gkr/benches/keccak256.rs b/gkr/benches/keccak256.rs deleted file mode 100644 index d48920dd7..000000000 --- a/gkr/benches/keccak256.rs +++ /dev/null @@ -1,67 +0,0 @@ -#![allow(clippy::manual_memcpy)] -#![allow(clippy::needless_range_loop)] - -use std::time::Duration; - -use criterion::*; - -use gkr::gadgets::keccak256::{keccak256_circuit, prove_keccak256, verify_keccak256}; -use goldilocks::GoldilocksExt2; -use multilinear_extensions::util::max_usable_threads; - -cfg_if::cfg_if! { - if #[cfg(feature = "flamegraph")] { - criterion_group! { - name = keccak256; - config = Criterion::default().warm_up_time(Duration::from_millis(3000)).with_profiler(pprof::criterion::PProfProfiler::new(100, pprof::criterion::Output::Flamegraph(None))); - targets = bench_keccak256 - } - } else { - criterion_group! { - name = keccak256; - config = Criterion::default().warm_up_time(Duration::from_millis(3000)); - targets = bench_keccak256 - } - } -} - -criterion_main!(keccak256); - -const NUM_SAMPLES: usize = 10; - -fn bench_keccak256(c: &mut Criterion) { - println!( - "#layers: {}", - keccak256_circuit::().layers.len() - ); - - let max_thread_id = max_usable_threads(); - - let circuit = keccak256_circuit::(); - - let Some((proof, witness)) = prove_keccak256(1, &circuit, 1) else { - return; - }; - assert!(verify_keccak256(1, &witness.witness_out_ref()[0], proof, &circuit).is_ok()); - - for log2_n in 0..10 { - // expand more input size once runtime is acceptable - let mut group = c.benchmark_group(format!("keccak256_log2_{}", log2_n)); - group.sample_size(NUM_SAMPLES); - - // Benchmark the proving time - group.bench_function( - BenchmarkId::new("prove_keccak256", format!("keccak256_log2_{}", log2_n)), - |b| { - b.iter(|| { - assert!( - prove_keccak256(log2_n, &circuit, (1 << log2_n).min(max_thread_id),) - .is_some() - ); - }); - }, - ); - - group.finish(); - } -} diff --git a/gkr/examples/keccak256.rs b/gkr/examples/keccak256.rs deleted file mode 100644 index 7554cf7d7..000000000 --- a/gkr/examples/keccak256.rs +++ /dev/null @@ -1,111 +0,0 @@ -#![allow(clippy::manual_memcpy)] -#![allow(clippy::needless_range_loop)] - -use std::env; - -use ff::Field; -use ff_ext::ExtensionField; -use gkr::{ - gadgets::keccak256::{keccak256_circuit, prove_keccak256, verify_keccak256}, - structs::CircuitWitness, -}; -use goldilocks::GoldilocksExt2; -use itertools::{Itertools, izip}; -use multilinear_extensions::mle::IntoMLE; -use tracing_flame::FlameLayer; -use tracing_subscriber::{EnvFilter, Registry, fmt, layer::SubscriberExt}; - -fn main() { - println!( - "#layers: {}", - keccak256_circuit::().layers.len() - ); - - #[allow(unused_mut)] - let mut max_thread_id: usize = env::var("RAYON_NUM_THREADS") - .map(|v| str::parse::(&v).unwrap_or(1)) - .unwrap(); - - if !max_thread_id.is_power_of_two() { - #[cfg(not(feature = "non_pow2_rayon_thread"))] - { - panic!( - "add --features non_pow2_rayon_thread to support non pow of 2 rayon thread pool" - ); - } - - #[cfg(feature = "non_pow2_rayon_thread")] - { - use sumcheck::{local_thread_pool::create_local_pool_once, util::ceil_log2}; - max_thread_id = 1 << ceil_log2(max_thread_id); - create_local_pool_once(max_thread_id, true); - } - } - - let circuit = keccak256_circuit::(); - // Sanity-check - { - let all_zero = vec![ - vec![::BaseField::ZERO; 25 * 64], - vec![::BaseField::ZERO; 17 * 64], - ] - .into_iter() - .map(|wit_in| wit_in.into_mle()) - .collect(); - let all_one = vec![ - vec![::BaseField::ONE; 25 * 64], - vec![::BaseField::ZERO; 17 * 64], - ] - .into_iter() - .map(|wit_in| wit_in.into_mle()) - .collect(); - let mut witness = CircuitWitness::new(&circuit, Vec::new()); - witness.add_instance(&circuit, all_zero); - witness.add_instance(&circuit, all_one); - - izip!( - witness.witness_out_ref()[0] - .get_base_field_vec() - .chunks(256), - [[0; 25], [u64::MAX; 25]] - ) - .for_each(|(wire_out, state)| { - let output = wire_out[..256] - .chunks_exact(64) - .map(|bits| { - bits.iter().fold(0, |acc, bit| { - (acc << 1) - + (*bit == ::BaseField::ONE) as u64 - }) - }) - .collect_vec(); - let expected = { - let mut state = state; - tiny_keccak::keccakf(&mut state); - state[0..4].to_vec() - }; - assert_eq!(output, expected) - }); - } - - let (flame_layer, _guard) = FlameLayer::with_file("./tracing.folded").unwrap(); - let subscriber = Registry::default() - .with( - fmt::layer() - .compact() - .with_thread_ids(false) - .with_thread_names(false), - ) - .with(EnvFilter::from_default_env()) - .with(flame_layer.with_threads_collapsed(true)); - tracing::subscriber::set_global_default(subscriber).unwrap(); - - for log2_n in 0..12 { - let Some((proof, witness)) = - prove_keccak256::(log2_n, &circuit, (1 << log2_n).min(max_thread_id)) - else { - return; - }; - assert!(verify_keccak256(log2_n, &witness.witness_out_ref()[0], proof, &circuit).is_ok()); - } -} diff --git a/gkr/src/circuit.rs b/gkr/src/circuit.rs deleted file mode 100644 index d5459f328..000000000 --- a/gkr/src/circuit.rs +++ /dev/null @@ -1,147 +0,0 @@ -use std::collections::HashMap; - -use ff_ext::ExtensionField; -use simple_frontend::structs::{ChallengeConst, ConstantType}; - -use crate::structs::{Gate1In, Gate2In, Gate3In, GateCIn}; - -mod circuit_layout; -mod circuit_witness; - -pub trait EvaluateGateCIn -where - E: ExtensionField, -{ - fn eval(&self, out_eq_vec: &[E], challenges: &HashMap>) -> E; - fn eval_subset_eq(&self, out_eq_vec: &[E], in_eq_vec: &[E]) -> E; -} - -impl EvaluateGateCIn for &[GateCIn>] -where - E: ExtensionField, -{ - fn eval(&self, out_eq_vec: &[E], challenges: &HashMap>) -> E { - self.iter().fold(E::ZERO, |acc, gate| { - acc + out_eq_vec[gate.idx_out] * gate.scalar.eval(challenges) - }) - } - fn eval_subset_eq(&self, out_eq_vec: &[E], in_eq_vec: &[E]) -> E { - self.iter().fold(E::ZERO, |acc, gate| { - acc + out_eq_vec[gate.idx_out] * in_eq_vec[gate.idx_out] - }) - } -} - -pub trait EvaluateGate1In -where - E: ExtensionField, -{ - fn eval( - &self, - out_eq_vec: &[E], - in_eq_vec: &[E], - challenges: &HashMap>, - ) -> E; -} - -impl EvaluateGate1In for &[Gate1In>] -where - E: ExtensionField, -{ - fn eval( - &self, - out_eq_vec: &[E], - in_eq_vec: &[E], - challenges: &HashMap>, - ) -> E { - self.iter().fold(E::ZERO, |acc, gate| { - acc + out_eq_vec[gate.idx_out] - * in_eq_vec[gate.idx_in[0]] - * gate.scalar.eval(challenges) - }) - } -} - -pub trait EvaluateGate2In -where - E: ExtensionField, -{ - fn eval( - &self, - out_eq_vec: &[E], - in1_eq_vec: &[E], - in2_eq_vec: &[E], - challenges: &HashMap>, - ) -> E; -} - -impl EvaluateGate2In for &[Gate2In>] -where - E: ExtensionField, -{ - fn eval( - &self, - out_eq_vec: &[E], - in1_eq_vec: &[E], - in2_eq_vec: &[E], - challenges: &HashMap>, - ) -> E { - self.iter().fold(E::ZERO, |acc, gate| { - acc + out_eq_vec[gate.idx_out] - * in1_eq_vec[gate.idx_in[0]] - * in2_eq_vec[gate.idx_in[1]] - * gate.scalar.eval(challenges) - }) - } -} - -pub trait EvaluateGate3In -where - E: ExtensionField, -{ - fn eval( - &self, - out_eq_vec: &[E], - in1_eq_vec: &[E], - in2_eq_vec: &[E], - in3_eq_vec: &[E], - challenges: &HashMap>, - ) -> E; -} - -impl EvaluateGate3In for &[Gate3In>] -where - E: ExtensionField, -{ - fn eval( - &self, - out_eq_vec: &[E], - in1_eq_vec: &[E], - in2_eq_vec: &[E], - in3_eq_vec: &[E], - challenges: &HashMap>, - ) -> E { - self.iter().fold(E::ZERO, |acc, gate| { - acc + out_eq_vec[gate.idx_out] - * in1_eq_vec[gate.idx_in[0]] - * in2_eq_vec[gate.idx_in[1]] - * in3_eq_vec[gate.idx_in[2]] - * gate.scalar.eval(challenges) - }) - } -} - -pub(crate) trait EvaluateConstant { - fn eval(&self, challenges: &HashMap>) -> E::BaseField; -} - -impl EvaluateConstant for ConstantType { - #[inline(always)] - fn eval(&self, challenges: &HashMap>) -> E::BaseField { - match self { - ConstantType::Challenge(c, j) => challenges[c][*j], - ConstantType::ChallengeScaled(c, j, scalar) => *scalar * challenges[c][*j], - ConstantType::Field(c) => *c, - } - } -} diff --git a/gkr/src/circuit/circuit_layout.rs b/gkr/src/circuit/circuit_layout.rs deleted file mode 100644 index 9fc2f5894..000000000 --- a/gkr/src/circuit/circuit_layout.rs +++ /dev/null @@ -1,920 +0,0 @@ -use core::{fmt, panic}; -use std::collections::{BTreeMap, HashMap}; - -use ff_ext::ExtensionField; -use itertools::Itertools; -use simple_frontend::structs::{ - CellId, CellType, ChallengeConst, CircuitBuilder, ConstantType, InType, LayerId, OutType, -}; -use sumcheck::util::ceil_log2; - -use crate::{ - structs::{Circuit, Gate1In, Gate2In, Gate3In, GateCIn, Layer, SumcheckStepType}, - utils::{MatrixMLEColumnFirst, MatrixMLERowFirst, i64_to_field}, -}; - -struct LayerSubsets { - subsets: BTreeMap<(u32, usize), usize>, - layer_id: LayerId, - wire_id_assigner: CellId, -} - -impl LayerSubsets { - fn new(layer_id: LayerId, layer_size: usize) -> Self { - Self { - subsets: BTreeMap::new(), - layer_id, - wire_id_assigner: layer_size, - } - } - - /// Compute the new `wire_id` after copy the cell from the old layer to the - /// new layer. If old layer == new layer, return the old wire_id. - fn update_wire_id(&mut self, old_layer_id: LayerId, old_wire_id: CellId) -> CellId { - if old_layer_id == self.layer_id { - return old_wire_id; - } - if let std::collections::btree_map::Entry::Vacant(e) = - self.subsets.entry((old_layer_id, old_wire_id)) - { - e.insert(self.wire_id_assigner); - self.wire_id_assigner += 1; - } - self.subsets[&(old_layer_id, old_wire_id)] - } - - /// Compute `paste_from` matrix and `max_previous_num_vars` for - /// `self.layer_id`, as well as `copy_to` for old layers. - fn update_layer_info(&self, layers: &mut [Layer]) { - let mut paste_from = BTreeMap::new(); - for ((old_layer_id, old_wire_id), new_wire_id) in self.subsets.iter() { - paste_from - .entry(*old_layer_id) - .or_insert(vec![]) - .push(*new_wire_id); - layers[*old_layer_id as usize] - .copy_to - .entry(self.layer_id) - .or_default() - .push(*old_wire_id); - } - layers[self.layer_id as usize].paste_from = paste_from; - - layers[self.layer_id as usize].num_vars = ceil_log2(self.wire_id_assigner); - layers[self.layer_id as usize].max_previous_num_vars = layers[self.layer_id as usize] - .max_previous_num_vars - .max(ceil_log2( - layers[self.layer_id as usize] - .paste_from - .values() - .map(|old_wire_ids| old_wire_ids.len()) - .max() - .unwrap_or(1), - )); - } -} - -impl Circuit { - /// Generate the circuit from circuit builder. - pub fn new(circuit_builder: &CircuitBuilder) -> Self { - assert!(circuit_builder.n_layers.is_some()); - let n_layers = circuit_builder.n_layers.unwrap(); - - // ================================== - // Put cells into layers. Maintain two vectors: - // - `layers_of_cell_id` stores all cell ids in each layer; - // - `wire_ids_in_layer` stores the wire id of each cell in its layer. - // ================================== - let (layers_of_cell_id, wire_ids_in_layer) = { - let mut layers_of_cell_id = vec![vec![]; n_layers as usize]; - let mut wire_ids_in_layer = vec![0; circuit_builder.cells.len()]; - for (i, cell) in circuit_builder.cells.iter().enumerate() { - // If layer isn't assigned, then the cell is not in the circuit. - if let Some(layer) = cell.layer { - wire_ids_in_layer[i] = layers_of_cell_id[layer as usize].len(); - layers_of_cell_id[layer as usize].push(i); - } - } - (layers_of_cell_id, wire_ids_in_layer) - }; - - let mut layers = vec![Layer::default(); n_layers as usize]; - - // ================================== - // From the input layer to the output layer, construct the gates. If a - // gate has the input from multiple previous layers, then we need to - // copy them to the current layer. - // ================================== - - // Input layer if pasted from wires_in and constant. - let in_cell_ids = { - let mut in_cell_ids = BTreeMap::new(); - for (cell_id, cell) in circuit_builder.cells.iter().enumerate() { - if let Some(CellType::In(in_type)) = cell.cell_type { - in_cell_ids.entry(in_type).or_insert(vec![]).push(cell_id); - } - } - in_cell_ids - }; - - let mut input_paste_from_wits_in = vec![(0, 0); circuit_builder.n_witness_in()]; - let mut input_paste_from_counter_in = Vec::new(); - let mut input_paste_from_consts_in = Vec::new(); - let mut max_in_wit_num_vars: Option = None; - for (ty, in_cell_ids) in in_cell_ids.iter() { - let segment = ( - wire_ids_in_layer[in_cell_ids[0]], - wire_ids_in_layer[in_cell_ids[in_cell_ids.len() - 1]] + 1, /* + 1 for exclusive - * last index */ - ); - match ty { - InType::Witness(wit_id) => { - input_paste_from_wits_in[*wit_id as usize] = segment; - max_in_wit_num_vars = max_in_wit_num_vars - .map_or(Some(ceil_log2(in_cell_ids.len())), |x| { - Some(x.max(ceil_log2(in_cell_ids.len()))) - }); - } - InType::Counter(num_vars) => { - input_paste_from_counter_in.push((*num_vars, segment)); - max_in_wit_num_vars = max_in_wit_num_vars - .map_or(Some(ceil_log2(in_cell_ids.len())), |x| { - Some(x.max(ceil_log2(in_cell_ids.len()))) - }); - } - InType::Constant(constant) => { - input_paste_from_consts_in.push((*constant, segment)); - } - } - } - - layers[n_layers as usize - 1].layer_id = n_layers - 1; - for layer_id in (0..n_layers - 1).rev() { - layers[layer_id as usize].layer_id = layer_id; - // current_subsets: (old_layer_id, old_wire_id) -> new_wire_id - // It only stores the wires not in the current layer. - let new_layer_id = layer_id + 1; - let mut subsets = LayerSubsets::new( - new_layer_id, - layers_of_cell_id[new_layer_id as usize] - .len() - .next_power_of_two(), - ); - - for (i, cell_id) in layers_of_cell_id[layer_id as usize].iter().enumerate() { - let cell = &circuit_builder.cells[*cell_id]; - for gate in cell.gates.iter() { - let idx_in = gate - .idx_in - .iter() - .map(|&cell_id| { - let old_layer_id = circuit_builder.cells[cell_id].layer.unwrap(); - let old_wire_id = wire_ids_in_layer[cell_id]; - subsets.update_wire_id(old_layer_id, old_wire_id) - }) - .collect_vec(); - match idx_in.len() { - 0 => layers[layer_id as usize].add_consts.push(GateCIn { - idx_in: [], - idx_out: i, - scalar: gate.scalar, - }), - 1 => { - let gate = Gate1In { - idx_in: idx_in.clone().try_into().unwrap(), - idx_out: i, - scalar: gate.scalar, - }; - layers[layer_id as usize].adds.push(gate.clone()); - for (i, idx_in) in idx_in.iter().enumerate() { - layers[layer_id as usize].adds_fanin_mapping[i] - .entry(*idx_in) - .or_insert(vec![]) - .push(gate.clone()); - } - } - 2 => { - let gate = Gate2In { - idx_in: idx_in.clone().try_into().unwrap(), - idx_out: i, - scalar: gate.scalar, - }; - layers[layer_id as usize].mul2s.push(gate.clone()); - for (i, idx_in) in idx_in.iter().enumerate() { - layers[layer_id as usize].mul2s_fanin_mapping[i] - .entry(*idx_in) - .or_insert(vec![]) - .push(gate.clone()); - } - } - 3 => { - let gate = Gate3In { - idx_in: idx_in.clone().try_into().unwrap(), - idx_out: i, - scalar: gate.scalar, - }; - layers[layer_id as usize].mul3s.push(gate.clone()); - for (i, idx_in) in idx_in.iter().enumerate() { - layers[layer_id as usize].mul3s_fanin_mapping[i] - .entry(*idx_in) - .or_insert(vec![]) - .push(gate.clone()); - } - } - _ => unreachable!(), - } - } - } - - subsets.update_layer_info(&mut layers); - // Initialize the next layer `max_previous_num_vars` equals that of the `self.layer_id`. - layers[layer_id as usize].max_previous_num_vars = - layers[new_layer_id as usize].num_vars; - } - - // Compute the copy_to from the output layer to the wires_out. Notice - // that we don't pad the original output layer elements to the power of - // two. - let mut output_subsets = LayerSubsets::new(0, layers_of_cell_id[0].len()); - let mut output_copy_to_wits_out = vec![vec![]; circuit_builder.n_witness_out()]; - let mut output_assert_const = vec![]; - for (cell_id, cell) in circuit_builder.cells.iter().enumerate() { - if let Some(CellType::Out(out)) = cell.cell_type { - if cell.layer.is_none() { - panic!("Output cell: {:?} should have a layer.", (cell_id, cell)); - } - let old_layer_id = cell.layer.unwrap(); - let old_wire_id = wire_ids_in_layer[cell_id]; - match out { - OutType::Witness(wit_id) => { - output_copy_to_wits_out[wit_id as usize] - .push(output_subsets.update_wire_id(old_layer_id, old_wire_id)); - } - OutType::AssertConst(constant) => { - let new_wire_id = output_subsets.update_wire_id(old_layer_id, old_wire_id); - output_assert_const.push(GateCIn { - idx_in: [], - idx_out: new_wire_id, - scalar: ConstantType::Field(i64_to_field(constant)), - }); - } - } - } - } - let output_copy_to = output_copy_to_wits_out.into_iter().collect_vec(); - output_subsets.update_layer_info(&mut layers); - - // Update sumcheck steps - (0..n_layers).for_each(|layer_id| { - let mut curr_sc_steps = vec![]; - let layer = &layers[layer_id as usize]; - if layer.layer_id == 0 { - let seg = (0..1 << layer.num_vars).collect_vec(); - if circuit_builder.n_witness_out() > 1 - || circuit_builder.n_witness_out() == 1 && output_copy_to[0] != seg - || !output_assert_const.is_empty() - { - curr_sc_steps.extend([SumcheckStepType::OutputPhase1Step1]); - } - } else { - let last_layer = &layers[(layer_id - 1) as usize]; - if !last_layer.is_linear() || !layer.copy_to.is_empty() { - curr_sc_steps.extend([SumcheckStepType::Phase1Step1]); - } - } - - if layer.layer_id == n_layers - 1 { - if input_paste_from_wits_in.len() > 1 - || input_paste_from_wits_in.len() == 1 - && input_paste_from_wits_in[0] != (0, 1 << layer.num_vars) - || !input_paste_from_counter_in.is_empty() - || !input_paste_from_consts_in.is_empty() - { - curr_sc_steps.push(SumcheckStepType::InputPhase2Step1); - } - } else if layer.is_linear() { - curr_sc_steps.push(SumcheckStepType::LinearPhase2Step1); - } else { - curr_sc_steps.push(SumcheckStepType::Phase2Step1); - if !layer.mul2s.is_empty() || !layer.mul3s.is_empty() { - if layer.mul3s.is_empty() { - curr_sc_steps.push(SumcheckStepType::Phase2Step2NoStep3); - } else { - curr_sc_steps.push(SumcheckStepType::Phase2Step2); - } - } - if !layer.mul3s.is_empty() { - curr_sc_steps.push(SumcheckStepType::Phase2Step3); - } - } - layers[layer_id as usize].sumcheck_steps = curr_sc_steps; - }); - - Self { - layers, - n_witness_in: circuit_builder.n_witness_in(), - n_witness_out: circuit_builder.n_witness_out(), - paste_from_wits_in: input_paste_from_wits_in, - paste_from_counter_in: input_paste_from_counter_in, - paste_from_consts_in: input_paste_from_consts_in, - copy_to_wits_out: output_copy_to, - assert_consts: output_assert_const, - max_wit_in_num_vars: max_in_wit_num_vars, - } - } - - pub(crate) fn generate_basefield_challenges( - &self, - challenges: &[E], - ) -> HashMap> { - let mut challenge_exps = HashMap::::new(); - let mut update_const = |constant| match constant { - ConstantType::Challenge(c, _) => { - challenge_exps - .entry(c) - .or_insert(challenges[c.challenge as usize].pow([c.exp])); - } - ConstantType::ChallengeScaled(c, _, _) => { - challenge_exps - .entry(c) - .or_insert(challenges[c.challenge as usize].pow([c.exp])); - } - _ => {} - }; - self.layers.iter().for_each(|layer| { - layer - .add_consts - .iter() - .for_each(|gate| update_const(gate.scalar)); - layer.adds.iter().for_each(|gate| update_const(gate.scalar)); - layer - .mul2s - .iter() - .for_each(|gate| update_const(gate.scalar)); - layer - .mul3s - .iter() - .for_each(|gate| update_const(gate.scalar)); - }); - challenge_exps - .into_iter() - .map(|(k, v)| (k, v.as_bases().to_vec())) - .collect() - } - - pub fn output_layer_ref(&self) -> &Layer { - self.layers.first().unwrap() - } - - pub fn first_layer_ref(&self) -> &Layer { - self.layers.last().unwrap() - } - - pub fn output_num_vars(&self) -> usize { - self.output_layer_ref().num_vars - } - - pub fn output_size(&self) -> usize { - 1 << self.output_layer_ref().num_vars - } - - pub fn is_input_layer(&self, layer_id: LayerId) -> bool { - layer_id as usize == self.layers.len() - 1 - } - - pub fn is_output_layer(&self, layer_id: LayerId) -> bool { - layer_id == 0 - } -} - -impl Layer { - pub fn is_linear(&self) -> bool { - self.mul2s.is_empty() && self.mul3s.is_empty() - } - - pub fn size(&self) -> usize { - 1 << self.num_vars - } - - pub fn num_vars(&self) -> usize { - self.num_vars - } - - pub fn max_previous_num_vars(&self) -> usize { - self.max_previous_num_vars - } - - pub fn max_previous_size(&self) -> usize { - 1 << self.max_previous_num_vars - } - - pub fn paste_from_fix_variables_eq( - &self, - old_layer_id: LayerId, - current_point_eq: &[E], - ) -> Vec { - assert_eq!(current_point_eq.len(), self.size()); - self.paste_from - .get(&old_layer_id) - .unwrap() - .as_slice() - .fix_row_col_first(current_point_eq, self.max_previous_num_vars) - } - - pub fn paste_from_eval_eq( - &self, - old_layer_id: LayerId, - current_point_eq: &[E], - subset_point_eq: &[E], - ) -> E { - assert_eq!(current_point_eq.len(), self.size()); - assert_eq!(subset_point_eq.len(), self.max_previous_size()); - self.paste_from - .get(&old_layer_id) - .unwrap() - .as_slice() - .eval_col_first(current_point_eq, subset_point_eq) - } - - pub fn copy_to_fix_variables(&self, new_layer_id: LayerId, subset_point_eq: &[E]) -> Vec { - let old_wire_ids = self.copy_to.get(&new_layer_id).unwrap(); - old_wire_ids - .as_slice() - .fix_row_row_first(subset_point_eq, self.num_vars) - } - - pub fn copy_to_eval_eq( - &self, - new_layer_id: LayerId, - subset_point_eq: &[E], - current_point_eq: &[E], - ) -> E { - self.copy_to - .get(&new_layer_id) - .unwrap() - .as_slice() - .eval_row_first(subset_point_eq, current_point_eq) - } -} - -impl fmt::Debug for Layer { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "Layer {{")?; - writeln!(f, " layer_id: {}", self.layer_id)?; - writeln!(f, " num_vars: {}", self.num_vars)?; - writeln!(f, " max_previous_num_vars: {}", self.max_previous_num_vars)?; - writeln!(f, " add_consts: ")?; - for add_const in self.add_consts.iter() { - writeln!(f, " {:?}", add_const)?; - } - writeln!(f, " adds: ")?; - for add in self.adds.iter() { - writeln!(f, " {:?}", add)?; - } - writeln!(f, " mul2s: ")?; - for mul2 in self.mul2s.iter() { - writeln!(f, " {:?}", mul2)?; - } - writeln!(f, " mul3s: ")?; - for mul3 in self.mul3s.iter() { - writeln!(f, " {:?}", mul3)?; - } - writeln!(f, " copy_to: {:?}", self.copy_to)?; - writeln!(f, " paste_from: {:?}", self.paste_from)?; - writeln!(f, "}}") - } -} - -impl fmt::Debug for Circuit { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "Circuit {{")?; - writeln!(f, " layers: ")?; - for layer in self.layers.iter() { - writeln!(f, " {:?}", layer)?; - } - writeln!(f, " n_witness_in: {}", self.n_witness_in)?; - writeln!(f, " paste_from_wits_in: {:?}", self.paste_from_wits_in)?; - writeln!( - f, - " paste_from_counter_in: {:?}", - self.paste_from_counter_in - )?; - writeln!(f, " paste_from_consts_in: {:?}", self.paste_from_consts_in)?; - writeln!(f, " copy_to_wits_out: {:?}", self.copy_to_wits_out)?; - writeln!(f, " assert_const: {:?}", self.assert_consts)?; - writeln!(f, " max_wires_in_num_vars: {:?}", self.max_wit_in_num_vars)?; - writeln!(f, "}}") - } -} - -#[cfg(test)] -mod tests { - use std::collections::BTreeMap; - - use ff::Field; - use goldilocks::{Goldilocks, GoldilocksExt2}; - use simple_frontend::structs::{ChallengeConst, ChallengeId, CircuitBuilder, ConstantType}; - - use crate::structs::{Circuit, Gate, GateCIn, SumcheckStepType}; - - #[test] - fn test_copy_and_paste() { - let mut circuit_builder = CircuitBuilder::::default(); - // Layer 3 - let (_, input) = circuit_builder.create_witness_in(4); - - // Layer 2 - let mul_01 = circuit_builder.create_cell(); - circuit_builder.mul2(mul_01, input[0], input[1], Goldilocks::ONE); - - // Layer 1 - let mul_012 = circuit_builder.create_cell(); - circuit_builder.mul2(mul_012, mul_01, input[2], Goldilocks::ONE); - - // Layer 0 - let (_, mul_001123) = circuit_builder.create_witness_out(1); - circuit_builder.mul3(mul_001123[0], mul_01, mul_012, input[3], Goldilocks::ONE); - - circuit_builder.configure(); - let circuit = Circuit::new(&circuit_builder); - - assert_eq!(circuit.layers.len(), 4); - assert_eq!(circuit.layers[3].num_vars, 2); - assert_eq!(circuit.layers[2].num_vars, 1); - assert_eq!(circuit.layers[1].num_vars, 2); - assert_eq!(circuit.layers[0].num_vars, 0); - - // layers[3][2] is copied to layers[2][1], layers[3][3] is copied to layers[1][2] - // layers[2][0] is copied to layers[1][1] - - let mut expected_paste_from_2 = BTreeMap::new(); - expected_paste_from_2.insert(3, vec![1]); - assert_eq!(circuit.layers[2].paste_from, expected_paste_from_2); - - let mut expected_paste_from_1 = BTreeMap::new(); - expected_paste_from_1.insert(2, vec![1]); - expected_paste_from_1.insert(3, vec![2]); - assert_eq!(circuit.layers[1].paste_from, expected_paste_from_1); - - let mut expected_copy_to_3 = BTreeMap::new(); - expected_copy_to_3.insert(2, vec![2]); - expected_copy_to_3.insert(1, vec![3]); - assert_eq!(circuit.layers[3].copy_to, expected_copy_to_3); - - let mut expected_copy_to_2 = BTreeMap::new(); - expected_copy_to_2.insert(1, vec![0]); - assert_eq!(circuit.layers[2].copy_to, expected_copy_to_2); - } - - #[test] - fn test_paste_from_wit_in() { - let mut circuit_builder = CircuitBuilder::::default(); - - // Layer 2 - let (leaf_id, leaves) = circuit_builder.create_witness_in(6); - // Unused input elements should also be in the circuit. - let (dummy_id, _) = circuit_builder.create_witness_in(3); - let _ = circuit_builder.create_counter_in(1); - let _ = circuit_builder.create_constant_in(2, 1); - - // Layer 1 - let (_, inners) = circuit_builder.create_witness_out(2); - circuit_builder.mul2(inners[0], leaves[0], leaves[1], Goldilocks::ONE); - circuit_builder.mul2(inners[1], leaves[2], leaves[3], Goldilocks::ONE); - - // Layer 0 - let (_, root) = circuit_builder.create_witness_out(1); - circuit_builder.mul2(root[0], inners[0], inners[1], Goldilocks::ONE); - - circuit_builder.configure(); - let circuit = Circuit::new(&circuit_builder); - - assert_eq!(circuit.layers.len(), 3); - assert_eq!(circuit.layers[2].num_vars, 4); - assert_eq!(circuit.layers[1].num_vars, 1); - // Layers[1][0] -> Layers[0][1], Layers[1][1] -> Layers[0][2] - assert_eq!(circuit.layers[0].num_vars, 2); - - let mut expected_paste_from_wits_in = vec![(0, 0); 2]; - expected_paste_from_wits_in[leaf_id as usize] = (0usize, 6usize); - expected_paste_from_wits_in[dummy_id as usize] = (6, 9); - let expected_paste_from_counter_in = vec![(1, (9, 11))]; - let expected_paste_from_consts_in = vec![(1, (11, 13))]; - assert_eq!(circuit.paste_from_wits_in, expected_paste_from_wits_in); - assert_eq!( - circuit.paste_from_counter_in, - expected_paste_from_counter_in - ); - assert_eq!(circuit.paste_from_consts_in, expected_paste_from_consts_in); - } - - #[test] - fn test_copy_to_wit_out() { - let mut circuit_builder = CircuitBuilder::::default(); - // Layer 2 - let (_, leaves) = circuit_builder.create_witness_in(4); - - // Layer 1 - let (_inner_id, inners) = circuit_builder.create_witness_out(2); - circuit_builder.mul2(inners[0], leaves[0], leaves[1], Goldilocks::ONE); - circuit_builder.mul2(inners[1], leaves[2], leaves[3], Goldilocks::ONE); - - // Layer 0 - let root = circuit_builder.create_cell(); - circuit_builder.mul2(root, inners[0], inners[1], Goldilocks::ONE); - circuit_builder.assert_const(root, 1); - - circuit_builder.configure(); - let circuit = Circuit::new(&circuit_builder); - - assert_eq!(circuit.layers.len(), 3); - assert_eq!(circuit.layers[2].num_vars, 2); - assert_eq!(circuit.layers[1].num_vars, 1); - assert_eq!(circuit.layers[0].num_vars, 2); - // Layers[1][0] -> Layers[0][1], Layers[1][1] -> Layers[0][2] - let mut expected_copy_to_1 = BTreeMap::new(); - expected_copy_to_1.insert(0, vec![0, 1]); - let mut expected_paste_from_0 = BTreeMap::new(); - expected_paste_from_0.insert(1, vec![1, 2]); - - assert_eq!(circuit.layers[1].copy_to, expected_copy_to_1); - assert_eq!(circuit.layers[0].paste_from, expected_paste_from_0); - - let expected_copy_to_wits_out = vec![vec![1, 2]]; - let expected_assert_const = vec![GateCIn { - idx_in: [], - idx_out: 0, - scalar: ConstantType::Field(Goldilocks::ONE), - }]; - - assert_eq!(circuit.copy_to_wits_out, expected_copy_to_wits_out); - assert_eq!(circuit.assert_consts, expected_assert_const); - } - - #[test] - fn test_rlc_circuit() { - let mut circuit_builder = CircuitBuilder::::default(); - // Layer 2 - let (_, leaves) = circuit_builder.create_witness_in(4); - - // Layer 1 - let inners = circuit_builder.create_ext_cells(2); - circuit_builder.rlc(&inners[0], &[leaves[0], leaves[1]], 0 as ChallengeId); - circuit_builder.rlc(&inners[1], &[leaves[2], leaves[3]], 1 as ChallengeId); - - // Layer 0 - let (_, roots) = circuit_builder.create_ext_witness_out(1); - circuit_builder.mul2_ext(&roots[0], &inners[0], &inners[1], Goldilocks::ONE); - - circuit_builder.configure(); - let circuit = Circuit::new(&circuit_builder); - - let expected_layer2_add_consts = vec![ - Gate { - idx_in: [], - idx_out: 0, - scalar: ConstantType::::Challenge( - ChallengeConst { - challenge: 0, - exp: 2, - }, - 0, - ), - }, - Gate { - idx_in: [], - idx_out: 1, - scalar: ConstantType::Challenge( - ChallengeConst { - challenge: 0, - exp: 2, - }, - 1, - ), - }, - Gate { - idx_in: [], - idx_out: 2, - scalar: ConstantType::Challenge( - ChallengeConst { - challenge: 1, - exp: 2, - }, - 0, - ), - }, - Gate { - idx_in: [], - idx_out: 3, - scalar: ConstantType::Challenge( - ChallengeConst { - challenge: 1, - exp: 2, - }, - 1, - ), - }, - ]; - - let expected_layer2_adds = vec![ - Gate { - idx_in: [0], - idx_out: 0, - scalar: ConstantType::::Challenge( - ChallengeConst { - challenge: 0, - exp: 0, - }, - 0, - ), - }, - Gate { - idx_in: [1], - idx_out: 0, - scalar: ConstantType::Challenge( - ChallengeConst { - challenge: 0, - exp: 1, - }, - 0, - ), - }, - Gate { - idx_in: [0], - idx_out: 1, - scalar: ConstantType::Challenge( - ChallengeConst { - challenge: 0, - exp: 0, - }, - 1, - ), - }, - Gate { - idx_in: [1], - idx_out: 1, - scalar: ConstantType::Challenge( - ChallengeConst { - challenge: 0, - exp: 1, - }, - 1, - ), - }, - Gate { - idx_in: [2], - idx_out: 2, - scalar: ConstantType::Challenge( - ChallengeConst { - challenge: 1, - exp: 0, - }, - 0, - ), - }, - Gate { - idx_in: [3], - idx_out: 2, - scalar: ConstantType::Challenge( - ChallengeConst { - challenge: 1, - exp: 1, - }, - 0, - ), - }, - Gate { - idx_in: [2], - idx_out: 3, - scalar: ConstantType::Challenge( - ChallengeConst { - challenge: 1, - exp: 0, - }, - 1, - ), - }, - Gate { - idx_in: [3], - idx_out: 3, - scalar: ConstantType::Challenge( - ChallengeConst { - challenge: 1, - exp: 1, - }, - 1, - ), - }, - ]; - - let expected_layer1_mul2s = vec![ - Gate { - idx_in: [0, 2], - idx_out: 0, - scalar: ConstantType::::Field(Goldilocks::ONE), - }, - Gate { - idx_in: [0, 3], - idx_out: 1, - scalar: ConstantType::Field(Goldilocks::ONE), - }, - Gate { - idx_in: [1, 2], - idx_out: 2, - scalar: ConstantType::Field(Goldilocks::ONE), - }, - Gate { - idx_in: [1, 3], - idx_out: 3, - scalar: ConstantType::Field(Goldilocks::ONE), - }, - ]; - - assert_eq!(circuit.layers[2].add_consts, expected_layer2_add_consts); - assert_eq!(circuit.layers[2].adds, expected_layer2_adds); - assert_eq!(circuit.layers[1].mul2s, expected_layer1_mul2s); - } - - #[test] - fn test_selector_sumcheck_steps() { - let mut circuit_builder = CircuitBuilder::::default(); - let _ = circuit_builder.create_constant_in(6, 1); - circuit_builder.configure(); - let circuit = Circuit::new(&circuit_builder); - assert_eq!(circuit.layers.len(), 1); - assert_eq!(circuit.layers[0].sumcheck_steps, vec![ - SumcheckStepType::InputPhase2Step1 - ]); - } - - #[test] - fn test_lookup_inner_sumcheck_steps() { - let mut circuit_builder = CircuitBuilder::::default(); - - // Layer 2 - let (_, input) = circuit_builder.create_ext_witness_in(4); - // Layer 0 - let output = circuit_builder.create_ext_cells(2); - // denominator - circuit_builder.mul2_ext( - &output[0], // output_den - &input[0], // input_den[0] - &input[2], // input_den[1] - Goldilocks::ONE, - ); - - // numerator - circuit_builder.mul2_ext( - &output[1], // output_num - &input[0], // input_den[0] - &input[3], // input_num[1] - Goldilocks::ONE, - ); - circuit_builder.mul2_ext( - &output[1], // output_num - &input[2], // input_den[1] - &input[1], // input_num[0] - Goldilocks::ONE, - ); - - circuit_builder.configure(); - let circuit = Circuit::new(&circuit_builder); - - assert_eq!(circuit.layers.len(), 3); - // Single input witness, therefore no input phase 2 steps. - assert_eq!(circuit.layers[2].sumcheck_steps, vec![ - SumcheckStepType::Phase1Step1 - ]); - // There are only one incoming evals since the last layer is linear, and - // no subset evals. Therefore, there are no phase1 steps. - assert_eq!(circuit.layers[1].sumcheck_steps, vec![ - SumcheckStepType::Phase2Step1, - SumcheckStepType::Phase2Step2NoStep3, - ]); - // Output layer, single output witness, therefore no output phase 1 steps. - assert_eq!(circuit.layers[0].sumcheck_steps, vec![ - SumcheckStepType::LinearPhase2Step1 - ]); - } - - #[test] - fn test_product_sumcheck_steps() { - let mut circuit_builder = CircuitBuilder::::default(); - let (_, input) = circuit_builder.create_witness_in(2); - let (_, output) = circuit_builder.create_witness_out(1); - circuit_builder.mul2(output[0], input[0], input[1], Goldilocks::ONE); - circuit_builder.configure(); - let circuit = Circuit::new(&circuit_builder); - - assert_eq!(circuit.layers.len(), 2); - // Single input witness, therefore no input phase 2 steps. - assert_eq!(circuit.layers[1].sumcheck_steps, vec![ - SumcheckStepType::Phase1Step1 - ]); - // Output layer, single output witness, therefore no output phase 1 steps. - assert_eq!(circuit.layers[0].sumcheck_steps, vec![ - SumcheckStepType::Phase2Step1, - SumcheckStepType::Phase2Step2NoStep3 - ]); - } -} diff --git a/gkr/src/circuit/circuit_witness.rs b/gkr/src/circuit/circuit_witness.rs deleted file mode 100644 index dd88319aa..000000000 --- a/gkr/src/circuit/circuit_witness.rs +++ /dev/null @@ -1,489 +0,0 @@ -use std::{collections::HashMap, sync::Arc}; - -use crate::circuit::EvaluateConstant; -use ff::Field; -use ff_ext::ExtensionField; -use itertools::Itertools; -use multilinear_extensions::{ - mle::{ - DenseMultilinearExtension, InstanceIntoIterator, InstanceIntoIteratorMut, IntoInstanceIter, - IntoInstanceIterMut, IntoMLE, MultilinearExtension, - }, - virtual_poly_v2::ArcMultilinearExtension, -}; -use simple_frontend::structs::{ChallengeConst, LayerId}; -use std::fmt::Debug; -use sumcheck::util::ceil_log2; - -use crate::{ - structs::{Circuit, CircuitWitness}, - utils::i64_to_field, -}; - -impl<'a, E: ExtensionField> CircuitWitness<'a, E> { - /// Initialize the structure of the circuit witness. - pub fn new(circuit: &Circuit, challenges: Vec) -> Self { - let create_default = |size| { - (0..size) - .map(|_| { - let a: ArcMultilinearExtension = - Arc::new(DenseMultilinearExtension::default()); - a - }) - .collect::>>() - }; - Self { - layers: create_default(circuit.layers.len()), - witness_in: create_default(circuit.n_witness_in), - witness_out: create_default(circuit.n_witness_out), - n_instances: 0, - challenges: circuit.generate_basefield_challenges(&challenges), - } - } - - /// Generate a fresh instance for the circuit, return layer witnesses and - /// wire out witnesses. - fn new_instances( - circuit: &Circuit, - wits_in: &[ArcMultilinearExtension<'a, E>], - challenges: &HashMap>, - n_instances: usize, - ) -> ( - Vec>, - Vec>, - ) { - let n_layers = circuit.layers.len(); - let mut layer_wits = vec![DenseMultilinearExtension::default(); n_layers]; - - // The first layer. - layer_wits[n_layers - 1] = { - let mut layer_wit = - vec![E::BaseField::ZERO; circuit.layers[n_layers - 1].size() * n_instances]; - for (wit_id, (l, r)) in circuit.paste_from_wits_in.iter().enumerate() { - let layer_wit_iter: InstanceIntoIteratorMut = - layer_wit.into_instance_iter_mut(n_instances); - let wit_in = wits_in[wit_id].get_base_field_vec(); - let wit_in_iter: InstanceIntoIterator = - wit_in.into_instance_iter(n_instances); - for (layer_wit, wit_in) in layer_wit_iter.zip_eq(wit_in_iter) { - for (layer_wit_elem, wit_in_elem) in - layer_wit[*l..*r].iter_mut().zip(&wit_in[..*r - *l]) - { - *layer_wit_elem = *wit_in_elem; - } - } - } - for (constant, (l, r)) in circuit.paste_from_consts_in.iter() { - let layer_wit_iter: InstanceIntoIteratorMut = - layer_wit.into_instance_iter_mut(n_instances); - for layer_wit in layer_wit_iter { - for layer_wit_elem in &mut layer_wit[*l..*r] { - *layer_wit_elem = i64_to_field(*constant); - } - } - } - for (num_vars, (l, r)) in circuit.paste_from_counter_in.iter() { - let layer_wit_iter: InstanceIntoIteratorMut = - layer_wit.into_instance_iter_mut(n_instances); - for (instance_id, layer_wit) in layer_wit_iter.enumerate() { - for (i, layer_wit_elem) in layer_wit[*l..*r].iter_mut().enumerate() { - *layer_wit_elem = E::BaseField::from(((instance_id << num_vars) ^ i) as u64) - } - } - } - layer_wit.into_mle() - }; - - for (layer_id, layer) in circuit.layers.iter().enumerate().rev().skip(1) { - let size = circuit.layers[layer_id].size(); - let mut current_layer_wit = vec![E::BaseField::ZERO; size * n_instances]; - - let current_layer_wit_instance_iter: InstanceIntoIteratorMut = - current_layer_wit.into_instance_iter_mut(n_instances); - current_layer_wit_instance_iter.enumerate().for_each( - |(instance_id, current_layer_wit)| { - layer - .paste_from - .iter() - .for_each(|(old_layer_id, new_wire_ids)| { - let layer_wits = - layer_wits[*old_layer_id as usize].get_base_field_vec(); - let old_layer_instance_start_index = - instance_id * circuit.layers[*old_layer_id as usize].size(); - - new_wire_ids.iter().enumerate().for_each( - |(subset_wire_id, new_wire_id)| { - let old_wire_id = circuit.layers[*old_layer_id as usize] - .copy_to - .get(&(layer_id as LayerId)) - .unwrap()[subset_wire_id]; - current_layer_wit[*new_wire_id] = - layer_wits[old_layer_instance_start_index + old_wire_id]; - }, - ); - }); - - let last_layer_wit = layer_wits[layer_id + 1].get_base_field_vec(); - let last_layer_instance_start_index = - instance_id * circuit.layers[layer_id + 1].size(); - for add_const in layer.add_consts.iter() { - current_layer_wit[add_const.idx_out] += add_const.scalar.eval(challenges); - } - - for add in layer.adds.iter() { - current_layer_wit[add.idx_out] += last_layer_wit - [last_layer_instance_start_index + add.idx_in[0]] - * add.scalar.eval(challenges); - } - - for mul2 in layer.mul2s.iter() { - current_layer_wit[mul2.idx_out] += last_layer_wit - [last_layer_instance_start_index + mul2.idx_in[0]] - * last_layer_wit[last_layer_instance_start_index + mul2.idx_in[1]] - * mul2.scalar.eval(challenges); - } - - for mul3 in layer.mul3s.iter() { - current_layer_wit[mul3.idx_out] += last_layer_wit - [last_layer_instance_start_index + mul3.idx_in[0]] - * last_layer_wit[last_layer_instance_start_index + mul3.idx_in[1]] - * last_layer_wit[last_layer_instance_start_index + mul3.idx_in[2]] - * mul3.scalar.eval(challenges); - } - }, - ); - - layer_wits[layer_id] = current_layer_wit.into_mle(); - } - let mut wits_out = vec![DenseMultilinearExtension::default(); circuit.n_witness_out]; - let output_layer_wit = layer_wits[0].get_base_field_vec(); - - circuit - .copy_to_wits_out - .iter() - .enumerate() - .for_each(|(wit_id, old_wire_ids)| { - let mut wit_out = - vec![E::BaseField::ZERO; old_wire_ids.len().next_power_of_two() * n_instances]; - let wit_out_instance_iter: InstanceIntoIteratorMut = - wit_out.into_instance_iter_mut(n_instances); - for (instance_id, wit_out) in wit_out_instance_iter.enumerate() { - let output_layer_instance_start_index = instance_id * circuit.layers[0].size(); - wit_out.iter_mut().zip(old_wire_ids.iter()).for_each( - |(wit_out_value, old_wire_id)| { - *wit_out_value = - output_layer_wit[output_layer_instance_start_index + *old_wire_id] - }, - ); - } - wits_out[wit_id] = wit_out.into_mle(); - }); - - (layer_wits, wits_out) - } - - pub fn add_instance( - &mut self, - circuit: &Circuit, - wits_in: Vec>, - ) { - self.add_instances(circuit, wits_in, 1); - } - - pub fn set_instances( - &mut self, - circuit: &Circuit, - new_wits_in: Vec>, - n_instances: usize, - ) { - assert_eq!(new_wits_in.len(), circuit.n_witness_in); - assert!(n_instances.is_power_of_two()); - assert!( - new_wits_in - .iter() - .all(|wit_in| wit_in.evaluations().len() % n_instances == 0) - ); - - let (inferred_layer_wits, inferred_wits_out) = - CircuitWitness::new_instances(circuit, &new_wits_in, &self.challenges, n_instances); - - assert_eq!(self.layers.len(), inferred_layer_wits.len()); - self.layers = inferred_layer_wits.into_iter().map(|n| n.into()).collect(); - assert_eq!(self.witness_out.len(), inferred_wits_out.len()); - self.witness_out = inferred_wits_out.into_iter().map(|n| n.into()).collect(); - assert_eq!(self.witness_in.len(), new_wits_in.len()); - self.witness_in = new_wits_in; - - self.n_instances = n_instances; - - // check correctness in debug build - if cfg!(debug_assertions) { - self.check_correctness(circuit); - } - } - - pub fn add_instances( - &mut self, - circuit: &Circuit, - new_wits_in: Vec>, - n_instances: usize, - ) { - assert_eq!(new_wits_in.len(), circuit.n_witness_in); - assert!(n_instances.is_power_of_two()); - assert!( - new_wits_in - .iter() - .all(|wit_in| wit_in.evaluations().len() % n_instances == 0) - ); - - let (inferred_layer_wits, inferred_wits_out) = CircuitWitness::new_instances( - circuit, - &new_wits_in - .iter() - .map(|w| { - let w: ArcMultilinearExtension = Arc::new(w.get_ranged_mle(1, 0)); - w - }) - .collect::>>(), - &self.challenges, - n_instances, - ); - - for (wit_out, inferred_wits_out) in self - .witness_out - .iter_mut() - .zip(inferred_wits_out.into_iter()) - { - Arc::get_mut(wit_out).unwrap().merge(inferred_wits_out); - } - - for (wit_in, new_wit_in) in self.witness_in.iter_mut().zip(new_wits_in.into_iter()) { - Arc::get_mut(wit_in).unwrap().merge(new_wit_in); - } - - // Merge self and circuit_witness. - for (layer_wit, inferred_layer_wit) in - self.layers.iter_mut().zip(inferred_layer_wits.into_iter()) - { - Arc::get_mut(layer_wit).unwrap().merge(inferred_layer_wit); - } - - self.n_instances += n_instances; - - // check correctness in debug build - if cfg!(debug_assertions) { - self.check_correctness(circuit); - } - } - - pub fn instance_num_vars(&self) -> usize { - ceil_log2(self.n_instances) - } - - pub fn check_correctness(&self, _circuit: &Circuit) { - // Check input. - - // let input_layer_wits = self.layers.last().unwrap(); - // let wits_in = self.witness_in_ref(); - // for copy_id in 0..self.n_instances { - // for (wit_id, (l, r)) in circuit.paste_from_wits_in.iter().enumerate() { - // for (subset_wire_id, new_wire_id) in (*l..*r).enumerate() { - // assert_eq!( - // input_layer_wits.instances[copy_id][new_wire_id], - // wits_in[wit_id].instances[copy_id][subset_wire_id], - // "input layer: {}, copy_id: {}, wire_id: {}, got != expected: {:?} != - // {:?}", circuit.layers.len() - 1, - // copy_id, - // new_wire_id, - // input_layer_wits.instances[copy_id][new_wire_id], - // wits_in[wit_id].instances[copy_id][subset_wire_id] - // ); - // } - // } - // for (constant, (l, r)) in circuit.paste_from_consts_in.iter() { - // for (_subset_wire_id, new_wire_id) in (*l..*r).enumerate() { - // assert_eq!( - // input_layer_wits.instances[copy_id][new_wire_id], - // i64_to_field(*constant), - // "input layer: {}, copy_id: {}, wire_id: {}, got != expected: {:?} != - // {:?}", circuit.layers.len() - 1, - // copy_id, - // new_wire_id, - // input_layer_wits.instances[copy_id][new_wire_id], - // constant - // ); - // } - // } - // for (num_vars, (l, r)) in circuit.paste_from_counter_in.iter() { - // for (subset_wire_id, new_wire_id) in (*l..*r).enumerate() { - // assert_eq!( - // input_layer_wits.instances[copy_id][new_wire_id], - // i64_to_field(((copy_id << num_vars) ^ subset_wire_id) as i64), - // "input layer: {}, copy_id: {}, wire_id: {}, got != expected: {:?} != - // {:?}", circuit.layers.len() - 1, - // copy_id, - // new_wire_id, - // input_layer_wits.instances[copy_id][new_wire_id], - // (copy_id << num_vars) ^ subset_wire_id - // ); - // } - // } - // } - - // for (layer_id, (layer_witnesses, layer)) in self - // .layers - // .iter() - // .zip(circuit.layers.iter()) - // .enumerate() - // .rev() - // .skip(1) - // { - // let prev_layer_wits = &self.layers[layer_id + 1]; - // for (copy_id, (prev, curr)) in prev_layer_wits - // .instances - // .iter() - // .zip(layer_witnesses.instances.iter()) - // .enumerate() - // { - // let mut expected = vec![E::ZERO; curr.len()]; - // for add_const in layer.add_consts.iter() { - // expected[add_const.idx_out] += add_const.scalar.eval(&self.challenges); - // } - // for add in layer.adds.iter() { - // expected[add.idx_out] += - // prev[add.idx_in[0]] * add.scalar.eval(&self.challenges); - // } - // for mul2 in layer.mul2s.iter() { - // expected[mul2.idx_out] += prev[mul2.idx_in[0]] - // * prev[mul2.idx_in[1]] - // * mul2.scalar.eval(&self.challenges); - // } - // for mul3 in layer.mul3s.iter() { - // expected[mul3.idx_out] += prev[mul3.idx_in[0]] - // * prev[mul3.idx_in[1]] - // * prev[mul3.idx_in[2]] - // * mul3.scalar.eval(&self.challenges); - // } - - // let mut expected_max_previous_size = prev.len(); - // for (old_layer_id, new_wire_ids) in layer.paste_from.iter() { - // expected_max_previous_size = - // expected_max_previous_size.max(new_wire_ids.len()); for - // (subset_wire_id, new_wire_id) in new_wire_ids.iter().enumerate() { - // let old_wire_id = circuit.layers[*old_layer_id as usize] - // .copy_to .get(&(layer_id as LayerId)) - // .unwrap()[subset_wire_id]; - // expected[*new_wire_id] = - // self.layers[*old_layer_id as usize].instances[copy_id][old_wire_id]; - // } - // } - // assert_eq!( - // ceil_log2(expected_max_previous_size), - // layer.max_previous_num_vars, - // "layer: {}, expected_max_previous_size: {}, got: {}", - // layer_id, - // expected_max_previous_size, - // layer.max_previous_num_vars - // ); - // for (wire_id, (got, expected)) in curr.iter().zip(expected.iter()).enumerate() { - // assert_eq!( - // *got, *expected, - // "layer: {}, copy_id: {}, wire_id: {}, got != expected: {:?} != {:?}", - // layer_id, copy_id, wire_id, got, expected - // ); - // } - - // if layer_id != 0 { - // for (new_layer_id, old_wire_ids) in layer.copy_to.iter() { - // for (subset_wire_id, old_wire_id) in old_wire_ids.iter().enumerate() { - // let new_wire_id = circuit.layers[*new_layer_id as usize] - // .paste_from - // .get(&(layer_id as LayerId)) - // .unwrap()[subset_wire_id]; - // assert_eq!( - // curr[*old_wire_id], - // self.layers[*new_layer_id as - // usize].instances[copy_id][new_wire_id], "copy_to check: - // layer: {}, copy_id: {}, wire_id: {}, got != expected: {:?} != {:?}", - // layer_id, copy_id, - // old_wire_id, - // curr[*old_wire_id], - // self.layers[*new_layer_id as - // usize].instances[copy_id][new_wire_id] ) - // } - // } - // } - // } - // } - - // let output_layer_witness = &self.layers[0]; - // let wits_out = self.witness_out_ref(); - // for (wit_id, old_wire_ids) in circuit.copy_to_wits_out.iter().enumerate() { - // for copy_id in 0..self.n_instances { - // for (new_wire_id, old_wire_id) in old_wire_ids.iter().enumerate() { - // assert_eq!( - // output_layer_witness.instances[copy_id][*old_wire_id], - // wits_out[wit_id].instances[copy_id][new_wire_id] - // ); - // } - // } - // } - // for gate in circuit.assert_consts.iter() { - // if let ConstantType::Field(constant) = gate.scalar { - // for copy_id in 0..self.n_instances { - // assert_eq!( - // output_layer_witness.instances[copy_id][gate.idx_out], - // constant - // ); - // } - // } - // } - } -} - -impl<'a, E: ExtensionField> CircuitWitness<'a, E> { - pub fn output_layer_witness_ref(&self) -> &ArcMultilinearExtension<'a, E> { - self.layers.first().unwrap() - } - - pub fn n_instances(&self) -> usize { - self.n_instances - } - - pub fn witness_in_ref(&self) -> &[ArcMultilinearExtension<'a, E>] { - &self.witness_in - } - - pub fn witness_out_ref(&self) -> &[ArcMultilinearExtension<'a, E>] { - &self.witness_out - } - - pub fn challenges(&self) -> &HashMap> { - &self.challenges - } - - pub fn layers_ref(&self) -> &[ArcMultilinearExtension<'a, E>] { - &self.layers - } -} - -impl<'a, F: ExtensionField> Debug for CircuitWitness<'a, F> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "CircuitWitness {{")?; - writeln!(f, " n_instances: {}", self.n_instances)?; - writeln!(f, " layers: ")?; - for (i, layer) in self.layers.iter().enumerate() { - writeln!(f, " {}: {:?}", i, layer.evaluations())?; - } - writeln!(f, " wires_in: ")?; - for (i, wire) in self.witness_in.iter().enumerate() { - writeln!(f, " {}: {:?}", i, &wire.evaluations())?; - } - writeln!(f, " wires_out: ")?; - for (i, wire) in self.witness_out.iter().enumerate() { - writeln!(f, " {}: {:?}", i, &wire.evaluations())?; - } - writeln!(f, " challenges: {:?}", self.challenges)?; - writeln!(f, "}}") - } -} diff --git a/gkr/src/error.rs b/gkr/src/error.rs deleted file mode 100644 index d5718272c..000000000 --- a/gkr/src/error.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[derive(Debug)] -pub enum GKRError { - InvalidCircuit, - VerifyError(&'static str), -} diff --git a/gkr/src/gadgets/keccak256.rs b/gkr/src/gadgets/keccak256.rs deleted file mode 100644 index 8d71c21c7..000000000 --- a/gkr/src/gadgets/keccak256.rs +++ /dev/null @@ -1,577 +0,0 @@ -#![allow(clippy::manual_memcpy)] -#![allow(clippy::needless_range_loop)] - -use crate::{ - error::GKRError, - structs::{Circuit, CircuitWitness, GKRInputClaims, IOPProof, IOPProverState, PointAndEval}, -}; -use ark_std::rand::{ - Rng, RngCore, SeedableRng, - rngs::{OsRng, StdRng}, -}; -use ff::Field; -use ff_ext::ExtensionField; -use itertools::{Itertools, izip}; -use multilinear_extensions::{ - mle::DenseMultilinearExtension, virtual_poly_v2::ArcMultilinearExtension, -}; -use simple_frontend::structs::CircuitBuilder; -use std::iter; -use sumcheck::util::ceil_log2; -use transcript::Transcript; - -const THETA: [(usize, [usize; 5], [usize; 5]); 25] = [ - // format: ( - // x + y*5, - // [(x+4, 0), (x+4, 1),... (x+4, 4)], // input - // [(x+1, 0), (x+1, 1), ..., (x+1, 4)] // rotated input - // ) - (0, [4, 9, 14, 19, 24], [1, 6, 11, 16, 21]), - (1, [0, 5, 10, 15, 20], [2, 7, 12, 17, 22]), - (2, [1, 6, 11, 16, 21], [3, 8, 13, 18, 23]), - (3, [2, 7, 12, 17, 22], [4, 9, 14, 19, 24]), - (4, [3, 8, 13, 18, 23], [0, 5, 10, 15, 20]), - (5, [4, 9, 14, 19, 24], [1, 6, 11, 16, 21]), - (6, [0, 5, 10, 15, 20], [2, 7, 12, 17, 22]), - (7, [1, 6, 11, 16, 21], [3, 8, 13, 18, 23]), - (8, [2, 7, 12, 17, 22], [4, 9, 14, 19, 24]), - (9, [3, 8, 13, 18, 23], [0, 5, 10, 15, 20]), - (10, [4, 9, 14, 19, 24], [1, 6, 11, 16, 21]), - (11, [0, 5, 10, 15, 20], [2, 7, 12, 17, 22]), - (12, [1, 6, 11, 16, 21], [3, 8, 13, 18, 23]), - (13, [2, 7, 12, 17, 22], [4, 9, 14, 19, 24]), - (14, [3, 8, 13, 18, 23], [0, 5, 10, 15, 20]), - (15, [4, 9, 14, 19, 24], [1, 6, 11, 16, 21]), - (16, [0, 5, 10, 15, 20], [2, 7, 12, 17, 22]), - (17, [1, 6, 11, 16, 21], [3, 8, 13, 18, 23]), - (18, [2, 7, 12, 17, 22], [4, 9, 14, 19, 24]), - (19, [3, 8, 13, 18, 23], [0, 5, 10, 15, 20]), - (20, [4, 9, 14, 19, 24], [1, 6, 11, 16, 21]), - (21, [0, 5, 10, 15, 20], [2, 7, 12, 17, 22]), - (22, [1, 6, 11, 16, 21], [3, 8, 13, 18, 23]), - (23, [2, 7, 12, 17, 22], [4, 9, 14, 19, 24]), - (24, [3, 8, 13, 18, 23], [0, 5, 10, 15, 20]), -]; - -const RHO: [u32; 24] = [ - 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44, -]; - -const PI: [usize; 24] = [ - 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1, -]; - -const ROUNDS: usize = 24; - -const RC: [u64; ROUNDS] = [ - 1u64, - 0x8082u64, - 0x800000000000808au64, - 0x8000000080008000u64, - 0x808bu64, - 0x80000001u64, - 0x8000000080008081u64, - 0x8000000000008009u64, - 0x8au64, - 0x88u64, - 0x80008009u64, - 0x8000000au64, - 0x8000808bu64, - 0x800000000000008bu64, - 0x8000000000008089u64, - 0x8000000000008003u64, - 0x8000000000008002u64, - 0x8000000000000080u64, - 0x800au64, - 0x800000008000000au64, - 0x8000000080008081u64, - 0x8000000000008080u64, - 0x80000001u64, - 0x8000000080008008u64, -]; - -/// Bits of a word in big-endianess -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -struct Word([usize; 64]); - -impl Default for Word { - fn default() -> Self { - Self([0; 64]) - } -} - -impl Word { - fn new(cb: &mut CircuitBuilder) -> Self { - Self(cb.create_cells(64).try_into().unwrap()) - } - - fn rotate_left(&self, mid: usize) -> Self { - let mut word = *self; - word.0.rotate_left(mid); - word - } -} - -// out = lhs ^ rhs = lhs + rhs - 2 * lhs * rhs -fn xor(cb: &mut CircuitBuilder, lhs: &Word, rhs: &Word) -> Word { - let out = Word::new(cb); - izip!(&out.0, &lhs.0, &rhs.0).for_each(|(out, lhs, rhs)| { - cb.add(*out, *lhs, E::BaseField::ONE); - cb.add(*out, *rhs, E::BaseField::ONE); - cb.mul2(*out, *lhs, *rhs, -E::BaseField::ONE.double()); - }); - out -} - -// out = lhs ^ rhs = lhs + rhs - 2 * lhs * rhs -fn relay(cb: &mut CircuitBuilder, lhs: &Word) -> Word { - let out = Word::new(cb); - izip!(&out.0, &lhs.0).for_each(|(out, lhs)| { - cb.add(*out, *lhs, E::BaseField::ONE); - }); - out -} - -// out = !lhs & rhs -#[allow(dead_code)] -fn not_lhs_and_rhs(cb: &mut CircuitBuilder, lhs: &Word, rhs: &Word) -> Word { - let out = Word::new(cb); - izip!(&out.0, &lhs.0, &rhs.0).for_each(|(out, lhs, rhs)| { - cb.add(*out, *rhs, E::BaseField::ONE); - cb.mul2(*out, *lhs, *rhs, -E::BaseField::ONE); - }); - out -} - -// (x0 + x1 + x2) - 2x0x2 - 2x1x2 - 2x0x1 + 4x0x1x2 -fn xor3(cb: &mut CircuitBuilder, words: &[Word; 3]) -> Word { - let out = Word::new(cb); - izip!(&out.0, &words[0].0, &words[1].0, &words[2].0).for_each( - |(out, wire_0, wire_1, wire_2)| { - // (x0 + x1 + x2) - cb.add(*out, *wire_0, E::BaseField::ONE); - cb.add(*out, *wire_1, E::BaseField::ONE); - cb.add(*out, *wire_2, E::BaseField::ONE); - // - 2x0x2 - 2x1x2 - 2x0x1 - cb.mul2(*out, *wire_0, *wire_1, -E::BaseField::ONE.double()); - cb.mul2(*out, *wire_0, *wire_2, -E::BaseField::ONE.double()); - cb.mul2(*out, *wire_1, *wire_2, -E::BaseField::ONE.double()); - // 4x0x1x2 - cb.mul3( - *out, - *wire_0, - *wire_1, - *wire_2, - E::BaseField::ONE.double().double(), - ); - }, - ); - out -} - -// chi truth table -// | x0 | x1 | x2 | x0 ^ ((not x1) & x2) | -// |----|----|----|----------------------| -// | 0 | 0 | 0 | 0 | -// | 0 | 0 | 1 | 1 | -// | 0 | 1 | 0 | 0 | -// | 0 | 1 | 1 | 0 | -// | 1 | 0 | 0 | 1 | -// | 1 | 0 | 1 | 0 | -// | 1 | 1 | 0 | 1 | -// | 1 | 1 | 1 | 1 | -// (1-x0)*(1-x1)*(x2) + x0(1-x1)(1-x2) + x0x1(1-x2) + x0x1x2 -// = x2 - x0x2 - x1x2 + x0x1x2 + x0 - x0x1 - x0x2 + x0x1x2 + x0x1 - x0x1x2 + x0x1x2 -// = (x0 + x2) - 2x0x2 - x1x2 + 2x0x1x2 -fn chi(cb: &mut CircuitBuilder, words: &[Word; 3]) -> Word { - let out = Word::new(cb); - izip!(&out.0, &words[0].0, &words[1].0, &words[2].0).for_each( - |(out, wire_0, wire_1, wire_2)| { - // (x0 + x2) - cb.add(*out, *wire_0, E::BaseField::ONE); - cb.add(*out, *wire_2, E::BaseField::ONE); - // - 2x0x2 - x1x2 - cb.mul2(*out, *wire_0, *wire_2, -E::BaseField::ONE.double()); - cb.mul2(*out, *wire_1, *wire_2, -E::BaseField::ONE); - // 2x0x1x2 - cb.mul3(*out, *wire_0, *wire_1, *wire_2, E::BaseField::ONE.double()); - }, - ); - out -} - -// chi_output xor constant -// = chi_output + constant - 2*chi_output*constant -// = c + (x0 + x2) - 2x0x2 - x1x2 + 2x0x1x2 - 2(c*x0 + c*x2 - 2c*x0*x2 - c*x1*x2 + 2*c*x0*x1*x2) -// = x0 + x2 + c - 2*x0*x2 - x1*x2 + 2*x0*x1*x2 - 2*c*x0 - 2*c*x2 + 4*c*x0*x2 + 2*c*x1*x2 - -// 4*c*x0*x1*x2 = x0*(1-2c) + x2*(1-2c) + c + x0*x2*(-2 + 4c) + x1*x2(-1 + 2c) + x0*x1*x2(2 - 4c) -fn chi_and_xor_constant( - cb: &mut CircuitBuilder, - words: &[Word; 3], - constant: u64, -) -> Word { - let out = Word::new(cb); - izip!( - &out.0, - &words[0].0, - &words[1].0, - &words[2].0, - iter::successors(Some(constant.reverse_bits()), |constant| { - Some(constant >> 1) - }) - ) - .for_each(|(out, wire_0, wire_1, wire_2, constant)| { - let const_bit = constant & 1; - // x0*(1-2c) + x2*(1-2c) + c - if const_bit & 1 == 1 { - // -x0 - cb.add(*out, *wire_0, -E::BaseField::ONE); - } else { - // x0 - cb.add(*out, *wire_0, 1.into()); - }; - if const_bit & 1 == 1 { - // -x2 - cb.add(*out, *wire_2, -E::BaseField::ONE); - } else { - // x2 - cb.add(*out, *wire_2, 1.into()); - }; - cb.add_const( - *out, - if const_bit & 1 == 1 { - E::BaseField::ONE - } else { - E::BaseField::ZERO - }, - ); - - // x0*x2*(-2 + 4c) + x1*x2(-1 + 2c) - if const_bit & 1 == 1 { - // 2*x0*x2 - cb.mul2(*out, *wire_0, *wire_2, E::BaseField::ONE.double()); - } else { - // -2*x0*x2 - cb.mul2(*out, *wire_0, *wire_2, -E::BaseField::ONE.double()); - }; - if const_bit & 1 == 1 { - // x1*x2 - cb.mul2(*out, *wire_1, *wire_2, E::BaseField::ONE); - } else { - // -x1*x2 - cb.mul2(*out, *wire_1, *wire_2, -E::BaseField::ONE); - }; - - // x0*x1*x2(2 - 4c) - if const_bit & 1 == 1 { - // -2*x0*x1*x2 - cb.mul3(*out, *wire_0, *wire_1, *wire_2, -E::BaseField::ONE.double()); - } else { - // 2*x0*x1*x2 - cb.mul3(*out, *wire_0, *wire_1, *wire_2, E::BaseField::ONE.double()); - } - }); - out -} - -#[allow(dead_code)] -fn xor2_constant( - cb: &mut CircuitBuilder, - words: &[Word; 2], - constant: u64, -) -> Word { - let out = Word::new(cb); - - izip!( - &out.0, - &words[0].0, - &words[1].0, - iter::successors(Some(constant.reverse_bits()), |constant| { - Some(constant >> 1) - }) - ) - .for_each(|(out, wire_0, wire_1, constant)| { - let const_bit = constant & 1; - // (x0 + x1 + x2) - cb.add(*out, *wire_0, E::BaseField::ONE); - cb.add(*out, *wire_1, E::BaseField::ONE); - cb.add_const( - *out, - if const_bit & 1 == 1 { - E::BaseField::ONE - } else { - E::BaseField::ZERO - }, - ); - // - 2x0x2 - 2x1x2 - 2x0x1 - if const_bit == 1 { - cb.add(*out, *wire_0, -E::BaseField::ONE.double()); - cb.add(*out, *wire_1, -E::BaseField::ONE.double()); - } - cb.mul2(*out, *wire_0, *wire_1, -E::BaseField::ONE.double()); - - // 4x0x1x2 - if const_bit == 1 { - cb.mul2(*out, *wire_0, *wire_1, E::BaseField::ONE.double().double()); - } - }); - out -} - -// TODO: Optimization: -// - Theta use lookup -// - Use mul3 to make Chi less layers -pub fn keccak256_circuit() -> Circuit { - let cb = &mut CircuitBuilder::default(); - - let [mut state, input] = [25 * 64, 17 * 64].map(|n| { - cb.create_witness_in(n) - .1 - .chunks(64) - .map(|word| Word(word.to_vec().try_into().unwrap())) - .collect_vec() - }); - - // Absorption - state = izip!( - state.iter(), - input.into_iter().map(Some).chain(iter::repeat(None)) - ) - .map(|(state, input)| { - if let Some(input) = input { - xor(cb, state, &input) - } else { - relay(cb, state) - } - }) - .collect_vec(); - - // Permutation - for i in 0..ROUNDS { - let mut array = [Word::default(); 5]; - - // Theta step - // state[x, y] = state[x, y] XOR state[x+4, 0] XOR state[x+4, 1] XOR state[x+4, 2] XOR - // state[x+4, 3] XOR state[x+4, 4] XOR state[x+1, 0] XOR state[x+1, 1] XOR - // state[x+1, 2] XOR state[x+1, 3] XOR state[x+1, 4] - state = THETA - .map(|(index, inputs, rotated_input)| { - let input = state[index]; - let input_words = inputs.map(|index| state[index]); - let rotated_input_words = rotated_input.map(|index| state[index].rotate_left(1)); - let xor_inputs = iter::once(input) - .chain(input_words) - .chain(rotated_input_words) - .collect::>(); - assert!(xor_inputs.len() == 11); - - // first layer => reduce size from 11 to 4 - let xor_inputs = xor_inputs - .chunks(3) - .map(|chunk| { - let chunked_inputs = chunk.to_vec(); - match chunked_inputs.len() { - 3 => xor3(cb, &chunked_inputs.try_into().unwrap()), - 2 => xor(cb, &chunked_inputs[0], &chunked_inputs[1]), - _ => unreachable!(), - } - }) - .collect::>(); - assert!(xor_inputs.len() == 4); - - // second layer => reduce size from 4 to 2 - let xor_inputs = xor_inputs - .chunks(2) - .map(|chunk| { - let chunked_inputs = chunk.to_vec(); - assert!(chunked_inputs.len() == 2); - xor(cb, &chunked_inputs[0], &chunked_inputs[1]) - }) - .collect::>(); - assert!(xor_inputs.len() == 2); - - // third layer => reduce size from 2 to 1 - let xor_inputs = xor_inputs - .chunks(2) - .map(|chunk| { - let chunked_inputs = chunk.to_vec(); - assert!(chunked_inputs.len() == 2); - xor(cb, &chunked_inputs[0], &chunked_inputs[1]) - }) - .collect::>(); - - assert!(xor_inputs.len() == 1); - xor_inputs[0] - }) - .to_vec(); - - assert!(state.len() == 25); - - // Rho and pi - let mut last = state[1]; - for x in 0..24 { - array[0] = state[PI[x]]; - state[PI[x]] = last.rotate_left(RHO[x] as usize); - last = array[0]; - } - - // Chi + Iota - for y_step in 0..5 { - let y = y_step * 5; - for x in 0..5 { - array[x] = state[y + x]; - } - for x in 0..5 { - if x == 0 && y == 0 { - // Chi + Iota - state[0] = chi_and_xor_constant(cb, &[array[0], array[1], array[2]], RC[i]); - } else { - // Chi - state[y + x] = chi(cb, &[array[x], array[(x + 1) % 5], array[(x + 2) % 5]]); - } - } - } - } - - // FIXME: If we use the `create_wire_out_from_cells`, the ordering of these cells in wire_out - // will be different, so it's duplicating cells to avoid that as a temporary solution. - // cb.create_wire_out_from_cells(&state.iter().flat_map(|word| word.0).collect_vec()); - - let (_, out) = cb.create_witness_out(256); - izip!(&out, state.iter().flat_map(|word| &word.0)) - .for_each(|(out, state)| cb.add(*out, *state, E::BaseField::ONE)); - - cb.configure(); - Circuit::new(cb) -} - -pub fn prove_keccak256( - instance_num_vars: usize, - circuit: &Circuit, - max_thread_id: usize, -) -> Option<(IOPProof, CircuitWitness)> { - assert!( - ceil_log2(max_thread_id) <= instance_num_vars, - "ceil_log2(N) {} > instance_num_vars {}", - ceil_log2(max_thread_id), - instance_num_vars - ); - // Sanity-check - #[cfg(test)] - { - use crate::structs::CircuitWitness; - use multilinear_extensions::mle::IntoMLE; - let all_zero: Vec> = vec![ - vec![E::BaseField::ZERO; 25 * 64], - vec![E::BaseField::ZERO; 17 * 64], - ] - .into_iter() - .map(|wit_in| wit_in.into_mle()) - .collect(); - let all_one = vec![vec![E::BaseField::ONE; 25 * 64], vec![ - E::BaseField::ZERO; - 17 * 64 - ]] - .into_iter() - .map(|wit_in| wit_in.into_mle()) - .collect(); - let mut witness = CircuitWitness::new(circuit, Vec::new()); - witness.add_instance(circuit, all_zero); - witness.add_instance(circuit, all_one); - - izip!( - witness.witness_out_ref()[0] - .get_base_field_vec() - .chunks(256), - [[0; 25], [u64::MAX; 25]] - ) - .for_each(|(wire_out, state)| { - let output = wire_out[..256] - .chunks_exact(64) - .map(|bits| { - bits.iter().fold(0, |acc, bit| { - (acc << 1) + (*bit == E::BaseField::ONE) as u64 - }) - }) - .collect_vec(); - let expected = { - let mut state = state; - tiny_keccak::keccakf(&mut state); - state[0..4].to_vec() - }; - assert_eq!(output, expected) - }); - } - - let mut rng = StdRng::seed_from_u64(OsRng.next_u64()); - let mut witness = CircuitWitness::new(circuit, Vec::new()); - for _ in 0..1 << instance_num_vars { - let [rand_state, rand_input] = [25 * 64, 17 * 64].map(|n| { - let mut data = vec![E::BaseField::ZERO; 1 << ceil_log2(n)]; - data.iter_mut() - .take(n) - .for_each(|d| *d = E::BaseField::from(rng.gen_bool(0.5) as u64)); - data - }); - witness.add_instance(circuit, vec![ - DenseMultilinearExtension::from_evaluations_vec( - ceil_log2(rand_state.len()), - rand_state, - ), - DenseMultilinearExtension::from_evaluations_vec( - ceil_log2(rand_input.len()), - rand_input, - ), - ]); - } - - let output_mle = &witness.witness_out_ref()[0]; - - let mut prover_transcript = Transcript::::new(b"test"); - let output_point = iter::repeat_with(|| { - prover_transcript - .get_and_append_challenge(b"output point") - .elements - }) - .take(output_mle.num_vars()) - .collect_vec(); - let output_eval = output_mle.evaluate(&output_point); - - let start = std::time::Instant::now(); - let (proof, _) = IOPProverState::prove_parallel( - circuit, - &witness, - vec![], - vec![PointAndEval::new(output_point, output_eval)], - max_thread_id, - &mut prover_transcript, - ); - println!("{}: {:?}", 1 << instance_num_vars, start.elapsed()); - Some((proof, witness)) -} - -pub fn verify_keccak256( - instance_num_vars: usize, - output_mle: &ArcMultilinearExtension, - proof: IOPProof, - circuit: &Circuit, -) -> Result, GKRError> { - let mut verifer_transcript = Transcript::::new(b"test"); - let output_point = iter::repeat_with(|| { - verifer_transcript - .get_and_append_challenge(b"output point") - .elements - }) - .take(output_mle.num_vars()) - .collect_vec(); - let output_eval = output_mle.evaluate(&output_point); - crate::structs::IOPVerifierState::verify_parallel( - circuit, - &[], - vec![], - vec![PointAndEval::new(output_point, output_eval)], - proof, - instance_num_vars, - &mut verifer_transcript, - ) -} diff --git a/gkr/src/gadgets/mod.rs b/gkr/src/gadgets/mod.rs deleted file mode 100644 index 43bf81bd1..000000000 --- a/gkr/src/gadgets/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod keccak256; diff --git a/gkr/src/lib.rs b/gkr/src/lib.rs deleted file mode 100644 index 214126a5b..000000000 --- a/gkr/src/lib.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![feature(associated_const_equality)] - -mod circuit; -pub mod error; -pub mod gadgets; -pub mod macros; -mod prover; -pub mod structs; -#[cfg(feature = "unsafe")] -pub mod unsafe_utils; -pub mod utils; -mod verifier; - -pub use sumcheck::util; - -#[cfg(test)] -mod test; diff --git a/gkr/src/macros.rs b/gkr/src/macros.rs deleted file mode 100644 index 70e223d84..000000000 --- a/gkr/src/macros.rs +++ /dev/null @@ -1,64 +0,0 @@ -#[macro_export] -macro_rules! entered_span { - ($first:expr $(,)*) => { - $crate::tracing_span!($first).entered() - }; -} - -#[macro_export] -macro_rules! tracing_span { - ($first:expr $(,)*) => { - tracing::span!(tracing::Level::DEBUG, $first) - }; -} - -#[macro_export] -macro_rules! exit_span { - ($first:expr $(,)*) => { - $first.exit(); - }; -} - -#[macro_export] -#[cfg(feature = "parallel")] -macro_rules! izip_parallizable { - (@closure $p:pat => $tup:expr) => { - |$p| $tup - }; - (@closure $p:pat => ($($tup:tt)*) , $_iter:expr $(, $tail:expr)*) => { - $crate::izip_parallizable!(@closure ($p, b) => ($($tup)*, b) $(, $tail)*) - }; - ($first:expr $(,)*) => { - rayon::iter::IntoParallelIterator::into_par_iter($first) - }; - ($first:expr, $second:expr $(,)*) => { - $crate::izip_parallizable!($first).zip($second) - }; - ($first:expr $(, $rest:expr)* $(,)*) => { - $crate::izip_parallizable!($first) - $(.zip($rest))* - .map($crate::izip_parallizable!(@closure a => (a) $(, $rest)*)) - }; -} - -#[macro_export] -#[cfg(not(feature = "parallel"))] -macro_rules! izip_parallizable { - (@closure $p:pat => $tup:expr) => { - |$p| $tup - }; - (@closure $p:pat => ($($tup:tt)*) , $_iter:expr $(, $tail:expr)*) => { - $crate::izip_parallizable!(@closure ($p, b) => ($($tup)*, b) $(, $tail)*) - }; - ($first:expr $(,)*) => { - $first.into_iter() - }; - ($first:expr, $second:expr $(,)*) => { - $crate::izip_parallizable!($first).zip($second) - }; - ($first:expr $(, $rest:expr)* $(,)*) => { - $crate::izip_parallizable!($first) - $(.zip($rest))* - .map($crate::izip_parallizable!(@closure a => (a) $(, $rest)*)) - }; -} diff --git a/gkr/src/prover.rs b/gkr/src/prover.rs deleted file mode 100644 index f10dbe808..000000000 --- a/gkr/src/prover.rs +++ /dev/null @@ -1,345 +0,0 @@ -use ark_std::{end_timer, start_timer}; -use ff_ext::ExtensionField; -use itertools::Itertools; -use multilinear_extensions::{ - virtual_poly::build_eq_x_r_vec, virtual_poly_v2::VirtualPolynomialV2, -}; - -use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}; -use simple_frontend::structs::LayerId; -use transcript::Transcript; - -use crate::{ - entered_span, exit_span, - structs::{ - Circuit, CircuitWitness, GKRInputClaims, IOPProof, IOPProverState, PointAndEval, - SumcheckStepType, - }, - tracing_span, -}; - -mod phase1; -mod phase1_output; -mod phase2; -mod phase2_input; -mod phase2_linear; - -#[cfg(test)] -mod test; - -type SumcheckStateV2<'a, F> = sumcheck::structs::IOPProverStateV2<'a, F>; - -impl IOPProverState { - /// Prove process for data parallel circuits. - #[tracing::instrument(skip_all, name = "gkr::prove_parallel")] - pub fn prove_parallel<'a>( - circuit: &Circuit, - circuit_witness: &CircuitWitness, - output_evals: Vec>, - wires_out_evals: Vec>, - max_thread_id: usize, - transcript: &mut Transcript, - ) -> (IOPProof, GKRInputClaims) { - let timer = start_timer!(|| "Proving"); - let span = entered_span!("Proving"); - // TODO: Currently haven't support non-power-of-two number of instances. - assert!(circuit_witness.n_instances == 1 << circuit_witness.instance_num_vars()); - - let mut prover_state = tracing_span!("prover_init_parallel").in_scope(|| { - Self::prover_init_parallel( - circuit, - circuit_witness.instance_num_vars(), - output_evals, - wires_out_evals, - transcript, - ) - }); - - let sumcheck_proofs = (0..circuit.layers.len() as LayerId) - .flat_map(|layer_id| { - let timer = start_timer!(|| format!("Prove layer {}", layer_id)); - - prover_state.layer_id = layer_id; - - let dummy_step = SumcheckStepType::Undefined; - let proofs = circuit.layers[layer_id as usize] - .sumcheck_steps - .iter() - .chain(vec![&dummy_step, &dummy_step]) - .tuple_windows() - .flat_map(|steps| match steps { - (SumcheckStepType::OutputPhase1Step1, _, _) => { - let alpha = transcript - .get_and_append_challenge(b"combine subset evals") - .elements; - let hi_num_vars = circuit_witness.instance_num_vars(); - let eq_t = prover_state - .to_next_phase_point_and_evals - .par_iter() - .chain( - prover_state.subset_point_and_evals[layer_id as usize] - .par_iter() - .map(|(_, point_and_eval)| point_and_eval), - ) - .chain( - vec![PointAndEval { - point: prover_state.assert_point.clone(), - eval: E::ZERO, // evaluation value doesn't matter - }] - .par_iter(), - ) - .map(|point_and_eval| { - let point_lo_num_vars = - point_and_eval.point.len() - hi_num_vars; - build_eq_x_r_vec(&point_and_eval.point[point_lo_num_vars..]) - }) - .collect::>>(); - - let virtual_polys: Vec> = (0..max_thread_id) - .into_par_iter() - .map(|thread_id| { - let span = entered_span!("build_poly"); - let virtual_poly = - Self::build_state_output_phase1_step1_sumcheck_poly( - &prover_state, - &eq_t, - alpha, - circuit, - circuit_witness, - (thread_id, max_thread_id), - ); - exit_span!(span); - virtual_poly - }) - .collect(); - - let (sumcheck_proof, sumcheck_prover_state) = - sumcheck::structs::IOPProverStateV2::::prove_batch_polys( - max_thread_id, - virtual_polys, - transcript, - ); - - let prover_msg = prover_state.combine_output_phase1_step1_evals( - sumcheck_proof, - sumcheck_prover_state, - ); - - vec![prover_msg] - } - (SumcheckStepType::Phase1Step1, _, _) => { - let alpha = transcript - .get_and_append_challenge(b"combine subset evals") - .elements; - let hi_num_vars = circuit_witness.instance_num_vars(); - let eq_t = prover_state - .to_next_phase_point_and_evals - .par_iter() - .chain( - prover_state.subset_point_and_evals[layer_id as usize] - .par_iter() - .map(|(_, point_and_eval)| point_and_eval), - ) - .map(|point_and_eval| { - let point_lo_num_vars = - point_and_eval.point.len() - hi_num_vars; - build_eq_x_r_vec(&point_and_eval.point[point_lo_num_vars..]) - }) - .collect::>>(); - - let virtual_polys: Vec> = (0..max_thread_id) - .into_par_iter() - .map(|thread_id| { - let span = entered_span!("build_poly"); - let virtual_poly = Self::build_phase1_step1_sumcheck_poly( - &prover_state, - layer_id, - alpha, - &eq_t, - circuit, - circuit_witness, - (thread_id, max_thread_id), - ); - exit_span!(span); - virtual_poly - }) - .collect(); - - let (sumcheck_proof, sumcheck_prover_state) = - sumcheck::structs::IOPProverStateV2::::prove_batch_polys( - max_thread_id, - virtual_polys, - transcript, - ); - - let prover_msg = prover_state - .combine_phase1_step1_evals(sumcheck_proof, sumcheck_prover_state); - - vec![prover_msg] - } - (SumcheckStepType::Phase2Step1, step2, _) => { - let span = entered_span!("phase2_gkr"); - let max_steps = match step2 { - SumcheckStepType::Phase2Step2 => 3, - SumcheckStepType::Phase2Step2NoStep3 => 2, - _ => unreachable!(), - }; - - let mut eqs = vec![]; - let mut res = vec![]; - for step in 0..max_steps { - let bounded_eval_point = prover_state.to_next_step_point.clone(); - eqs.push(build_eq_x_r_vec(&bounded_eval_point)); - // build step round poly - let virtual_polys: Vec> = (0..max_thread_id) - .into_par_iter() - .map(|thread_id| { - let span = entered_span!("build_poly"); - let virtual_poly = match step { - 0 => { - let virtual_poly = - Self::build_phase2_step1_sumcheck_poly( - eqs.as_slice().try_into().unwrap(), - layer_id, - circuit, - circuit_witness, - (thread_id, max_thread_id), - ); - virtual_poly - } - 1 => { - let virtual_poly = - Self::build_phase2_step2_sumcheck_poly( - layer_id, - eqs.as_slice().try_into().unwrap(), - circuit, - circuit_witness, - (thread_id, max_thread_id), - ); - virtual_poly - } - 2 => { - let virtual_poly = - Self::build_phase2_step3_sumcheck_poly( - layer_id, - eqs.as_slice().try_into().unwrap(), - circuit, - circuit_witness, - (thread_id, max_thread_id), - ); - virtual_poly - } - _ => unimplemented!(), - }; - exit_span!(span); - virtual_poly - }) - .collect(); - - let (sumcheck_proof, sumcheck_prover_state) = - sumcheck::structs::IOPProverStateV2::::prove_batch_polys( - max_thread_id, - virtual_polys, - transcript, - ); - - let iop_prover_step = match step { - 0 => prover_state.combine_phase2_step1_evals( - circuit, - sumcheck_proof, - sumcheck_prover_state, - ), - 1 => { - let no_step3: bool = max_steps == 2; - prover_state.combine_phase2_step2_evals( - circuit, - sumcheck_proof, - sumcheck_prover_state, - no_step3, - ) - } - 2 => prover_state.combine_phase2_step3_evals( - circuit, - sumcheck_proof, - sumcheck_prover_state, - ), - _ => unimplemented!(), - }; - - res.push(iop_prover_step); - } - exit_span!(span); - res - } - (SumcheckStepType::LinearPhase2Step1, _, _) => [prover_state - .prove_and_update_state_linear_phase2_step1( - circuit, - circuit_witness, - transcript, - )] - .to_vec(), - (SumcheckStepType::InputPhase2Step1, _, _) => [prover_state - .prove_and_update_state_input_phase2_step1( - circuit, - circuit_witness, - transcript, - )] - .to_vec(), - _ => { - vec![] - } - }) - .collect_vec(); - end_timer!(timer); - - proofs - }) - .collect_vec(); - end_timer!(timer); - exit_span!(span); - - (IOPProof { sumcheck_proofs }, GKRInputClaims { - point_and_evals: prover_state.to_next_phase_point_and_evals, - }) - } - - /// Initialize proving state for data parallel circuits. - fn prover_init_parallel( - circuit: &Circuit, - instance_num_vars: usize, - output_evals: Vec>, - wires_out_evals: Vec>, - transcript: &mut Transcript, - ) -> Self { - let n_layers = circuit.layers.len(); - let output_wit_num_vars = circuit.layers[0].num_vars + instance_num_vars; - let mut subset_point_and_evals = vec![vec![]; n_layers]; - let to_next_step_point = if output_evals.is_empty() { - wires_out_evals.last().unwrap().point.clone() - } else { - output_evals.last().unwrap().point.clone() - }; - let assert_point = (0..output_wit_num_vars) - .map(|_| { - transcript - .get_and_append_challenge(b"assert_point") - .elements - }) - .collect_vec(); - let to_next_phase_point_and_evals = output_evals; - subset_point_and_evals[0] = wires_out_evals - .into_iter() - .map(|p| (0 as LayerId, p)) - .collect(); - - Self { - to_next_phase_point_and_evals, - subset_point_and_evals, - to_next_step_point, - - assert_point, - // Default - layer_id: 0, - } - } -} diff --git a/gkr/src/prover/phase1.rs b/gkr/src/prover/phase1.rs deleted file mode 100644 index bfa50ec90..000000000 --- a/gkr/src/prover/phase1.rs +++ /dev/null @@ -1,186 +0,0 @@ -use ark_std::{end_timer, start_timer}; -use ff::Field; -use ff_ext::ExtensionField; -use itertools::{Itertools, izip}; -use multilinear_extensions::{ - mle::DenseMultilinearExtension, - virtual_poly::build_eq_x_r_vec_sequential, - virtual_poly_v2::{ArcMultilinearExtension, VirtualPolynomialV2}, -}; -use simple_frontend::structs::LayerId; -use std::sync::Arc; -use sumcheck::{entered_span, util::ceil_log2}; - -use crate::{ - exit_span, - structs::{ - Circuit, CircuitWitness, IOPProverState, IOPProverStepMessage, PointAndEval, SumcheckProof, - }, - utils::{MatrixMLERowFirst, tensor_product}, -}; - -// Prove the items copied from the current layer to later layers for data parallel circuits. -impl IOPProverState { - /// Sumcheck 1: sigma = \sum_{t || y}(f1({t || y}) * (\sum_j g1^{(j)}({t || y}))) - /// sigma = \sum_j( \alpha^j * subset[i][j](rt_j || ry_j) ) - /// f1^{(j)}(y) = layers[i](t || y) - /// g1^{(j)}(y) = \alpha^j * eq(rt_j, t) * eq(ry_j, y) - /// g1^{(j)}(y) = \alpha^j * eq(rt_j, t) * copy_to[j](ry_j, y) - #[tracing::instrument(skip_all, name = "build_phase1_step1_sumcheck_poly")] - pub(super) fn build_phase1_step1_sumcheck_poly<'a>( - &self, - layer_id: LayerId, - alpha: E, - eq_t: &Vec>, - circuit: &Circuit, - circuit_witness: &'a CircuitWitness, - multi_threads_meta: (usize, usize), - ) -> VirtualPolynomialV2<'a, E> { - let span = entered_span!("preparation"); - let timer = start_timer!(|| "Prover sumcheck phase 1 step 1"); - - let total_length = self.to_next_phase_point_and_evals.len() - + self.subset_point_and_evals[self.layer_id as usize].len() - + 1; - let alpha_pows = { - let mut alpha_pows = vec![E::ONE; total_length]; - for i in 0..total_length.saturating_sub(1) { - alpha_pows[i + 1] = alpha_pows[i] * alpha; - } - alpha_pows - }; - - let lo_num_vars = circuit.layers[self.layer_id as usize].num_vars; - let hi_num_vars = circuit_witness.instance_num_vars(); - - // parallel unit logic handling - let (thread_id, max_thread_id) = multi_threads_meta; - let log2_max_thread_id = ceil_log2(max_thread_id); - - exit_span!(span); - - // f1^{(j)}(y) = layers[i](t || y) - let f1: ArcMultilinearExtension = Arc::new( - circuit_witness.layers_ref()[layer_id as usize] - .get_ranged_mle(multi_threads_meta.1, multi_threads_meta.0), - ); - - assert_eq!( - f1.num_vars(), - hi_num_vars + lo_num_vars - log2_max_thread_id - ); - - let span = entered_span!("g1"); - // g1^{(j)}(y) = \alpha^j * eq(rt_j, t) * eq(ry_j, y) - // g1^{(j)}(y) = \alpha^j * eq(rt_j, t) * copy_to[j](ry_j, y) - let copy_to_matrices = &circuit.layers[self.layer_id as usize].copy_to; - let g1: ArcMultilinearExtension<'a, E> = { - let gs = izip!(&self.to_next_phase_point_and_evals, &alpha_pows, eq_t) - .map(|(point_and_eval, alpha_pow, eq_t)| { - // g1^{(j)}(y) = \alpha^j * eq(rt_j, t) * eq(ry_j, y) - let point_lo_num_vars = point_and_eval.point.len() - hi_num_vars; - - let eq_y = - build_eq_x_r_vec_sequential(&point_and_eval.point[..point_lo_num_vars]) - .into_iter() - .take(1 << lo_num_vars) - .map(|eq| *alpha_pow * eq) - .collect_vec(); - - let eq_t_unit_len = eq_t.len() / max_thread_id; - let start_index = thread_id * eq_t_unit_len; - let g1_j = - tensor_product(&eq_t[start_index..(start_index + eq_t_unit_len)], &eq_y); - - assert_eq!( - g1_j.len(), - (1 << (hi_num_vars + lo_num_vars - log2_max_thread_id)) - ); - - g1_j - }) - .chain( - izip!( - &self.subset_point_and_evals[self.layer_id as usize], - &alpha_pows[self.to_next_phase_point_and_evals.len()..], - eq_t.iter().skip(self.to_next_phase_point_and_evals.len()) - ) - .map( - |((new_layer_id, point_and_eval), alpha_pow, eq_t)| { - let point_lo_num_vars = point_and_eval.point.len() - hi_num_vars; - let copy_to = ©_to_matrices[new_layer_id]; - let lo_eq_w_p = build_eq_x_r_vec_sequential( - &point_and_eval.point[..point_lo_num_vars], - ); - - // g2^{(j)}(y) = \alpha^j * eq(rt_j, t) * copy_to[j](ry_j, y) - let eq_t_unit_len = eq_t.len() / max_thread_id; - let start_index = thread_id * eq_t_unit_len; - let g2_j = tensor_product( - &eq_t[start_index..(start_index + eq_t_unit_len)], - ©_to.as_slice().fix_row_row_first_with_scalar( - &lo_eq_w_p, - lo_num_vars, - alpha_pow, - ), - ); - - assert_eq!( - g2_j.len(), - (1 << (hi_num_vars + lo_num_vars - log2_max_thread_id)) - ); - g2_j - }, - ), - ) - .collect::>>(); - - DenseMultilinearExtension::from_evaluations_ext_vec( - hi_num_vars + lo_num_vars - log2_max_thread_id, - gs.into_iter() - .fold(vec![E::ZERO; 1 << f1.num_vars()], |mut acc, g| { - assert_eq!(1 << f1.num_vars(), g.len()); - acc.iter_mut().enumerate().for_each(|(i, v)| *v += g[i]); - acc - }), - ) - .into() - }; - exit_span!(span); - - // sumcheck: sigma = \sum_{s || y}(f1({s || y}) * (\sum_j g1^{(j)}({s || y}))) - let span = entered_span!("virtual_poly"); - let mut virtual_poly_1: VirtualPolynomialV2 = - VirtualPolynomialV2::new_from_mle(f1, E::ONE); - virtual_poly_1.mul_by_mle(g1, E::BaseField::ONE); - exit_span!(span); - end_timer!(timer); - - virtual_poly_1 - } - - pub(super) fn combine_phase1_step1_evals( - &mut self, - sumcheck_proof_1: SumcheckProof, - prover_state: sumcheck::structs::IOPProverStateV2, - ) -> IOPProverStepMessage { - let (mut f1, _): (Vec<_>, Vec<_>) = prover_state - .get_mle_final_evaluations() - .into_iter() - .enumerate() - .partition(|(i, _)| i % 2 == 0); - let eval_value_1 = f1.remove(0).1; - - self.to_next_step_point.clone_from(&sumcheck_proof_1.point); - self.to_next_phase_point_and_evals = vec![PointAndEval::new_from_ref( - &self.to_next_step_point, - &eval_value_1, - )]; - self.subset_point_and_evals[self.layer_id as usize].clear(); - - IOPProverStepMessage { - sumcheck_proof: sumcheck_proof_1, - sumcheck_eval_values: vec![eval_value_1], - } - } -} diff --git a/gkr/src/prover/phase1_output.rs b/gkr/src/prover/phase1_output.rs deleted file mode 100644 index 76fb8521d..000000000 --- a/gkr/src/prover/phase1_output.rs +++ /dev/null @@ -1,204 +0,0 @@ -use ark_std::{end_timer, iterable::Iterable, start_timer}; -use ff::Field; -use ff_ext::ExtensionField; -use itertools::{Itertools, izip}; -use multilinear_extensions::{ - mle::{ - DenseMultilinearExtension, InstanceIntoIteratorMut, IntoInstanceIter, IntoInstanceIterMut, - }, - util::ceil_log2, - virtual_poly::build_eq_x_r_vec_sequential, - virtual_poly_v2::{ArcMultilinearExtension, VirtualPolynomialV2}, -}; -use std::{iter, sync::Arc}; - -use crate::{ - entered_span, exit_span, - structs::{ - Circuit, CircuitWitness, IOPProverState, IOPProverStepMessage, PointAndEval, SumcheckProof, - }, - utils::{MatrixMLERowFirst, tensor_product}, -}; - -// Prove the items copied from the output layer to the output witness for data parallel circuits. -// \sum_j( \alpha^j * subset[i][j](rt_j || ry_j) ) -// = \sum_{t || y} ( \sum_j( \alpha^j (eq or copy_to[j] or assert_subset_eq)(ry_j, y) eq(rt_j, -// t) * layers[i](t || y) ) ) -impl IOPProverState { - /// Sumcheck 1: sigma = \sum_{t || y} \sum_j ( f1^{(j)}(t || y) * g1^{(j)}(t || y) ) - /// sigma = \sum_j( \alpha^j * subset[i][j](rt_j || ry_j) ) - /// f1^{(j)}(y) = layers[i](t || y) - /// g1^{(j)}(y) = \alpha^j * eq(rt_j, t) * eq(ry_j, y) - /// g1^{(j)}(y) = \alpha^j * eq(rt_j, t) * copy_to[j](ry_j, y) - /// g1^{(j)}(y) = \alpha^j * eq(rt_j, t) * assert_subset_eq(ry, y) - #[tracing::instrument(skip_all, name = "prove_and_update_state_output_phase1_step1")] - pub(super) fn build_state_output_phase1_step1_sumcheck_poly<'a>( - &self, - eq_t: &Vec>, - alpha: E, - circuit: &Circuit, - circuit_witness: &'a CircuitWitness, - multi_threads_meta: (usize, usize), - ) -> VirtualPolynomialV2<'a, E> { - let timer = start_timer!(|| "Prover sumcheck output phase 1 step 1"); - let total_length = self.to_next_phase_point_and_evals.len() - + self.subset_point_and_evals[self.layer_id as usize].len() - + 1; - let alpha_pows = { - let mut alpha_pows = vec![E::ONE; total_length]; - for i in 0..total_length.saturating_sub(1) { - alpha_pows[i + 1] = alpha * alpha_pows[i]; - } - alpha_pows - }; - - let lo_num_vars = circuit.layers[self.layer_id as usize].num_vars; - let hi_num_vars = circuit_witness.instance_num_vars(); - - // parallel unit logic handling - let (thread_id, max_thread_id) = multi_threads_meta; - let log2_max_thread_id = ceil_log2(max_thread_id); - let num_thread_instances = 1 << (hi_num_vars - log2_max_thread_id); - - let f1: ArcMultilinearExtension = Arc::new( - circuit_witness.layers_ref()[self.layer_id as usize] - .get_ranged_mle(multi_threads_meta.1, multi_threads_meta.0), - ); - - assert_eq!( - f1.num_vars(), - hi_num_vars + lo_num_vars - log2_max_thread_id - ); - // TODO: Double check the soundness here. - let span = entered_span!("g1"); - // g1^{(j)}(y) = \alpha^j * eq(rt_j, t) * eq(ry_j, y) or - // g1^{(j)}(y) = \alpha^j * eq(rt_j, t) * copy_to[j](ry_j, y) or - // g1^{(j)}(y) = \alpha^j * eq(rt_j, t) * assert_subset_eq(ry, y) - let g1: ArcMultilinearExtension = { - let gs = izip!(&self.to_next_phase_point_and_evals, &alpha_pows, eq_t) - .map(|(point_and_eval, alpha_pow, eq_t)| { - // g1^{(j)}(y) = \alpha^j * eq(rt_j, t) * eq(ry_j, y) - let point_lo_num_vars = point_and_eval.point.len() - hi_num_vars; - - let eq_y = - build_eq_x_r_vec_sequential(&point_and_eval.point[..point_lo_num_vars]) - .into_iter() - .take(1 << lo_num_vars) - .map(|eq| *alpha_pow * eq) - .collect_vec(); - - let eq_t_unit_len = eq_t.len() / max_thread_id; - let start_index = thread_id * eq_t_unit_len; - let g1_j = tensor_product(&eq_t[start_index..][..eq_t_unit_len], &eq_y); - - assert_eq!( - g1_j.len(), - (1 << (hi_num_vars + lo_num_vars - log2_max_thread_id)) - ); - - g1_j - }) - .chain( - izip!( - &circuit.copy_to_wits_out, - &self.subset_point_and_evals[self.layer_id as usize], - &alpha_pows[self.to_next_phase_point_and_evals.len()..], - eq_t.iter().skip(self.to_next_phase_point_and_evals.len()) - ) - .map(|(copy_to, (_, point_and_eval), alpha_pow, eq_t)| { - let point_lo_num_vars = point_and_eval.point.len() - hi_num_vars; - let lo_eq_w_p = - build_eq_x_r_vec_sequential(&point_and_eval.point[..point_lo_num_vars]); - - // g2^{(j)}(y) = \alpha^j * eq(rt_j, t) * copy_to[j](ry_j, y) - let eq_t_unit_len = eq_t.len() / max_thread_id; - let start_index = thread_id * eq_t_unit_len; - let g2_j = tensor_product( - &eq_t[start_index..][..eq_t_unit_len], - ©_to.as_slice().fix_row_row_first_with_scalar( - &lo_eq_w_p, - lo_num_vars, - alpha_pow, - ), - ); - assert_eq!( - g2_j.len(), - (1 << (hi_num_vars + lo_num_vars - log2_max_thread_id)) - ); - g2_j - }), - ) - .chain(iter::once_with(|| { - let alpha_pow = alpha_pows.last().unwrap(); - let eq_t = eq_t.last().unwrap(); - let eq_y = build_eq_x_r_vec_sequential(&self.assert_point[..lo_num_vars]); - - let eq_t_unit_len = eq_t.len() / max_thread_id; - let start_index = thread_id * eq_t_unit_len; - let g1_j = tensor_product(&eq_t[start_index..][..eq_t_unit_len], &eq_y); - - let mut g_last = - vec![E::ZERO; 1 << (hi_num_vars + lo_num_vars - log2_max_thread_id)]; - assert_eq!(g1_j.len(), g_last.len()); - - let g_last_iter: InstanceIntoIteratorMut = - g_last.into_instance_iter_mut(num_thread_instances); - g_last_iter - .zip(g1_j.as_slice().into_instance_iter(num_thread_instances)) - .for_each(|(g_last, g1_j)| { - circuit.assert_consts.iter().for_each(|gate| { - g_last[gate.idx_out] = g1_j[gate.idx_out] * alpha_pow; - }); - }); - g_last - })) - .collect::>>(); - - DenseMultilinearExtension::from_evaluations_ext_vec( - hi_num_vars + lo_num_vars - log2_max_thread_id, - gs.into_iter() - .fold(vec![E::ZERO; 1 << f1.num_vars()], |mut acc, g| { - assert_eq!(1 << f1.num_vars(), g.len()); - acc.iter_mut().enumerate().for_each(|(i, v)| *v += g[i]); - acc - }), - ) - .into() - }; - exit_span!(span); - - // sumcheck: sigma = \sum_y( \sum_j f1^{(j)}(y) * g1^{(j)}(y)) - let span = entered_span!("virtual_poly"); - let mut virtual_poly_1: VirtualPolynomialV2 = - VirtualPolynomialV2::new_from_mle(f1, E::ONE); - virtual_poly_1.mul_by_mle(g1, E::BaseField::ONE); - exit_span!(span); - end_timer!(timer); - virtual_poly_1 - } - - pub(super) fn combine_output_phase1_step1_evals( - &mut self, - sumcheck_proof_1: SumcheckProof, - prover_state: sumcheck::structs::IOPProverStateV2, - ) -> IOPProverStepMessage { - let (mut f1, _): (Vec<_>, Vec<_>) = prover_state - .get_mle_final_evaluations() - .into_iter() - .enumerate() - .partition(|(i, _)| i % 2 == 0); - let eval_value_1 = f1.remove(0).1; - - self.to_next_step_point.clone_from(&sumcheck_proof_1.point); - self.to_next_phase_point_and_evals = vec![PointAndEval::new_from_ref( - &self.to_next_step_point, - &eval_value_1, - )]; - self.subset_point_and_evals[self.layer_id as usize].clear(); - - IOPProverStepMessage { - sumcheck_proof: sumcheck_proof_1, - sumcheck_eval_values: vec![eval_value_1], - } - } -} diff --git a/gkr/src/prover/phase2.rs b/gkr/src/prover/phase2.rs deleted file mode 100644 index ff58b1c24..000000000 --- a/gkr/src/prover/phase2.rs +++ /dev/null @@ -1,450 +0,0 @@ -use ark_std::{end_timer, iterable::Iterable, start_timer}; -use ff::Field; -use ff_ext::ExtensionField; -use itertools::{Itertools, izip}; -use multilinear_extensions::{ - mle::{ArcDenseMultilinearExtension, DenseMultilinearExtension}, - virtual_poly_v2::{ArcMultilinearExtension, VirtualPolynomialV2}, -}; -use simple_frontend::structs::LayerId; -use std::sync::Arc; - -use sumcheck::{entered_span, exit_span, util::ceil_log2}; - -use crate::structs::{ - CircuitWitness, IOPProverState, - Step::{Step1, Step2, Step3}, -}; -use multilinear_extensions::mle::MultilinearExtension; - -use crate::{ - circuit::EvaluateConstant, - structs::{Circuit, IOPProverStepMessage, PointAndEval, SumcheckProof}, -}; - -macro_rules! prepare_stepx_g_fn { - (&mut $a1:ident, $s_in:ident, $s_out:ident, $d:ident $(,$c:ident, |$f_s_in:ident, $f_s_out:ident, $g:ident| $op:expr)* $(,)?) => { - $a1.chunks_mut(1 << $s_in) - // enumerated index is the instance index - .fold([$d << $s_in, $d << $s_out], |mut s_acc, evals_vec| { - // prefix s with global thread id $d - let (s_in, s_out) = (&s_acc[0], &s_acc[1]); - $( - $c.iter().for_each(|(fanin_cellid, gates)| { - let eval = gates.iter().map(|$g| { - let $f_s_in = s_in; - let $f_s_out = s_out; - $op - }).fold(E::ZERO, |acc, item| acc + item); - evals_vec[*fanin_cellid] += eval; - }); - )* - s_acc[0] += (1 << $s_in); - s_acc[1] += (1 << $s_out); - s_acc - }); - }; -} - -// Prove the computation in the current layer for data parallel circuits. -// The number of terms depends on the gate. -// Here is an example of degree 3: -// layers[i](rt || ry) = \sum_{s1}( \sum_{s2}( \sum_{s3}( \sum_{x1}( \sum_{x2}( \sum_{x3}( -// eq(rt, s1, s2, s3) * mul3(ry, x1, x2, x3) * layers[i + 1](s1 || x1) * layers[i + 1](s2 || x2) * layers[i + 1](s3 || x3) -// ) ) ) ) ) ) + sum_s1( sum_s2( sum_{x1}( sum_{x2}( -// eq(rt, s1, s2) * mul2(ry, x1, x2) * layers[i + 1](s1 || x1) * layers[i + 1](s2 || x2) -// ) ) ) ) + \sum_{s1}( \sum_{x1}( -// eq(rt, s1) * add(ry, x1) * layers[i + 1](s1 || x1) -// ) ) + \sum_{s1}( \sum_{x1}( -// \sum_j eq(rt, s1) paste_from[j](ry, x1) * subset[j][i](s1 || x1) -// ) ) + add_const(ry) -impl IOPProverState { - /// Sumcheck 1: sigma = \sum_{s1 || x1} f1(s1 || x1) * g1(s1 || x1) + \sum_j f1'_j(s1 || x1) * g1'_j(s1 || x1) - /// sigma = layers[i](rt || ry) - add_const(ry), - /// f1(s1 || x1) = layers[i + 1](s1 || x1) - /// g1(s1 || x1) = \sum_{s2}( \sum_{s3}( \sum_{x2}( \sum_{x3}( - /// eq(rt, s1, s2, s3) * mul3(ry, x1, x2, x3) * layers[i + 1](s2 || x2) * layers[i + 1](s3 || x3) - /// ) ) ) ) + \sum_{s2}( \sum_{x2}( - /// eq(rt, s1, s2) * mul2(ry, x1, x2) * layers[i + 1](s2 || x2) - /// ) ) + eq(rt, s1) * add(ry, x1) - /// f1'^{(j)}(s1 || x1) = subset[j][i](s1 || x1) - /// g1'^{(j)}(s1 || x1) = eq(rt, s1) paste_from[j](ry, x1) - #[tracing::instrument(skip_all, name = "build_phase2_step1_sumcheck_poly")] - pub(super) fn build_phase2_step1_sumcheck_poly<'a>( - eq: &[Vec; 1], - layer_id: LayerId, - circuit: &Circuit, - circuit_witness: &'a CircuitWitness, - multi_threads_meta: (usize, usize), - ) -> VirtualPolynomialV2<'a, E> { - let timer = start_timer!(|| "Prover sumcheck phase 2 step 1"); - let layer = &circuit.layers[layer_id as usize]; - let lo_out_num_vars = layer.num_vars; - let lo_in_num_vars = layer.max_previous_num_vars; - let hi_num_vars = circuit_witness.instance_num_vars(); - let eq = &eq[0]; - - // parallel unit logic handling - let (thread_id, max_thread_id) = multi_threads_meta; - let log2_max_thread_id = ceil_log2(max_thread_id); - let threads_num_vars = hi_num_vars - log2_max_thread_id; - let thread_s = thread_id << threads_num_vars; - - let challenges = &circuit_witness.challenges; - - let span = entered_span!("f1_g1"); - // merge next_layer_vec with next_layer_poly - let next_layer_vec = - circuit_witness.layers_ref()[layer_id as usize + 1].get_base_field_vec(); - - let next_layer_poly: ArcMultilinearExtension<'a, E> = - if circuit_witness.layers_ref()[layer_id as usize + 1].num_vars() - hi_num_vars - < lo_in_num_vars - { - Arc::new( - // TODO(Matthias, by 2024-11-01): review whether we can redesign this API to avoid the deprecated resize_ranged - #[allow(deprecated)] - circuit_witness.layers_ref()[layer_id as usize + 1].resize_ranged( - 1 << hi_num_vars, - 1 << lo_in_num_vars, - multi_threads_meta.1, - multi_threads_meta.0, - ), - ) - } else { - Arc::new( - circuit_witness.layers_ref()[layer_id as usize + 1] - .get_ranged_mle(multi_threads_meta.1, multi_threads_meta.0), - ) - }; - // f1(s1 || x1) = layers[i + 1](s1 || x1) - let f1: ArcMultilinearExtension<'a, E> = next_layer_poly.clone(); - - // g1(s1 || x1) = \sum_{s2}( \sum_{s3}( \sum_{x2}( \sum_{x3}( - // eq(rt, s1, s2, s3) * mul3(ry, x1, x2, x3) * layers[i + 1](s2 || x2) * layers[i + 1](s3 || x3) - // ) ) ) ) + \sum_{s2}( \sum_{x2}( - // eq(rt, s1, s2) * mul2(ry, x1, x2) * layers[i + 1](s2 || x2) - // ) ) + eq(rt, s1) * add(ry, x1) - let mut g1 = vec![E::ZERO; 1 << f1.num_vars()]; - let mul3s_fanin_mapping = &layer.mul3s_fanin_mapping[Step1 as usize]; - let mul2s_fanin_mapping = &layer.mul2s_fanin_mapping[Step1 as usize]; - let adds_fanin_mapping = &layer.adds_fanin_mapping[Step1 as usize]; - - prepare_stepx_g_fn!( - &mut g1, - lo_in_num_vars, - lo_out_num_vars, - thread_s, - mul3s_fanin_mapping, - |s_in, s_out, gate| { - eq[s_out ^ gate.idx_out] - * next_layer_vec[s_in + gate.idx_in[1]] - * next_layer_vec[s_in + gate.idx_in[2]] - * gate.scalar.eval(challenges) - }, - mul2s_fanin_mapping, - |s_in, s_out, gate| { - eq[s_out ^ gate.idx_out] - * next_layer_vec[s_in + gate.idx_in[1]] - * gate.scalar.eval(challenges) - }, - adds_fanin_mapping, - |_s_in, s_out, gate| eq[s_out ^ gate.idx_out] * gate.scalar.eval(challenges) - ); - let g1 = DenseMultilinearExtension::from_evaluations_ext_vec(f1.num_vars(), g1).into(); - exit_span!(span); - - // f1'^{(j)}(s1 || x1) = subset[j][i](s1 || x1) - // g1'^{(j)}(s1 || x1) = eq(rt, s1) paste_from[j](ry, x1) - let span = entered_span!("f1j_g1j"); - let (f1_j, g1_j): ( - Vec>, - Vec>, - ) = izip!(&layer.paste_from) - .map(|(j, paste_from)| { - let paste_from_sources = - circuit_witness.layers_ref()[*j as usize].get_base_field_vec(); - let layer_per_instance_size = circuit_witness.layers_ref()[*j as usize] - .evaluations() - .len() - / circuit_witness.n_instances(); - - let old_wire_id = |old_layer_id: usize, subset_wire_id: usize| -> usize { - circuit.layers[old_layer_id].copy_to[&{ layer_id }][subset_wire_id] - }; - - let mut f1_j = vec![0.into(); 1 << f1.num_vars()]; - let mut g1_j = vec![E::ZERO; 1 << f1.num_vars()]; - - for s in 0..(1 << (hi_num_vars - log2_max_thread_id)) { - let global_s = thread_s + s; - let instance_start_index = layer_per_instance_size * global_s; - // TODO find max consecutive subset_wire_ids and optimize by copy_from_slice - paste_from - .iter() - .enumerate() - .for_each(|(subset_wire_id, &new_wire_id)| { - f1_j[(s << lo_in_num_vars) ^ subset_wire_id] = paste_from_sources - [instance_start_index + old_wire_id(*j as usize, subset_wire_id)]; - g1_j[(s << lo_in_num_vars) ^ subset_wire_id] += - eq[(global_s << lo_out_num_vars) ^ new_wire_id]; - }); - } - let f1_j: ArcMultilinearExtension<'a, E> = Arc::new( - DenseMultilinearExtension::from_evaluations_vec(f1.num_vars(), f1_j), - ); - let g1_j: ArcMultilinearExtension<'a, E> = Arc::new( - DenseMultilinearExtension::from_evaluations_ext_vec(f1.num_vars(), g1_j), - ); - (f1_j, g1_j) - }) - .unzip::<_, _, Vec<_>, Vec<_>>(); - - let (f, g): ( - Vec>, - Vec>, - ) = ([vec![f1], f1_j].concat(), [vec![g1], g1_j].concat()); - - // sumcheck: sigma = \sum_{s1 || x1} f1(s1 || x1) * g1(s1 || x1) + \sum_j f1'_j(s1 || x1) * g1'_j(s1 || x1) - let mut virtual_poly_1 = VirtualPolynomialV2::new(f[0].num_vars()); - for (f, g) in f.into_iter().zip(g.into_iter()) { - let mut tmp = VirtualPolynomialV2::new_from_mle(f, E::ONE); - tmp.mul_by_mle(g, E::BaseField::ONE); - virtual_poly_1.merge(&tmp); - } - exit_span!(span); - end_timer!(timer); - - virtual_poly_1 - } - - pub(super) fn combine_phase2_step1_evals( - &mut self, - circuit: &Circuit, - sumcheck_proof_1: SumcheckProof, - prover_state: sumcheck::structs::IOPProverStateV2, - ) -> IOPProverStepMessage { - let layer = &circuit.layers[self.layer_id as usize]; - let eval_point_1 = sumcheck_proof_1.point.clone(); - let (f1_vec, g1_vec): (Vec<_>, Vec<_>) = prover_state - .get_mle_final_evaluations() - .into_iter() - .enumerate() - .partition(|(i, _)| i % 2 == 0); - let f1_vec_len = f1_vec.len(); - // eval_values_f1 - let mut eval_values_1 = f1_vec.into_iter().map(|(_, f1_j)| f1_j).collect_vec(); - - // eval_values_g1[0] - eval_values_1.push(g1_vec[0].1); - - self.to_next_phase_point_and_evals = - vec![PointAndEval::new_from_ref(&eval_point_1, &eval_values_1[0])]; - izip!( - layer.paste_from.iter(), - eval_values_1[..f1_vec_len].iter().skip(1) - ) - .for_each(|((&old_layer_id, _), &subset_value)| { - self.subset_point_and_evals[old_layer_id as usize].push(( - self.layer_id, - PointAndEval::new_from_ref(&eval_point_1, &subset_value), - )); - }); - self.to_next_step_point = eval_point_1; - - IOPProverStepMessage { - sumcheck_proof: sumcheck_proof_1, - sumcheck_eval_values: eval_values_1, - } - } - - /// Sumcheck 2 sigma = \sum_{s2 || x2} f2(s2 || x2) * g2(s2 || x2) - /// sigma = g1(rs1 || rx1) - eq(rt, rs1) * add(ry, rx1) - /// f2(s2 || x2) = layers[i + 1](s2 || x2) - /// g2(s2 || x2) = \sum_{s3}( \sum_{x3}( - /// eq(rt, rs1, s2, s3) * mul3(ry, rx1, x2, x3) * layers[i + 1](s3 || x3) - /// ) ) + eq(rt, rs1, s2) * mul2(ry, rx1, x2) - #[tracing::instrument(skip_all, name = "build_phase2_step2_sumcheck_poly")] - pub(super) fn build_phase2_step2_sumcheck_poly<'a>( - layer_id: LayerId, - eqs: &[Vec; 2], - circuit: &Circuit, - circuit_witness: &'a CircuitWitness, - multi_threads_meta: (usize, usize), - ) -> VirtualPolynomialV2<'a, E> { - let timer = start_timer!(|| "Prover sumcheck phase 2 step 2"); - let layer = &circuit.layers[layer_id as usize]; - let lo_out_num_vars = layer.num_vars; - let lo_in_num_vars = layer.max_previous_num_vars; - let (eq0, eq1) = (&eqs[0], &eqs[1]); - - // parallel unit logic handling - let hi_num_vars = circuit_witness.instance_num_vars(); - let (thread_id, max_thread_id) = multi_threads_meta; - let log2_max_thread_id = ceil_log2(max_thread_id); - let threads_num_vars = hi_num_vars - log2_max_thread_id; - let thread_s = thread_id << threads_num_vars; - - let next_layer_vec = circuit_witness.layers[layer_id as usize + 1].get_base_field_vec(); - - let challenges = &circuit_witness.challenges; - - let span = entered_span!("f2_g2"); - // f2(s2 || x2) = layers[i + 1](s2 || x2) - let f2 = Arc::new( - circuit_witness.layers_ref()[layer_id as usize + 1] - .get_ranged_mle(multi_threads_meta.1, multi_threads_meta.0), - ); - - // g2(s2 || x2) = \sum_{s3}( \sum_{x3}( - // eq(rt, rs1, s2, s3) * mul3(ry, rx1, x2, x3) * layers[i + 1](s3 || x3) - // ) ) + eq(rt, rs1, s2) * mul2(ry, rx1, x2) - let g2: ArcDenseMultilinearExtension = { - let mut g2 = vec![E::ZERO; 1 << (f2.num_vars())]; - let mul3s_fanin_mapping = &layer.mul3s_fanin_mapping[Step2 as usize]; - let mul2s_fanin_mapping = &layer.mul2s_fanin_mapping[Step2 as usize]; - prepare_stepx_g_fn!( - &mut g2, - lo_in_num_vars, - lo_out_num_vars, - thread_s, - mul3s_fanin_mapping, - |s_in, s_out, gate| { - eq0[s_out ^ gate.idx_out] - * eq1[s_in ^ gate.idx_in[0]] - * next_layer_vec[s_in + gate.idx_in[2]] - * gate.scalar.eval(challenges) - }, - mul2s_fanin_mapping, - |s_in, s_out, gate| { - eq0[s_out ^ gate.idx_out] - * eq1[s_in ^ gate.idx_in[0]] - * gate.scalar.eval(challenges) - }, - ); - DenseMultilinearExtension::from_evaluations_ext_vec(f2.num_vars(), g2).into() - }; - exit_span!(span); - end_timer!(timer); - - // sumcheck: sigma = \sum_{s2 || x2} f2(s2 || x2) * g2(s2 || x2) - let mut virtual_poly_2 = VirtualPolynomialV2::new_from_mle(f2, E::ONE); - virtual_poly_2.mul_by_mle(g2, E::BaseField::ONE); - - virtual_poly_2 - } - - pub(super) fn combine_phase2_step2_evals( - &mut self, - _circuit: &Circuit, - sumcheck_proof_2: SumcheckProof, - prover_state: sumcheck::structs::IOPProverStateV2, - no_step3: bool, - ) -> IOPProverStepMessage { - let eval_point_2 = sumcheck_proof_2.point.clone(); - let (f2, g2): (Vec<_>, Vec<_>) = prover_state - .get_mle_final_evaluations() - .into_iter() - .enumerate() - .partition(|(i, _)| i % 2 == 0); - let (eval_value_f2, eval_value_g2) = (f2[0].1, g2[0].1); - - self.to_next_phase_point_and_evals - .push(PointAndEval::new_from_ref(&eval_point_2, &eval_value_f2)); - self.to_next_step_point = eval_point_2; - if no_step3 { - IOPProverStepMessage { - sumcheck_proof: sumcheck_proof_2, - sumcheck_eval_values: vec![eval_value_f2], - } - } else { - IOPProverStepMessage { - sumcheck_proof: sumcheck_proof_2, - sumcheck_eval_values: vec![eval_value_f2, eval_value_g2], - } - } - } - - /// Sumcheck 3 sigma = \sum_{s3 || x3} f3(s3 || x3) * g3(s3 || x3) - /// sigma = g2(rs2 || rx2) - eq(rt, rs1, rs2) * mul2(ry, rx1, rx2) - /// f3(s3 || x3) = layers[i + 1](s3 || x3) - /// g3(s3 || x3) = eq(rt, rs1, rs2, s3) * mul3(ry, rx1, rx2, x3) - #[tracing::instrument(skip_all, name = "build_phase2_step3_sumcheck_poly")] - pub(super) fn build_phase2_step3_sumcheck_poly<'a>( - layer_id: LayerId, - eqs: &[Vec; 3], - circuit: &Circuit, - circuit_witness: &'a CircuitWitness, - multi_threads_meta: (usize, usize), - ) -> VirtualPolynomialV2<'a, E> { - let timer = start_timer!(|| "Prover sumcheck phase 2 step 3"); - let layer = &circuit.layers[layer_id as usize]; - let lo_out_num_vars = layer.num_vars; - let lo_in_num_vars = layer.max_previous_num_vars; - let (eq0, eq1, eq2) = (&eqs[0], &eqs[1], &eqs[2]); - - // parallel unit logic handling - let hi_num_vars = circuit_witness.instance_num_vars(); - let (thread_id, max_thread_id) = multi_threads_meta; - let log2_max_thread_id = ceil_log2(max_thread_id); - let threads_num_vars = hi_num_vars - log2_max_thread_id; - let thread_s = thread_id << threads_num_vars; - - let challenges = &circuit_witness.challenges; - - let span = entered_span!("f3_g3"); - // f3(s3 || x3) = layers[i + 1](s3 || x3) - let f3 = Arc::new( - circuit_witness.layers_ref()[layer_id as usize + 1] - .get_ranged_mle(multi_threads_meta.1, multi_threads_meta.0), - ); - // g3(s3 || x3) = eq(rt, rs1, rs2, s3) * mul3(ry, rx1, rx2, x3) - let g3 = { - let mut g3 = vec![E::ZERO; 1 << (f3.num_vars())]; - let fanin_mapping = &layer.mul3s_fanin_mapping[Step3 as usize]; - prepare_stepx_g_fn!( - &mut g3, - lo_in_num_vars, - lo_out_num_vars, - thread_s, - fanin_mapping, - |s_in, s_out, gate| { - eq0[s_out ^ gate.idx_out] - * eq1[s_in ^ gate.idx_in[0]] - * eq2[s_in ^ gate.idx_in[1]] - * gate.scalar.eval(challenges) - } - ); - DenseMultilinearExtension::from_evaluations_ext_vec(f3.num_vars(), g3).into() - }; - - let mut virtual_poly_3 = VirtualPolynomialV2::new_from_mle(f3, E::ONE); - virtual_poly_3.mul_by_mle(g3, E::BaseField::ONE); - - exit_span!(span); - end_timer!(timer); - virtual_poly_3 - } - - pub(super) fn combine_phase2_step3_evals( - &mut self, - _circuit: &Circuit, - sumcheck_proof_3: SumcheckProof, - prover_state: sumcheck::structs::IOPProverStateV2, - ) -> IOPProverStepMessage { - let eval_point_3 = sumcheck_proof_3.point.clone(); - let (f3, _): (Vec<_>, Vec<_>) = prover_state - .get_mle_final_evaluations() - .into_iter() - .enumerate() - .partition(|(i, _)| i % 2 == 0); - let eval_values_3 = vec![f3[0].1]; - self.to_next_phase_point_and_evals - .push(PointAndEval::new_from_ref(&eval_point_3, &eval_values_3[0])); - self.to_next_step_point = eval_point_3; - IOPProverStepMessage { - sumcheck_proof: sumcheck_proof_3, - sumcheck_eval_values: eval_values_3, - } - } -} diff --git a/gkr/src/prover/phase2_input.rs b/gkr/src/prover/phase2_input.rs deleted file mode 100644 index b65ea34ee..000000000 --- a/gkr/src/prover/phase2_input.rs +++ /dev/null @@ -1,166 +0,0 @@ -use ark_std::{end_timer, start_timer}; -use ff::Field; -use ff_ext::ExtensionField; -use itertools::{Itertools, izip}; -use multilinear_extensions::{ - mle::{ArcDenseMultilinearExtension, DenseMultilinearExtension, MultilinearExtension}, - virtual_poly::build_eq_x_r_vec, - virtual_poly_v2::VirtualPolynomialV2, -}; -#[cfg(feature = "parallel")] -use rayon::iter::{IndexedParallelIterator, ParallelIterator}; - -use sumcheck::util::ceil_log2; -use transcript::Transcript; - -use crate::{ - izip_parallizable, - prover::SumcheckStateV2, - structs::{Circuit, CircuitWitness, IOPProverState, IOPProverStepMessage, PointAndEval}, -}; - -// Prove the computation in the current layer for data parallel circuits. -// The number of terms depends on the gate. -// Here is an example of degree 3: -// layers[i](rt || ry) = \sum_x( -// \sum_j paste_from[j](ry, x) * subset[j][i](rt || x) -// ) + add_const(ry) -impl IOPProverState { - /// Sumcheck 1: sigma = \sum_j f1'_j(x1) * g1'_j(x1) - /// sigma = layers[i](rt || ry) - add_const(ry), - /// f1'^{(j)}(x1) = subset[j][i](rt || x1) - /// g1'^{(j)}(x1) = paste_from[j](ry, x1) - #[tracing::instrument(skip_all, name = "prove_and_update_state_input_phase2_step1")] - pub(super) fn prove_and_update_state_input_phase2_step1( - &mut self, - circuit: &Circuit, - circuit_witness: &CircuitWitness, - transcript: &mut Transcript, - ) -> IOPProverStepMessage { - let timer = start_timer!(|| "Prover sumcheck input phase 2 step 1"); - let layer = &circuit.layers[self.layer_id as usize]; - let lo_out_num_vars = layer.num_vars; - let max_lo_in_num_vars = circuit.max_wit_in_num_vars.unwrap_or(0); - let hi_num_vars = circuit_witness.instance_num_vars(); - let hi_point = &self.to_next_step_point[lo_out_num_vars..]; - - let eq_y_ry = build_eq_x_r_vec(&self.to_next_step_point[..lo_out_num_vars]); - - let paste_from_wit_in = &circuit.paste_from_wits_in; - let wits_in = circuit_witness.witness_in_ref(); - - let (mut f_vec, mut g_vec): ( - Vec>, - Vec>, - ) = izip_parallizable!(paste_from_wit_in) - .enumerate() - .map(|(j, (l, r))| { - let wit_in = circuit_witness.witness_in_ref()[j].get_base_field_vec(); - let per_instance_size = wit_in.len() / circuit_witness.n_instances(); - let mut f = vec![0.into(); 1 << (max_lo_in_num_vars + hi_num_vars)]; - let mut g = vec![E::ZERO; 1 << max_lo_in_num_vars]; - - for (subset_wire_id, &eq_val) in eq_y_ry[*l..*r].iter().enumerate() { - for s in 0..(1 << hi_num_vars) { - let instance_start_index = s * per_instance_size; - f[(s << max_lo_in_num_vars) ^ subset_wire_id] = - wit_in[instance_start_index + subset_wire_id]; - } - g[subset_wire_id] = eq_val; - } - - ( - { - let mut f = DenseMultilinearExtension::from_evaluations_vec( - max_lo_in_num_vars + hi_num_vars, - f, - ); - f.fix_high_variables_in_place(hi_point); - f.into() - }, - DenseMultilinearExtension::from_evaluations_ext_vec(max_lo_in_num_vars, g) - .into(), - ) - }) - .unzip(); - - let paste_from_counter_in = &circuit.paste_from_counter_in; - let (f_vec_counter_in, g_vec_counter_in): ( - Vec>, - Vec>, - ) = izip_parallizable!(paste_from_counter_in) - .map(|(num_vars, (l, r))| { - let mut f = vec![0.into(); 1 << (max_lo_in_num_vars + hi_num_vars)]; - let mut g = vec![E::ZERO; 1 << max_lo_in_num_vars]; - - for (subset_wire_id, &eq_val) in eq_y_ry[*l..*r].iter().enumerate() { - for s in 0..(1 << hi_num_vars) { - f[(s << max_lo_in_num_vars) ^ subset_wire_id] = - E::BaseField::from(((s << num_vars) ^ subset_wire_id) as u64); - } - g[subset_wire_id] = eq_val; - } - ( - { - let mut f = DenseMultilinearExtension::from_evaluations_vec( - max_lo_in_num_vars + hi_num_vars, - f, - ); - f.fix_high_variables_in_place(hi_point); - f.into() - }, - DenseMultilinearExtension::from_evaluations_ext_vec(max_lo_in_num_vars, g) - .into(), - ) - }) - .unzip(); - - f_vec.extend(f_vec_counter_in); - g_vec.extend(g_vec_counter_in); - - let mut virtual_poly = VirtualPolynomialV2::new(max_lo_in_num_vars); - for (f, g) in f_vec.into_iter().zip(g_vec.into_iter()) { - let mut tmp = VirtualPolynomialV2::new_from_mle(f, E::ONE); - tmp.mul_by_mle(g, E::BaseField::ONE); - virtual_poly.merge(&tmp); - } - - #[allow(deprecated)] - let (sumcheck_proofs, prover_state) = - SumcheckStateV2::prove_parallel(virtual_poly, transcript); - let eval_point = sumcheck_proofs.point.clone(); - let (f_vec, _): (Vec<_>, Vec<_>) = prover_state - .get_mle_final_evaluations() - .into_iter() - .enumerate() - .partition(|(i, _)| i % 2 == 0); - let eval_values_f = f_vec - .into_iter() - .take(wits_in.len()) - .map(|(_, f)| f) - .collect_vec(); - - self.to_next_phase_point_and_evals = izip!(paste_from_wit_in.iter(), eval_values_f.iter()) - .map(|((l, r), eval)| { - let num_vars = ceil_log2(*r - *l); - let point = [&eval_point[..num_vars], hi_point].concat(); - let wit_in_eval = *eval - * eval_point[num_vars..] - .iter() - .map(|x| E::ONE - *x) - .product::() - .invert() - .unwrap(); - PointAndEval::new_from_ref(&point, &wit_in_eval) - }) - .collect_vec(); - self.to_next_step_point = [&eval_point, hi_point].concat(); - - end_timer!(timer); - - IOPProverStepMessage { - sumcheck_proof: sumcheck_proofs, - sumcheck_eval_values: eval_values_f, - } - } -} diff --git a/gkr/src/prover/phase2_linear.rs b/gkr/src/prover/phase2_linear.rs deleted file mode 100644 index ddf13835d..000000000 --- a/gkr/src/prover/phase2_linear.rs +++ /dev/null @@ -1,162 +0,0 @@ -use std::sync::Arc; - -use ark_std::{end_timer, start_timer}; -use ff::Field; -use ff_ext::ExtensionField; -use itertools::{Itertools, izip}; -use multilinear_extensions::{ - mle::{ArcDenseMultilinearExtension, DenseMultilinearExtension, MultilinearExtension}, - virtual_poly::build_eq_x_r_vec, - virtual_poly_v2::{ArcMultilinearExtension, VirtualPolynomialV2}, -}; -use transcript::Transcript; - -use crate::{ - circuit::EvaluateConstant, - prover::SumcheckStateV2, - structs::{Circuit, CircuitWitness, IOPProverState, IOPProverStepMessage, PointAndEval}, -}; - -// Prove the computation in the current layer for data parallel circuits. -// The number of terms depends on the gate. -// Here is an example of degree 3: -// layers[i](rt || ry) = \sum_x( -// add(ry, x) * layers[i + 1](rt || x) + \sum_j paste_from[j](ry, x) * subset[j][i](rt || x) -// ) + add_const(ry) -impl IOPProverState { - /// Sumcheck 1: sigma = \sum_{x1} f1(x1) * g1(x1) + \sum_j f1'_j(x1) * g1'_j(x1) - /// sigma = layers[i](rt || ry) - add_const(ry), - /// f1(x1) = layers[i + 1](rt || x1) - /// g1(x1) = add(ry, x1) - /// f1'^{(j)}(x1) = subset[j][i](rt || x1) - /// g1'^{(j)}(x1) = paste_from[j](ry, x1) - #[tracing::instrument(skip_all, name = "prove_and_update_state_linear_phase2_step1")] - pub(super) fn prove_and_update_state_linear_phase2_step1( - &mut self, - circuit: &Circuit, - circuit_witness: &CircuitWitness, - transcript: &mut Transcript, - ) -> IOPProverStepMessage { - let timer = start_timer!(|| "Prover sumcheck phase 2 step 1"); - let layer = &circuit.layers[self.layer_id as usize]; - let lo_out_num_vars = layer.num_vars; - let lo_in_num_vars = layer.max_previous_num_vars; - let hi_num_vars = circuit_witness.instance_num_vars(); - let hi_point = &self.to_next_step_point[lo_out_num_vars..]; - - let eq_y_ry = build_eq_x_r_vec(&self.to_next_step_point[..lo_out_num_vars]); - - let challenges = &circuit_witness.challenges; - - let f1_g1 = || { - assert_eq!( - circuit_witness.layers_ref()[self.layer_id as usize + 1].num_vars() - hi_num_vars, - lo_in_num_vars, - "next layer num var {} - hi_num_vars {} != lo_in_num_vars {}", - circuit_witness.layers_ref()[self.layer_id as usize + 1].num_vars(), - hi_num_vars, - lo_in_num_vars - ); - - // f1(x1) = layers[i + 1](rt || x1) - let f1: ArcMultilinearExtension = Arc::new( - circuit_witness.layers_ref()[self.layer_id as usize + 1] - .fix_high_variables(hi_point), - ); - - // g1(x1) = add(ry, x1) - let g1 = { - let mut g1 = vec![E::ZERO; 1 << lo_in_num_vars]; - layer.adds.iter().for_each(|gate| { - g1[gate.idx_in[0]] += eq_y_ry[gate.idx_out] * gate.scalar.eval(challenges); - }); - - DenseMultilinearExtension::from_evaluations_ext_vec(lo_in_num_vars, g1) - }; - (vec![f1], vec![g1.into()]) - }; - - let (mut f1_vec, mut g1_vec): ( - Vec>, - Vec>, - ) = f1_g1(); - - // f1'^{(j)}(x1) = subset[j][i](rt || x1) - // g1'^{(j)}(x1) = paste_from[j](ry, x1) - let old_wire_id = |old_layer_id: usize, subset_wire_id: usize| -> usize { - circuit.layers[old_layer_id].copy_to[&self.layer_id][subset_wire_id] - }; - layer.paste_from.iter().for_each(|(&j, paste_from)| { - let paste_from_sources = circuit_witness.layers_ref()[j as usize].get_base_field_vec(); - let layer_per_instance_size = - circuit_witness.layers_ref()[j as usize].evaluations().len() - / circuit_witness.n_instances(); - - let mut f1_j = vec![0.into(); 1 << (lo_in_num_vars + hi_num_vars)]; - let mut g1_j = vec![E::ZERO; 1 << lo_in_num_vars]; - - paste_from - .iter() - .enumerate() - .for_each(|(subset_wire_id, &new_wire_id)| { - // TODO seems cache unfriendly if iterating from s - for s in 0..(1 << hi_num_vars) { - let instance_start_index = layer_per_instance_size * s; - f1_j[(s << lo_in_num_vars) ^ subset_wire_id] = paste_from_sources - [instance_start_index + old_wire_id(j as usize, subset_wire_id)]; - } - g1_j[subset_wire_id] += eq_y_ry[new_wire_id]; - }); - f1_vec.push({ - let mut f1_j = DenseMultilinearExtension::from_evaluations_vec( - lo_in_num_vars + hi_num_vars, - f1_j, - ); - f1_j.fix_high_variables_in_place(hi_point); - Arc::new(f1_j) - }); - g1_vec.push( - DenseMultilinearExtension::from_evaluations_ext_vec(lo_in_num_vars, g1_j).into(), - ); - }); - - // sumcheck: sigma = \sum_{x1} f1(x1) * g1(x1) + \sum_j f1'_j(x1) * g1'_j(x1) - let mut virtual_poly_1 = VirtualPolynomialV2::new(lo_in_num_vars); - for (f1_j, g1_j) in izip!(f1_vec.into_iter(), g1_vec.into_iter()) { - let mut tmp = VirtualPolynomialV2::new_from_mle(f1_j, E::ONE); - tmp.mul_by_mle(g1_j, E::BaseField::ONE); - virtual_poly_1.merge(&tmp); - } - - // TODO(Matthias, by 2024-11-01): review whether we can replace this function. - #[allow(deprecated)] - let (sumcheck_proof_1, prover_state) = - SumcheckStateV2::prove_parallel(virtual_poly_1, transcript); - let eval_point_1 = sumcheck_proof_1.point.clone(); - let (f1_vec, _): (Vec<_>, Vec<_>) = prover_state - .get_mle_final_evaluations() - .into_iter() - .enumerate() - .partition(|(i, _)| i % 2 == 0); - let eval_values_f1 = f1_vec.into_iter().map(|(_, f1_j)| f1_j).collect_vec(); - - let new_point = [&eval_point_1, hi_point].concat(); - self.to_next_phase_point_and_evals = - vec![PointAndEval::new_from_ref(&new_point, &eval_values_f1[0])]; - izip!(layer.paste_from.iter(), eval_values_f1.iter().skip(1)).for_each( - |((&old_layer_id, _), &subset_value)| { - self.subset_point_and_evals[old_layer_id as usize].push(( - self.layer_id, - PointAndEval::new_from_ref(&new_point, &subset_value), - )); - }, - ); - self.to_next_step_point = new_point; - end_timer!(timer); - - IOPProverStepMessage { - sumcheck_proof: sumcheck_proof_1, - sumcheck_eval_values: eval_values_f1, - } - } -} diff --git a/gkr/src/prover/test.rs b/gkr/src/prover/test.rs deleted file mode 100644 index 540065f66..000000000 --- a/gkr/src/prover/test.rs +++ /dev/null @@ -1,797 +0,0 @@ -use std::collections::HashMap; - -use ark_std::test_rng; -use ff::Field; -use ff_ext::ExtensionField; -use goldilocks::GoldilocksExt2; -use itertools::{Itertools, izip}; -use multilinear_extensions::mle::DenseMultilinearExtension; -use simple_frontend::structs::{ChallengeConst, ChallengeId, CircuitBuilder, MixedCell}; -use transcript::Transcript; - -use crate::{ - structs::{Circuit, CircuitWitness, IOPProverState, IOPVerifierState, PointAndEval}, - utils::i64_to_field, -}; - -fn copy_and_paste_circuit() -> Circuit { - let mut circuit_builder = CircuitBuilder::::default(); - // Layer 3 - let (_, input) = circuit_builder.create_witness_in(4); - - // Layer 2 - let mul_01 = circuit_builder.create_cell(); - circuit_builder.mul2(mul_01, input[0], input[1], Ext::BaseField::ONE); - - // Layer 1 - let mul_012 = circuit_builder.create_cell(); - circuit_builder.mul2(mul_012, mul_01, input[2], Ext::BaseField::ONE); - - // Layer 0 - let (_, mul_001123) = circuit_builder.create_witness_out(1); - circuit_builder.mul3( - mul_001123[0], - mul_01, - mul_012, - input[3], - Ext::BaseField::ONE, - ); - - circuit_builder.configure(); - - Circuit::new(&circuit_builder) -} - -fn copy_and_paste_witness<'a, Ext: ExtensionField>() --> (Vec>, CircuitWitness<'a, Ext>) { - // witness_in, single instance - let inputs = vec![vec![ - i64_to_field(5), - i64_to_field(7), - i64_to_field(11), - i64_to_field(13), - ]]; - let witness_in: Vec> = vec![inputs.into()]; - - let layers: Vec> = vec![ - vec![vec![i64_to_field(175175)]].into(), - vec![vec![ - i64_to_field(385), - i64_to_field(35), - i64_to_field(13), - i64_to_field(0), // pad - ]] - .into(), - vec![vec![i64_to_field(35), i64_to_field(11)]].into(), - vec![vec![ - i64_to_field(5), - i64_to_field(7), - i64_to_field(11), - i64_to_field(13), - ]] - .into(), - ]; - - let outputs = vec![vec![i64_to_field(175175)]]; - let witness_out: Vec> = vec![outputs.into()]; - - (witness_in.clone(), CircuitWitness { - layers: layers.into_iter().map(|w| w.into()).collect(), - witness_in: witness_in.into_iter().map(|w| w.into()).collect(), - witness_out: witness_out.into_iter().map(|w| w.into()).collect(), - n_instances: 1, - challenges: HashMap::new(), - }) -} - -fn paste_from_wit_in_circuit() -> Circuit { - let mut circuit_builder = CircuitBuilder::::default(); - - // Layer 2 - let (_leaf_id1, leaves1) = circuit_builder.create_witness_in(3); - let (_leaf_id2, leaves2) = circuit_builder.create_witness_in(3); - // Unused input elements should also be in the circuit. - let (_dummy_id, _) = circuit_builder.create_witness_in(3); - let _ = circuit_builder.create_counter_in(1); - let _ = circuit_builder.create_constant_in(2, 1); - - // Layer 1 - let (_, inners) = circuit_builder.create_witness_out(2); - circuit_builder.mul2(inners[0], leaves1[0], leaves1[1], Ext::BaseField::ONE); - circuit_builder.mul2(inners[1], leaves1[2], leaves2[0], Ext::BaseField::ONE); - - // Layer 0 - let (_, root) = circuit_builder.create_witness_out(1); - circuit_builder.mul2(root[0], inners[0], inners[1], Ext::BaseField::ONE); - - circuit_builder.configure(); - - Circuit::new(&circuit_builder) -} - -fn paste_from_wit_in_witness<'a, Ext: ExtensionField>() --> (Vec>, CircuitWitness<'a, Ext>) { - // witness_in, single instance - let leaves1 = vec![vec![i64_to_field(5), i64_to_field(7), i64_to_field(11)]]; - let leaves2 = vec![vec![i64_to_field(13), i64_to_field(17), i64_to_field(19)]]; - let dummy = vec![vec![i64_to_field(13), i64_to_field(17), i64_to_field(19)]]; - let witness_in = vec![leaves1.into(), leaves2.into(), dummy.into()]; - - let layers: Vec> = vec![ - vec![vec![ - i64_to_field(5005), - i64_to_field(35), - i64_to_field(143), - i64_to_field(0), // pad - ]] - .into(), - vec![vec![i64_to_field(35), i64_to_field(143)]].into(), - vec![vec![ - i64_to_field(5), // leaves1 - i64_to_field(7), - i64_to_field(11), - i64_to_field(13), // leaves2 - i64_to_field(17), - i64_to_field(19), - i64_to_field(13), // dummy - i64_to_field(17), - i64_to_field(19), - i64_to_field(0), // counter - i64_to_field(1), - i64_to_field(1), // constant - i64_to_field(1), - i64_to_field(0), // pad - i64_to_field(0), - i64_to_field(0), - ]] - .into(), - ]; - - let outputs1 = vec![vec![i64_to_field(35), i64_to_field(143)]]; - let outputs2 = vec![vec![i64_to_field(5005)]]; - let witness_out: Vec> = vec![outputs1.into(), outputs2.into()]; - - (witness_in.clone(), CircuitWitness { - layers: layers.into_iter().map(|w| w.into()).collect(), - witness_in: witness_in.into_iter().map(|w| w.into()).collect(), - witness_out: witness_out.into_iter().map(|w| w.into()).collect(), - n_instances: 1, - challenges: HashMap::new(), - }) -} - -fn copy_to_wit_out_circuit() -> Circuit { - let mut circuit_builder = CircuitBuilder::::default(); - // Layer 2 - let (_, leaves) = circuit_builder.create_witness_in(4); - - // Layer 1 - let (_inner_id, inners) = circuit_builder.create_witness_out(2); - circuit_builder.mul2(inners[0], leaves[0], leaves[1], Ext::BaseField::ONE); - circuit_builder.mul2(inners[1], leaves[2], leaves[3], Ext::BaseField::ONE); - - // Layer 0 - let root = circuit_builder.create_cell(); - circuit_builder.mul2(root, inners[0], inners[1], Ext::BaseField::ONE); - circuit_builder.assert_const(root, 5005); - - circuit_builder.configure(); - - Circuit::new(&circuit_builder) -} - -fn copy_to_wit_out_witness<'a, Ext: ExtensionField>() --> (Vec>, CircuitWitness<'a, Ext>) { - // witness_in, single instance - let leaves = vec![vec![ - i64_to_field(5), - i64_to_field(7), - i64_to_field(11), - i64_to_field(13), - ]] - .into(); - let witness_in = vec![leaves]; - - let layers: Vec> = vec![ - vec![vec![ - i64_to_field(5005), - i64_to_field(35), - i64_to_field(143), - i64_to_field(0), // pad - ]] - .into(), - vec![vec![i64_to_field(35), i64_to_field(143)]].into(), - vec![vec![ - i64_to_field(5), - i64_to_field(7), - i64_to_field(11), - i64_to_field(13), - ]] - .into(), - ]; - - let outputs = vec![vec![i64_to_field(35), i64_to_field(143)]]; - let witness_out: Vec> = vec![outputs.into()]; - - (witness_in.clone(), CircuitWitness { - layers: layers.into_iter().map(|w| w.into()).collect(), - witness_in: witness_in.into_iter().map(|w| w.into()).collect(), - witness_out: witness_out.into_iter().map(|w| w.into()).collect(), - n_instances: 1, - challenges: HashMap::new(), - }) -} - -fn copy_to_wit_out_witness_2<'a, Ext: ExtensionField>() --> (Vec>, CircuitWitness<'a, Ext>) { - // witness_in, 2 instances - let leaves = vec![ - vec![ - i64_to_field(5), - i64_to_field(7), - i64_to_field(11), - i64_to_field(13), - ], - vec![ - i64_to_field(5), - i64_to_field(13), - i64_to_field(11), - i64_to_field(7), - ], - ]; - let witness_in = vec![leaves.into()]; - - let layers: Vec> = vec![ - vec![ - vec![ - i64_to_field(5005), - i64_to_field(35), - i64_to_field(143), - i64_to_field(0), // pad - ], - vec![ - i64_to_field(5005), - i64_to_field(65), - i64_to_field(77), - i64_to_field(0), // pad - ], - ] - .into(), - vec![vec![i64_to_field(35), i64_to_field(143)], vec![ - i64_to_field(65), - i64_to_field(77), - ]] - .into(), - vec![ - vec![ - i64_to_field(5), - i64_to_field(7), - i64_to_field(11), - i64_to_field(13), - ], - vec![ - i64_to_field(5), - i64_to_field(13), - i64_to_field(11), - i64_to_field(7), - ], - ] - .into(), - ]; - - let outputs = vec![vec![i64_to_field(35), i64_to_field(143)], vec![ - i64_to_field(65), - i64_to_field(77), - ]]; - let witness_out: Vec> = vec![outputs.into()]; - - (witness_in.clone(), CircuitWitness { - layers: layers.into_iter().map(|w| w.into()).collect(), - witness_in: witness_in.into_iter().map(|w| w.into()).collect(), - witness_out: witness_out.into_iter().map(|w| w.into()).collect(), - n_instances: 2, - challenges: HashMap::new(), - }) -} - -fn rlc_circuit() -> Circuit { - let mut circuit_builder = CircuitBuilder::::default(); - // Layer 2 - let (_, leaves) = circuit_builder.create_witness_in(4); - - // Layer 1 - let inners = circuit_builder.create_ext_cells(2); - circuit_builder.rlc(&inners[0], &[leaves[0], leaves[1]], 0 as ChallengeId); - circuit_builder.rlc(&inners[1], &[leaves[2], leaves[3]], 1 as ChallengeId); - - // Layer 0 - let (_root_id, roots) = circuit_builder.create_ext_witness_out(1); - circuit_builder.mul2_ext(&roots[0], &inners[0], &inners[1], Ext::BaseField::ONE); - - circuit_builder.configure(); - - Circuit::new(&circuit_builder) -} - -fn rlc_witness<'a, Ext>() -> ( - Vec>, - CircuitWitness<'a, Ext>, - Vec, -) -where - Ext: ExtensionField, -{ - let challenges = vec![ - Ext::from_bases(&[i64_to_field(31), i64_to_field(37)]), - Ext::from_bases(&[i64_to_field(97), i64_to_field(23)]), - ]; - let challenge_pows = challenges - .iter() - .enumerate() - .map(|(i, x)| { - (0..3) - .map(|j| { - ( - ChallengeConst { - challenge: i as u8, - exp: j as u64, - }, - x.pow([j as u64]), - ) - }) - .collect_vec() - }) - .collect_vec(); - - // witness_in, double instances - let leaves = vec![ - vec![ - i64_to_field(5), - i64_to_field(7), - i64_to_field(11), - i64_to_field(13), - ], - vec![ - i64_to_field(5), - i64_to_field(13), - i64_to_field(11), - i64_to_field(7), - ], - ]; - let witness_in = vec![leaves.clone().into()]; - - let inner00: Ext = challenge_pows[0][0].1 * leaves[0][0] - + challenge_pows[0][1].1 * leaves[0][1] - + challenge_pows[0][2].1; - let inner01: Ext = challenge_pows[1][0].1 * leaves[0][2] - + challenge_pows[1][1].1 * leaves[0][3] - + challenge_pows[1][2].1; - let inner10: Ext = challenge_pows[0][0].1 * leaves[1][0] - + challenge_pows[0][1].1 * leaves[1][1] - + challenge_pows[0][2].1; - let inner11: Ext = challenge_pows[1][0].1 * leaves[1][2] - + challenge_pows[1][1].1 * leaves[1][3] - + challenge_pows[1][2].1; - - let inners = vec![ - [inner00.clone().as_bases(), inner01.clone().as_bases()].concat(), - [inner10.clone().as_bases(), inner11.clone().as_bases()].concat(), - ]; - - let root_tmp0 = vec![ - inners[0][0] * inners[0][2], - inners[0][0] * inners[0][3], - inners[0][1] * inners[0][2], - inners[0][1] * inners[0][3], - ]; - let root_tmp1 = vec![ - inners[1][0] * inners[1][2], - inners[1][0] * inners[1][3], - inners[1][1] * inners[1][2], - inners[1][1] * inners[1][3], - ]; - let root_tmps = vec![root_tmp0, root_tmp1]; - - let root0 = inner00 * inner01; - let root1 = inner10 * inner11; - let roots = vec![ - root0.as_bases().iter().cloned().collect_vec(), - root1.as_bases().iter().cloned().collect_vec(), - ]; - - let layers: Vec> = vec![ - roots.clone().into(), - root_tmps.into(), - inners.into(), - leaves.into(), - ]; - - let outputs = roots; - let witness_out: Vec> = vec![outputs.into()]; - - ( - witness_in.clone(), - CircuitWitness { - layers: layers.into_iter().map(|w| w.into()).collect(), - witness_in: witness_in.into_iter().map(|w| w.into()).collect(), - witness_out: witness_out.into_iter().map(|w| w.into()).collect(), - n_instances: 2, - challenges: challenge_pows - .iter() - .flatten() - .cloned() - .map(|(k, v)| (k, v.as_bases().to_vec())) - .collect::>(), - }, - challenges, - ) -} - -fn inv_sum_circuit() -> Circuit { - let mut circuit_builder = CircuitBuilder::::default(); - let (_input_id, input) = circuit_builder.create_ext_witness_in(2); - let (_cond_id, cond) = circuit_builder.create_witness_in(2); - let (_, output) = circuit_builder.create_ext_witness_out(2); - // selector denominator 1 or input[0] or input[0] * input[1] - let den_mul = circuit_builder.create_ext_cell(); - circuit_builder.mul2_ext(&den_mul, &input[0], &input[1], Ext::BaseField::ONE); - let tmp = circuit_builder.create_ext_cell(); - circuit_builder.sel_mixed_and_ext( - &tmp, - &MixedCell::Constant(Ext::BaseField::ONE), - &input[0], - cond[0], - ); - circuit_builder.sel_ext(&output[0], &tmp, &den_mul, cond[1]); - - // select the numerator 0 or 1 or input[0] + input[1] - let den_add = circuit_builder.create_ext_cell(); - circuit_builder.add_ext(&den_add, &input[0], Ext::BaseField::ONE); - circuit_builder.add_ext(&den_add, &input[1], Ext::BaseField::ONE); - circuit_builder.sel_mixed_and_ext(&output[1], &cond[0].into(), &den_add, cond[1]); - - circuit_builder.configure(); - Circuit::new(&circuit_builder) -} - -fn inv_sum_witness_4_instances<'a, Ext: ExtensionField>() -> CircuitWitness<'a, Ext> { - let circuit = inv_sum_circuit::(); - // witness_in, double instances - let leaves = vec![ - vec![ - i64_to_field(5), - i64_to_field(7), - i64_to_field(11), - i64_to_field(13), - ], - vec![ - i64_to_field(5), - i64_to_field(13), - i64_to_field(11), - i64_to_field(7), - ], - vec![ - i64_to_field(23), - i64_to_field(29), - i64_to_field(17), - i64_to_field(19), - ], - vec![ - i64_to_field(29), - i64_to_field(17), - i64_to_field(19), - i64_to_field(23), - ], - ]; - let cond: Vec::BaseField>> = vec![ - vec![i64_to_field(1), i64_to_field(1)], - vec![i64_to_field(1), i64_to_field(1)], - vec![i64_to_field(1), i64_to_field(1)], - vec![i64_to_field(0), i64_to_field(0)], - ]; - let witness_in = vec![leaves.into(), cond.into()]; - let mut circuit_wits = CircuitWitness::new(&circuit, vec![]); - circuit_wits.add_instances(&circuit, witness_in, 4); - circuit_wits -} - -fn lookup_inner_circuit() -> Circuit { - let mut circuit_builder = CircuitBuilder::::default(); - - // Layer 2 - let (_, input) = circuit_builder.create_ext_witness_in(4); - // Layer 0 - let output = circuit_builder.create_ext_cells(2); - // denominator - circuit_builder.mul2_ext( - &output[0], // output_den - &input[0], // input_den[0] - &input[2], // input_den[1] - Ext::BaseField::ONE, - ); - - // numerator - circuit_builder.mul2_ext( - &output[1], // output_num - &input[0], // input_den[0] - &input[3], // input_num[1] - Ext::BaseField::ONE, - ); - circuit_builder.mul2_ext( - &output[1], // output_num - &input[2], // input_den[1] - &input[1], // input_num[0] - Ext::BaseField::ONE, - ); - - circuit_builder.configure(); - Circuit::new(&circuit_builder) -} - -fn lookup_inner_witness_4_instances<'a, Ext: ExtensionField>() -> CircuitWitness<'a, Ext> { - let circuit = lookup_inner_circuit::(); - // witness_in, double instances - let leaves = vec![ - vec![ - i64_to_field(5), - i64_to_field(7), - i64_to_field(11), - i64_to_field(13), - i64_to_field(17), - i64_to_field(19), - i64_to_field(23), - i64_to_field(29), - ], - vec![ - i64_to_field(5), - i64_to_field(13), - i64_to_field(11), - i64_to_field(7), - i64_to_field(19), - i64_to_field(23), - i64_to_field(29), - i64_to_field(17), - ], - vec![ - i64_to_field(13), - i64_to_field(11), - i64_to_field(7), - i64_to_field(5), - i64_to_field(23), - i64_to_field(29), - i64_to_field(17), - i64_to_field(19), - ], - vec![ - i64_to_field(11), - i64_to_field(7), - i64_to_field(13), - i64_to_field(5), - i64_to_field(29), - i64_to_field(17), - i64_to_field(19), - i64_to_field(23), - ], - ]; - let witness_in = vec![leaves.into()]; - let mut circuit_wits = CircuitWitness::new(&circuit, vec![]); - circuit_wits.add_instances(&circuit, witness_in, 4); - circuit_wits -} - -fn mixed_in_circuit() -> Circuit { - let mut circuit_builder = CircuitBuilder::::default(); - - // Layer 1 - let (_, _input) = circuit_builder.create_witness_in(5); - let (_, _input_ext) = circuit_builder.create_ext_witness_in(3); - let _input_const1 = circuit_builder.create_constant_in(2, 11); - let _input_counter = circuit_builder.create_counter_in(1); - let _input_const2 = circuit_builder.create_constant_in(2, 17); - - circuit_builder.configure(); - Circuit::new(&circuit_builder) -} - -fn mixed_in_witness_4_instances<'a, Ext: ExtensionField>() -> CircuitWitness<'a, Ext> { - let circuit = mixed_in_circuit::(); - // witness_in, double instances - let input = vec![ - vec![ - i64_to_field(5), - i64_to_field(7), - i64_to_field(11), - i64_to_field(13), - i64_to_field(17), - ], - vec![ - i64_to_field(13), - i64_to_field(11), - i64_to_field(7), - i64_to_field(19), - i64_to_field(11), - ], - vec![ - i64_to_field(7), - i64_to_field(5), - i64_to_field(23), - i64_to_field(29), - i64_to_field(41), - ], - vec![ - i64_to_field(29), - i64_to_field(17), - i64_to_field(97), - i64_to_field(19), - i64_to_field(23), - ], - ]; - let input_ext = vec![ - vec![ - i64_to_field(5), - i64_to_field(7), - i64_to_field(11), - i64_to_field(23), - i64_to_field(29), - i64_to_field(31), - ], - vec![ - i64_to_field(23), - i64_to_field(29), - i64_to_field(31), - i64_to_field(13), - i64_to_field(17), - i64_to_field(19), - ], - vec![ - i64_to_field(31), - i64_to_field(13), - i64_to_field(17), - i64_to_field(23), - i64_to_field(29), - i64_to_field(31), - ], - vec![ - i64_to_field(13), - i64_to_field(17), - i64_to_field(19), - i64_to_field(37), - i64_to_field(41), - i64_to_field(43), - ], - ]; - let witness_in = vec![input.into(), input_ext.into()]; - let mut circuit_wits = CircuitWitness::new(&circuit, vec![]); - circuit_wits.add_instances(&circuit, witness_in, 4); - circuit_wits -} - -fn prove_and_verify( - circuit: Circuit, - circuit_wits: CircuitWitness<'_, Ext>, - challenges: Vec, -) { - let mut rng = test_rng(); - println!( - "circuit_wits.instance_num_vars() {}, circuit.output_num_vars() {}", - circuit_wits.instance_num_vars(), - circuit.output_num_vars() - ); - let out_num_vars = circuit.output_num_vars() + circuit_wits.instance_num_vars(); - let out_point = (0..out_num_vars) - .map(|_| Ext::random(&mut rng)) - .collect_vec(); - - let out_point_and_evals = if circuit.n_witness_out == 0 { - vec![PointAndEval::new( - out_point.clone(), - circuit_wits.output_layer_witness_ref().evaluate(&out_point), - )] - } else { - vec![] - }; - let wit_out_point_and_evals = circuit_wits - .witness_out_ref() - .iter() - .map(|wit| { - println!("wit {:?}", wit.evaluations()); - PointAndEval::new( - out_point[..wit.num_vars()].to_vec(), - wit.evaluate(&out_point[..wit.num_vars()]), - ) - }) - .collect_vec(); - - let mut prover_transcript = Transcript::new(b"transcript"); - let (proof, prover_input_claim) = IOPProverState::prove_parallel( - &circuit, - &circuit_wits, - out_point_and_evals.clone(), - wit_out_point_and_evals.clone(), - 1, - &mut prover_transcript, - ); - - let mut verifier_transcript = Transcript::new(b"transcript"); - let verifier_input_claim = IOPVerifierState::verify_parallel( - &circuit, - &challenges, - out_point_and_evals, - wit_out_point_and_evals, - proof, - circuit_wits.instance_num_vars(), - &mut verifier_transcript, - ) - .expect("Verification failed"); - - assert!( - !izip!( - prover_input_claim.point_and_evals.iter(), - verifier_input_claim.point_and_evals.iter() - ) - .any(|(p, v)| p.point != v.point || p.eval != v.eval) - ); - assert!( - !izip!( - circuit_wits.witness_in.iter(), - prover_input_claim.point_and_evals.iter() - ) - .any(|(wit, p)| wit.evaluate(&p.point) != p.eval) - ); -} - -#[test] -fn test_copy_and_paste() { - let circuit = copy_and_paste_circuit::(); - let (_, circuit_wits) = copy_and_paste_witness::(); - prove_and_verify(circuit, circuit_wits, vec![]); -} - -#[test] -fn test_paste_from_wit_in() { - let circuit = paste_from_wit_in_circuit::(); - let (_, circuit_wits) = paste_from_wit_in_witness::(); - prove_and_verify(circuit, circuit_wits, vec![]); -} - -#[test] -fn test_copy_to_wit_out() { - let circuit = copy_to_wit_out_circuit::(); - let (_, circuit_wits) = copy_to_wit_out_witness::(); - prove_and_verify(circuit, circuit_wits, vec![]); -} - -#[test] -fn test_copy_to_wit_out_2_instances() { - let circuit = copy_to_wit_out_circuit::(); - let (_, circuit_wits) = copy_to_wit_out_witness_2::(); - prove_and_verify(circuit, circuit_wits, vec![]); -} - -#[test] -fn test_challenges() { - let circuit = rlc_circuit::(); - let (_, circuit_wits, challenges) = rlc_witness::(); - prove_and_verify(circuit, circuit_wits, challenges); -} - -#[test] -fn test_inv_sum() { - let circuit = inv_sum_circuit::(); - let circuit_wits = inv_sum_witness_4_instances::(); - prove_and_verify(circuit, circuit_wits, vec![]); -} - -#[test] -fn test_lookup_inner_output_eval() { - let circuit = lookup_inner_circuit::(); - let circuit_wits = lookup_inner_witness_4_instances::(); - prove_and_verify(circuit, circuit_wits, vec![]); -} - -#[test] -fn test_mixed_in() { - let circuit = mixed_in_circuit::(); - let circuit_wits = mixed_in_witness_4_instances::(); - prove_and_verify(circuit, circuit_wits, vec![]); -} diff --git a/gkr/src/structs.rs b/gkr/src/structs.rs deleted file mode 100644 index 17a6b11da..000000000 --- a/gkr/src/structs.rs +++ /dev/null @@ -1,246 +0,0 @@ -use std::{ - array, - collections::{BTreeMap, HashMap}, -}; - -use ff_ext::ExtensionField; -use goldilocks::SmallField; -use multilinear_extensions::virtual_poly_v2::ArcMultilinearExtension; -use serde::{Deserialize, Serialize, Serializer}; -use simple_frontend::structs::{CellId, ChallengeConst, ConstantType, LayerId}; - -pub(crate) type SumcheckProof = sumcheck::structs::IOPProof; - -/// A point is a vector of num_var length -pub type Point = Vec; - -/// A point and the evaluation of this point. -#[derive(Clone, Debug, PartialEq)] -pub struct PointAndEval { - pub point: Point, - pub eval: F, -} - -impl Default for PointAndEval { - fn default() -> Self { - Self { - point: vec![], - eval: E::ZERO, - } - } -} - -impl PointAndEval { - /// Construct a new pair of point and eval. - /// Caller gives up ownership - pub fn new(point: Point, eval: F) -> Self { - Self { point, eval } - } - - /// Construct a new pair of point and eval. - /// Performs deep copy. - pub fn new_from_ref(point: &Point, eval: &F) -> Self { - Self { - point: (*point).clone(), - eval: eval.clone(), - } - } -} - -/// Represent the prover state for each layer in the IOP protocol. To support -/// gates between non-adjacent layers, we leverage the techniques in -/// [Virgo++](https://eprint.iacr.org/2020/1247). -pub struct IOPProverState { - pub(crate) layer_id: LayerId, - /// Evaluations to the next phase. - pub(crate) to_next_phase_point_and_evals: Vec>, - /// Evaluations of subsets from layers __closer__ to the output. - /// __closer__ as in the layer that the subset elements lie in has not been processed. - /// - /// LayerId is the layer id of the incoming subset point and evaluation. - pub(crate) subset_point_and_evals: Vec)>>, - - /// The point to the next step. - pub(crate) to_next_step_point: Point, - - // Especially for output phase1. - pub(crate) assert_point: Point, -} - -/// Represent the verifier state for each layer in the IOP protocol. -pub struct IOPVerifierState { - pub(crate) layer_id: LayerId, - /// Evaluations from the next layer. - pub(crate) to_next_phase_point_and_evals: Vec>, - /// Evaluations of subsets from layers closer to the output. LayerId is the - /// layer id of the incoming subset point and evaluation. - pub(crate) subset_point_and_evals: Vec)>>, - - pub(crate) challenges: HashMap>, - pub(crate) instance_num_vars: usize, - - pub(crate) to_next_step_point_and_eval: PointAndEval, - - // Especially for output phase1. - pub(crate) assert_point: Point, - // Especially for phase2. - pub(crate) out_point: Point, - pub(crate) eq_y_ry: Vec, - pub(crate) eq_x1_rx1: Vec, - pub(crate) eq_x2_rx2: Vec, -} - -#[allow(clippy::too_long_first_doc_paragraph)] -/// Phase 1 is a sumcheck protocol merging the subset evaluations from the -/// layers closer to the circuit output to an evaluation to the output of the -/// current layer. -/// Phase 2 is several sumcheck protocols (depending on the degree of gates), -/// reducing the correctness of the output of the current layer to the input of -/// the current layer. -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct IOPProverStepMessage { - /// Sumcheck proofs for each sumcheck protocol. - pub sumcheck_proof: SumcheckProof, - pub sumcheck_eval_values: Vec, -} - -pub struct IOPProof { - pub sumcheck_proofs: Vec>, -} - -/// Represent the point at the final step and the evaluations of the subsets of -/// the input layer. -#[derive(Clone, Debug, PartialEq)] -pub struct GKRInputClaims { - pub point_and_evals: Vec>, -} - -#[derive(Clone, Copy, Debug, PartialEq, Serialize)] -pub(crate) enum SumcheckStepType { - OutputPhase1Step1, - Phase1Step1, - Phase2Step1, - Phase2Step2, - Phase2Step2NoStep3, - Phase2Step3, - LinearPhase2Step1, - InputPhase2Step1, - Undefined, -} - -pub(crate) enum Step { - Step1 = 0, - Step2, - Step3, -} - -#[derive(Clone, Serialize)] -pub struct Layer { - pub(crate) layer_id: u32, - pub(crate) sumcheck_steps: Vec, - pub(crate) num_vars: usize, - - // Gates. Should be all None if it's the input layer. - pub(crate) add_consts: Vec>>, - pub(crate) adds: Vec>>, - pub(crate) adds_fanin_mapping: [BTreeMap>>>; 1], /* grouping for 1 fanins */ - pub(crate) mul2s: Vec>>, - pub(crate) mul2s_fanin_mapping: [BTreeMap>>>; 2], /* grouping for 2 fanins */ - pub(crate) mul3s: Vec>>, - pub(crate) mul3s_fanin_mapping: [BTreeMap>>>; 3], /* grouping for 3 fanins */ - - /// The corresponding wires copied from this layer to later layers. It is - /// (later layer id -> current wire id to be copied). It stores the non-zero - /// entry of copy_to[layer_id] for each row. - pub(crate) copy_to: BTreeMap>, - /// The corresponding wires from previous layers pasted to this layer. It is - /// (shallower layer id -> pasted to the current id). It stores the non-zero - /// entry of paste_from[layer_id] for each column. Undefined for the input. - pub(crate) paste_from: BTreeMap>, - /// Maximum size of the subsets pasted from the previous layers, rounded up - /// to the next power of two. This is the logarithm of the rounded size. - /// Undefined for the input layer. - pub(crate) max_previous_num_vars: usize, -} - -impl Default for Layer { - fn default() -> Self { - Layer:: { - layer_id: 0, - sumcheck_steps: vec![], - add_consts: vec![], - adds: vec![], - adds_fanin_mapping: [BTreeMap::new(); 1], - mul2s: vec![], - mul2s_fanin_mapping: array::from_fn(|_| BTreeMap::new()), - mul3s: vec![], - mul3s_fanin_mapping: array::from_fn(|_| BTreeMap::new()), - copy_to: BTreeMap::new(), - paste_from: BTreeMap::new(), - num_vars: 0, - max_previous_num_vars: 0, - } - } -} - -#[derive(Clone, Serialize)] -pub struct Circuit { - pub layers: Vec>, - - pub n_witness_in: usize, - pub n_witness_out: usize, - /// The endpoints in the input layer copied from each input witness. - pub paste_from_wits_in: Vec<(CellId, CellId)>, - /// The endpoints in the input layer copied from counter. - pub paste_from_counter_in: Vec<(usize, (CellId, CellId))>, - /// The endpoints in the input layer copied from constants - pub paste_from_consts_in: Vec<(i64, (CellId, CellId))>, - /// The wires copied to the output witness - pub copy_to_wits_out: Vec>, - pub assert_consts: Vec>>, - pub max_wit_in_num_vars: Option, -} - -pub type GateCIn = Gate; -pub type Gate1In = Gate; -pub type Gate2In = Gate; -pub type Gate3In = Gate; - -#[derive(Clone, Debug, PartialEq)] -/// Macro struct for Gate -pub struct Gate { - pub(crate) idx_in: [CellId; FAN_IN], - pub(crate) idx_out: CellId, - pub(crate) scalar: C, -} - -impl Serialize for Gate { - fn serialize(&self, _: S) -> Result<::Ok, ::Error> - where - S: Serializer, - { - // TODO! - todo!() - } -} - -#[derive(Clone)] -pub struct CircuitWitness<'a, E: ExtensionField> { - /// Three vectors denote 1. layer_id, 2. instance_id || wire_id. - pub(crate) layers: Vec>, - /// Three vectors denote 1. wires_in id, 2. instance_id || wire_id. - pub(crate) witness_in: Vec>, - /// Three vectors denote 1. wires_out id, 2. instance_id || wire_id. - pub(crate) witness_out: Vec>, - /// Challenges - pub(crate) challenges: HashMap>, - /// The number of instances for the same sub-circuit. - pub(crate) n_instances: usize, -} - -#[derive(Clone, Debug, Default, PartialEq, Serialize)] -pub struct LayerWitness { - pub instances: Vec>, -} - -pub type LayerInstanceWit = Vec; diff --git a/gkr/src/test.rs b/gkr/src/test.rs deleted file mode 100644 index 970be5bdf..000000000 --- a/gkr/src/test.rs +++ /dev/null @@ -1 +0,0 @@ -mod is_zero_gadget; diff --git a/gkr/src/test/is_zero_gadget.rs b/gkr/src/test/is_zero_gadget.rs deleted file mode 100644 index 697eb4cdc..000000000 --- a/gkr/src/test/is_zero_gadget.rs +++ /dev/null @@ -1,313 +0,0 @@ -use crate::structs::{Circuit, CircuitWitness, IOPProverState, IOPVerifierState, PointAndEval}; -use ff::Field; -use ff_ext::ExtensionField; -use goldilocks::{Goldilocks, GoldilocksExt2}; -use itertools::Itertools; -use multilinear_extensions::mle::{DenseMultilinearExtension, IntoMLE}; -use simple_frontend::structs::{CellId, CircuitBuilder}; -use std::{iter, time::Duration}; -use transcript::Transcript; - -// build an IsZero Gadget -// IsZero Gadget returns 1 when value == 0, and returns 0 otherwise. -// when value != 0 check inv = value ^ {-1}: cond1 = value * (value * -// inv - 1) = 0 -// when value == 0 check inv = 0: cond2 = inv â‹… (value * -// inv - 1) = 0 -// value and inv must occupy one cell and -// all intermediate computations are restricted by field size -pub fn is_zero_gadget( - circuit_builder: &mut CircuitBuilder, - value: CellId, - inv: CellId, -) -> (CellId, CellId, CellId) { - // value * inv - let value_mul_inv = circuit_builder.create_cell(); - circuit_builder.mul2(value_mul_inv, value, inv, Ext::BaseField::ONE); - // value * inv - 1 - let value_mul_inv_minus_one = value_mul_inv; - circuit_builder.add_const(value_mul_inv_minus_one, -Ext::BaseField::ONE); - // cond1 = value * (value * inv - 1) - let cond1 = circuit_builder.create_cell(); - circuit_builder.mul2(cond1, value, value_mul_inv_minus_one, Ext::BaseField::ONE); - // cond2 = inv * (value * inv - 1) - let cond2 = circuit_builder.create_cell(); - circuit_builder.mul2(cond2, inv, value_mul_inv_minus_one, Ext::BaseField::ONE); - // is_zero is a copy of value_mul_inv_minus_one - let is_zero = circuit_builder.create_cell(); - circuit_builder.add(is_zero, value_mul_inv_minus_one, Ext::BaseField::ONE); - - (is_zero, cond1, cond2) -} - -#[test] -fn test_gkr_circuit_is_zero_gadget_simple() { - // input and output - let in_value = vec![Goldilocks::from(5)]; - let in_inv = vec![Goldilocks::from(5).invert().unwrap()]; - let out_is_zero = Goldilocks::from(0); - - // build the circuit, only one cell for value, inv and value * inv etc - let mut circuit_builder = CircuitBuilder::::default(); - let (value_wire_in_id, value) = circuit_builder.create_witness_in(1); - let (inv_wire_in_id, inv) = circuit_builder.create_witness_in(1); - let (is_zero, cond1, cond2) = is_zero_gadget(&mut circuit_builder, value[0], inv[0]); - let cond_wire_out_id = circuit_builder.create_witness_out_from_cells(&[cond1, cond2]); - let is_zero_wire_out_id = circuit_builder.create_witness_out_from_cells(&[is_zero]); - - circuit_builder.configure(); - #[cfg(debug_assertions)] - circuit_builder.print_info(); - let circuit = Circuit::new(&circuit_builder); - println!("circuit: {:?}", circuit); - - // assign wire in - let n_wits_in = circuit.n_witness_in; - let mut wit_in = vec![DenseMultilinearExtension::default(); n_wits_in]; - wit_in[value_wire_in_id as usize] = in_value.into_mle(); - wit_in[inv_wire_in_id as usize] = in_inv.into_mle(); - let circuit_witness = { - let challenges = vec![GoldilocksExt2::from(2)]; - let mut circuit_witness = CircuitWitness::new(&circuit, challenges); - circuit_witness.add_instance(&circuit, wit_in); - circuit_witness - }; - println!("circuit witness: {:?}", circuit_witness); - // use of check_correctness will panic - // circuit_witness.check_correctness(&circuit); - - // check the result - let layers = circuit_witness.layers_ref(); - println!("layers: {:?}", layers); - - let wits_out = circuit_witness.witness_out_ref(); - let cond_wire_out_ref = &wits_out[cond_wire_out_id as usize]; - let is_zero_wire_out_ref = &wits_out[is_zero_wire_out_id as usize]; - println!( - "cond wire outs: {:?}, is zero wire out {:?}", - cond_wire_out_ref, is_zero_wire_out_ref - ); - - // cond1 and cond2 - assert_eq!( - cond_wire_out_ref.get_base_field_vec()[0], - Goldilocks::from(0) - ); - assert_eq!( - cond_wire_out_ref.get_base_field_vec()[1], - Goldilocks::from(0) - ); - // is_zero - assert_eq!(is_zero_wire_out_ref.get_base_field_vec()[0], out_is_zero); - - // add prover-verifier process - let mut prover_transcript = - Transcript::::new(b"test_gkr_circuit_IsZeroGadget_simple"); - let mut verifier_transcript = - Transcript::::new(b"test_gkr_circuit_IsZeroGadget_simple"); - - let mut prover_wires_out_evals = vec![]; - let mut verifier_wires_out_evals = vec![]; - let instance_num_vars = 1_u32.ilog2() as usize; - for wire_out_id in [cond_wire_out_id, is_zero_wire_out_id] { - let output_mle = &wits_out[wire_out_id as usize]; - let prover_output_point = iter::repeat_with(|| { - prover_transcript - .get_and_append_challenge(b"output_point_test_gkr_circuit_IsZeroGadget_simple") - .elements - }) - .take(output_mle.num_vars()) - .collect_vec(); - let verifier_output_point = iter::repeat_with(|| { - verifier_transcript - .get_and_append_challenge(b"output_point_test_gkr_circuit_IsZeroGadget_simple") - .elements - }) - .take(output_mle.num_vars()) - .collect_vec(); - let prover_output_eval = output_mle.evaluate(&prover_output_point); - let verifier_output_eval = output_mle.evaluate(&verifier_output_point); - prover_wires_out_evals.push(PointAndEval::new(prover_output_point, prover_output_eval)); - verifier_wires_out_evals.push(PointAndEval::new( - verifier_output_point, - verifier_output_eval, - )); - } - - let start = std::time::Instant::now(); - let (proof, _) = IOPProverState::prove_parallel( - &circuit, - &circuit_witness, - vec![], - prover_wires_out_evals, - 1, - &mut prover_transcript, - ); - let proof_time: Duration = start.elapsed(); - - let start = std::time::Instant::now(); - let _claim = IOPVerifierState::verify_parallel( - &circuit, - &[], - vec![], - verifier_wires_out_evals, - proof, - instance_num_vars, - &mut verifier_transcript, - ) - .unwrap(); - let verification_time: Duration = start.elapsed(); - - println!( - "proof time: {:?}, verification time: {:?}", - proof_time, verification_time - ); -} - -#[test] -fn test_gkr_circuit_is_zero_gadget_u256() { - // IsZero for U256. Each cell holds 4 bits preventing multiplication overflow. - // value is decomposed into 64 cells - // assert IsZero(value) when all 64 cells are zero - const UINT256_4_N_OPERAND_CELLS: usize = 64; - - // input and output - let mut in_value = vec![Goldilocks::from(0), Goldilocks::from(5)]; - in_value.resize(UINT256_4_N_OPERAND_CELLS, Goldilocks::from(0)); - let mut in_inv = vec![Goldilocks::from(0), Goldilocks::from(5).invert().unwrap()]; - in_inv.resize(UINT256_4_N_OPERAND_CELLS, Goldilocks::from(0)); - let out_is_zero = Goldilocks::from(0); - - // build the circuit, number of cells for value is UINT256_4_N_OPERAND_CELLS - // inv is the inverse of each cell's value, if value = 0 then inv = 0 - let mut circuit_builder = CircuitBuilder::::default(); - let (value_wire_in_id, value) = circuit_builder.create_witness_in(UINT256_4_N_OPERAND_CELLS); - let (inv_wire_in_id, inv) = circuit_builder.create_witness_in(UINT256_4_N_OPERAND_CELLS); - - // is_zero_value = prod_{value_item} (is_zero_value_item) - let mut cond1: Vec = vec![]; - let mut cond2: Vec = vec![]; - let mut is_zero_prev_items = circuit_builder.create_cell(); - circuit_builder.add_const(is_zero_prev_items, Goldilocks::from(1)); - for (value_item, inv_item) in value.into_iter().zip(inv) { - let (is_zero_item, cond1_item, cond2_item) = - is_zero_gadget(&mut circuit_builder, value_item, inv_item); - cond1.push(cond1_item); - cond2.push(cond2_item); - let is_zero = circuit_builder.create_cell(); - // TODO: can optimize using mul3 - circuit_builder.mul2( - is_zero, - is_zero_prev_items, - is_zero_item, - Goldilocks::from(1), - ); - is_zero_prev_items = is_zero; - } - - let cond_wire_out_id = circuit_builder - .create_witness_out_from_cells(&[cond1.as_slice(), cond2.as_slice()].concat()); - let is_zero_wire_out_id = circuit_builder.create_witness_out_from_cells(&[is_zero_prev_items]); - - circuit_builder.configure(); - - #[cfg(debug_assertions)] - circuit_builder.print_info(); - - let circuit = Circuit::new(&circuit_builder); - println!("circuit: {:?}", circuit); - - // assign wire in - let n_wits_in = circuit.n_witness_in; - let mut wits_in = vec![DenseMultilinearExtension::::default(); n_wits_in]; - wits_in[value_wire_in_id as usize] = in_value.into_mle(); - wits_in[inv_wire_in_id as usize] = in_inv.into_mle(); - let circuit_witness = { - let challenges = vec![GoldilocksExt2::from(2)]; - let mut circuit_witness = CircuitWitness::new(&circuit, challenges); - circuit_witness.add_instance(&circuit, wits_in); - circuit_witness - }; - println!("circuit witness: {:?}", circuit_witness); - // use of check_correctness will panic - // circuit_witness.check_correctness(&circuit); - - // check the result - let layers = circuit_witness.layers_ref(); - println!("layers: {:?}", layers); - - let wits_out = circuit_witness.witness_out_ref(); - let cond_wire_out_ref = &wits_out[cond_wire_out_id as usize]; - let is_zero_wire_out_ref = &wits_out[is_zero_wire_out_id as usize]; - println!( - "cond wire outs: {:?}, is zero wire out {:?}", - cond_wire_out_ref, is_zero_wire_out_ref - ); - - // cond1 and cond2 - // for cond_item in cond_wire_out_ref.instances[0].clone().into_iter() { - // assert_eq!(cond_item, Goldilocks::from(0)); - // } - // is_zero - assert_eq!(is_zero_wire_out_ref.get_base_field_vec()[0], out_is_zero); - - // add prover-verifier process - let mut prover_transcript = - Transcript::::new(b"test_gkr_circuit_IsZeroGadget_simple"); - let mut verifier_transcript = - Transcript::::new(b"test_gkr_circuit_IsZeroGadget_simple"); - - let mut prover_wires_out_evals = vec![]; - let mut verifier_wires_out_evals = vec![]; - for wire_out_id in [cond_wire_out_id, is_zero_wire_out_id] { - let output_mle = &wits_out[wire_out_id as usize]; - let prover_output_point = iter::repeat_with(|| { - prover_transcript - .get_and_append_challenge(b"output_point_test_gkr_circuit_IsZeroGadget_simple") - .elements - }) - .take(output_mle.num_vars()) - .collect_vec(); - let verifier_output_point = iter::repeat_with(|| { - verifier_transcript - .get_and_append_challenge(b"output_point_test_gkr_circuit_IsZeroGadget_simple") - .elements - }) - .take(output_mle.num_vars()) - .collect_vec(); - let prover_output_eval = output_mle.evaluate(&prover_output_point); - let verifier_output_eval = output_mle.evaluate(&verifier_output_point); - prover_wires_out_evals.push(PointAndEval::new(prover_output_point, prover_output_eval)); - verifier_wires_out_evals.push(PointAndEval::new( - verifier_output_point, - verifier_output_eval, - )); - } - - let start = std::time::Instant::now(); - let _proof = IOPProverState::prove_parallel( - &circuit, - &circuit_witness, - vec![], - prover_wires_out_evals, - 1, - &mut prover_transcript, - ); - let proof_time: Duration = start.elapsed(); - - // verifier panics due to mismatch of number of variables - // let start = std::time::Instant::now(); - // let _claim = IOPVerifierState::verify_parallel( - // &circuit, - // &[], - // &[], - // &verifier_wires_out_evals, - // &proof, - // instance_num_vars, - // &mut verifier_transcript, - // ).unwrap(); - // let verification_time: Duration = start.elapsed(); - // - // println!("proof time: {:?}, verification time: {:?}", proof_time, verification_time); - println!("proof time: {:?}", proof_time); -} diff --git a/gkr/src/unsafe_utils.rs b/gkr/src/unsafe_utils.rs deleted file mode 100644 index d64392f05..000000000 --- a/gkr/src/unsafe_utils.rs +++ /dev/null @@ -1,26 +0,0 @@ -use std::cell::UnsafeCell; - -// UnsafeSlice to mutate a vector concurrently while index disjoint are promised by caller -#[derive(Copy, Clone)] -pub struct UnsafeSlice<'a, T> { - slice: &'a [UnsafeCell], -} -unsafe impl<'a, T: Send + Sync> Send for UnsafeSlice<'a, T> {} -unsafe impl<'a, T: Send + Sync> Sync for UnsafeSlice<'a, T> {} - -impl<'a, T> UnsafeSlice<'a, T> { - pub fn new(slice: &'a mut [T]) -> Self { - let ptr = slice as *mut [T] as *const [UnsafeCell]; - Self { - slice: unsafe { &*ptr }, - } - } - - /// SAFETY: It is undefined-behavior if two threads write to the same index without - /// synchronization. - #[inline(always)] - pub unsafe fn write(&self, i: usize, value: T) { - let ptr = self.slice[i].get(); - *ptr = value; - } -} diff --git a/gkr/src/utils.rs b/gkr/src/utils.rs deleted file mode 100644 index 960ddc2c6..000000000 --- a/gkr/src/utils.rs +++ /dev/null @@ -1,492 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; -use sumcheck::util::ceil_log2; - -use std::{iter, sync::Arc}; - -use ark_std::{end_timer, iterable::Iterable, start_timer}; -use goldilocks::SmallField; -use itertools::Itertools; -use multilinear_extensions::mle::{ArcDenseMultilinearExtension, DenseMultilinearExtension}; - -pub fn i64_to_field(x: i64) -> F { - if x >= 0 { - F::from(x as u64) - } else { - -F::from((-x) as u64) - } -} - -/// This is to compute a segment indicator. Specifically, it is an MLE of the -/// following vector: -/// segment_{\mathbf{x}} -/// = \sum_{\mathbf{b}=min_idx + 1}^{2^n - 1} \prod_{i=0}^{n-1} (x_i b_i + (1 - x_i)(1 - b_i)) -pub(crate) fn segment_eval_greater_than(min_idx: usize, a: &[E]) -> E { - let running_product2 = { - let mut running_product = vec![E::ZERO; a.len() + 1]; - running_product[a.len()] = E::ONE; - for i in (0..a.len()).rev() { - let bit = E::from(((min_idx >> i) & 1) as u64); - running_product[i] = - running_product[i + 1] * (a[i] * bit + (E::ONE - a[i]) * (E::ONE - bit)); - } - running_product - }; - // Here is an example of how this works: - // Suppose min_idx = (110101)_2 - // Then ans = eq(11011, a[1..6]) - // + eq(111, a[3..6], b[3..6]) - let mut ans = E::ZERO; - for i in 0..a.len() { - let bit = (min_idx >> i) & 1; - if bit == 1 { - continue; - } - ans += running_product2[i + 1] * a[i]; - } - ans -} - -/// This is to compute a variant of eq(\mathbf{x}, \mathbf{y}) for indices in -/// (min_idx, 2^n]. Specifically, it is an MLE of the following vector: -/// partial_eq_{\mathbf{x}}(\mathbf{y}) -/// = \sum_{\mathbf{b}=min_idx + 1}^{2^n - 1} \prod_{i=0}^{n-1} (x_i y_i b_i + (1 - x_i)(1 - y_i)(1 - b_i)) -#[allow(dead_code)] -pub(crate) fn eq_eval_greater_than(min_idx: usize, a: &[F], b: &[F]) -> F { - assert!(a.len() >= b.len()); - // Compute running product of ( x_i y_i + (1 - x_i)(1 - y_i) )_{0 <= i <= n} - let running_product = { - let mut running_product = Vec::with_capacity(a.len() + 1); - running_product.push(F::ONE); - for i in 0..b.len() { - let x = running_product[i] * (a[i] * b[i] + (F::ONE - a[i]) * (F::ONE - b[i])); - running_product.push(x); - } - running_product - }; - - let running_product2 = { - let mut running_product = vec![F::ZERO; b.len() + 1]; - running_product[b.len()] = F::ONE; - for i in (0..b.len()).rev() { - let bit = F::from(((min_idx >> i) & 1) as u64); - running_product[i] = running_product[i + 1] - * (a[i] * b[i] * bit + (F::ONE - a[i]) * (F::ONE - b[i]) * (F::ONE - bit)); - } - running_product - }; - - // Here is an example of how this works: - // Suppose min_idx = (110101)_2 - // Then ans = eq(11011, a[1..6], b[1..6])eq(a[0..1], b[0..1]) - // + eq(111, a[3..6], b[3..6])eq(a[0..3], b[0..3]) - let mut ans = F::ZERO; - for i in 0..b.len() { - let bit = (min_idx >> i) & 1; - if bit == 1 { - continue; - } - ans += running_product[i] * running_product2[i + 1] * a[i] * b[i]; - } - for &ai in &a[b.len()..] { - ans *= F::ONE - ai; - } - ans -} - -/// This is to compute a variant of eq(\mathbf{x}, \mathbf{y}) for indices in -/// [0, max_idx]. Specifically, it is an MLE of the following vector: -/// partial_eq_{\mathbf{x}}(\mathbf{y}) -/// = \sum_{\mathbf{b}=0}^{max_idx} \prod_{i=0}^{n-1} (x_i y_i b_i + (1 - x_i)(1 - y_i)(1 - b_i)) -// TODO(Matthias): See whether we can remove this function. -#[allow(dead_code)] -pub(crate) fn eq_eval_less_or_equal_than(max_idx: usize, a: &[E], b: &[E]) -> E { - assert!(a.len() >= b.len()); - // Compute running product of ( x_i y_i + (1 - x_i)(1 - y_i) )_{0 <= i <= n} - let running_product = { - let mut running_product = Vec::with_capacity(b.len() + 1); - running_product.push(E::ONE); - for i in 0..b.len() { - let x = running_product[i] * (a[i] * b[i] + (E::ONE - a[i]) * (E::ONE - b[i])); - running_product.push(x); - } - running_product - }; - - let running_product2 = { - let mut running_product = vec![E::ZERO; b.len() + 1]; - running_product[b.len()] = E::ONE; - for i in (0..b.len()).rev() { - let bit = E::from(((max_idx >> i) & 1) as u64); - running_product[i] = running_product[i + 1] - * (a[i] * b[i] * bit + (E::ONE - a[i]) * (E::ONE - b[i]) * (E::ONE - bit)); - } - running_product - }; - - // Here is an example of how this works: - // Suppose max_idx = (110101)_2 - // Then ans = eq(a, b) - // - eq(11011, a[1..6], b[1..6])eq(a[0..1], b[0..1]) - // - eq(111, a[3..6], b[3..6])eq(a[0..3], b[0..3]) - let mut ans = running_product[b.len()]; - for i in 0..b.len() { - let bit = (max_idx >> i) & 1; - if bit == 1 { - continue; - } - ans -= running_product[i] * running_product2[i + 1] * a[i] * b[i]; - } - for &ai in &a[b.len()..] { - ans *= E::ONE - ai; - } - ans -} - -pub fn counter_eval(num_vars: usize, x: &[E]) -> E { - assert_eq!(x.len(), num_vars, "invalid size of x"); - let mut ans = E::ZERO; - for (i, &xi) in x.iter().enumerate() { - ans += xi * E::from(1 << i) - } - ans -} - -/// Evaluate eq polynomial for 3 random points. -pub fn eq3_eval(x: &[E], y: &[E], z: &[E]) -> E { - assert_eq!(x.len(), y.len(), "x and y have different length"); - - let start = start_timer!(|| "eq3_eval"); - let mut res = E::ONE; - for ((&xi, &yi), &zi) in x.iter().zip(y.iter()).zip(z.iter()) { - res *= xi * yi * zi + (E::ONE - xi) * (E::ONE - yi) * (E::ONE - zi); - } - end_timer!(start); - res -} - -/// Evaluate eq polynomial for 4 random points -pub fn eq4_eval(x: &[E], y: &[E], z: &[E], w: &[E]) -> E { - assert_eq!(x.len(), y.len(), "x and y have different length"); - - let start = start_timer!(|| "eq3_eval"); - let mut res = E::ONE; - for (((&xi, &yi), &zi), &wi) in x.iter().zip(y.iter()).zip(z.iter()).zip(w.iter()) { - res *= xi * yi * zi * wi + (E::ONE - xi) * (E::ONE - yi) * (E::ONE - zi) * (E::ONE - wi); - } - end_timer!(start); - res -} - -pub fn tensor_product(a: &[F], b: &[F]) -> Vec { - let mut res = vec![F::ZERO; a.len() * b.len()]; - for i in 0..a.len() { - for j in 0..b.len() { - res[i * b.len() + j] = a[i] * b[j]; - } - } - res -} - -pub trait MultilinearExtensionFromVectors { - fn mle(&self, lo_num_vars: usize, hi_num_vars: usize) -> ArcDenseMultilinearExtension; - fn mle_with_meta( - &self, - lo_num_vars: usize, - hi_num_vars: usize, - multi_threads_meta: (usize, usize), - ) -> ArcDenseMultilinearExtension; - fn original_mle(&self) -> ArcDenseMultilinearExtension; -} - -impl MultilinearExtensionFromVectors for &[Vec] { - fn mle_with_meta( - &self, - lo_num_vars: usize, - hi_num_vars: usize, - multi_threads_meta: (usize, usize), - ) -> ArcDenseMultilinearExtension { - let (thread_id, max_thread_id) = multi_threads_meta; - let log2_max_thread_id = ceil_log2(max_thread_id); - assert!(log2_max_thread_id <= hi_num_vars); - assert!( - self.len().is_power_of_two(), - "not supporting non-power of 2 vector len {} ", - self.len() - ); - - let num_instances_per_thread = 1 << (hi_num_vars - log2_max_thread_id); - let vector_len_per_thread = 1 << (hi_num_vars - log2_max_thread_id + lo_num_vars); - - let mle = DenseMultilinearExtension::from_evaluations_vec( - hi_num_vars - log2_max_thread_id + lo_num_vars, - self.iter() - .skip(thread_id * num_instances_per_thread) - .flat_map(|instance| { - instance - .iter() - .cloned() - .chain(iter::repeat(E::BaseField::ZERO)) - .take(1 << lo_num_vars) - }) - .take(vector_len_per_thread) - .collect_vec(), - ); - assert!(mle.evaluations.len() == vector_len_per_thread); - mle.into() - } - fn mle(&self, lo_num_vars: usize, hi_num_vars: usize) -> ArcDenseMultilinearExtension { - assert!( - self.len().is_power_of_two(), - "not supporting non-power of 2 vector len {} ", - self.len() - ); - Arc::new(DenseMultilinearExtension::from_evaluations_vec( - lo_num_vars + hi_num_vars, - self.iter() - .flat_map(|instance| { - instance - .iter() - .cloned() - .chain(iter::repeat(E::BaseField::ZERO)) - .take(1 << lo_num_vars) - }) - .take(1 << (lo_num_vars + hi_num_vars)) - .collect_vec(), - )) - } - fn original_mle(&self) -> ArcDenseMultilinearExtension { - let lo_num_vars = ceil_log2(self[0].len()); - let hi_num_vars = ceil_log2(self.len()); - let n_zeros = (1 << lo_num_vars) - self[0].len(); - let n_zero_vecs = (1 << hi_num_vars) - self.len(); - let vecs = self.to_vec(); - - DenseMultilinearExtension::from_evaluations_vec( - lo_num_vars + hi_num_vars, - vecs.into_iter() - .flat_map(|instance| { - instance - .into_iter() - .chain(iter::repeat(E::BaseField::ZERO).take(n_zeros)) - }) - .chain(vec![E::BaseField::ZERO; n_zero_vecs]) - .collect_vec(), - ) - .into() - } -} - -pub(crate) trait MatrixMLEColumnFirst { - fn fix_row_col_first(&self, row_point_eq: &[E], col_num_vars: usize) -> Vec; - // TODO(Matthias): See whether we can remove this method. - #[allow(dead_code)] - fn fix_row_col_first_with_scalar( - &self, - row_point_eq: &[E], - col_num_vars: usize, - scalar: &E, - ) -> Vec; - fn eval_col_first(&self, row_point_eq: &[E], col_point_eq: &[E]) -> E; -} - -impl MatrixMLEColumnFirst for &[usize] { - fn fix_row_col_first(&self, row_point_eq: &[E], col_num_vars: usize) -> Vec { - let mut ans = vec![E::ZERO; 1 << col_num_vars]; - for (col, &non_zero_row) in self.iter().enumerate() { - ans[col] = row_point_eq[non_zero_row]; - } - ans - } - - fn fix_row_col_first_with_scalar( - &self, - row_point_eq: &[E], - col_num_vars: usize, - scalar: &E, - ) -> Vec { - let mut ans = vec![E::ZERO; 1 << col_num_vars]; - for (col, &non_zero_row) in self.iter().enumerate() { - ans[col] = row_point_eq[non_zero_row] * scalar; - } - ans - } - - fn eval_col_first(&self, row_point_eq: &[E], col_point_eq: &[E]) -> E { - self.iter() - .enumerate() - .fold(E::ZERO, |acc, (col, &non_zero_row)| { - acc + row_point_eq[non_zero_row] * col_point_eq[col] - }) - } -} - -pub(crate) trait MatrixMLERowFirst { - fn fix_row_row_first(&self, row_point_eq: &[E], col_num_vars: usize) -> Vec; - fn fix_row_row_first_with_scalar( - &self, - row_point_eq: &[E], - col_num_vars: usize, - scalar: &E, - ) -> Vec; - fn eval_row_first(&self, row_point_eq: &[E], col_point_eq: &[E]) -> E; -} - -impl MatrixMLERowFirst for &[usize] { - fn fix_row_row_first(&self, row_point_eq: &[E], col_num_vars: usize) -> Vec { - let mut ans = vec![E::ZERO; 1 << col_num_vars]; - for (row, &non_zero_col) in self.iter().enumerate() { - ans[non_zero_col] = row_point_eq[row]; - } - ans - } - - fn fix_row_row_first_with_scalar( - &self, - row_point_eq: &[E], - col_num_vars: usize, - scalar: &E, - ) -> Vec { - let mut ans = vec![E::ZERO; 1 << col_num_vars]; - for (row, &non_zero_col) in self.iter().enumerate() { - ans[non_zero_col] = row_point_eq[row] * scalar; - } - ans - } - - fn eval_row_first(&self, row_point_eq: &[E], col_point_eq: &[E]) -> E { - self.iter() - .enumerate() - .fold(E::ZERO, |acc, (row, &non_zero_col)| { - acc + row_point_eq[row] * col_point_eq[non_zero_col] - }) - } -} - -// TODO(Matthias): See whether we can remove this trait. -#[allow(dead_code)] -pub(crate) trait SubsetIndices { - fn subset_eq_with_scalar(&self, eq: &[F], scalar: &F) -> Vec; - fn subset_eq_eval(&self, eq_1: &[F]) -> F; - fn subset_eq2_eval(&self, eq_1: &[F], eq_2: &[F]) -> F; -} - -impl SubsetIndices for &[usize] { - fn subset_eq_with_scalar(&self, eq: &[F], scalar: &F) -> Vec { - let mut ans = vec![F::ZERO; eq.len()]; - for &non_zero_i in self.iter() { - ans[non_zero_i] = eq[non_zero_i] * scalar; - } - ans - } - fn subset_eq_eval(&self, eq_1: &[F]) -> F { - self.iter() - .fold(F::ZERO, |acc, &non_zero_i| acc + eq_1[non_zero_i]) - } - fn subset_eq2_eval(&self, eq_1: &[F], eq_2: &[F]) -> F { - self.iter().fold(F::ZERO, |acc, &non_zero_i| { - acc + eq_1[non_zero_i] * eq_2[non_zero_i] - }) - } -} - -// test -#[cfg(test)] -mod test { - use super::*; - use ark_std::test_rng; - use ff::Field; - use goldilocks::GoldilocksExt2; - use itertools::Itertools; - use multilinear_extensions::{ - mle::{DenseMultilinearExtension, MultilinearExtension}, - virtual_poly::build_eq_x_r_vec, - }; - - #[test] - fn test_ceil_log2() { - assert_eq!(ceil_log2(1), 0); - assert_eq!(ceil_log2(2), 1); - assert_eq!(ceil_log2(3), 2); - assert_eq!(ceil_log2(4), 2); - assert_eq!(ceil_log2(5), 3); - assert_eq!(ceil_log2(8), 3); - assert_eq!(ceil_log2(9), 4); - assert_eq!(ceil_log2(16), 4); - } - - #[test] - fn test_eq_eval_less_or_equal_than() { - let mut rng = test_rng(); - let n = 5; - let pow_n = 1 << n; - let a = (0..n) - .map(|_| GoldilocksExt2::random(&mut rng)) - .collect_vec(); - let b = (0..n) - .map(|_| GoldilocksExt2::random(&mut rng)) - .collect_vec(); - - let eq_vec = build_eq_x_r_vec(&a); - - { - let max_idx = 0; - let mut partial_eq_vec: Vec<_> = eq_vec[0..=max_idx].to_vec(); - partial_eq_vec.extend(vec![GoldilocksExt2::ZERO; pow_n - max_idx - 1]); - let expected_ans = - DenseMultilinearExtension::from_evaluations_ext_vec(n, partial_eq_vec).evaluate(&b); - assert_eq!(expected_ans, eq_eval_less_or_equal_than(max_idx, &a, &b)); - } - - { - let max_idx = 1; - let mut partial_eq_vec: Vec<_> = eq_vec[0..=max_idx].to_vec(); - partial_eq_vec.extend(vec![GoldilocksExt2::ZERO; pow_n - max_idx - 1]); - let expected_ans = - DenseMultilinearExtension::from_evaluations_ext_vec(n, partial_eq_vec).evaluate(&b); - assert_eq!(expected_ans, eq_eval_less_or_equal_than(max_idx, &a, &b)); - } - - { - let max_idx = 12; - let mut partial_eq_vec: Vec<_> = eq_vec[0..=max_idx].to_vec(); - partial_eq_vec.extend(vec![GoldilocksExt2::ZERO; pow_n - max_idx - 1]); - let expected_ans = - DenseMultilinearExtension::from_evaluations_ext_vec(n, partial_eq_vec).evaluate(&b); - assert_eq!(expected_ans, eq_eval_less_or_equal_than(max_idx, &a, &b)); - } - - { - let max_idx = 1 << ((n - 1) - 1); - let mut partial_eq_vec: Vec<_> = eq_vec[0..=max_idx].to_vec(); - partial_eq_vec.extend(vec![GoldilocksExt2::ZERO; pow_n - max_idx - 1]); - let expected_ans = - DenseMultilinearExtension::from_evaluations_ext_vec(n, partial_eq_vec).evaluate(&b); - assert_eq!(expected_ans, eq_eval_less_or_equal_than(max_idx, &a, &b)); - } - - { - let max_idx = 1 << (n - 1); - let mut partial_eq_vec: Vec<_> = eq_vec[0..=max_idx].to_vec(); - partial_eq_vec.extend(vec![GoldilocksExt2::ZERO; pow_n - max_idx - 1]); - let expected_ans = - DenseMultilinearExtension::from_evaluations_ext_vec(n, partial_eq_vec).evaluate(&b); - assert_eq!(expected_ans, eq_eval_less_or_equal_than(max_idx, &a, &b)); - } - } - - #[test] - fn test_counter_eval() { - let vec = (0..(1 << 4)).map(GoldilocksExt2::from).collect_vec(); - let point = vec![ - GoldilocksExt2::from(97), - GoldilocksExt2::from(101), - GoldilocksExt2::from(23), - GoldilocksExt2::from(29), - ]; - let got_value = counter_eval(4, &point); - let poly = DenseMultilinearExtension::from_evaluations_ext_vec(4, vec); - let expected_value = poly.evaluate(&point); - assert_eq!(got_value, expected_value); - } -} diff --git a/gkr/src/verifier.rs b/gkr/src/verifier.rs deleted file mode 100644 index 10f609396..000000000 --- a/gkr/src/verifier.rs +++ /dev/null @@ -1,137 +0,0 @@ -use ark_std::{end_timer, start_timer}; -use ff_ext::ExtensionField; -use itertools::{Itertools, izip}; -use simple_frontend::structs::{ChallengeConst, LayerId}; -use std::collections::HashMap; -use transcript::Transcript; - -use crate::{ - error::GKRError, - structs::{ - Circuit, GKRInputClaims, IOPProof, IOPVerifierState, PointAndEval, SumcheckStepType, - }, -}; - -mod phase1; -mod phase1_output; -mod phase2; -mod phase2_input; -mod phase2_linear; - -type SumcheckState = sumcheck::structs::IOPVerifierState; - -impl IOPVerifierState { - /// Verify process for data parallel circuits. - pub fn verify_parallel( - circuit: &Circuit, - challenges: &[E], - output_evals: Vec>, - wires_out_evals: Vec>, - proof: IOPProof, - instance_num_vars: usize, - transcript: &mut Transcript, - ) -> Result, GKRError> { - let timer = start_timer!(|| "Verification"); - let challenges = circuit.generate_basefield_challenges(challenges); - - let mut verifier_state = Self::verifier_init_parallel( - circuit.layers.len(), - challenges, - output_evals, - wires_out_evals, - instance_num_vars, - transcript, - circuit.layers[0].num_vars + instance_num_vars, - ); - - let mut sumcheck_proofs_iter = proof.sumcheck_proofs.into_iter(); - for layer_id in 0..circuit.layers.len() { - let timer = start_timer!(|| format!("Verify layer {}", layer_id)); - verifier_state.layer_id = layer_id as LayerId; - - let layer = &circuit.layers[layer_id]; - for (step, step_proof) in izip!(layer.sumcheck_steps.iter(), &mut sumcheck_proofs_iter) - { - match step { - SumcheckStepType::OutputPhase1Step1 => verifier_state - .verify_and_update_state_output_phase1_step1( - circuit, step_proof, transcript, - )?, - SumcheckStepType::Phase1Step1 => verifier_state - .verify_and_update_state_phase1_step1(circuit, step_proof, transcript)?, - SumcheckStepType::Phase2Step1 => verifier_state - .verify_and_update_state_phase2_step1(circuit, step_proof, transcript)?, - SumcheckStepType::Phase2Step2 => verifier_state - .verify_and_update_state_phase2_step2( - circuit, step_proof, transcript, false, - )?, - SumcheckStepType::Phase2Step2NoStep3 => verifier_state - .verify_and_update_state_phase2_step2( - circuit, step_proof, transcript, true, - )?, - SumcheckStepType::Phase2Step3 => verifier_state - .verify_and_update_state_phase2_step3(circuit, step_proof, transcript)?, - SumcheckStepType::LinearPhase2Step1 => verifier_state - .verify_and_update_state_linear_phase2_step1( - circuit, step_proof, transcript, - )?, - SumcheckStepType::InputPhase2Step1 => verifier_state - .verify_and_update_state_input_phase2_step1( - circuit, step_proof, transcript, - )?, - _ => unreachable!(), - } - } - end_timer!(timer); - } - - end_timer!(timer); - - Ok(GKRInputClaims { - point_and_evals: verifier_state.to_next_phase_point_and_evals, - }) - } - - /// Initialize verifying state for data parallel circuits. - fn verifier_init_parallel( - n_layers: usize, - challenges: HashMap>, - output_evals: Vec>, - wires_out_evals: Vec>, - instance_num_vars: usize, - transcript: &mut Transcript, - output_wit_num_vars: usize, - ) -> Self { - let mut subset_point_and_evals = vec![vec![]; n_layers]; - let to_next_step_point_and_eval = if !output_evals.is_empty() { - output_evals.last().unwrap().clone() - } else { - wires_out_evals.last().unwrap().clone() - }; - let assert_point = (0..output_wit_num_vars) - .map(|_| { - transcript - .get_and_append_challenge(b"assert_point") - .elements - }) - .collect_vec(); - let to_next_phase_point_and_evals = output_evals; - subset_point_and_evals[0] = wires_out_evals.into_iter().map(|p| (0, p)).collect(); - Self { - to_next_phase_point_and_evals, - subset_point_and_evals, - to_next_step_point_and_eval, - - challenges, - instance_num_vars, - - assert_point, - // Default - layer_id: 0, - out_point: vec![], - eq_y_ry: vec![], - eq_x1_rx1: vec![], - eq_x2_rx2: vec![], - } - } -} diff --git a/gkr/src/verifier/phase1.rs b/gkr/src/verifier/phase1.rs deleted file mode 100644 index 552490b6b..000000000 --- a/gkr/src/verifier/phase1.rs +++ /dev/null @@ -1,127 +0,0 @@ -use ark_std::{end_timer, start_timer}; -use ff_ext::ExtensionField; -use itertools::{Itertools, chain, izip}; -use multilinear_extensions::virtual_poly::{VPAuxInfo, build_eq_x_r_vec, eq_eval}; -use std::marker::PhantomData; -use transcript::Transcript; - -use crate::{ - error::GKRError, - structs::{Circuit, IOPProverStepMessage, IOPVerifierState, PointAndEval}, - utils::MatrixMLERowFirst, -}; - -use super::SumcheckState; - -impl IOPVerifierState { - pub(super) fn verify_and_update_state_phase1_step1( - &mut self, - circuit: &Circuit, - step_msg: IOPProverStepMessage, - transcript: &mut Transcript, - ) -> Result<(), GKRError> { - let timer = start_timer!(|| "Verifier sumcheck phase 1 step 1"); - let alpha = transcript - .get_and_append_challenge(b"combine subset evals") - .elements; - let total_length = self.to_next_phase_point_and_evals.len() - + self.subset_point_and_evals[self.layer_id as usize].len() - + 1; - let alpha_pows = { - let mut alpha_pows = vec![E::ONE; total_length]; - for i in 0..total_length.saturating_sub(1) { - alpha_pows[i + 1] = alpha_pows[i] * alpha; - } - alpha_pows - }; - - let lo_num_vars = circuit.layers[self.layer_id as usize].num_vars; - let hi_num_vars = self.instance_num_vars; - - // sigma = \sum_j( \alpha^j * subset[i][j](rt_j || ry_j) ) - let mut sigma_1 = izip!(self.to_next_phase_point_and_evals.iter(), alpha_pows.iter()) - .fold(E::ZERO, |acc, (point_and_eval, alpha_pow)| { - acc + point_and_eval.eval * alpha_pow - }); - sigma_1 += izip!( - self.subset_point_and_evals[self.layer_id as usize].iter(), - alpha_pows - .iter() - .skip(self.to_next_phase_point_and_evals.len()) - ) - .fold(E::ZERO, |acc, ((_, point_and_eval), alpha_pow)| { - acc + point_and_eval.eval * alpha_pow - }); - - // Sumcheck: sigma = \sum_{t || y}(f1({t || y}) * (\sum_j g1^{(j)}({t || y}))) - // f1^{(j)}(y) = layers[i](t || y) - // g1^{(j)}(t || y) = \alpha^j * eq(rt_j, t) * eq(ry_j, y) - // g1^{(j)}(t || y) = \alpha^j * eq(rt_j, t) * copy_to[j](ry_j, y) - let claim_1 = SumcheckState::verify( - sigma_1, - &step_msg.sumcheck_proof, - &VPAuxInfo { - max_degree: 2, - num_variables: lo_num_vars + hi_num_vars, - phantom: PhantomData, - }, - transcript, - ); - - let claim1_point = claim_1.point.iter().map(|x| x.elements).collect_vec(); - let claim1_point_lo_num_vars = claim1_point.len() - hi_num_vars; - let eq_y_ry = build_eq_x_r_vec(&claim1_point[..claim1_point_lo_num_vars]); - - assert_eq!(step_msg.sumcheck_eval_values.len(), 1); - let f_value = step_msg.sumcheck_eval_values[0]; - - let g_value: E = chain![ - izip!(self.to_next_phase_point_and_evals.iter(), alpha_pows.iter()).map( - |(point_and_eval, alpha_pow)| { - let point_lo_num_vars = point_and_eval.point.len() - hi_num_vars; - let eq_t = eq_eval( - &point_and_eval.point[point_lo_num_vars..], - &claim1_point[(claim1_point.len() - hi_num_vars)..], - ); - let eq_y = eq_eval( - &point_and_eval.point[..point_lo_num_vars], - &claim1_point[..point_lo_num_vars], - ); - eq_t * eq_y * alpha_pow - } - ), - izip!( - self.subset_point_and_evals[self.layer_id as usize].iter(), - alpha_pows - .iter() - .skip(self.to_next_phase_point_and_evals.len()) - ) - .map(|((new_layer_id, point_and_eval), alpha_pow)| { - let point_lo_num_vars = point_and_eval.point.len() - hi_num_vars; - let eq_t = eq_eval( - &point_and_eval.point[point_lo_num_vars..], - &claim1_point[(claim1_point.len() - hi_num_vars)..], - ); - let eq_yj_ryj = build_eq_x_r_vec(&point_and_eval.point[..point_lo_num_vars]); - eq_t * circuit.layers[self.layer_id as usize].copy_to[new_layer_id] - .as_slice() - .eval_row_first(&eq_yj_ryj, &eq_y_ry) - * alpha_pow - }), - ] - .sum(); - - let got_value = f_value * g_value; - - end_timer!(timer); - if claim_1.expected_evaluation != got_value { - return Err(GKRError::VerifyError("phase1 step1 failed")); - } - self.to_next_step_point_and_eval = PointAndEval::new_from_ref(&claim1_point, &f_value); - self.to_next_phase_point_and_evals = vec![self.to_next_step_point_and_eval.clone()]; - - self.subset_point_and_evals[self.layer_id as usize].clear(); - - Ok(()) - } -} diff --git a/gkr/src/verifier/phase1_output.rs b/gkr/src/verifier/phase1_output.rs deleted file mode 100644 index 2efb91cef..000000000 --- a/gkr/src/verifier/phase1_output.rs +++ /dev/null @@ -1,143 +0,0 @@ -use ark_std::{end_timer, start_timer}; -use ff_ext::ExtensionField; -use itertools::{Itertools, chain, izip}; -use multilinear_extensions::virtual_poly::{VPAuxInfo, build_eq_x_r_vec, eq_eval}; -use std::{iter, marker::PhantomData}; -use transcript::Transcript; - -use crate::{ - circuit::EvaluateGateCIn, - error::GKRError, - structs::{Circuit, IOPProverStepMessage, IOPVerifierState, PointAndEval}, - utils::MatrixMLERowFirst, -}; - -use super::SumcheckState; - -impl IOPVerifierState { - pub(super) fn verify_and_update_state_output_phase1_step1( - &mut self, - circuit: &Circuit, - step_msg: IOPProverStepMessage, - transcript: &mut Transcript, - ) -> Result<(), GKRError> { - let timer = start_timer!(|| "Verifier sumcheck phase 1 step 1"); - let alpha = transcript - .get_and_append_challenge(b"combine subset evals") - .elements; - let total_length = self.to_next_phase_point_and_evals.len() - + self.subset_point_and_evals[self.layer_id as usize].len() - + 1; - let alpha_pows = { - let mut alpha_pows = vec![E::ONE; total_length]; - for i in 0..total_length.saturating_sub(1) { - alpha_pows[i + 1] = alpha_pows[i] * alpha; - } - alpha_pows - }; - - let lo_num_vars = circuit.layers[self.layer_id as usize].num_vars; - let hi_num_vars = self.instance_num_vars; - - // sigma = \sum_j( \alpha^j * subset[i][j](rt_j || ry_j) ) - let mut sigma_1 = izip!(self.to_next_phase_point_and_evals.iter(), alpha_pows.iter()) - .fold(E::ZERO, |acc, (point_and_eval, alpha_pow)| { - acc + point_and_eval.eval * alpha_pow - }); - sigma_1 += izip!( - self.subset_point_and_evals[self.layer_id as usize].iter(), - alpha_pows - .iter() - .skip(self.to_next_phase_point_and_evals.len()) - ) - .fold(E::ZERO, |acc, ((_, point_and_eval), alpha_pow)| { - acc + point_and_eval.eval * alpha_pow - }); - - let assert_eq_yj_ryj = build_eq_x_r_vec(&self.assert_point[..lo_num_vars]); - sigma_1 += circuit - .assert_consts - .as_slice() - .eval(&assert_eq_yj_ryj, &self.challenges) - * alpha_pows.last().unwrap(); - - // Sumcheck: sigma = \sum_{t || y}( \sum_j f1^{(j)}( t || y) * g1^{(j)}(t || y) ) - // f1^{(j)}(y) = layers[i](t || y) - // g1^{(j)}(t || y) = \alpha^j * eq(rt_j, t) * eq(ry_j, y) - // g1^{(j)}(t || y) = \alpha^j * eq(rt_j, t) * copy_to[j](ry_j, y) - // g1^{(j)}(t || y) = \alpha^j * eq(rt_j, t) * assert_subset_eq(ry, y) - let claim_1 = SumcheckState::verify( - sigma_1, - &step_msg.sumcheck_proof, - &VPAuxInfo { - max_degree: 2, - num_variables: lo_num_vars + hi_num_vars, - phantom: PhantomData, - }, - transcript, - ); - - let claim1_point = claim_1.point.iter().map(|x| x.elements).collect_vec(); - let claim1_point_lo_num_vars = claim1_point.len() - hi_num_vars; - let eq_y_ry = build_eq_x_r_vec(&claim1_point[..claim1_point_lo_num_vars]); - - assert_eq!(step_msg.sumcheck_eval_values.len(), 1); - let f_value = step_msg.sumcheck_eval_values[0]; - - let g_value: E = chain![ - izip!(self.to_next_phase_point_and_evals.iter(), alpha_pows.iter()).map( - |(point_and_eval, alpha_pow)| { - let point_lo_num_vars = point_and_eval.point.len() - hi_num_vars; - let eq_t = eq_eval( - &point_and_eval.point[point_lo_num_vars..], - &claim1_point[(claim1_point.len() - hi_num_vars)..], - ); - let eq_y = eq_eval( - &point_and_eval.point[..point_lo_num_vars], - &claim1_point[..point_lo_num_vars], - ); - eq_t * eq_y * alpha_pow - } - ), - izip!( - circuit.copy_to_wits_out.iter(), - self.subset_point_and_evals[self.layer_id as usize].iter(), - alpha_pows - .iter() - .skip(self.to_next_phase_point_and_evals.len()) - ) - .map(|(copy_to, (_, point_and_eval), alpha_pow)| { - let point_lo_num_vars = point_and_eval.point.len() - hi_num_vars; - let eq_t = eq_eval( - &point_and_eval.point[point_lo_num_vars..], - &claim1_point[(claim1_point.len() - hi_num_vars)..], - ); - let eq_yj_ryj = build_eq_x_r_vec(&point_and_eval.point[..point_lo_num_vars]); - eq_t * copy_to.as_slice().eval_row_first(&eq_yj_ryj, &eq_y_ry) * alpha_pow - }), - iter::once( - eq_eval( - &self.assert_point[lo_num_vars..][..hi_num_vars], - &claim1_point[(claim1_point.len() - hi_num_vars)..][..hi_num_vars], - ) * circuit - .assert_consts - .as_slice() - .eval_subset_eq(&assert_eq_yj_ryj, &eq_y_ry) - * alpha_pows.last().unwrap() - ) - ] - .sum(); - - let got_value = f_value * g_value; - - end_timer!(timer); - if claim_1.expected_evaluation != got_value { - return Err(GKRError::VerifyError("phase1 output step1 failed")); - } - self.to_next_step_point_and_eval = PointAndEval::new_from_ref(&claim1_point, &f_value); - self.to_next_phase_point_and_evals = vec![self.to_next_step_point_and_eval.clone()]; - - self.subset_point_and_evals[self.layer_id as usize].clear(); - Ok(()) - } -} diff --git a/gkr/src/verifier/phase2.rs b/gkr/src/verifier/phase2.rs deleted file mode 100644 index d8170225a..000000000 --- a/gkr/src/verifier/phase2.rs +++ /dev/null @@ -1,240 +0,0 @@ -use ark_std::{end_timer, start_timer}; -use ff_ext::ExtensionField; -use itertools::{Itertools, chain, izip}; -use multilinear_extensions::virtual_poly::{VPAuxInfo, build_eq_x_r_vec, eq_eval}; -use std::mem; -use transcript::Transcript; - -use crate::{ - circuit::{EvaluateGate1In, EvaluateGate2In, EvaluateGate3In, EvaluateGateCIn}, - error::GKRError, - structs::{Circuit, IOPProverStepMessage, IOPVerifierState, PointAndEval}, - utils::{MatrixMLEColumnFirst, eq3_eval, eq4_eval}, -}; - -use super::SumcheckState; - -impl IOPVerifierState { - pub(super) fn verify_and_update_state_phase2_step1( - &mut self, - circuit: &Circuit, - step_msg: IOPProverStepMessage, - transcript: &mut Transcript, - ) -> Result<(), GKRError> { - let timer = start_timer!(|| "Verifier sumcheck phase 2 step 1"); - let layer = &circuit.layers[self.layer_id as usize]; - let lo_out_num_vars = layer.num_vars; - let lo_in_num_vars = layer.max_previous_num_vars; - let hi_num_vars = self.instance_num_vars; - let in_num_vars = lo_in_num_vars + hi_num_vars; - - self.out_point = mem::take(&mut self.to_next_step_point_and_eval.point); - let lo_point = &self.out_point[..lo_out_num_vars]; - let hi_point = &self.out_point[lo_out_num_vars..]; - - self.eq_y_ry = build_eq_x_r_vec(lo_point); - - // sigma = layers[i](rt || ry) - add_const(ry), - let sumcheck_sigma = self.to_next_step_point_and_eval.eval - - layer - .add_consts - .as_slice() - .eval(&self.eq_y_ry, &self.challenges); - - // Sumcheck 1: sigma = \sum_{s1 || x1} f1(s1 || x1) * g1(s1 || x1) + \sum_j f1'_j(s1 || x1) - // * g1'_j(s1 || x1) f1(s1 || x1) = layers[i + 1](s1 || x1) g1(s1 || x1) = \sum_{s2}( - // \sum_{s3}( \sum_{x2}( \sum_{x3}( eq(rt, s1, s2, s3) * mul3(ry, x1, x2, x3) * layers[i + - // 1](s2 || x2) * layers[i + - // 1](s3 || x3) ) ) ) ) + \sum_{s2}( \sum_{x2}( - // eq(rt, s1, s2) * mul2(ry, x1, x2) * layers[i + 1](s2 || x2) - // ) ) + eq(rt, s1) * add(ry, x1) - // f1'^{(j)}(s1 || x1) = subset[j][i](s1 || x1) - // g1'^{(j)}(s1 || x1) = eq(rt, s1) paste_from[j](ry, x1) - let claim_1 = SumcheckState::verify( - sumcheck_sigma, - &step_msg.sumcheck_proof, - &VPAuxInfo { - max_degree: 2, - num_variables: in_num_vars, - phantom: std::marker::PhantomData, - }, - transcript, - ); - let claim1_point = claim_1.point.iter().map(|x| x.elements).collect_vec(); - let hi_point_sc1 = &claim1_point[lo_in_num_vars..]; - - let (f1_values, received_g1_values) = step_msg - .sumcheck_eval_values - .split_at(step_msg.sumcheck_eval_values.len() - 1); - - let hi_eq_eval = eq_eval(hi_point, hi_point_sc1); - self.eq_x1_rx1 = build_eq_x_r_vec(&claim1_point[..lo_in_num_vars]); - let g1_values_iter = chain![ - received_g1_values.iter().cloned(), - layer.paste_from.values().map(|paste_from| { - hi_eq_eval - * paste_from - .as_slice() - .eval_col_first(&self.eq_y_ry, &self.eq_x1_rx1) - }) - ]; - let got_value_1 = - izip!(f1_values.iter(), g1_values_iter).fold(E::ZERO, |acc, (&f1, g1)| acc + f1 * g1); - - end_timer!(timer); - if claim_1.expected_evaluation != got_value_1 { - return Err(GKRError::VerifyError("phase2 step1 failed")); - } - - self.to_next_phase_point_and_evals = - vec![PointAndEval::new_from_ref(&claim1_point, &f1_values[0])]; - izip!(layer.paste_from.iter(), f1_values.iter().skip(1)).for_each( - |((&old_layer_id, _), &subset_value)| { - self.subset_point_and_evals[old_layer_id as usize].push(( - self.layer_id, - PointAndEval::new_from_ref(&claim1_point, &subset_value), - )); - }, - ); - self.to_next_step_point_and_eval = PointAndEval::new(claim1_point, received_g1_values[0]); - - Ok(()) - } - - pub(super) fn verify_and_update_state_phase2_step2( - &mut self, - circuit: &Circuit, - step_msg: IOPProverStepMessage, - transcript: &mut Transcript, - no_step3: bool, - ) -> Result<(), GKRError> { - let timer = start_timer!(|| "Verifier sumcheck phase 2 step 2"); - let layer = &circuit.layers[self.layer_id as usize]; - let lo_out_num_vars = layer.num_vars; - let lo_in_num_vars = layer.max_previous_num_vars; - let hi_num_vars = self.instance_num_vars; - let in_num_vars = lo_in_num_vars + hi_num_vars; - - // sigma = g1(rs1 || rx1) - eq(rt, rs1) * add(ry, rx1) - let sumcheck_sigma = self.to_next_step_point_and_eval.eval - - eq_eval( - &self.out_point[lo_out_num_vars..], - &self.to_next_phase_point_and_evals[0].point[lo_in_num_vars..], - ) * layer - .adds - .as_slice() - .eval(&self.eq_y_ry, &self.eq_x1_rx1, &self.challenges); - - // Sumcheck 2 sigma = \sum_{s2 || x2} f2(s2 || x2) * g2(s2 || x2) - // f2(s2 || x2) = layers[i + 1](s2 || x2) - // g2(s2 || x2) = \sum_{s3}( \sum_{x3}( - // eq(rt, rs1, s2, s3) * mul3(ry, rx1, x2, x3) * layers[i + 1](s3 || x3) - // ) ) + eq(rt, rs1, s2) * mul2(ry, rx1, x2)} - let claim_2 = SumcheckState::verify( - sumcheck_sigma, - &step_msg.sumcheck_proof, - &VPAuxInfo { - max_degree: 2, - num_variables: in_num_vars, - phantom: std::marker::PhantomData, - }, - transcript, - ); - let claim2_point = claim_2.point.iter().map(|x| x.elements).collect_vec(); - let f2_value = step_msg.sumcheck_eval_values[0]; - - self.eq_x2_rx2 = build_eq_x_r_vec(&claim2_point[..lo_in_num_vars]); - let g2_value = if no_step3 { - eq3_eval( - &self.out_point[lo_out_num_vars..], - &self.to_next_phase_point_and_evals[0].point[lo_in_num_vars..], - &claim2_point[lo_in_num_vars..], - ) * layer.mul2s.as_slice().eval( - &self.eq_y_ry, - &self.eq_x1_rx1, - &self.eq_x2_rx2, - &self.challenges, - ) - } else { - step_msg.sumcheck_eval_values[1] - }; - let got_value_2 = f2_value * g2_value; - - end_timer!(timer); - if claim_2.expected_evaluation != got_value_2 { - return Err(GKRError::VerifyError("phase2 step2 failed")); - } - - self.to_next_phase_point_and_evals - .push(PointAndEval::new_from_ref(&claim2_point, &f2_value)); - self.to_next_step_point_and_eval = PointAndEval::new(claim2_point, g2_value); - Ok(()) - } - - pub(super) fn verify_and_update_state_phase2_step3( - &mut self, - circuit: &Circuit, - step_msg: IOPProverStepMessage, - transcript: &mut Transcript, - ) -> Result<(), GKRError> { - let timer = start_timer!(|| "Verifier sumcheck phase 2 step 3"); - let layer = &circuit.layers[self.layer_id as usize]; - let lo_out_num_vars = layer.num_vars; - let lo_in_num_vars = layer.max_previous_num_vars; - let hi_num_vars = self.instance_num_vars; - let in_num_vars = lo_in_num_vars + hi_num_vars; - - // sigma = g2(rs2 || rx2) - eq(rt, rs1, rs2) * mul2(ry, rx1, rx2) - let sumcheck_sigma = self.to_next_step_point_and_eval.eval - - eq3_eval( - &self.out_point[lo_out_num_vars..], - &self.to_next_phase_point_and_evals[0].point[lo_in_num_vars..], - &self.to_next_phase_point_and_evals[1].point[lo_in_num_vars..], - ) * layer.mul2s.as_slice().eval( - &self.eq_y_ry, - &self.eq_x1_rx1, - &self.eq_x2_rx2, - &self.challenges, - ); - - // Sumcheck 3 sigma = \sum_{s3 || x3} f3(s3 || x3) * g3(s3 || x3) - // f3(s3 || x3) = layers[i + 1](s3 || x3) - // g3(s3 || x3) = eq(rt, rs1, rs2, s3) * mul3(ry, rx1, rx2, x3) - let claim_3 = SumcheckState::verify( - sumcheck_sigma, - &step_msg.sumcheck_proof, - &VPAuxInfo { - max_degree: 2, - num_variables: in_num_vars, - phantom: std::marker::PhantomData, - }, - transcript, - ); - let claim3_point = claim_3.point.iter().map(|x| x.elements).collect_vec(); - let eq_x3_rx3 = build_eq_x_r_vec(&claim3_point[..lo_in_num_vars]); - let f3_value = step_msg.sumcheck_eval_values[0]; - let g3_value = eq4_eval( - &self.out_point[lo_out_num_vars..], - &self.to_next_phase_point_and_evals[0].point[lo_in_num_vars..], - &self.to_next_phase_point_and_evals[1].point[lo_in_num_vars..], - &claim3_point[lo_in_num_vars..], - ) * layer.mul3s.as_slice().eval( - &self.eq_y_ry, - &self.eq_x1_rx1, - &self.eq_x2_rx2, - &eq_x3_rx3, - &self.challenges, - ); - - let got_value_3 = f3_value * g3_value; - end_timer!(timer); - if claim_3.expected_evaluation != got_value_3 { - return Err(GKRError::VerifyError("phase2 step3 failed")); - } - - self.to_next_phase_point_and_evals - .push(PointAndEval::new_from_ref(&claim3_point, &f3_value)); - self.to_next_step_point_and_eval = PointAndEval::new(claim3_point, E::ZERO); - Ok(()) - } -} diff --git a/gkr/src/verifier/phase2_input.rs b/gkr/src/verifier/phase2_input.rs deleted file mode 100644 index 6c31af23b..000000000 --- a/gkr/src/verifier/phase2_input.rs +++ /dev/null @@ -1,146 +0,0 @@ -use ark_std::{end_timer, start_timer}; -use ff_ext::ExtensionField; -use itertools::{Itertools, chain, izip}; -use multilinear_extensions::virtual_poly::{VPAuxInfo, build_eq_x_r_vec}; -use std::mem; -use sumcheck::util::ceil_log2; -use transcript::Transcript; - -use crate::{ - circuit::EvaluateGateCIn, - error::GKRError, - structs::{Circuit, IOPProverStepMessage, IOPVerifierState, PointAndEval}, - utils::{counter_eval, i64_to_field, segment_eval_greater_than}, -}; - -use super::SumcheckState; - -impl IOPVerifierState { - pub(super) fn verify_and_update_state_input_phase2_step1( - &mut self, - circuit: &Circuit, - step_msg: IOPProverStepMessage, - transcript: &mut Transcript, - ) -> Result<(), GKRError> { - let timer = start_timer!(|| "Verifier sumcheck phase 2 step 1"); - let layer = &circuit.layers[self.layer_id as usize]; - let lo_out_num_vars = layer.num_vars; - let lo_in_num_vars = circuit.max_wit_in_num_vars; - let hi_num_vars = self.instance_num_vars; - - self.out_point = mem::take(&mut self.to_next_step_point_and_eval.point); - let lo_point = &self.out_point[..lo_out_num_vars]; - let hi_point = &self.out_point[lo_out_num_vars..]; - - self.eq_y_ry = build_eq_x_r_vec(lo_point); - - let g_value_const = circuit - .paste_from_consts_in - .iter() - .map(|(c, (l, r))| { - let c = i64_to_field::(*c); - let segment_greater_than_l_1 = if *l == 0 { - E::ONE - } else { - segment_eval_greater_than(l - 1, lo_point) - }; - let segment_greater_than_r_1 = segment_eval_greater_than(r - 1, lo_point); - (segment_greater_than_l_1 - segment_greater_than_r_1) * c - }) - .sum::(); - - let mut sumcheck_sigma = self.to_next_step_point_and_eval.eval - g_value_const; - if !layer.add_consts.is_empty() { - sumcheck_sigma -= layer - .add_consts - .as_slice() - .eval(&self.eq_y_ry, &self.challenges); - } - - if lo_in_num_vars.is_none() { - if sumcheck_sigma != E::ZERO { - return Err(GKRError::VerifyError("input phase2 step1 failed")); - } - return Ok(()); - } - - let lo_in_num_vars = lo_in_num_vars.unwrap(); - - let claim = SumcheckState::verify( - sumcheck_sigma, - &step_msg.sumcheck_proof, - &VPAuxInfo { - max_degree: 2, - num_variables: lo_in_num_vars, - phantom: std::marker::PhantomData, - }, - transcript, - ); - - let claim_point = claim.point.iter().map(|x| x.elements).collect_vec(); - - self.eq_x1_rx1 = build_eq_x_r_vec(&claim_point); - let g_values_iter = chain![ - circuit.paste_from_wits_in.iter().cloned(), - circuit - .paste_from_counter_in - .iter() - .map(|(_, (l, r))| (*l, *r)) - ] - .map(|(l, r)| { - (l..r) - .map(|i| self.eq_y_ry[i] * self.eq_x1_rx1[i - l]) - .sum::() - }); - - // TODO: Double check here. - let f_counter_values = circuit - .paste_from_counter_in - .iter() - .map(|(num_vars, _)| { - let point = [&claim_point[..*num_vars], hi_point].concat(); - counter_eval(num_vars + hi_num_vars, &point) - * claim_point[*num_vars..] - .iter() - .map(|x| E::ONE - *x) - .product::() - }) - .collect_vec(); - let got_value = izip!( - chain![ - step_msg.sumcheck_eval_values.iter(), - f_counter_values.iter() - ], - g_values_iter - ) - .map(|(f, g)| *f * g) - .sum::(); - - self.to_next_phase_point_and_evals = izip!( - circuit.paste_from_wits_in.iter(), - step_msg.sumcheck_eval_values.into_iter() - ) - .map(|((l, r), eval)| { - let num_vars = ceil_log2(*r - *l); - let point = [&claim_point[..num_vars], hi_point].concat(); - let wit_in_eval = eval - * claim_point[num_vars..] - .iter() - .map(|x| E::ONE - *x) - .product::() - .invert() - .unwrap(); - PointAndEval::new_from_ref(&point, &wit_in_eval) - }) - .collect_vec(); - self.to_next_step_point_and_eval = - PointAndEval::new([&claim_point, hi_point].concat(), E::ZERO); - - end_timer!(timer); - if claim.expected_evaluation != got_value { - return Err(GKRError::VerifyError("input phase2 step1 failed")); - } - - Ok(()) - } -} diff --git a/gkr/src/verifier/phase2_linear.rs b/gkr/src/verifier/phase2_linear.rs deleted file mode 100644 index 4e1c41fe0..000000000 --- a/gkr/src/verifier/phase2_linear.rs +++ /dev/null @@ -1,97 +0,0 @@ -use ark_std::{end_timer, start_timer}; -use ff_ext::ExtensionField; -use itertools::{Itertools, chain, izip}; -use multilinear_extensions::virtual_poly::{VPAuxInfo, build_eq_x_r_vec}; -use std::{iter, mem}; -use transcript::Transcript; - -use crate::{ - circuit::{EvaluateGate1In, EvaluateGateCIn}, - error::GKRError, - structs::{Circuit, IOPProverStepMessage, IOPVerifierState, PointAndEval}, - utils::MatrixMLEColumnFirst, -}; - -use super::SumcheckState; - -impl IOPVerifierState { - pub(super) fn verify_and_update_state_linear_phase2_step1( - &mut self, - circuit: &Circuit, - step_msg: IOPProverStepMessage, - transcript: &mut Transcript, - ) -> Result<(), GKRError> { - let timer = start_timer!(|| "Verifier sumcheck phase 2 step 1"); - let layer = &circuit.layers[self.layer_id as usize]; - let lo_out_num_vars = layer.num_vars; - let lo_in_num_vars = layer.max_previous_num_vars; - - self.out_point = mem::take(&mut self.to_next_step_point_and_eval.point); - let lo_point = &self.out_point[..lo_out_num_vars]; - - self.eq_y_ry = build_eq_x_r_vec(lo_point); - - // sigma = layers[i](rt || ry) - add_const(ry), - let sumcheck_sigma = self.to_next_step_point_and_eval.eval - - layer - .add_consts - .as_slice() - .eval(&self.eq_y_ry, &self.challenges); - - // Sumcheck 1: sigma = \sum_{x1} f1(x1) * g1(x1) + \sum_j f1'_j(x1) * g1'_j(x1) - // sigma = layers[i](rt || ry) - add_const(ry), - // f1(x1) = layers[i + 1](rt || x1) - // g1(x1) = add(ry, x1) - // f1'^{(j)}(x1) = subset[j][i](rt || x1) - // g1'^{(j)}(x1) = paste_from[j](ry, x1) - let claim_1 = SumcheckState::verify( - sumcheck_sigma, - &step_msg.sumcheck_proof, - &VPAuxInfo { - max_degree: 2, - num_variables: lo_in_num_vars, - phantom: std::marker::PhantomData, - }, - transcript, - ); - let claim1_point = claim_1.point.iter().map(|x| x.elements).collect_vec(); - - self.eq_x1_rx1 = build_eq_x_r_vec(&claim1_point[..lo_in_num_vars]); - let g1_values_iter = chain![ - iter::once(layer.adds.as_slice().eval( - &self.eq_y_ry, - &self.eq_x1_rx1, - &self.challenges - )), - layer.paste_from.values().map(|paste_from| { - paste_from - .as_slice() - .eval_col_first(&self.eq_y_ry, &self.eq_x1_rx1) - }) - ]; - - let f1_values = &step_msg.sumcheck_eval_values; - let got_value_1 = - izip!(f1_values.iter(), g1_values_iter).fold(E::ZERO, |acc, (&f1, g1)| acc + f1 * g1); - - end_timer!(timer); - if claim_1.expected_evaluation != got_value_1 { - return Err(GKRError::VerifyError("phase2 step1 failed")); - } - - let new_point = [&claim1_point, &self.out_point[lo_out_num_vars..]].concat(); - self.to_next_phase_point_and_evals = - vec![PointAndEval::new_from_ref(&new_point, &f1_values[0])]; - izip!(layer.paste_from.iter(), f1_values.iter().skip(1)).for_each( - |((&old_layer_id, _), &subset_value)| { - self.subset_point_and_evals[old_layer_id as usize].push(( - self.layer_id, - PointAndEval::new_from_ref(&new_point, &subset_value), - )); - }, - ); - self.to_next_step_point_and_eval = self.to_next_phase_point_and_evals[0].clone(); - - Ok(()) - } -} diff --git a/simple-frontend/Cargo.toml b/simple-frontend/Cargo.toml deleted file mode 100644 index 1e642b3e6..000000000 --- a/simple-frontend/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ - -[package] -edition.workspace = true -license.workspace = true -name = "simple-frontend" -version.workspace = true - -[dependencies] -ff.workspace = true -ff_ext = { path = "../ff_ext" } -goldilocks.workspace = true -itertools.workspace = true -serde.workspace = true diff --git a/simple-frontend/examples/poseidon.rs b/simple-frontend/examples/poseidon.rs deleted file mode 100644 index 79c08e080..000000000 --- a/simple-frontend/examples/poseidon.rs +++ /dev/null @@ -1,493 +0,0 @@ -//! Poseidon hash function. This is modified from https://github.com/iden3/circomlib/blob/master/circuits/poseidon.circom. - -use ff::Field; -use ff_ext::ExtensionField; -use goldilocks::GoldilocksExt2; -use mock_constant::{poseidon_c, poseidon_m, poseidon_p, poseidon_s}; -use simple_frontend::structs::{CellId, CircuitBuilder}; - -// round constant -const N_ROUNDS_P: [usize; 16] = [ - 56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68, -]; - -// template Sigma() { -// signal input in; -// signal output out; - -// signal in2; -// signal in4; - -// in2 <== in*in; -// in4 <== in2*in2; - -// out <== in4*in; -// } - -fn sigma(circuit_builder: &mut CircuitBuilder, in_: CellId) -> CellId { - let in2 = circuit_builder.create_cell(); - let in4 = circuit_builder.create_cell(); - - let out = circuit_builder.create_cell(); - - let one = E::BaseField::ONE; - circuit_builder.mul2(in2, in_, in_, one); - circuit_builder.mul2(in4, in2, in2, one); - circuit_builder.mul2(out, in4, in_, one); - - out -} - -// template Ark(t, C, r) { -// signal input in[t]; -// signal output out[t]; - -// for (var i=0; i( - circuit_builder: &mut CircuitBuilder, - in_: &[CellId], - c: &[E::BaseField], - r: usize, -) -> Vec { - let out = circuit_builder.create_cells(in_.len()); - - let one = E::BaseField::ONE; - - for i in 0..in_.len() { - circuit_builder.add(out[i], in_[i], one); - circuit_builder.add_const(out[i], c[i + r]); - } - - out -} - -// template Mix(t, M) { -// signal input in[t]; -// signal output out[t]; - -// var lc; -// for (var i=0; i( - circuit_builder: &mut CircuitBuilder, - in_: &[CellId], - m: &[&[E::BaseField]], -) -> Vec { - let out = circuit_builder.create_cells(in_.len()); - - for (i, out_elem) in out.iter().enumerate() { - for (j, in_elem) in in_.iter().enumerate() { - circuit_builder.add(*out_elem, *in_elem, m[j][i]); - } - } - - out -} - -// template MixLast(t, M, s) { -// signal input in[t]; -// signal output out; - -// var lc = 0; -// for (var j=0; j( - circuit_builder: &mut CircuitBuilder, - in_: &[CellId], - m: &[&[E::BaseField]], - s: usize, -) -> CellId { - let out = circuit_builder.create_cell(); - - for j in 0..in_.len() { - circuit_builder.add(out, in_[j], m[j][s]); - } - - out -} - -// template MixS(t, S, r) { -// signal input in[t]; -// signal output out[t]; - -// var lc = 0; -// for (var i=0; i( - circuit_builder: &mut CircuitBuilder, - in_: &[CellId], - s: &[E::BaseField], - r: usize, -) -> Vec { - let t = in_.len(); - let out = circuit_builder.create_cells(t); - - let one = E::BaseField::ONE; - - for i in 0..in_.len() { - circuit_builder.add(out[0], in_[i], s[(t * 2 - 1) * r + i]); - } - - for i in 1..t { - circuit_builder.add(out[i], in_[0], s[(t * 2 - 1) * r + t + i - 1]); - circuit_builder.add(out[i], in_[i], one); - } - - out -} - -fn poseidon_ex( - circuit_builder: &mut CircuitBuilder, - n_outs: usize, - inputs: &[CellId], - initial_state: CellId, -) -> Vec { - // signal input inputs[nInputs]; - // signal input initialState; - // signal output out[nOuts]; - - // var N_ROUNDS_P[16] = [56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68]; - // var t = nInputs + 1; - // var nRoundsF = 8; - // var nRoundsP = N_ROUNDS_P[t - 2]; - // var C[t*nRoundsF + nRoundsP] = POSEIDON_C(t); - // var S[ N_ROUNDS_P[t-2] * (t*2-1) ] = POSEIDON_S(t); - // var M[t][t] = POSEIDON_M(t); - // var P[t][t] = POSEIDON_P(t); - - let n_inputs = inputs.len(); - - let t = n_inputs + 1; - let n_rounds_f = 8; - let n_rounds_p = N_ROUNDS_P[t - 2]; - let c = poseidon_c::(t); - let s = poseidon_s::(t); - let m = poseidon_m::(t); - let m_slices = m - .iter() - .map(|row| row.as_slice()) - .collect::>(); - let p = poseidon_p::(t); - let p_slices = p - .iter() - .map(|row| row.as_slice()) - .collect::>(); - - // component ark[nRoundsF]; - // component sigmaF[nRoundsF][t]; - // component sigmaP[nRoundsP]; - // component mix[nRoundsF-1]; - // component mixS[nRoundsP]; - // component mixLast[nOuts]; - - let mut ark_in = vec![vec![]; n_rounds_f]; - let mut ark_out = vec![vec![]; n_rounds_f]; - let mut sigma_f_in = vec![vec![0usize; t]; n_rounds_f]; - let mut sigma_f_out = vec![vec![0usize; t]; n_rounds_f]; - let mut sigma_p_in = vec![0usize; n_rounds_p]; - let mut sigma_p_out = vec![0usize; n_rounds_p]; - let mut mix_in = vec![vec![]; n_rounds_f - 1]; - let mut mix_out = vec![vec![]; n_rounds_f - 1]; - let mut mix_s_in = vec![vec![]; n_rounds_p]; - let mut mix_s_out = vec![vec![]; n_rounds_p]; - let mut mix_last_in = vec![vec![]; n_outs]; - let mut mix_last_out = vec![0usize; n_outs]; - - // ark[0] = Ark(t, C, 0); - // for (var j=0; j0) { - // ark[0].in[j] <== inputs[j-1]; - // } else { - // ark[0].in[j] <== initialState; - // } - // } - - ark_in[0] = { - let mut ark_in_0 = Vec::with_capacity(t); - ark_in_0.push(initial_state); - ark_in_0.extend_from_slice(&inputs[0..t - 1]); - ark_in_0 - }; - - ark_out[0] = ark(circuit_builder, &ark_in[0], &c, 0); - - // for (var r = 0; r < nRoundsF\2-1; r++) { - // for (var j=0; j::default(); - let n_inputs = 4; - let (_, poseidon_ex_initial_state) = circuit_builder.create_witness_in(1); - let (_, poseidon_ex_inputs) = circuit_builder.create_witness_in(n_inputs); - let poseidon_ex_out = poseidon_ex( - &mut circuit_builder, - 1, - poseidon_ex_inputs.as_slice(), - poseidon_ex_initial_state[0], - ); - println!("The output is located at cell {:?}", poseidon_ex_out[0]); - circuit_builder.configure(); - #[cfg(debug_assertions)] - circuit_builder.print_info(); -} - -mod mock_constant { - use goldilocks::SmallField; - - use crate::N_ROUNDS_P; - - pub(crate) fn poseidon_c(t: usize) -> Vec { - let n = t * 8 + N_ROUNDS_P[t - 2]; - let mut c = Vec::with_capacity(n); - for i in 0..n { - c.push(F::from(i as u64)); - } - c - } - - pub(crate) fn poseidon_s(t: usize) -> Vec { - let n = N_ROUNDS_P[t - 2] * (t * 2 - 1); - let mut s = Vec::with_capacity(n); - for i in 0..n { - s.push(F::from(i as u64)); - } - s - } - - pub(crate) fn poseidon_m(t: usize) -> Vec> { - let mut m = Vec::with_capacity(t); - for i in 0..t { - let mut row = Vec::with_capacity(t); - for j in 0..t { - row.push(F::from((i * t + j) as u64)); - } - m.push(row); - } - m - } - - pub(crate) fn poseidon_p(t: usize) -> Vec> { - let mut p = Vec::with_capacity(t); - for i in 0..t { - let mut row = Vec::with_capacity(t); - for j in 0..t { - row.push(F::from((i * t + j) as u64)); - } - p.push(row); - } - p - } -} diff --git a/simple-frontend/src/circuit_builder.rs b/simple-frontend/src/circuit_builder.rs deleted file mode 100644 index c7022c754..000000000 --- a/simple-frontend/src/circuit_builder.rs +++ /dev/null @@ -1,248 +0,0 @@ -use std::collections::VecDeque; - -use ff_ext::ExtensionField; - -use crate::structs::{CellId, CellType, CircuitBuilder, ConstantType, GateType, LayerId}; - -mod base_opt; -mod derives; -mod ext_opt; - -impl GateType { - pub(crate) fn add_const(constant: ConstantType) -> Self { - Self { - idx_in: vec![], - scalar: constant, - } - } - - pub(crate) fn add(in0: CellId, scalar: ConstantType) -> Self { - Self { - idx_in: vec![in0], - scalar, - } - } - - pub(crate) fn mul2(in0: CellId, in1: CellId, scalar: ConstantType) -> Self { - Self { - idx_in: vec![in0, in1], - scalar, - } - } - - pub(crate) fn mul3(in0: CellId, in1: CellId, in2: CellId, scalar: ConstantType) -> Self { - Self { - idx_in: vec![in0, in1, in2], - scalar, - } - } -} - -impl CircuitBuilder { - /// Prepare the circuit. This is to assign the layers and challenge levels - /// to the cells. - pub fn configure(&mut self) { - // Topological sort. - // Initialize the out degree. - let mut degrees = vec![0; self.cells.len()]; - for cell in self.cells.iter() { - for gate in cell.gates.iter() { - for input in gate.idx_in.iter() { - degrees[*input] += 1; - } - } - } - - let mut queue = VecDeque::new(); - let mut visited = vec![false; self.cells.len()]; - let mut layers = vec![0 as LayerId; self.cells.len()]; - for cell_id in 0..self.cells.len() { - if degrees[cell_id] == 0 { - queue.push_front(cell_id); - visited[cell_id] = true; - layers[cell_id] = 0; - } - } - - // Assign layers to all cells: - let mut max_layer_id = 0; - while !queue.is_empty() { - let curr = queue.pop_back().unwrap(); - let cell = &mut self.cells[curr]; - - let curr_layer = layers[curr]; - max_layer_id = max_layer_id.max(curr_layer); - cell.layer = Some(curr_layer); - - for gate in cell.gates.iter() { - for input in gate.idx_in.iter() { - degrees[*input] -= 1; - layers[*input] = layers[*input].max(curr_layer + 1); - if degrees[*input] == 0 && !visited[*input] { - queue.push_front(*input); - visited[*input] = true; - } - } - } - } - - // Force input cells to be in the input layer. - for cell_id in 0..self.cells.len() { - if matches!(self.cells[cell_id].cell_type, Some(CellType::In(_))) { - self.cells[cell_id].layer = Some(max_layer_id); - } - } - - self.n_layers = Some(max_layer_id + 1); - } - - pub fn n_witness_in(&self) -> usize { - self.n_witness_in - } - - pub fn n_witness_out(&self) -> usize { - self.n_witness_out - } - - #[cfg(debug_assertions)] - pub fn print_info(&self) { - println!("The number of layers: {}", self.n_layers.as_ref().unwrap()); - println!("The number of cells: {}", self.cells.len()); - - for (i, cell) in self.cells.iter().enumerate() { - println!("Cell {}: {:?}", i, cell); - } - } -} - -#[cfg(test)] -mod tests { - use ff::Field; - use goldilocks::{Goldilocks, GoldilocksExt2}; - use itertools::enumerate; - - use crate::structs::CircuitBuilder; - - #[test] - fn test_cb_empty_cells() { - let mut circuit_builder = CircuitBuilder::::default(); - let (_, input_cells) = circuit_builder.create_witness_in(4); - let zero_cells = circuit_builder.create_cells(2); - let leaves = input_cells - .iter() - .chain(zero_cells.iter()) - .cloned() - .collect::>(); - let inners = circuit_builder.create_cells(2); - circuit_builder.mul3(inners[0], leaves[0], leaves[1], leaves[2], Goldilocks::ONE); - circuit_builder.mul3(inners[1], leaves[3], leaves[4], leaves[5], Goldilocks::ONE); - circuit_builder.create_witness_out_from_cells(&inners); - - circuit_builder.configure(); - - assert_eq!(circuit_builder.cells.len(), 8); - let layers = [1, 1, 1, 1, 1, 1, 0, 0]; - for (cell_id, layer) in enumerate(layers).take(8) { - assert_eq!(circuit_builder.cells[cell_id].layer, Some(layer)); - } - } - - #[test] - fn test_cb_const_cells() { - let mut circuit_builder = CircuitBuilder::::default(); - let (_, input_cells) = circuit_builder.create_witness_in(4); - let const_cells = circuit_builder.create_cells(2); - circuit_builder.add_const(const_cells[0], Goldilocks::ONE); - circuit_builder.add_const(const_cells[1], Goldilocks::ONE); - - let leaves = input_cells - .iter() - .chain(const_cells.iter()) - .cloned() - .collect::>(); - let inners = circuit_builder.create_cells(2); - circuit_builder.mul3(inners[0], leaves[0], leaves[1], leaves[2], Goldilocks::ONE); - circuit_builder.mul3(inners[1], leaves[3], leaves[4], leaves[5], Goldilocks::ONE); - circuit_builder.create_witness_out_from_cells(&inners); - - circuit_builder.configure(); - - assert_eq!(circuit_builder.cells.len(), 8); - let layers = [1, 1, 1, 1, 1, 1, 0, 0]; - for (cell_id, &layer) in layers.iter().enumerate().take(8) { - assert_eq!(circuit_builder.cells[cell_id].layer, Some(layer)); - } - } - - #[test] - fn test_assert_cells() { - let mut circuit_builder = CircuitBuilder::::default(); - let (_, leaves) = circuit_builder.create_witness_in(4); - - let inners = circuit_builder.create_cells(2); - circuit_builder.mul2(inners[0], leaves[0], leaves[1], Goldilocks::ONE); - circuit_builder.mul2(inners[1], leaves[2], leaves[3], Goldilocks::ONE); - circuit_builder.assert_const(inners[0], 1); - circuit_builder.assert_const(inners[1], 1); - - circuit_builder.configure(); - - assert_eq!(circuit_builder.cells.len(), 6); - let layers = [1, 1, 1, 1, 0, 0]; - for (cell_id, &layer) in layers.iter().enumerate().take(6) { - assert_eq!( - circuit_builder.cells[cell_id].layer, - Some(layer), - "cell_id: {}", - cell_id - ); - } - } - - #[test] - fn test_inner_output_cells() { - let mut circuit_builder = CircuitBuilder::::default(); - let (_, leaves) = circuit_builder.create_witness_in(4); - - let (_, inners) = circuit_builder.create_witness_out(2); - circuit_builder.mul2(inners[0], leaves[0], leaves[1], Goldilocks::ONE); - circuit_builder.mul2(inners[1], leaves[2], leaves[3], Goldilocks::ONE); - - let (_, root) = circuit_builder.create_witness_out(1); - circuit_builder.mul2(root[0], inners[0], inners[1], Goldilocks::ONE); - - circuit_builder.configure(); - - assert_eq!(circuit_builder.cells.len(), 7); - let layers = [2, 2, 2, 2, 1, 1, 0]; - for (cell_id, &layer) in layers.iter().enumerate().take(7) { - assert_eq!(circuit_builder.cells[cell_id].layer, Some(layer)); - } - } - - #[test] - fn test_force_input_layer() { - let mut circuit_builder = CircuitBuilder::::default(); - let (_, leaves) = circuit_builder.create_witness_in(6); - - // Unused input elements should also be in the circuit. - let _ = circuit_builder.create_witness_in(3); - let _ = circuit_builder.create_counter_in(1); - let _ = circuit_builder.create_constant_in(2, 1); - - let (_, inners) = circuit_builder.create_witness_out(2); - circuit_builder.mul2(inners[0], leaves[0], leaves[1], Goldilocks::ONE); - circuit_builder.mul2(inners[1], leaves[2], leaves[3], Goldilocks::ONE); - - let (_, root) = circuit_builder.create_witness_out(1); - circuit_builder.mul2(root[0], inners[0], inners[1], Goldilocks::ONE); - - circuit_builder.configure(); - - assert_eq!(circuit_builder.cells.len(), 16); - let layers = [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 0]; - for (cell_id, &layer) in layers.iter().enumerate() { - assert_eq!(circuit_builder.cells[cell_id].layer, Some(layer)); - } - } -} diff --git a/simple-frontend/src/circuit_builder/base_opt.rs b/simple-frontend/src/circuit_builder/base_opt.rs deleted file mode 100644 index f6c14bc8f..000000000 --- a/simple-frontend/src/circuit_builder/base_opt.rs +++ /dev/null @@ -1,218 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; - -use crate::structs::{ - Cell, CellId, CellType, CircuitBuilder, ConstantType, GateType, InType, MixedCell, OutType, - WitnessId, -}; - -impl CircuitBuilder { - pub fn create_cell(&mut self) -> CellId { - self.cells.push(Cell::default()); - self.cells.len() - 1 - } - - pub fn create_cells(&mut self, num: usize) -> Vec { - self.cells.extend((0..num).map(|_| Cell::default())); - (self.cells.len() - num..self.cells.len()).collect() - } - - /// This is to mark the cells with special functionality. - pub(crate) fn mark_cells(&mut self, cell_type: CellType, cells: &[CellId]) { - cells.iter().for_each(|cell| { - self.cells[*cell].cell_type = Some(cell_type); - }); - } - - pub fn create_witness_in(&mut self, num: usize) -> (WitnessId, Vec) { - let cell = self.create_cells(num); - self.mark_cells( - CellType::In(InType::Witness(self.n_witness_in as WitnessId)), - &cell, - ); - self.n_witness_in += 1; - ((self.n_witness_in - 1) as WitnessId, cell) - } - - /// Create input cells and assign it to be constant. - pub fn create_constant_in(&mut self, num: usize, constant: i64) -> Vec { - let cell = self.create_cells(num); - self.mark_cells(CellType::In(InType::Constant(constant)), &cell); - cell - } - - /// Create input cells as a counter. It should count from 0 to n_instance * - /// num through the whole circuit. - pub fn create_counter_in(&mut self, num_vars: usize) -> Vec { - let cell = self.create_cells(1 << num_vars); - self.mark_cells(CellType::In(InType::Counter(num_vars)), &cell); - cell - } - - pub fn create_witness_out(&mut self, num: usize) -> (WitnessId, Vec) { - let cell = self.create_cells(num); - self.mark_cells( - CellType::Out(OutType::Witness(self.n_witness_out as WitnessId)), - &cell, - ); - self.n_witness_out += 1; - ((self.n_witness_out - 1) as WitnessId, cell) - } - - pub fn create_witness_out_from_cells(&mut self, cells: &[CellId]) -> WitnessId { - self.mark_cells( - CellType::Out(OutType::Witness(self.n_witness_out as WitnessId)), - cells, - ); - self.n_witness_out += 1; - (self.n_witness_out - 1) as WitnessId - } - - pub fn add_const(&mut self, out: CellId, constant: Ext::BaseField) { - if constant == Ext::BaseField::ZERO { - return; - } - self.add_const_internal(out, ConstantType::Field(constant)); - } - - pub fn add_const_type(&mut self, out: CellId, constant_type: ConstantType) { - self.add_const_internal(out, constant_type); - } - - pub(crate) fn add_const_internal(&mut self, out: CellId, constant: ConstantType) { - let out_cell = &mut self.cells[out]; - out_cell.gates.push(GateType::add_const(constant)); - } - - pub fn add(&mut self, out: CellId, in_0: CellId, scalar: Ext::BaseField) { - if scalar == Ext::BaseField::ZERO { - return; - } - self.add_internal(out, in_0, ConstantType::Field(scalar)); - } - - pub(crate) fn add_internal(&mut self, out: CellId, in_0: CellId, scalar: ConstantType) { - let out_cell = &mut self.cells[out]; - out_cell.gates.push(GateType::add(in_0, scalar)); - } - - pub fn mul2(&mut self, out: CellId, in_0: CellId, in_1: CellId, scalar: Ext::BaseField) { - if scalar == Ext::BaseField::ZERO { - return; - } - self.mul2_internal(out, in_0, in_1, ConstantType::Field(scalar)); - } - - pub(crate) fn mul2_internal( - &mut self, - out: CellId, - in_0: CellId, - in_1: CellId, - scalar: ConstantType, - ) { - let out_cell = &mut self.cells[out]; - out_cell.gates.push(GateType::mul2(in_0, in_1, scalar)); - } - - pub fn mul3( - &mut self, - out: CellId, - in_0: CellId, - in_1: CellId, - in_2: CellId, - scalar: Ext::BaseField, - ) { - if scalar == Ext::BaseField::ZERO { - return; - } - self.mul3_internal(out, in_0, in_1, in_2, ConstantType::Field(scalar)); - } - - pub(crate) fn mul3_internal( - &mut self, - out: CellId, - in_0: CellId, - in_1: CellId, - in_2: CellId, - scalar: ConstantType, - ) { - let out_cell = &mut self.cells[out]; - out_cell - .gates - .push(GateType::mul3(in_0, in_1, in_2, scalar)); - } - - pub fn assert_const(&mut self, out: CellId, constant: i64) { - // check cell and if it's belong to input cell, we must create an intermediate cell - // and avoid edit input layer cell_type, otherwise it will be orphan cell with no fan-in. - let out_cell = if let Some(CellType::In(_)) = self.cells[out].cell_type { - let out_cell = self.create_cell(); - self.add(out_cell, out, Ext::BaseField::ONE); - out_cell - } else { - out - }; - self.mark_cells(CellType::Out(OutType::AssertConst(constant)), &[out_cell]); - } - - pub fn add_cell_expr(&mut self, out: CellId, in_0: MixedCell) { - match in_0 { - MixedCell::Constant(constant) => { - self.add_const(out, constant); - } - MixedCell::Cell(cell_id) => { - self.add(out, cell_id, Ext::BaseField::ONE); - } - MixedCell::CellExpr(cell_id, a, b) => { - self.add(out, cell_id, a); - self.add_const(out, b); - } - }; - } - - /// IMHO This is `enforce_selection` gate rather than `select` gate. - pub fn select(&mut self, out: CellId, in_0: CellId, in_1: CellId, cond: CellId) { - // (1 - cond) * in_0 + cond * in_1 = (in_1 - in_0) * cond + in_0 - let diff = self.create_cell(); - self.add(diff, in_1, Ext::BaseField::ONE); - self.add(diff, in_0, -Ext::BaseField::ONE); - self.mul2(out, diff, cond, Ext::BaseField::ONE); - self.add(out, in_0, Ext::BaseField::ONE); - } - - pub fn sel_mixed( - &mut self, - out: CellId, - in_0: MixedCell, - in_1: MixedCell, - cond: CellId, - ) { - // (1 - cond) * in_0 + cond * in_1 = (in_1 - in_0) * cond + in_0 - match (in_0, in_1) { - (MixedCell::Constant(in_0), MixedCell::Constant(in_1)) => { - self.add(out, cond, in_1 - in_0); - } - (MixedCell::Constant(in_0), in_1) => { - let diff = in_1.expr(Ext::BaseField::ONE, -in_0); - let diff_cell = self.create_cell(); - self.add_cell_expr(diff_cell, diff); - self.mul2(out, diff_cell, cond, Ext::BaseField::ONE); - self.add_const(out, in_0); - } - (in_0, MixedCell::Constant(in_1)) => { - self.add_cell_expr(out, in_0); - let diff = in_0.expr(-Ext::BaseField::ONE, in_1); - let diff_cell = self.create_cell(); - self.add_cell_expr(diff_cell, diff); - self.mul2(out, diff_cell, cond, Ext::BaseField::ONE); - } - (in_0, in_1) => { - self.add_cell_expr(out, in_0); - let diff = self.create_cell(); - self.add_cell_expr(diff, in_1); - self.add_cell_expr(diff, in_0.expr(-Ext::BaseField::ONE, Ext::BaseField::ZERO)); - self.mul2(out, diff, cond, Ext::BaseField::ONE); - } - } - } -} diff --git a/simple-frontend/src/circuit_builder/derives.rs b/simple-frontend/src/circuit_builder/derives.rs deleted file mode 100644 index a15170d0c..000000000 --- a/simple-frontend/src/circuit_builder/derives.rs +++ /dev/null @@ -1,30 +0,0 @@ -#[macro_export] -macro_rules! rlc_const_term { - ($builder:ident, $n_ext:expr, $out:expr; $c:expr) => { - for j in 0..$n_ext { - $builder.add_const_internal($out[j], ConstantType::Challenge($c, j)); - } - }; - ($builder:ident, $n_ext:expr, $out:expr; $c:expr, $scalar:expr) => { - for j in 0..$n_ext { - $builder.add_const_internal($out[j], ConstantType::ChallengeScaled($c, j, $scalar)); - } - }; -} -#[macro_export] -macro_rules! rlc_base_term { - ($builder:ident, $n_ext:expr, $out:expr, $in_0:expr; $c:expr) => { - for j in 0..$n_ext { - $builder.add_internal($out[j], $in_0, ConstantType::Challenge($c, j)); - } - }; - ($builder:ident, $n_ext:expr, $out:expr, $in_0:expr; $c:expr, $scalar:expr) => { - for j in 0..$n_ext { - $builder.add_internal( - $out[j], - $in_0, - ConstantType::ChallengeScaled($c, j, $scalar), - ); - } - }; -} diff --git a/simple-frontend/src/circuit_builder/ext_opt.rs b/simple-frontend/src/circuit_builder/ext_opt.rs deleted file mode 100644 index 564252ea7..000000000 --- a/simple-frontend/src/circuit_builder/ext_opt.rs +++ /dev/null @@ -1,518 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; -use itertools::Itertools; -use std::marker::PhantomData; - -use crate::{ - rlc_base_term, rlc_const_term, - structs::{ - CellId, CellType, ChallengeConst, ChallengeId, CircuitBuilder, ConstantType, ExtCellId, - InType, MixedCell, OutType, WitnessId, - }, -}; - -impl From> for ExtCellId { - /// converting a vector of CellIds into an ext cell - fn from(cells: Vec) -> Self { - Self { - cells, - phantom: PhantomData, - } - } -} - -impl From> for Vec { - /// converting an ext cell into a vector of CellIds - fn from(val: ExtCellId) -> Self { - val.cells - } -} - -impl AsRef<[CellId]> for ExtCellId { - fn as_ref(&self) -> &[CellId] { - &self.cells - } -} - -impl ExtCellId { - /// converting a vector of ext cells into a vector of CellIds - pub fn exts_to_cells(exts: &[Self]) -> Vec { - exts.iter().flat_map(|ext| ext.cells.clone()).collect() - } - - /// degree of the ext cell - pub fn degree(&self) -> usize { - self.cells.len() - } -} - -// Public APIs -impl CircuitBuilder { - // ====================================== - // Cell creations - // ====================================== - - /// Create an ExtCellId for an extension field element. - /// Note: an extension cell already consists of multiple cells. - pub fn create_ext_cell(&mut self) -> ExtCellId { - self.create_cells(::DEGREE).into() - } - - /// Create a vector of ExtCells for a vector of extension field elements. - /// Note: an extension cell already consists of multiple cells. - pub fn create_ext_cells(&mut self, num: usize) -> Vec> { - let cells = self.create_cells(num * ::DEGREE); - cells - .chunks_exact(::DEGREE) - .map(|x| x.to_vec().into()) - .collect() - } - - pub fn create_ext_witness_in(&mut self, num: usize) -> (WitnessId, Vec>) { - let cells = self.create_cells(num * ::DEGREE); - self.mark_cells( - CellType::In(InType::Witness(self.n_witness_in as WitnessId)), - &cells, - ); - self.n_witness_in += 1; - ( - (self.n_witness_in - 1) as WitnessId, - cells - .chunks_exact(::DEGREE) - .map(|x| x.to_vec().into()) - .collect(), - ) - } - - /// Create input cells and assign it to be constant. - pub fn create_ext_constant_in(&mut self, num: usize, constant: i64) -> Vec> { - let cells = self.create_ext_cells(num); - cells.iter().for_each(|ext_cell| { - // first base field is the constant - self.mark_cells(CellType::In(InType::Constant(constant)), &[ - ext_cell.cells[0] - ]); - // the rest fields are 0s - self.mark_cells(CellType::In(InType::Constant(0)), &ext_cell.cells[1..]); - }); - cells - } - - pub fn create_ext_witness_out(&mut self, num: usize) -> (WitnessId, Vec>) { - let cells = self.create_cells(num * ::DEGREE); - self.mark_cells( - CellType::Out(OutType::Witness(self.n_witness_out as WitnessId)), - &cells, - ); - self.n_witness_out += 1; - ( - (self.n_witness_out - 1) as WitnessId, - cells - .chunks_exact(::DEGREE) - .map(|x| x.to_vec().into()) - .collect(), - ) - } - - pub fn create_witness_out_from_exts(&mut self, exts: &[ExtCellId]) -> WitnessId { - for ext in exts { - self.mark_cells( - CellType::Out(OutType::Witness(self.n_witness_out as WitnessId)), - ext.as_ref(), - ); - } - self.n_witness_out += 1; - (self.n_witness_out - 1) as WitnessId - } - - // ====================================== - // Cell selections - // ====================================== - /// Base on the condition, select - /// - either extension cell in_0, - /// - or a new extension cell from [in_1, 0, 0, 0 ...] - pub fn sel_ext_and_mixed( - &mut self, - out: &ExtCellId, - in_0: &ExtCellId, - in_1: &MixedCell, - cond: CellId, - ) { - assert_eq!(out.degree(), ::DEGREE); - assert_eq!(in_0.degree(), ::DEGREE); - - out.cells - .iter() - .zip_eq( - in_0.cells.iter().zip_eq( - [*in_1].iter().chain( - std::iter::repeat(&MixedCell::Constant(Ext::BaseField::ZERO)) - .take(::DEGREE - 1), - ), - ), - ) - .for_each(|(&out, (&in0, &in1))| self.sel_mixed(out, in0.into(), in1, cond)); - } - - /// Base on the condition, select - /// - either a new extension cell from [in_0, 0, 0, 0 ...] - /// - or extension cell in_1, - pub fn sel_mixed_and_ext( - &mut self, - out: &ExtCellId, - in_0: &MixedCell, - in_1: &ExtCellId, - cond: CellId, - ) { - assert_eq!(out.degree(), ::DEGREE); - assert_eq!(in_1.degree(), ::DEGREE); - - out.cells - .iter() - .zip_eq( - in_1.cells.iter().zip_eq( - [*in_0].iter().chain( - std::iter::repeat(&MixedCell::Constant(Ext::BaseField::ZERO)) - .take(::DEGREE - 1), - ), - ), - ) - .for_each(|(&out, (&in1, &in0))| self.sel_mixed(out, in0, in1.into(), cond)); - } - - /// Base on the condition, select extension cells in_0 or in_1 - pub fn sel_ext( - &mut self, - out: &ExtCellId, - in_0: &ExtCellId, - in_1: &ExtCellId, - cond: CellId, - ) { - // we only need to check one degree since the rest are - // enforced by zip_eq - assert_eq!(out.degree(), ::DEGREE); - - out.cells - .iter() - .zip_eq(in_0.cells.iter().zip_eq(in_1.cells.iter())) - .for_each(|(&out, (&in0, &in1))| self.select(out, in0, in1, cond)); - } - - // ====================================== - // Cell arithmetics - // ====================================== - /// Constrain out += in_0*scalar - pub fn add_ext(&mut self, out: &ExtCellId, in_0: &ExtCellId, scalar: Ext::BaseField) { - // we only need to check one degree since the rest are - // enforced by zip_eq - assert_eq!(out.degree(), ::DEGREE); - out.cells - .iter() - .zip_eq(in_0.cells.iter()) - .for_each(|(&o, &i)| self.add(o, i, scalar)); - } - - /// Constrain - /// - out[i] += in_0[i] * in_1 * scalar for i in 0..DEGREE-1 - pub fn mul_ext_base( - &mut self, - out: &ExtCellId, - in_0: &ExtCellId, - in_1: CellId, - scalar: Ext::BaseField, - ) { - assert_eq!(out.degree(), ::DEGREE); - out.cells - .iter() - .zip_eq(in_0.cells.iter()) - .for_each(|(&o, &i)| self.mul2(o, i, in_1, scalar)); - } - - /// Constrain Extension field multiplications. - /// In the case of DEGREE = 2, it is - /// - out[0] += (in_0[0] * in_1[0] + 7 * in_0[1] * in_1[1]) * scalar - /// - out[1] += (in_0[0] * in_1[1] + in_0[1] * in_1[0]) * scalar - pub fn mul2_ext( - &mut self, - out: &ExtCellId, - in_0: &ExtCellId, - in_1: &ExtCellId, - scalar: Ext::BaseField, - ) { - assert_eq!(out.degree(), ::DEGREE); - match ::DEGREE { - 2 => self.mul2_degree_2_ext_internal(&out.cells, &in_0.cells, &in_1.cells, scalar), - 3 => self.mul2_degree_3_ext_internal(&out.cells, &in_0.cells, &in_1.cells, scalar), - // we do not support extension field beyond 3 at the moment - _ => unimplemented!(), - } - } - - /// Constrain out += in_0 * c - pub fn add_product_of_ext_and_challenge( - &mut self, - out: &ExtCellId, - in_0: &ExtCellId, - c: ChallengeConst, - ) { - assert_eq!(out.degree(), ::DEGREE); - assert_eq!(in_0.degree(), ::DEGREE); - match ::DEGREE { - 2 => self.add_ext_mul_challenge_2(&out.cells, &in_0.cells, c), - 3 => self.add_ext_mul_challenge_3(&out.cells, &in_0.cells, c), - _ => unimplemented!(), - } - } - - // ====================================== - // Cell random linear combinations - // ====================================== - - /// Compute the random linear combination of `in_array` by challenge. - /// out = (\sum_{i = 0}^{in_array.len()} challenge^i * in_array[i]) + challenge^{in_array.len()}. - pub fn rlc(&mut self, out: &ExtCellId, in_array: &[CellId], challenge: ChallengeId) { - assert_eq!(out.degree(), ::DEGREE); - for (i, item) in in_array.iter().enumerate() { - let c = ChallengeConst { - challenge, - exp: i as u64, - }; - rlc_base_term!(self, ::DEGREE, out.cells, *item; c); - } - let c = ChallengeConst { - challenge, - exp: in_array.len() as u64, - }; - rlc_const_term!(self, ::DEGREE, out.cells; c); - } - - /// Compute the random linear combination of `in_array` by challenge. - /// out = \sum_{i = 0}^{in_array.len()} challenge^i * in_array[i] + challenge^{in_array.len()}. - pub fn rlc_ext( - &mut self, - out: &ExtCellId, - in_array: &[ExtCellId], - challenge: ChallengeId, - ) { - assert_eq!(out.degree(), ::DEGREE); - match ::DEGREE { - 2 => self.rlc_ext_2(out, in_array, challenge), - 3 => self.rlc_ext_3(out, in_array, challenge), - _ => unimplemented!(), - } - } - - /// Compute the random linear combination of `in_array` with mixed types by challenge. - /// out = (\sum_{i = 0}^{in_array.len()} challenge^i * (\sum_j in_array[i][j])) + challenge^{in_array.len()}. - pub fn rlc_mixed( - &mut self, - out: &ExtCellId, - in_array: &[MixedCell], - challenge: ChallengeId, - ) { - assert_eq!(out.degree(), ::DEGREE); - for (i, item) in in_array.iter().enumerate() { - let c: ChallengeConst = ChallengeConst { - challenge, - exp: i as u64, - }; - match item { - MixedCell::Constant(constant) => { - rlc_const_term!(self, ::DEGREE, out.cells; c, *constant) - } - MixedCell::Cell(cell_id) => { - rlc_base_term!(self, ::DEGREE, out.cells, *cell_id; c) - } - MixedCell::CellExpr(cell_id, a, b) => { - rlc_base_term!(self, ::DEGREE, out.cells, *cell_id; c, *a); - rlc_const_term!(self, ::DEGREE, out.cells; c, *b); - } - } - } - let c: ChallengeConst = ChallengeConst { - challenge, - exp: in_array.len() as u64, - }; - rlc_const_term!(self, ::DEGREE, out.cells; c); - } -} - -// Internal APIs -impl CircuitBuilder { - /// let a1b1 = a.0[0] * b.0[0]; - /// let a1b2 = a.0[0] * b.0[1]; - /// let a2b1 = a.0[1] * b.0[0]; - /// let a2b2 = a.0[1] * b.0[1]; - /// let c1 = a1b1 + Goldilocks(7) * a2b2; - /// let c2 = a2b1 + a1b2; - fn mul2_degree_2_ext_internal( - &mut self, - out: &[CellId], - in_0: &[CellId], - in_1: &[CellId], - scalar: Ext::BaseField, - ) { - let a0b0 = self.create_cell(); - self.mul2(a0b0, in_0[0], in_1[0], Ext::BaseField::ONE); - let a0b1 = self.create_cell(); - self.mul2(a0b1, in_0[0], in_1[1], Ext::BaseField::ONE); - let a1b0 = self.create_cell(); - self.mul2(a1b0, in_0[1], in_1[0], Ext::BaseField::ONE); - let a1b1 = self.create_cell(); - self.mul2(a1b1, in_0[1], in_1[1], Ext::BaseField::ONE); - self.add(out[0], a0b0, scalar); - self.add(out[0], a1b1, Ext::BaseField::from(7) * scalar); - self.add(out[1], a1b0, scalar); - self.add(out[1], a0b1, scalar); - } - - fn add_ext_mul_challenge_2(&mut self, out: &[CellId], in_0: &[CellId], c: ChallengeConst) { - let a0b0 = self.create_cell(); - let in_1 = [ConstantType::Challenge(c, 0), ConstantType::Challenge(c, 1)]; - self.add_internal(a0b0, in_0[0], in_1[0]); - let a0b1 = self.create_cell(); - self.add_internal(a0b1, in_0[0], in_1[1]); - let a1b0 = self.create_cell(); - self.add_internal(a1b0, in_0[1], in_1[0]); - let a1b1 = self.create_cell(); - self.add_internal(a1b1, in_0[1], in_1[1]); - self.add(out[0], a0b0, Ext::BaseField::ONE); - self.add(out[0], a1b1, Ext::BaseField::from(7)); - self.add(out[1], a1b0, Ext::BaseField::ONE); - self.add(out[1], a0b1, Ext::BaseField::ONE); - } - - /// Random linear combinations for extension cells with degree = 2 - fn rlc_ext_2( - &mut self, - out: &ExtCellId, - in_array: &[ExtCellId], - challenge: ChallengeId, - ) { - assert_eq!(out.degree(), ::DEGREE); - for (i, item) in in_array.iter().enumerate() { - let c = ChallengeConst { - challenge, - exp: i as u64, - }; - self.add_ext_mul_challenge_2(&out.cells, &item.cells, c); - } - let c = ChallengeConst { - challenge, - exp: in_array.len() as u64, - }; - rlc_const_term!(self, ::DEGREE, out.cells; c); - } - - /// Random linear combinations for extension cells with degree = 3 - fn rlc_ext_3( - &mut self, - out: &ExtCellId, - in_array: &[ExtCellId], - challenge: ChallengeId, - ) { - assert_eq!(out.degree(), 3); - for (i, item) in in_array.iter().enumerate() { - let c = ChallengeConst { - challenge, - exp: i as u64, - }; - self.add_ext_mul_challenge_3(&out.cells, &item.cells, c); - } - let c = ChallengeConst { - challenge, - exp: in_array.len() as u64, - }; - rlc_const_term!(self, 3, out.cells; c); - } - - /// let a1b1 = a.0[0] * b.0[0]; - /// let a1b2 = a.0[0] * b.0[1]; - /// let a1b3 = a.0[0] * b.0[2]; - /// let a2b1 = a.0[1] * b.0[0]; - /// let a2b2 = a.0[1] * b.0[1]; - /// let a2b3 = a.0[1] * b.0[2]; - /// let a3b1 = a.0[2] * b.0[0]; - /// let a3b2 = a.0[2] * b.0[1]; - /// let a3b3 = a.0[2] * b.0[2]; - /// let c1 = a1b1 + a3b2 + a2b3; - /// let c2 = a2b1 + a1b2 + a2b3 + a3b2 + a3b3; - /// let c3 = a3b1 + a2b2 + a1b3 + a3b3; - /// GoldilocksExt3([c1, c2, c3]) - fn mul2_degree_3_ext_internal( - &mut self, - out: &[CellId], - in_0: &[CellId], - in_1: &[CellId], - scalar: Ext::BaseField, - ) { - let a0b0 = self.create_cell(); - self.mul2(a0b0, in_0[0], in_1[0], Ext::BaseField::ONE); - let a0b1 = self.create_cell(); - self.mul2(a0b1, in_0[0], in_1[1], Ext::BaseField::ONE); - let a0b2 = self.create_cell(); - self.mul2(a0b2, in_0[0], in_1[2], Ext::BaseField::ONE); - let a1b0 = self.create_cell(); - self.mul2(a1b0, in_0[1], in_1[0], Ext::BaseField::ONE); - let a1b1 = self.create_cell(); - self.mul2(a1b1, in_0[1], in_1[1], Ext::BaseField::ONE); - let a1b2 = self.create_cell(); - self.mul2(a1b2, in_0[1], in_1[2], Ext::BaseField::ONE); - let a2b0 = self.create_cell(); - self.mul2(a2b0, in_0[2], in_1[0], Ext::BaseField::ONE); - let a2b1 = self.create_cell(); - self.mul2(a2b1, in_0[2], in_1[1], Ext::BaseField::ONE); - let a2b2 = self.create_cell(); - self.mul2(a2b2, in_0[2], in_1[2], Ext::BaseField::ONE); - self.add(out[0], a0b0, scalar); - self.add(out[0], a2b1, scalar); - self.add(out[0], a1b2, scalar); - self.add(out[1], a1b0, scalar); - self.add(out[1], a0b1, scalar); - self.add(out[1], a2b1, scalar); - self.add(out[1], a1b2, scalar); - self.add(out[1], a2b2, scalar); - self.add(out[2], a2b0, scalar); - self.add(out[2], a1b1, scalar); - self.add(out[2], a0b2, scalar); - self.add(out[2], a2b2, scalar); - } - - fn add_ext_mul_challenge_3(&mut self, out: &[CellId], in_0: &[CellId], c: ChallengeConst) { - let in_1 = [ - ConstantType::Challenge(c, 0), - ConstantType::Challenge(c, 1), - ConstantType::Challenge(c, 2), - ]; - let a0b0 = self.create_cell(); - self.add_internal(a0b0, in_0[0], in_1[0]); - let a0b1 = self.create_cell(); - self.add_internal(a0b1, in_0[0], in_1[1]); - let a0b2 = self.create_cell(); - self.add_internal(a0b2, in_0[0], in_1[2]); - let a1b0 = self.create_cell(); - self.add_internal(a1b0, in_0[1], in_1[0]); - let a1b1 = self.create_cell(); - self.add_internal(a1b1, in_0[1], in_1[1]); - let a1b2 = self.create_cell(); - self.add_internal(a1b2, in_0[1], in_1[2]); - let a2b0 = self.create_cell(); - self.add_internal(a2b0, in_0[2], in_1[0]); - let a2b1 = self.create_cell(); - self.add_internal(a2b1, in_0[2], in_1[1]); - let a2b2 = self.create_cell(); - self.add_internal(a2b2, in_0[2], in_1[2]); - self.add(out[0], a0b0, Ext::BaseField::ONE); - self.add(out[0], a2b1, Ext::BaseField::ONE); - self.add(out[0], a1b2, Ext::BaseField::ONE); - self.add(out[1], a1b0, Ext::BaseField::ONE); - self.add(out[1], a0b1, Ext::BaseField::ONE); - self.add(out[1], a2b1, Ext::BaseField::ONE); - self.add(out[1], a1b2, Ext::BaseField::ONE); - self.add(out[1], a2b2, Ext::BaseField::ONE); - self.add(out[2], a2b0, Ext::BaseField::ONE); - self.add(out[2], a1b1, Ext::BaseField::ONE); - self.add(out[2], a0b2, Ext::BaseField::ONE); - self.add(out[2], a2b2, Ext::BaseField::ONE); - } -} diff --git a/simple-frontend/src/lib.rs b/simple-frontend/src/lib.rs deleted file mode 100644 index 20fba7b18..000000000 --- a/simple-frontend/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod circuit_builder; -pub mod structs; diff --git a/simple-frontend/src/structs.rs b/simple-frontend/src/structs.rs deleted file mode 100644 index 2398fd729..000000000 --- a/simple-frontend/src/structs.rs +++ /dev/null @@ -1,133 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; -use serde::Serialize; -use std::{hash::Hash, marker::PhantomData}; - -// We make use of the following identifiers. -// For type safety we want different alias for those identifiers; while disallow arithmetics cross different identifiers. -// We achieve this via setting them to different primitive types. -// This works better/simpler than struct-wrapping -pub type ChallengeId = u8; -pub type TableType = u16; -pub type WitnessId = u16; -pub type LayerId = u32; -pub type CellId = usize; - -#[derive(Clone, Copy, Debug, Serialize, Eq, PartialEq, Hash)] -pub struct ChallengeConst { - pub challenge: ChallengeId, - pub exp: u64, -} - -#[derive(Clone, Copy, Debug, PartialEq, Serialize)] -pub enum ConstantType { - Field(Ext::BaseField), - /// ChallengeConst is an extension field element represents a power of ChallengeId. - /// The usize here denotes the `usize`-th base field element of the const. - Challenge(ChallengeConst, usize), - ChallengeScaled(ChallengeConst, usize, Ext::BaseField), -} - -/// Represent a gate in the circuit. The inner variables denote the input -/// indices and scalar. -#[derive(Clone, Debug)] -pub struct GateType { - pub idx_in: Vec, - pub scalar: ConstantType, -} - -/// Store wire structure of the circuit. -#[derive(Clone, Debug, Default)] -pub struct Cell { - /// The layer of the cell. - pub layer: Option, - /// The value of the cell is the sum of all gates. - pub gates: Vec>, - /// The type of the cell, e.g., public input, witness, challenge, etc. - pub cell_type: Option, -} - -/// An ExtCell consists DEGREE number of cells. -#[derive(Clone, Debug)] -pub struct ExtCellId { - pub cells: Vec, - pub phantom: PhantomData, -} - -#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug, Serialize, Ord, PartialOrd)] -pub enum InType { - Witness(WitnessId), - /// Constant(num_vars) acts like a counter (0, 1, 2, ...) through all - /// instances. Each instance holds 1 << num_vars of them. - Counter(usize), - /// Constant keeps the same for all instances. - Constant(i64), -} - -#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug, Serialize, Ord, PartialOrd)] -pub enum OutType { - Witness(WitnessId), - AssertConst(i64), -} - -#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug, Serialize, Ord, PartialOrd)] -pub enum CellType { - In(InType), - Out(OutType), -} - -/// A MixedCell can be a constant, a cell, or a Cell Expression -#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)] -pub enum MixedCell { - Constant(Ext::BaseField), - Cell(CellId), - CellExpr(CellId, Ext::BaseField, Ext::BaseField), -} - -impl From for MixedCell { - fn from(cell_id: CellId) -> Self { - MixedCell::Cell(cell_id) - } -} - -impl MixedCell { - pub fn add(&self, shift: Ext::BaseField) -> Self { - match self { - MixedCell::Constant(c) => MixedCell::Constant(*c + shift), - MixedCell::Cell(c) => MixedCell::CellExpr(*c, Ext::BaseField::ONE, shift), - MixedCell::CellExpr(c, s, sh) => MixedCell::CellExpr(*c, *s, *sh + shift), - } - } - pub fn sub(&self, shift: Ext::BaseField) -> Self { - match self { - MixedCell::Constant(c) => MixedCell::Constant(*c - shift), - MixedCell::Cell(c) => MixedCell::CellExpr(*c, Ext::BaseField::ONE, -shift), - MixedCell::CellExpr(c, s, sh) => MixedCell::CellExpr(*c, *s, *sh - shift), - } - } - pub fn mul(&self, scalar: Ext::BaseField) -> Self { - match self { - MixedCell::Constant(c) => MixedCell::Constant(*c * scalar), - MixedCell::Cell(c) => MixedCell::CellExpr(*c, scalar, Ext::BaseField::ZERO), - MixedCell::CellExpr(c, s, sh) => MixedCell::CellExpr(*c, *s * scalar, *sh * scalar), - } - } - pub fn expr(&self, scalar: Ext::BaseField, shift: Ext::BaseField) -> Self { - match self { - MixedCell::Constant(c) => MixedCell::Constant(*c * scalar + shift), - MixedCell::Cell(c) => MixedCell::Cell(*c), - MixedCell::CellExpr(c, s, sh) => MixedCell::CellExpr(*c, *s * scalar, *sh * shift), - } - } -} - -#[derive(Default)] -pub struct CircuitBuilder { - pub cells: Vec>, - - /// Number of layers in the circuit. - pub n_layers: Option, - - pub(crate) n_witness_in: usize, - pub(crate) n_witness_out: usize, -} diff --git a/singer-utils/Cargo.toml b/singer-utils/Cargo.toml deleted file mode 100644 index e679b06ef..000000000 --- a/singer-utils/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -edition.workspace = true -license.workspace = true -name = "singer-utils" -version.workspace = true - -[dependencies] -ark-std.workspace = true -ff.workspace = true -ff_ext = { path = "../ff_ext" } -goldilocks.workspace = true - -gkr = { path = "../gkr", features = ["parallel"] } -gkr-graph = { version = "0", path = "../gkr-graph" } -itertools.workspace = true -multilinear_extensions = { path = "../multilinear_extensions", features = ["parallel"] } -simple-frontend = { version = "0", path = "../simple-frontend" } -strum.workspace = true -strum_macros.workspace = true -sumcheck = { version = "0", path = "../sumcheck" } -transcript = { version = "0", path = "../transcript" } diff --git a/singer-utils/src/chip_handler.rs b/singer-utils/src/chip_handler.rs deleted file mode 100644 index c9b3d2488..000000000 --- a/singer-utils/src/chip_handler.rs +++ /dev/null @@ -1,68 +0,0 @@ -use crate::{ - chip_handler::{ram_handler::RAMHandler, rom_handler::ROMHandler}, - structs::ChipChallenges, -}; -use ff_ext::ExtensionField; -use simple_frontend::structs::{ChallengeId, CircuitBuilder, WitnessId}; - -pub mod bytecode; -pub mod calldata; -pub mod global_state; -pub mod memory; -pub mod ram_handler; -pub mod range; -pub mod rom_handler; -pub mod stack; -mod util; - -impl Default for ChipChallenges { - fn default() -> Self { - Self { - record_rlc: 1, - record_item_rlc: 0, - } - } -} - -impl ChipChallenges { - pub fn new(record_rlc: ChallengeId, record_item_rlc: ChallengeId) -> Self { - Self { - record_rlc, - record_item_rlc, - } - } - pub fn record_item_rlc(&self) -> ChallengeId { - self.record_item_rlc - } - pub fn record_rlc(&self) -> ChallengeId { - self.record_rlc - } -} - -pub struct ChipHandler { - pub ram_handler: RAMHandler, - pub rom_handler: ROMHandler, -} - -impl ChipHandler { - pub fn new(challenge: ChipChallenges) -> Self { - Self { - ram_handler: RAMHandler::new(challenge), - rom_handler: ROMHandler::new(challenge), - } - } - - #[allow(clippy::type_complexity)] - pub fn finalize( - &mut self, - circuit_builder: &mut CircuitBuilder, - ) -> ( - Option<(WitnessId, usize)>, - Option<(WitnessId, usize)>, - Option<(WitnessId, usize)>, - ) { - let (ram_load_id, ram_store_id) = self.ram_handler.finalize(circuit_builder); - let rom_id = self.rom_handler.finalize(circuit_builder); - (ram_load_id, ram_store_id, rom_id) - } -} diff --git a/singer-utils/src/chip_handler/bytecode.rs b/singer-utils/src/chip_handler/bytecode.rs deleted file mode 100644 index 68bfde11b..000000000 --- a/singer-utils/src/chip_handler/bytecode.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::{ - chip_handler::{ChipHandler, util::cell_to_mixed}, - constants::OpcodeType, - structs::ROMType, -}; -use ff_ext::ExtensionField; -use simple_frontend::structs::{CellId, CircuitBuilder, MixedCell}; - -pub struct BytecodeChip {} - -impl BytecodeChip { - pub fn bytecode_with_pc_opcode( - chip_handler: &mut ChipHandler, - circuit_builder: &mut CircuitBuilder, - pc: &[CellId], - opcode: OpcodeType, - ) { - let key = [ - vec![MixedCell::Constant(Ext::BaseField::from( - ROMType::Bytecode as u64, - ))], - cell_to_mixed(pc), - ] - .concat(); - - chip_handler - .rom_handler - .read_mixed(circuit_builder, &key, &[MixedCell::Constant( - Ext::BaseField::from(opcode as u64), - )]); - } - - pub fn bytecode_with_pc_byte( - chip_handler: &mut ChipHandler, - circuit_builder: &mut CircuitBuilder, - pc: &[CellId], - byte: CellId, - ) { - let key = [ - vec![MixedCell::Constant(Ext::BaseField::from( - ROMType::Bytecode as u64, - ))], - cell_to_mixed(pc), - ] - .concat(); - chip_handler - .rom_handler - .read_mixed(circuit_builder, &key, &[byte.into()]); - } -} diff --git a/singer-utils/src/chip_handler/calldata.rs b/singer-utils/src/chip_handler/calldata.rs deleted file mode 100644 index 67535c068..000000000 --- a/singer-utils/src/chip_handler/calldata.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::{ - chip_handler::{ChipHandler, util::cell_to_mixed}, - structs::ROMType, -}; -use ff_ext::ExtensionField; -use itertools::Itertools; -use simple_frontend::structs::{CellId, CircuitBuilder, MixedCell}; - -pub struct CalldataChip {} - -impl CalldataChip { - pub fn load( - chip_handler: &mut ChipHandler, - circuit_builder: &mut CircuitBuilder, - offset: &[CellId], - data: &[CellId], - ) { - let key = [ - vec![MixedCell::Constant(Ext::BaseField::from( - ROMType::Calldata as u64, - ))], - cell_to_mixed(offset), - ] - .concat(); - let data = data.iter().map(|&x| x.into()).collect_vec(); - chip_handler - .rom_handler - .read_mixed(circuit_builder, &key, &data); - } -} diff --git a/singer-utils/src/chip_handler/global_state.rs b/singer-utils/src/chip_handler/global_state.rs deleted file mode 100644 index b02827a8d..000000000 --- a/singer-utils/src/chip_handler/global_state.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::{ - chip_handler::{ChipHandler, util::cell_to_mixed}, - structs::RAMType, -}; -use ff_ext::ExtensionField; -use simple_frontend::structs::{CellId, CircuitBuilder, MixedCell}; - -pub struct GlobalStateChip {} - -impl GlobalStateChip { - pub fn state_in( - chip_handler: &mut ChipHandler, - circuit_builder: &mut CircuitBuilder, - pc: &[CellId], - stack_ts: &[CellId], - memory_ts: &[CellId], - stack_top: CellId, - clk: CellId, - ) { - let key = [ - vec![MixedCell::Constant(Ext::BaseField::from( - RAMType::GlobalState as u64, - ))], - cell_to_mixed(pc), - cell_to_mixed(stack_ts), - cell_to_mixed(memory_ts), - vec![stack_top.into(), clk.into()], - ] - .concat(); - - chip_handler - .ram_handler - .read_oam_mixed(circuit_builder, &[], &key, &[]); - } - - pub fn state_out( - chip_handler: &mut ChipHandler, - circuit_builder: &mut CircuitBuilder, - pc: &[CellId], - stack_ts: &[CellId], - memory_ts: &[CellId], - stack_top: MixedCell, - clk: MixedCell, - ) { - let key = [ - vec![MixedCell::Constant(Ext::BaseField::from( - RAMType::GlobalState as u64, - ))], - cell_to_mixed(pc), - cell_to_mixed(stack_ts), - cell_to_mixed(memory_ts), - vec![stack_top, clk], - ] - .concat(); - - chip_handler - .ram_handler - .write_oam_mixed(circuit_builder, &[], &key, &[]); - } -} diff --git a/singer-utils/src/chip_handler/memory.rs b/singer-utils/src/chip_handler/memory.rs deleted file mode 100644 index 7765abe0b..000000000 --- a/singer-utils/src/chip_handler/memory.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::{ - chip_handler::{ChipHandler, util::cell_to_mixed}, - structs::RAMType, -}; -use ff_ext::ExtensionField; -use simple_frontend::structs::{CellId, CircuitBuilder, MixedCell}; - -pub struct MemoryChip {} - -impl MemoryChip { - pub fn read( - chip_handler: &mut ChipHandler, - circuit_builder: &mut CircuitBuilder, - offset: &[CellId], - old_ts: &[CellId], - cur_ts: &[CellId], - byte: CellId, - ) { - let key = [ - vec![MixedCell::Constant(Ext::BaseField::from( - RAMType::Memory as u64, - ))], - cell_to_mixed(offset), - ] - .concat(); - let old_ts = cell_to_mixed(old_ts); - let cur_ts = cell_to_mixed(cur_ts); - chip_handler.ram_handler.read_mixed( - circuit_builder, - &old_ts, - &cur_ts, - &key, - &[byte.into()], - ); - } - - pub fn write( - chip_handler: &mut ChipHandler, - circuit_builder: &mut CircuitBuilder, - offset: &[CellId], - old_ts: &[CellId], - cur_ts: &[CellId], - old_byte: CellId, - cur_byte: CellId, - ) { - let key = [ - vec![MixedCell::Constant(Ext::BaseField::from( - RAMType::Memory as u64, - ))], - cell_to_mixed(offset), - ] - .concat(); - let old_ts = cell_to_mixed(old_ts); - let cur_ts = cell_to_mixed(cur_ts); - chip_handler.ram_handler.write_mixed( - circuit_builder, - &old_ts, - &cur_ts, - &key, - &[old_byte.into()], - &[cur_byte.into()], - ); - } -} diff --git a/singer-utils/src/chip_handler/ram_handler.rs b/singer-utils/src/chip_handler/ram_handler.rs deleted file mode 100644 index 5c7731bc5..000000000 --- a/singer-utils/src/chip_handler/ram_handler.rs +++ /dev/null @@ -1,178 +0,0 @@ -use crate::structs::ChipChallenges; -use ff::Field; -use ff_ext::ExtensionField; -use simple_frontend::structs::{CellId, CircuitBuilder, ExtCellId, MixedCell, WitnessId}; - -pub struct RAMHandler { - read_records: Vec>, - write_records: Vec>, - challenge: ChipChallenges, -} - -impl RAMHandler { - /// Instantiate new `OAMHandler` given chip challenge - pub fn new(challenge: ChipChallenges) -> Self { - Self { - read_records: Vec::new(), - write_records: Vec::new(), - challenge, - } - } - - pub fn read( - &mut self, - circuit_builder: &mut CircuitBuilder, - old_ts: &[CellId], - cur_ts: &[CellId], - key: &[CellId], - value: &[CellId], - ) { - self.read_oam(circuit_builder, old_ts, key, value); - self.write_oam(circuit_builder, cur_ts, key, value); - } - - pub fn read_mixed( - &mut self, - circuit_builder: &mut CircuitBuilder, - old_ts: &[MixedCell], - cur_ts: &[MixedCell], - key: &[MixedCell], - value: &[MixedCell], - ) { - self.read_oam_mixed(circuit_builder, old_ts, key, value); - self.write_oam_mixed(circuit_builder, cur_ts, key, value); - } - - pub fn read_oam( - &mut self, - circuit_builder: &mut CircuitBuilder, - old_ts: &[CellId], - key: &[CellId], - value: &[CellId], - ) { - let item_rlc = circuit_builder.create_ext_cell(); - let items = [old_ts.to_vec(), key.to_vec(), value.to_vec()].concat(); - circuit_builder.rlc(&item_rlc, &items, self.challenge.record_item_rlc()); - - let out = circuit_builder.create_ext_cell(); - circuit_builder.rlc_ext(&out, &[item_rlc], self.challenge.record_rlc()); - self.read_records.push(out); - } - - pub fn read_oam_mixed( - &mut self, - circuit_builder: &mut CircuitBuilder, - old_ts: &[MixedCell], - key: &[MixedCell], - value: &[MixedCell], - ) { - let item_rlc = circuit_builder.create_ext_cell(); - let items = [old_ts.to_vec(), key.to_vec(), value.to_vec()].concat(); - circuit_builder.rlc_mixed(&item_rlc, &items, self.challenge.record_item_rlc()); - - let out = circuit_builder.create_ext_cell(); - circuit_builder.rlc_ext(&out, &[item_rlc], self.challenge.record_rlc()); - self.read_records.push(out); - } - - pub fn write( - &mut self, - circuit_builder: &mut CircuitBuilder, - old_ts: &[CellId], - cur_ts: &[CellId], - key: &[CellId], - old_value: &[CellId], - cur_value: &[CellId], - ) { - self.read_oam(circuit_builder, old_ts, key, old_value); - self.write_oam(circuit_builder, cur_ts, key, cur_value); - } - - pub fn write_mixed( - &mut self, - circuit_builder: &mut CircuitBuilder, - old_ts: &[MixedCell], - cur_ts: &[MixedCell], - key: &[MixedCell], - old_value: &[MixedCell], - cur_value: &[MixedCell], - ) { - self.read_oam_mixed(circuit_builder, old_ts, key, old_value); - self.write_oam_mixed(circuit_builder, cur_ts, key, cur_value); - } - - pub fn write_oam( - &mut self, - circuit_builder: &mut CircuitBuilder, - curr_ts: &[CellId], - key: &[CellId], - value: &[CellId], - ) { - let item_rlc = circuit_builder.create_ext_cell(); - let items = [curr_ts.to_vec(), key.to_vec(), value.to_vec()].concat(); - circuit_builder.rlc(&item_rlc, &items, self.challenge.record_item_rlc()); - - let out = circuit_builder.create_ext_cell(); - circuit_builder.rlc_ext(&out, &[item_rlc], self.challenge.record_rlc()); - self.write_records.push(out); - } - - pub fn write_oam_mixed( - &mut self, - circuit_builder: &mut CircuitBuilder, - curr_ts: &[MixedCell], - key: &[MixedCell], - value: &[MixedCell], - ) { - let item_rlc = circuit_builder.create_ext_cell(); - let items = [curr_ts.to_vec(), key.to_vec(), value.to_vec()].concat(); - circuit_builder.rlc_mixed(&item_rlc, &items, self.challenge.record_item_rlc()); - - let out = circuit_builder.create_ext_cell(); - circuit_builder.rlc_ext(&out, &[item_rlc], self.challenge.record_rlc()); - self.write_records.push(out); - } - - #[allow(clippy::type_complexity)] - pub fn finalize( - &mut self, - circuit_builder: &mut CircuitBuilder, - ) -> (Option<(WitnessId, usize)>, Option<(WitnessId, usize)>) { - let mut read_records = self.read_records.clone(); - let mut write_records = self.write_records.clone(); - - let read_record_output = - pad_and_generate_output_witness(circuit_builder, &mut read_records); - let write_record_output = - pad_and_generate_output_witness(circuit_builder, &mut write_records); - - (read_record_output, write_record_output) - } -} - -fn pad_and_generate_output_witness( - circuit_builder: &mut CircuitBuilder, - records: &mut Vec>, -) -> Option<(WitnessId, usize)> { - if records.is_empty() { - None - } else { - pad_with_one(circuit_builder, records); - Some(( - circuit_builder.create_witness_out_from_exts(records), - records.len(), - )) - } -} - -fn pad_with_one( - circuit_builder: &mut CircuitBuilder, - records: &mut Vec>, -) { - let padding_count = records.len().next_power_of_two() - records.len(); - for _ in 0..padding_count { - let out = circuit_builder.create_ext_cell(); - circuit_builder.add_const(out.cells[0], Ext::BaseField::ONE); - records.push(out); - } -} diff --git a/singer-utils/src/chip_handler/range.rs b/singer-utils/src/chip_handler/range.rs deleted file mode 100644 index fc0f40f47..000000000 --- a/singer-utils/src/chip_handler/range.rs +++ /dev/null @@ -1,204 +0,0 @@ -use crate::{ - chip_handler::{ChipHandler, util::cell_to_mixed}, - constants::{RANGE_CHIP_BIT_WIDTH, STACK_TOP_BIT_WIDTH}, - error::UtilError, - structs::{PCUInt, TSUInt}, - uint::UInt, -}; -use ff::Field; -use ff_ext::ExtensionField; -use simple_frontend::structs::{CellId, CircuitBuilder, MixedCell}; - -pub struct RangeChip {} - -impl RangeChip { - pub fn small_range_check( - chip_handler: &mut ChipHandler, - circuit_builder: &mut CircuitBuilder, - value: MixedCell, - bit_width: usize, - ) -> Result<(), UtilError> { - if bit_width > RANGE_CHIP_BIT_WIDTH { - return Err(UtilError::ChipHandlerError); - } - - let items = [value.mul(Ext::BaseField::from( - 1 << (RANGE_CHIP_BIT_WIDTH - bit_width), - ))]; - - chip_handler - .rom_handler - .read_mixed(circuit_builder, &[], &items); - - Ok(()) - } - - pub fn range_check_stack_top( - chip_handler: &mut ChipHandler, - circuit_builder: &mut CircuitBuilder, - stack_top: MixedCell, - ) -> Result<(), UtilError> { - Self::small_range_check( - chip_handler, - circuit_builder, - stack_top, - STACK_TOP_BIT_WIDTH, - ) - } - - pub fn range_check_bytes( - chip_handler: &mut ChipHandler, - circuit_builder: &mut CircuitBuilder, - bytes: &[CellId], - ) -> Result<(), UtilError> { - let bytes = cell_to_mixed(bytes); - for byte in bytes { - Self::small_range_check(chip_handler, circuit_builder, byte, 8)? - } - Ok(()) - } - - pub fn range_check_table_item( - chip_handler: &mut ChipHandler, - circuit_builder: &mut CircuitBuilder, - item: CellId, - ) { - chip_handler.rom_handler.read(circuit_builder, &[], &[item]) - } - - /// Ensures that the value represented in a `UInt` (as field elements) - /// matches its definition. - /// i.e. total_represented_value <= M and each value represented per cell <= max_cell_width - pub fn range_check_uint( - chip_handler: &mut ChipHandler, - circuit_builder: &mut CircuitBuilder, - uint: &UInt, - range_value_witness: Option<&[CellId]>, - ) -> Result, UtilError> { - let uint_cell_width = UInt::::MAX_CELL_BIT_WIDTH; - - if uint_cell_width <= RANGE_CHIP_BIT_WIDTH { - // the range_table can range check any value up to RANGE_CHIP_BIT_WIDTH - // since the uint_cell_width is less than or equal to RANGE_CHIP_BIT_WIDTH - // the uint cell values can be range checked directly (i.e. no need for decomposition witness) - for (index, cell) in uint.values.iter().enumerate() { - // compute the maximum_bit_width for each cell (will be used to perform range check) - let range_check_width = if index == 0 { - // index == 0 represents the least significant cell (cells are represented in little endian). - // if n represents the total number of cells, n - 1 cells take full width - // maximum_value for this cell = total_bits - (n - 1) * full_cell_width - M - ((UInt::::N_OPERAND_CELLS - 1) * uint_cell_width) - } else { - // the maximum value for every cell other than the least significant cell is - // equal to the maximum cell width - uint_cell_width - }; - - // perform range check on cell - Self::small_range_check( - chip_handler, - circuit_builder, - (*cell).into(), - range_check_width, - )?; - } - return Ok(uint.clone()); - } - - // max_cell_bit_width is greater than the range_chip_bit_width - // in-order to avoid decomposition within the circuit, we take the range values as witness - if let Some(range_values) = range_value_witness { - // first we ensure the range_value is exactly equal to the witness - let range_value_as_uint = - UInt::::from_range_values(circuit_builder, range_values)?; - UInt::::assert_eq(circuit_builder, uint, &range_value_as_uint)?; - - let n_range_cells_per_cell = UInt::::N_RANGE_CELLS_PER_CELL; - - debug_assert!(range_values.len() % n_range_cells_per_cell == 0); - - for range_cells in range_values.chunks(n_range_cells_per_cell) { - // the range cells are big endian relative to the uint cell they represent - // hence the first n - 1 range cells should take full width - for range_cell in &range_cells[..(n_range_cells_per_cell - 1)] { - Self::small_range_check( - chip_handler, - circuit_builder, - (*range_cell).into(), - RANGE_CHIP_BIT_WIDTH, - )?; - } - - // the last range cell represents the least significant range cell - // hence we truncate the max_value accordingly - Self::small_range_check( - chip_handler, - circuit_builder, - range_cells[n_range_cells_per_cell - 1].into(), - uint_cell_width - ((n_range_cells_per_cell - 1) * RANGE_CHIP_BIT_WIDTH), - )?; - } - - Ok(range_value_as_uint) - } else { - Err(UtilError::ChipHandlerError) - } - } - - pub fn non_zero( - chip_handler: &mut ChipHandler, - circuit_builder: &mut CircuitBuilder, - val: CellId, - wit: CellId, - ) -> Result { - let prod = circuit_builder.create_cell(); - circuit_builder.mul2(prod, val, wit, Ext::BaseField::ONE); - Self::small_range_check(chip_handler, circuit_builder, prod.into(), 1)?; - - let statement = circuit_builder.create_cell(); - // If val != 0, then prod = 1. => assert!( val (prod - 1) = 0 ) - circuit_builder.mul2(statement, val, prod, Ext::BaseField::ONE); - circuit_builder.add(statement, val, -Ext::BaseField::ONE); - circuit_builder.assert_const(statement, 0); - Ok(prod) - } - - pub fn add_pc_const( - circuit_builder: &mut CircuitBuilder, - pc: &PCUInt, - constant: i64, - witness: &[CellId], - ) -> Result { - let carry = PCUInt::extract_unsafe_carry_add(witness); - PCUInt::add_const_unsafe( - circuit_builder, - pc, - i64_to_base_field::(constant), - carry, - ) - } - - pub fn add_ts_with_const( - chip_handler: &mut ChipHandler, - circuit_builder: &mut CircuitBuilder, - ts: &TSUInt, - constant: i64, - witness: &[CellId], - ) -> Result { - TSUInt::add_const( - circuit_builder, - chip_handler, - ts, - i64_to_base_field::(constant), - witness, - ) - } -} - -fn i64_to_base_field(x: i64) -> E::BaseField { - if x >= 0 { - E::BaseField::from(x as u64) - } else { - -E::BaseField::from((-x) as u64) - } -} diff --git a/singer-utils/src/chip_handler/rom_handler.rs b/singer-utils/src/chip_handler/rom_handler.rs deleted file mode 100644 index 478b8fee1..000000000 --- a/singer-utils/src/chip_handler/rom_handler.rs +++ /dev/null @@ -1,73 +0,0 @@ -use crate::structs::ChipChallenges; -use ff::Field; -use ff_ext::ExtensionField; -use simple_frontend::structs::{CellId, CircuitBuilder, ExtCellId, MixedCell, WitnessId}; - -pub struct ROMHandler { - records: Vec>, - challenge: ChipChallenges, -} - -impl ROMHandler { - /// Instantiate new `ROMHandler` given chip challenge - pub fn new(challenge: ChipChallenges) -> Self { - Self { - records: Vec::new(), - challenge, - } - } - - pub fn read( - &mut self, - circuit_builder: &mut CircuitBuilder, - key: &[CellId], - value: &[CellId], - ) { - let item_rlc = circuit_builder.create_ext_cell(); - let items = [key.to_vec(), value.to_vec()].concat(); - circuit_builder.rlc(&item_rlc, &items, self.challenge.record_item_rlc()); - - let out = circuit_builder.create_ext_cell(); - circuit_builder.rlc_ext(&out, &[item_rlc], self.challenge.record_rlc()); - self.records.push(out); - } - - pub fn read_mixed( - &mut self, - circuit_builder: &mut CircuitBuilder, - key: &[MixedCell], - value: &[MixedCell], - ) { - let item_rlc = circuit_builder.create_ext_cell(); - let items = [key.to_vec(), value.to_vec()].concat(); - circuit_builder.rlc_mixed(&item_rlc, &items, self.challenge.record_item_rlc()); - - let out = circuit_builder.create_ext_cell(); - circuit_builder.rlc_ext(&out, &[item_rlc], self.challenge.record_rlc()); - self.records.push(out); - } - - pub fn finalize( - &mut self, - circuit_builder: &mut CircuitBuilder, - ) -> Option<(WitnessId, usize)> { - if self.records.is_empty() { - return None; - } - - let padding_count = self.records.len().next_power_of_two() - self.records.len(); - let last_cell = self.records.last().expect("confirmed records.len() > 0"); - let mut records = self.records.clone(); - - for _ in 0..padding_count { - let out = circuit_builder.create_ext_cell(); - circuit_builder.add_ext(&out, last_cell, Ext::BaseField::ONE); - records.push(out); - } - - Some(( - circuit_builder.create_witness_out_from_exts(&records), - records.len(), - )) - } -} diff --git a/singer-utils/src/chip_handler/stack.rs b/singer-utils/src/chip_handler/stack.rs deleted file mode 100644 index 697b3327a..000000000 --- a/singer-utils/src/chip_handler/stack.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::{ - chip_handler::{ChipHandler, util::cell_to_mixed}, - structs::RAMType, -}; -use ff_ext::ExtensionField; -use simple_frontend::structs::{CellId, CircuitBuilder, MixedCell}; - -pub struct StackChip {} - -impl StackChip { - pub fn push( - chip_handler: &mut ChipHandler, - circuit_builder: &mut CircuitBuilder, - stack_top: MixedCell, - stack_ts: &[CellId], - values: &[CellId], - ) { - let key = [ - MixedCell::Constant(Ext::BaseField::from(RAMType::Stack as u64)), - stack_top, - ]; - let stack_ts = cell_to_mixed(stack_ts); - let values = cell_to_mixed(values); - chip_handler - .ram_handler - .write_oam_mixed(circuit_builder, &stack_ts, &key, &values); - } - - pub fn pop( - chip_handler: &mut ChipHandler, - circuit_builder: &mut CircuitBuilder, - stack_top: MixedCell, - stack_ts: &[CellId], - values: &[CellId], - ) { - let key = [ - MixedCell::Constant(Ext::BaseField::from(RAMType::Stack as u64)), - stack_top, - ]; - let stack_ts = cell_to_mixed(stack_ts); - let values = cell_to_mixed(values); - chip_handler - .ram_handler - .read_oam_mixed(circuit_builder, &stack_ts, &key, &values); - } -} diff --git a/singer-utils/src/chip_handler/util.rs b/singer-utils/src/chip_handler/util.rs deleted file mode 100644 index 23ea9bca3..000000000 --- a/singer-utils/src/chip_handler/util.rs +++ /dev/null @@ -1,7 +0,0 @@ -use ff_ext::ExtensionField; -use itertools::Itertools; -use simple_frontend::structs::{CellId, MixedCell}; - -pub fn cell_to_mixed(cells: &[CellId]) -> Vec> { - cells.iter().map(|&x| x.into()).collect_vec() -} diff --git a/singer-utils/src/chips.rs b/singer-utils/src/chips.rs deleted file mode 100644 index 8cadeb745..000000000 --- a/singer-utils/src/chips.rs +++ /dev/null @@ -1,421 +0,0 @@ -use std::{mem, sync::Arc}; - -use ff_ext::ExtensionField; -use gkr::structs::Circuit; -use gkr_graph::structs::{CircuitGraphBuilder, NodeOutputType, PredType}; -use multilinear_extensions::mle::DenseMultilinearExtension; -use simple_frontend::structs::WitnessId; -pub use strum::IntoEnumIterator; -use strum_macros::EnumIter; -use sumcheck::util::ceil_log2; - -use crate::{ - constants::RANGE_CHIP_BIT_WIDTH, - error::UtilError, - structs::{ChipChallenges, InstOutChipType}, -}; - -use self::{ - bytecode::{construct_bytecode_table, construct_bytecode_table_and_witness}, - calldata::{construct_calldata_table, construct_calldata_table_and_witness}, - circuit_gadgets::{LeafCircuit, LeafFracSumCircuit, LeafFracSumNoSelectorCircuit}, - range::{construct_range_table, construct_range_table_and_witness}, -}; - -mod bytecode; -mod calldata; -mod range; - -pub mod circuit_gadgets; - -#[derive(Clone, Debug)] -pub struct SingerChipBuilder { - pub chip_circuit_gadgets: ChipCircuitGadgets, - pub output_wires_id: Vec>, -} - -impl Default for SingerChipBuilder { - fn default() -> Self { - Self { - chip_circuit_gadgets: ChipCircuitGadgets::default(), - output_wires_id: vec![vec![]; InstOutChipType::iter().count()], - } - } -} - -impl SingerChipBuilder { - /// Construct the product of frac sum circuits for to chips of each circuit - /// and witnesses. This includes computing the LHS and RHS of the set - /// equality check, and the input of lookup arguments. - pub fn construct_chip_check_graph_and_witness( - &mut self, - graph_builder: &mut CircuitGraphBuilder<'_, E>, - node_id: usize, - to_chip_ids: &[Option<(WitnessId, usize)>], - real_challenges: &[E], - real_n_instances: usize, - ) -> Result<(), UtilError> { - let mut build = |real_n_instances: usize, - num: usize, - input_wit_id: WitnessId, - leaf: &LeafCircuit, - inner: &Arc>| - -> Result { - let selector = ChipCircuitGadgets::construct_prefix_selector(real_n_instances, num); - let selector_node_id = graph_builder.add_node_with_witness( - "selector circuit", - &selector.circuit, - vec![], - real_challenges.to_vec(), - vec![], - real_n_instances.next_power_of_two(), - )?; - let mut preds = vec![PredType::Source; 2]; - preds[leaf.input_id as usize] = - PredType::PredWire(NodeOutputType::WireOut(node_id, input_wit_id)); - preds[leaf.cond_id as usize] = - PredType::PredWire(NodeOutputType::OutputLayer(selector_node_id)); - - let instance_num_vars = ceil_log2(real_n_instances * num) - 1; - build_tree_graph_and_witness( - graph_builder, - preds, - &leaf.circuit, - inner, - vec![DenseMultilinearExtension::default(); 2], - real_challenges, - instance_num_vars, - ) - }; - - // Set equality argument - for output_type in [InstOutChipType::RAMLoad, InstOutChipType::RAMStore] { - if let Some((id, num)) = to_chip_ids[output_type as usize] { - // println!("output_type: {:?}", output_type); - // println!("real_n_instances: {:?}", real_n_instances); - let out = build( - real_n_instances, - num, - id, - &self.chip_circuit_gadgets.product_leaf, - &self.chip_circuit_gadgets.product_inner, - )?; - self.output_wires_id[output_type as usize].push(out); - } - } - - // Lookup argument - for output_type in [InstOutChipType::ROMInput] { - // println!("output_type: {:?}", output_type); - // println!("real_n_instances: {:?}", real_n_instances); - if let Some((id, num)) = to_chip_ids[output_type as usize] { - let out = build( - real_n_instances, - num, - id, - &self.chip_circuit_gadgets.inv_sum, - &self.chip_circuit_gadgets.frac_sum_inner, - )?; - self.output_wires_id[output_type as usize].push(out); - } - } - - Ok(()) - } - - /// Construct the product of frac sum circuits for to chips of each circuit. - /// This includes computing the LHS and RHS of the set equality check, and - /// the input of lookup arguments. - pub fn construct_chip_check_graph( - &mut self, - graph_builder: &mut CircuitGraphBuilder, - node_id: usize, - to_chip_ids: &[Option<(WitnessId, usize)>], - real_n_instances: usize, - ) -> Result<(), UtilError> { - let mut build = |n_instances: usize, - num: usize, - input_wit_id: WitnessId, - leaf: &LeafCircuit, - inner: &Arc>| - -> Result { - let selector = ChipCircuitGadgets::construct_prefix_selector(n_instances, num); - let selector_node_id = - graph_builder.add_node("selector circuit", &selector.circuit, vec![])?; - let mut preds = vec![PredType::Source; 2]; - preds[leaf.input_id as usize] = - PredType::PredWire(NodeOutputType::WireOut(node_id, input_wit_id)); - preds[leaf.cond_id as usize] = - PredType::PredWire(NodeOutputType::OutputLayer(selector_node_id)); - - let instance_num_vars = ceil_log2(real_n_instances) - 1; - build_tree_graph( - graph_builder, - preds, - &leaf.circuit, - inner, - instance_num_vars, - ) - }; - - // Set equality argument - for output_type in [InstOutChipType::RAMLoad, InstOutChipType::RAMStore] { - if let Some((id, num)) = to_chip_ids[output_type as usize] { - let out = build( - real_n_instances, - num, - id, - &self.chip_circuit_gadgets.product_leaf, - &self.chip_circuit_gadgets.product_inner, - )?; - self.output_wires_id[output_type as usize].push(out); - } - } - - // Lookup argument - for output_type in [InstOutChipType::ROMInput] { - if let Some((id, num)) = to_chip_ids[output_type as usize] { - let out = build( - real_n_instances, - num, - id, - &self.chip_circuit_gadgets.inv_sum, - &self.chip_circuit_gadgets.frac_sum_inner, - )?; - self.output_wires_id[output_type as usize].push(out); - } - } - - Ok(()) - } - - /// Construct circuits and witnesses to generate the lookup table for each - /// table, including bytecode, range and calldata. Also generate the - /// tree-structured circuits to fold the summation. - pub fn construct_lookup_table_graph_and_witness( - &self, - graph_builder: &mut CircuitGraphBuilder<'_, E>, - bytecode: &[u8], - program_input: &[u8], - mut table_count_witness: Vec>, - challenges: &ChipChallenges, - real_challenges: &[E], - ) -> Result, UtilError> { - let mut tables_out = vec![NodeOutputType::OutputLayer(0); LookupChipType::iter().count()]; - - let leaf = &self.chip_circuit_gadgets.frac_sum_leaf; - let inner = &self.chip_circuit_gadgets.frac_sum_inner; - let mut pred_source = |table_type, table_pred, selector_pred| { - let mut preds = vec![PredType::Source; 3]; - preds[leaf.input_den_id as usize] = table_pred; - preds[leaf.cond_id as usize] = selector_pred; - let mut sources = vec![DenseMultilinearExtension::default(); 3]; - sources[leaf.input_num_id as usize] = mem::take(&mut table_count_witness[table_type]); - (preds, sources) - }; - - let (input_pred, selector_pred, instance_num_vars) = construct_bytecode_table_and_witness( - graph_builder, - bytecode, - challenges, - real_challenges, - )?; - let (preds, sources) = pred_source( - LookupChipType::BytecodeChip as usize, - input_pred, - selector_pred, - ); - tables_out[LookupChipType::BytecodeChip as usize] = build_tree_graph_and_witness( - graph_builder, - preds, - &leaf.circuit, - inner, - sources, - real_challenges, - instance_num_vars, - )?; - - let (input_pred, selector_pred, instance_num_vars) = construct_calldata_table_and_witness( - graph_builder, - program_input, - challenges, - real_challenges, - )?; - let (preds, sources) = pred_source( - LookupChipType::CalldataChip as usize, - input_pred, - selector_pred, - ); - tables_out[LookupChipType::CalldataChip as usize] = build_tree_graph_and_witness( - graph_builder, - preds, - &leaf.circuit, - inner, - sources, - real_challenges, - instance_num_vars, - )?; - - let leaf = &self.chip_circuit_gadgets.frac_sum_leaf_no_selector; - let mut preds_no_selector = |table_type, table_pred| { - let mut preds = vec![PredType::Source; 2]; - preds[leaf.input_den_id as usize] = table_pred; - let mut sources = vec![DenseMultilinearExtension::default(); 3]; - sources[leaf.input_num_id as usize] = mem::take(&mut table_count_witness[table_type]); - (preds, sources) - }; - let (input_pred, instance_num_vars) = construct_range_table_and_witness( - graph_builder, - RANGE_CHIP_BIT_WIDTH, - challenges, - real_challenges, - )?; - let (preds, sources) = preds_no_selector(LookupChipType::RangeChip as usize, input_pred); - tables_out[LookupChipType::RangeChip as usize] = build_tree_graph_and_witness( - graph_builder, - preds, - &leaf.circuit, - inner, - sources, - real_challenges, - instance_num_vars, - )?; - Ok(tables_out) - } - - /// Construct circuits to generate the lookup table for each table, including - /// bytecode, range and calldata. Also generate the tree-structured circuits to - /// fold the summation. - pub fn construct_lookup_table_graph( - &self, - graph_builder: &mut CircuitGraphBuilder, - byte_code_len: usize, - program_input_len: usize, - challenges: &ChipChallenges, - ) -> Result, UtilError> { - let mut tables_out = vec![NodeOutputType::OutputLayer(0); LookupChipType::iter().count()]; - - let leaf = &self.chip_circuit_gadgets.frac_sum_leaf; - let inner = &self.chip_circuit_gadgets.frac_sum_inner; - let compute_preds = |table_pred, selector_pred| { - let mut preds = vec![PredType::Source; 3]; - preds[leaf.input_den_id as usize] = table_pred; - preds[leaf.cond_id as usize] = selector_pred; - preds - }; - - let (input_pred, selector_pred, instance_num_vars) = - construct_bytecode_table(graph_builder, byte_code_len, challenges)?; - let preds = compute_preds(input_pred, selector_pred); - tables_out[LookupChipType::BytecodeChip as usize] = build_tree_graph( - graph_builder, - preds, - &leaf.circuit, - inner, - instance_num_vars, - )?; - - let (input_pred, selector_pred, instance_num_vars) = - construct_calldata_table(graph_builder, program_input_len, challenges)?; - let preds = compute_preds(input_pred, selector_pred); - tables_out[LookupChipType::CalldataChip as usize] = build_tree_graph( - graph_builder, - preds, - &leaf.circuit, - inner, - instance_num_vars, - )?; - - let leaf = &self.chip_circuit_gadgets.frac_sum_leaf_no_selector; - let compute_preds_no_selector = |table_pred| { - let mut preds = vec![PredType::Source; 2]; - preds[leaf.input_den_id as usize] = table_pred; - preds - }; - let (input_pred, instance_num_vars) = - construct_range_table(graph_builder, RANGE_CHIP_BIT_WIDTH, challenges)?; - let preds = compute_preds_no_selector(input_pred); - tables_out[LookupChipType::RangeChip as usize] = build_tree_graph( - graph_builder, - preds, - &leaf.circuit, - inner, - instance_num_vars, - )?; - Ok(tables_out) - } -} - -#[derive(Clone, Debug)] -pub struct ChipCircuitGadgets { - inv_sum: LeafCircuit, - frac_sum_inner: Arc>, - frac_sum_leaf: LeafFracSumCircuit, - frac_sum_leaf_no_selector: LeafFracSumNoSelectorCircuit, - product_inner: Arc>, - product_leaf: LeafCircuit, -} - -#[derive(Clone, Copy, Debug, EnumIter)] -pub enum LookupChipType { - BytecodeChip, - RangeChip, - CalldataChip, -} - -/// Generate the tree-structured circuit and witness to compute the product or -/// summation. `instance_num_vars` is corresponding to the leaves. -fn build_tree_graph_and_witness( - graph_builder: &mut CircuitGraphBuilder<'_, E>, - first_pred: Vec, - leaf: &Arc>, - inner: &Arc>, - first_source: Vec>, - real_challenges: &[E], - instance_num_vars: usize, -) -> Result { - let (last_pred, _) = - (0..=instance_num_vars).try_fold((first_pred, first_source), |(pred, source), i| { - let circuit = if i == 0 { leaf } else { inner }; - graph_builder - .add_node_with_witness( - "tree inner node", - circuit, - pred, - real_challenges.to_vec(), - source, - 1 << (instance_num_vars - i), - ) - .map(|id| { - ( - vec![PredType::PredWire(NodeOutputType::OutputLayer(id))], - vec![DenseMultilinearExtension::default()], - ) - }) - })?; - match last_pred[0] { - PredType::PredWire(out) => Ok(out), - _ => unreachable!(), - } -} - -/// Generate the tree-structured circuit to compute the product or summation. -/// `instance_num_vars` is corresponding to the leaves. -fn build_tree_graph( - graph_builder: &mut CircuitGraphBuilder, - first_pred: Vec, - leaf: &Arc>, - inner: &Arc>, - instance_num_vars: usize, -) -> Result { - let last_pred = (0..=instance_num_vars).try_fold(first_pred, |pred, i| { - let circuit = if i == 0 { leaf } else { inner }; - graph_builder - .add_node("tree inner node", circuit, pred) - .map(|id| vec![PredType::PredWire(NodeOutputType::OutputLayer(id))]) - })?; - match last_pred[0] { - PredType::PredWire(out) => Ok(out), - _ => unreachable!(), - } -} diff --git a/singer-utils/src/chips/bytecode.rs b/singer-utils/src/chips/bytecode.rs deleted file mode 100644 index 741eb2f92..000000000 --- a/singer-utils/src/chips/bytecode.rs +++ /dev/null @@ -1,113 +0,0 @@ -use std::sync::Arc; - -use ff_ext::ExtensionField; -use gkr::structs::Circuit; -use gkr_graph::structs::{CircuitGraphBuilder, NodeOutputType, PredType}; -use itertools::Itertools; -use multilinear_extensions::mle::IntoMLE; -use simple_frontend::structs::CircuitBuilder; -use sumcheck::util::ceil_log2; - -use crate::{ - chip_handler::{ChipHandler, bytecode::BytecodeChip}, - error::UtilError, - structs::{ChipChallenges, PCUInt}, -}; - -use super::ChipCircuitGadgets; - -fn construct_circuit(challenges: &ChipChallenges) -> Arc> { - let mut circuit_builder = CircuitBuilder::::default(); - let (_, pc_cells) = circuit_builder.create_witness_in(PCUInt::N_OPERAND_CELLS); - let (_, bytecode_cells) = circuit_builder.create_witness_in(1); - - let mut chip_handler = ChipHandler::new(*challenges); - - BytecodeChip::bytecode_with_pc_byte( - &mut chip_handler, - &mut circuit_builder, - &pc_cells, - bytecode_cells[0], - ); - let _ = chip_handler.finalize(&mut circuit_builder); - - circuit_builder.configure(); - Arc::new(Circuit::new(&circuit_builder)) -} - -/// Add bytecode table circuit and witness to the circuit graph. Return node id -/// and lookup instance log size. -pub(crate) fn construct_bytecode_table_and_witness( - builder: &mut CircuitGraphBuilder<'_, E>, - bytecode: &[u8], - challenges: &ChipChallenges, - real_challenges: &[E], -) -> Result<(PredType, PredType, usize), UtilError> { - let bytecode_circuit = construct_circuit(challenges); - let selector = ChipCircuitGadgets::construct_prefix_selector(bytecode.len(), 1); - - let selector_node_id = builder.add_node_with_witness( - "bytecode selector circuit", - &selector.circuit, - vec![], - real_challenges.to_vec(), - vec![], - bytecode.len().next_power_of_two(), - )?; - - let wits_in = vec![ - PCUInt::counter_vector::(bytecode.len().next_power_of_two()) - .into_iter() - .flatten() - .collect_vec() - .into_mle(), - { - let bytecode = bytecode - .iter() - .map(|x| E::BaseField::from(*x as u64)) - .collect_vec(); - bytecode.into_mle() - }, - ]; - - let table_node_id = builder.add_node_with_witness( - "bytecode table circuit", - &bytecode_circuit, - vec![PredType::Source; 2], - real_challenges.to_vec(), - wits_in, - bytecode.len().next_power_of_two(), - )?; - - Ok(( - PredType::PredWire(NodeOutputType::OutputLayer(table_node_id)), - PredType::PredWire(NodeOutputType::OutputLayer(selector_node_id)), - ceil_log2(bytecode.len()) - 1, - )) -} - -/// Add bytecode table circuit to the circuit graph. Return node id and lookup -/// instance log size. -pub(crate) fn construct_bytecode_table( - builder: &mut CircuitGraphBuilder, - bytecode_len: usize, - challenges: &ChipChallenges, -) -> Result<(PredType, PredType, usize), UtilError> { - let bytecode_circuit = construct_circuit(challenges); - let selector = ChipCircuitGadgets::construct_prefix_selector(bytecode_len, 1); - - let selector_node_id = - builder.add_node("bytecode selector circuit", &selector.circuit, vec![])?; - - let table_node_id = builder.add_node( - "bytecode table circuit", - &bytecode_circuit, - vec![PredType::Source; 2], - )?; - - Ok(( - PredType::PredWire(NodeOutputType::OutputLayer(table_node_id)), - PredType::PredWire(NodeOutputType::OutputLayer(selector_node_id)), - ceil_log2(bytecode_len) - 1, - )) -} diff --git a/singer-utils/src/chips/calldata.rs b/singer-utils/src/chips/calldata.rs deleted file mode 100644 index f0fdb6dbb..000000000 --- a/singer-utils/src/chips/calldata.rs +++ /dev/null @@ -1,129 +0,0 @@ -use std::sync::Arc; - -use crate::{ - error::UtilError, - structs::{ChipChallenges, StackUInt, UInt64}, -}; - -use super::ChipCircuitGadgets; -use crate::chip_handler::{ChipHandler, calldata::CalldataChip}; -use ff::Field; -use ff_ext::ExtensionField; -use gkr::structs::Circuit; -use gkr_graph::structs::{CircuitGraphBuilder, NodeOutputType, PredType}; -use itertools::Itertools; -use multilinear_extensions::mle::DenseMultilinearExtension; -use simple_frontend::structs::CircuitBuilder; -use sumcheck::util::ceil_log2; - -fn construct_circuit(challenges: &ChipChallenges) -> Arc> { - let mut circuit_builder = CircuitBuilder::::default(); - let (_, id_cells) = circuit_builder.create_witness_in(UInt64::N_OPERAND_CELLS); - let (_, calldata_cells) = circuit_builder.create_witness_in(StackUInt::N_OPERAND_CELLS); - - let mut chip_handler = ChipHandler::new(*challenges); - - CalldataChip::load( - &mut chip_handler, - &mut circuit_builder, - &id_cells, - &calldata_cells, - ); - - let _ = chip_handler.finalize(&mut circuit_builder); - - circuit_builder.configure(); - Arc::new(Circuit::new(&circuit_builder)) -} - -/// Add calldata table circuit and witness to the circuit graph. Return node id -/// and lookup instance log size. -pub(crate) fn construct_calldata_table_and_witness( - builder: &mut CircuitGraphBuilder, - program_input: &[u8], - challenges: &ChipChallenges, - real_challenges: &[E], -) -> Result<(PredType, PredType, usize), UtilError> { - let calldata_circuit = construct_circuit(challenges); - let selector = ChipCircuitGadgets::construct_prefix_selector(program_input.len(), 1); - - let selector_node_id = builder.add_node_with_witness( - "calldata selector circuit", - &selector.circuit, - vec![], - real_challenges.to_vec(), - vec![], - program_input.len().next_power_of_two(), - )?; - - let calldata = program_input - .iter() - .map(|x| E::BaseField::from(*x as u64)) - .collect_vec(); - - let wits_in = vec![ - { - let len = calldata.len().next_power_of_two(); - DenseMultilinearExtension::from_evaluations_vec( - ceil_log2(len), - (0..len).map(|x| E::BaseField::from(x as u64)).collect_vec(), - ) - }, - { - let len = calldata.len().next_power_of_two(); - let mut calldata = (0..calldata.len()) - .step_by(StackUInt::N_OPERAND_CELLS) - .flat_map(|i| { - calldata[i..(i + StackUInt::N_OPERAND_CELLS).min(calldata.len())] - .iter() - .cloned() - .rev() - .collect_vec() - }) - .collect_vec(); - calldata.resize(len, E::BaseField::ZERO); - DenseMultilinearExtension::from_evaluations_vec(ceil_log2(len), calldata) - }, - ]; - - let table_node_id = builder.add_node_with_witness( - "calldata table circuit", - &calldata_circuit, - vec![PredType::Source; 2], - real_challenges.to_vec(), - wits_in, - program_input.len().next_power_of_two(), - )?; - - Ok(( - PredType::PredWire(NodeOutputType::OutputLayer(table_node_id)), - PredType::PredWire(NodeOutputType::OutputLayer(selector_node_id)), - ceil_log2(program_input.len()) - 1, - )) -} - -/// Add calldata table circuit to the circuit graph. Return node id and lookup -/// instance log size. -pub(crate) fn construct_calldata_table( - builder: &mut CircuitGraphBuilder, - program_input_len: usize, - challenges: &ChipChallenges, -) -> Result<(PredType, PredType, usize), UtilError> { - let calldata_circuit = construct_circuit(challenges); - let selector = ChipCircuitGadgets::construct_prefix_selector(program_input_len, 1); - - let selector_node_id = - builder.add_node("calldata selector circuit", &selector.circuit, vec![])?; - - let table_node_id = builder.add_node( - "calldata table circuit", - &calldata_circuit, - vec![PredType::Source; 2], - )?; - - Ok(( - PredType::PredWire(NodeOutputType::OutputLayer(table_node_id)), - PredType::PredWire(NodeOutputType::OutputLayer(selector_node_id)), - ceil_log2(program_input_len) - 1, - )) -} diff --git a/singer-utils/src/chips/circuit_gadgets.rs b/singer-utils/src/chips/circuit_gadgets.rs deleted file mode 100644 index 07eb6da00..000000000 --- a/singer-utils/src/chips/circuit_gadgets.rs +++ /dev/null @@ -1,262 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; -use std::sync::Arc; - -use gkr::structs::Circuit; -use simple_frontend::structs::{CircuitBuilder, MixedCell, WitnessId}; - -use super::ChipCircuitGadgets; - -#[derive(Clone, Debug)] -pub(crate) struct PrefixSelectorCircuit { - pub(crate) circuit: Arc>, -} - -#[derive(Clone, Debug)] -pub(crate) struct LeafFracSumCircuit { - pub(crate) circuit: Arc>, - pub(crate) input_den_id: WitnessId, - pub(crate) input_num_id: WitnessId, - pub(crate) cond_id: WitnessId, -} - -#[derive(Clone, Debug)] -pub(crate) struct LeafFracSumNoSelectorCircuit { - pub(crate) circuit: Arc>, - pub(crate) input_den_id: WitnessId, - pub(crate) input_num_id: WitnessId, -} - -#[derive(Clone, Debug)] -pub(crate) struct LeafCircuit { - pub(crate) circuit: Arc>, - pub(crate) input_id: WitnessId, - pub(crate) cond_id: WitnessId, -} - -impl Default for ChipCircuitGadgets { - fn default() -> Self { - Self { - inv_sum: Self::construct_inv_sum(), - frac_sum_inner: Self::construct_frac_sum_inner(), - frac_sum_leaf: Self::construct_frac_sum_leaf(), - frac_sum_leaf_no_selector: Self::construct_frac_sum_leaf_no_selector(), - product_inner: Self::construct_product_inner(), - product_leaf: Self::construct_product_leaf(), - } - } -} - -impl ChipCircuitGadgets { - /// Construct a selector for n_instances and each instance contains `num` - /// items. `num` must be a power of 2. - pub(crate) fn construct_prefix_selector( - n_instances: usize, - num: usize, - ) -> PrefixSelectorCircuit { - assert_eq!(num, num.next_power_of_two()); - let mut circuit_builder = CircuitBuilder::::default(); - let _ = circuit_builder.create_constant_in(n_instances * num, 1); - circuit_builder.configure(); - PrefixSelectorCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - } - } - - /// Construct a circuit to compute the inverse sum of two extension field - /// elements. - /// Wire in 0: 2 extension field elements. - /// Wire in 1: 2-bit selector. - /// output layer: the denominator and the numerator. - pub(crate) fn construct_inv_sum() -> LeafCircuit { - let mut circuit_builder = CircuitBuilder::::default(); - let (input_id, input) = circuit_builder.create_ext_witness_in(2); - let (cond_id, cond) = circuit_builder.create_witness_in(2); - let output = circuit_builder.create_ext_cells(2); - // selector denominator 1 or input[0] or input[0] * input[1] - let den_mul = circuit_builder.create_ext_cell(); - circuit_builder.mul2_ext(&den_mul, &input[0], &input[1], E::BaseField::ONE); - let tmp = circuit_builder.create_ext_cell(); - circuit_builder.sel_mixed_and_ext( - &tmp, - &MixedCell::Constant(E::BaseField::ONE), - &input[0], - cond[0], - ); - circuit_builder.sel_ext(&output[0], &tmp, &den_mul, cond[1]); - - // select the numerator 0 or 1 or input[0] + input[1] - let den_add = circuit_builder.create_ext_cell(); - circuit_builder.add_ext(&den_add, &input[0], E::BaseField::ONE); - circuit_builder.add_ext(&den_add, &input[0], E::BaseField::ONE); - circuit_builder.sel_mixed_and_ext(&output[1], &cond[0].into(), &den_add, cond[1]); - - circuit_builder.configure(); - LeafCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - input_id, - cond_id, - } - } - - /// Construct a circuit to compute the sum of two fractions. The - /// denominators are on the extension field. The numerators are on the base - /// field. - /// Wire in 0: denominators, 2 extension field elements. - /// Wire in 1: numerators, 2 base field elements. - /// Wire in 2: 2-bit selector. - /// output layer: the denominator and the numerator. - pub(crate) fn construct_frac_sum_leaf() -> LeafFracSumCircuit { - let mut circuit_builder = CircuitBuilder::::default(); - let (input_den_id, input_den) = circuit_builder.create_ext_witness_in(2); - let (input_num_id, input_num) = circuit_builder.create_witness_in(2); - let (cond_id, cond) = circuit_builder.create_witness_in(2); - let output = circuit_builder.create_ext_cells(2); - // selector denominator, 1 or input_den[0] or input_den[0] * input_den[1] - let den_mul = circuit_builder.create_ext_cell(); - circuit_builder.mul2_ext(&den_mul, &input_den[0], &input_den[1], E::BaseField::ONE); - let tmp = circuit_builder.create_ext_cell(); - circuit_builder.sel_mixed_and_ext( - &tmp, - &MixedCell::Constant(E::BaseField::ONE), - &input_den[0], - cond[0], - ); - circuit_builder.sel_ext(&output[0], &tmp, &den_mul, cond[1]); - - // select the numerator, 0 or input_num[0] or input_den[0] * input_num[1] + input_num[0] * input_den[1] - let num = circuit_builder.create_ext_cell(); - circuit_builder.mul_ext_base(&num, &input_den[0], input_num[1], E::BaseField::ONE); - circuit_builder.mul_ext_base(&num, &input_den[1], input_num[0], E::BaseField::ONE); - let tmp = circuit_builder.create_cell(); - circuit_builder.sel_mixed( - tmp, - MixedCell::Constant(E::BaseField::ZERO), - input_num[0].into(), - cond[0], - ); - circuit_builder.sel_mixed_and_ext(&output[1], &tmp.into(), &num, cond[1]); - - circuit_builder.configure(); - LeafFracSumCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - input_den_id, - input_num_id, - cond_id, - } - } - - /// Construct a circuit to compute the sum of two fractions. The - /// denominators are on the extension field. The numerators are on the base - /// field. - /// Wire in 0: denominators, 2 extension field elements. - /// Wire in 1: numerators, 2 base field elements. - /// output layer: the denominator and the numerator. - pub(crate) fn construct_frac_sum_leaf_no_selector() -> LeafFracSumNoSelectorCircuit { - let mut circuit_builder = CircuitBuilder::::default(); - let (input_den_id, input_den) = circuit_builder.create_ext_witness_in(2); - let (input_num_id, input_num) = circuit_builder.create_witness_in(2); - let output = circuit_builder.create_ext_cells(2); - // denominator - circuit_builder.mul2_ext( - &output[0], // output_den - &input_den[0], - &input_den[1], - E::BaseField::ONE, - ); - - // numerator - circuit_builder.mul_ext_base( - &output[1], // output_num - &input_den[0], - input_num[1], - E::BaseField::ONE, - ); - circuit_builder.mul_ext_base( - &output[1], // output_num - &input_den[1], - input_num[0], - E::BaseField::ONE, - ); - - circuit_builder.configure(); - LeafFracSumNoSelectorCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - input_den_id, - input_num_id, - } - } - - /// Construct a circuit to compute the sum of two fractions. The - /// denominators and numerators are on the extension field - /// Wire in 0: denominators, 2 extension field elements. - /// Wire in 1: numerators, 2 extensuin field elements. - /// Wire out 0: the denominator. - /// Wire out 1: the numerator. - pub(crate) fn construct_frac_sum_inner() -> Arc> { - let mut circuit_builder = CircuitBuilder::::default(); - let (_, input) = circuit_builder.create_ext_witness_in(4); - let output = circuit_builder.create_ext_cells(2); - // denominator - circuit_builder.mul2_ext( - &output[0], // output_den - &input[0], // input_den[0] - &input[2], // input_den[1] - E::BaseField::ONE, - ); - - // numerator - circuit_builder.mul2_ext( - &output[1], // output_num - &input[0], // input_den[0] - &input[3], // input_num[1] - E::BaseField::ONE, - ); - circuit_builder.mul2_ext( - &output[1], // output_num - &input[2], // input_den[1] - &input[1], // input_num[0] - E::BaseField::ONE, - ); - - circuit_builder.configure(); - Arc::new(Circuit::new(&circuit_builder)) - } - - /// Construct a circuit to compute the product of two extension field elements. - pub(crate) fn construct_product_leaf() -> LeafCircuit { - let mut circuit_builder = CircuitBuilder::::default(); - let (input_id, input) = circuit_builder.create_ext_witness_in(2); - let (cond_id, sel) = circuit_builder.create_witness_in(2); - let output = circuit_builder.create_ext_cells(1); - // selector elements, 1 or input[0] or input[0] * input[1] - let mul = circuit_builder.create_ext_cell(); - circuit_builder.mul2_ext(&mul, &input[0], &input[1], E::BaseField::ONE); - let tmp = circuit_builder.create_ext_cell(); - circuit_builder.sel_mixed_and_ext( - &tmp, - &MixedCell::Constant(E::BaseField::ONE), - &input[0], - sel[0], - ); - circuit_builder.sel_ext(&output[0], &tmp, &mul, sel[1]); - - circuit_builder.configure(); - LeafCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - input_id, - cond_id, - } - } - - /// Construct a circuit to compute the product of two extension field elements. - pub(crate) fn construct_product_inner() -> Arc> { - let mut circuit_builder = CircuitBuilder::::default(); - let (_, input) = circuit_builder.create_ext_witness_in(2); - let output = circuit_builder.create_ext_cells(1); - circuit_builder.mul2_ext(&output[0], &input[0], &input[1], E::BaseField::ONE); - - circuit_builder.configure(); - Arc::new(Circuit::new(&circuit_builder)) - } -} diff --git a/singer-utils/src/chips/range.rs b/singer-utils/src/chips/range.rs deleted file mode 100644 index d1cd8d5ea..000000000 --- a/singer-utils/src/chips/range.rs +++ /dev/null @@ -1,67 +0,0 @@ -use std::sync::Arc; - -use ff_ext::ExtensionField; -use gkr::structs::Circuit; -use gkr_graph::structs::{CircuitGraphBuilder, NodeOutputType, PredType}; -use simple_frontend::structs::CircuitBuilder; - -use crate::{ - chip_handler::{ChipHandler, range::RangeChip}, - constants::RANGE_CHIP_BIT_WIDTH, - error::UtilError, - structs::ChipChallenges, -}; - -fn construct_circuit(challenges: &ChipChallenges) -> Arc> { - let mut circuit_builder = CircuitBuilder::::default(); - let cells = circuit_builder.create_counter_in(0); - - let mut chip_handler = ChipHandler::new(*challenges); - - RangeChip::range_check_table_item(&mut chip_handler, &mut circuit_builder, cells[0]); - - let _ = chip_handler.finalize(&mut circuit_builder); - - circuit_builder.configure(); - Arc::new(Circuit::new(&circuit_builder)) -} - -/// Add range table circuit and witness to the circuit graph. Return node id and -/// lookup instance log size. -pub(crate) fn construct_range_table_and_witness( - builder: &mut CircuitGraphBuilder<'_, E>, - bit_with: usize, - challenges: &ChipChallenges, - real_challenges: &[E], -) -> Result<(PredType, usize), UtilError> { - let range_circuit = construct_circuit(challenges); - - let table_node_id = builder.add_node_with_witness( - "range table circuit", - &range_circuit, - vec![], - real_challenges.to_vec(), - vec![], - 1 << RANGE_CHIP_BIT_WIDTH, - )?; - Ok(( - PredType::PredWire(NodeOutputType::OutputLayer(table_node_id)), - bit_with - 1, - )) -} - -/// Add range table circuit to the circuit graph. Return node id and lookup -/// instance log size. -pub(crate) fn construct_range_table( - builder: &mut CircuitGraphBuilder, - bit_with: usize, - challenges: &ChipChallenges, -) -> Result<(PredType, usize), UtilError> { - let range_circuit = construct_circuit(challenges); - - let table_node_id = builder.add_node("range table circuit", &range_circuit, vec![])?; - Ok(( - PredType::PredWire(NodeOutputType::OutputLayer(table_node_id)), - bit_with - 1, - )) -} diff --git a/singer-utils/src/constants.rs b/singer-utils/src/constants.rs deleted file mode 100644 index 7427f4a62..000000000 --- a/singer-utils/src/constants.rs +++ /dev/null @@ -1,31 +0,0 @@ -use strum_macros::EnumIter; - -pub const STACK_TOP_BIT_WIDTH: usize = 10; - -pub const BYTE_BIT_WIDTH: usize = 8; -pub const RANGE_CHIP_BIT_WIDTH: usize = 16; -pub const VALUE_BIT_WIDTH: usize = 32; -pub const EVM_STACK_BIT_WIDTH: usize = 256; -pub const EVM_STACK_BYTE_WIDTH: usize = EVM_STACK_BIT_WIDTH / 8; - -// opcode bytecode -#[derive(Debug, Clone, Copy, EnumIter)] -pub enum OpcodeType { - UNKNOWN = 0x00, - ADD = 0x01, - GT = 0x11, - CALLDATALOAD = 0x35, - POP = 0x50, - MSTORE = 0x52, - JUMP = 0x56, - JUMPI = 0x57, - JUMPDEST = 0x5b, - PUSH0 = 0x5F, - PUSH1 = 0x60, - DUP1 = 0x80, - DUP2 = 0x81, - SWAP1 = 0x90, - SWAP2 = 0x91, - SWAP4 = 0x93, - RETURN = 0xf3, -} diff --git a/singer-utils/src/error.rs b/singer-utils/src/error.rs deleted file mode 100644 index 2c17c10b0..000000000 --- a/singer-utils/src/error.rs +++ /dev/null @@ -1,16 +0,0 @@ -use gkr_graph::error::GKRGraphError; - -#[derive(Debug)] -pub enum UtilError { - ChipError, - ChipHandlerError, - // TODO: consider splitting this into smaller errors - UIntError(String), - GKRGraphError(GKRGraphError), -} - -impl From for UtilError { - fn from(error: GKRGraphError) -> Self { - Self::GKRGraphError(error) - } -} diff --git a/singer-utils/src/lib.rs b/singer-utils/src/lib.rs deleted file mode 100644 index 66b7a70e3..000000000 --- a/singer-utils/src/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![allow(incomplete_features)] -#![feature(generic_const_exprs)] - -pub mod chips; -pub mod constants; -pub mod error; -pub mod structs; -pub mod uint; - -#[macro_use] -pub mod macros; -pub mod chip_handler; diff --git a/singer-utils/src/macros.rs b/singer-utils/src/macros.rs deleted file mode 100644 index b869e3ee5..000000000 --- a/singer-utils/src/macros.rs +++ /dev/null @@ -1,125 +0,0 @@ -#[macro_export] -macro_rules! register_witness { - // phaseX_size() implementation - ($struct_name:ident, $($wire_name:ident { $($slice_name:ident => $length:expr),* }),*) => { - paste! { - impl $struct_name { - $( - #[inline] - pub fn [<$wire_name _ size>]() -> usize { - (0 $(+ $length)* as usize).next_power_of_two() - } - - register_witness!(@internal $wire_name, 0usize; $($slice_name => $length),*); - - #[inline] - pub fn [<$wire_name _ idxes_map>]() -> BTreeMap<&'static str, std::ops::Range> { - let mut map = BTreeMap::new(); - - $( - map.insert(stringify!([<$wire_name _ $slice_name>]), Self::[<$wire_name _ $slice_name>]()); - )* - - map - } - - )* - } - } - }; - - - ($struct_name:ident, $($wire_name:ident { $($slice_name:ident => $length:expr),* }),*) => { - paste! { - impl $struct_name { - $( - #[inline] - pub fn [<$wire_name _ size>]() -> usize { - (0 $(+ $length)* as usize).next_power_of_two() - } - - register_witness!(@internal $wire_name, 0usize; $($slice_name => $length),*); - - #[inline] - pub fn [<$wire_name _ idxes_map>]() -> BTreeMap<&'static str, std::ops::Range> { - let mut map = BTreeMap::new(); - - $( - map.insert(stringify!([<$wire_name _ $slice_name>]), Self::[<$wire_name _ $slice_name>]()); - )* - - map - } - - )* - } - } - }; - - (@internal $wire_name:ident, $offset:expr; $name:ident => $length:expr $(, $rest:ident => $rest_length:expr)*) => { - paste! { - pub fn [<$wire_name _ $name>]() -> std::ops::Range { - $offset..$offset + $length - } - - pub fn [<$wire_name _ $name _ str>]() -> &'static str { - stringify!([<$wire_name _ $name>]) - } - register_witness!(@internal $wire_name, $offset + $length; $($rest => $rest_length),*); - } - }; - - (@internal $wire_name:ident, $offset:expr;) => {}; -} - -#[macro_export] -macro_rules! register_multi_witness { - ($struct_name:ident, $($wire_name:ident($($wire_param:ident)*) { $($slice_name:ident$(($num:expr))? => $length:expr),* }),*) => { - paste! { - impl $struct_name { - $( - register_multi_witness!(@internal $wire_name($($wire_param)*), 0usize; $($slice_name$(($num))? => $length),*); - )* - } - } - }; - - ($struct_name:ident, $($wire_name:ident($($wire_param:ident)*) { $($slice_name:ident$(($num:expr))? => $length:expr),* }),*) => { - paste! { - impl $struct_name { - $( - register_multi_witness!(@internal $wire_name($($wire_param)*), 0usize; $($slice_name$(($num))? => $length),*); - )* - } - } - }; - - (@internal $wire_name:ident($($wire_param:ident)*), $offset:expr; $name:ident($num:expr) => $length:expr $(, $rest:ident$(($rest_num:expr))? => $rest_length:expr)*) => { - paste! { - #[inline] - pub fn [<$wire_name _ $name>](idx: usize$(, $wire_param: usize)*) -> std::ops::Range { - $offset + $length * idx..$offset + $length * (idx + 1) - } - register_multi_witness!(@internal $wire_name($($wire_param)*), $offset + $length * $num; $($rest$(($rest_num))? => $rest_length),*); - } - }; - - (@internal $wire_name:ident($($wire_param:ident)*), $offset:expr; $name:ident => $length:expr $(, $rest:ident$(($rest_num:expr))? => $rest_length:expr)*) => { - paste! { - #[inline] - pub fn [<$wire_name _ $name>]($($wire_param: usize)*) -> std::ops::Range { - $offset..$offset + $length - } - register_multi_witness!(@internal $wire_name($($wire_param)*), $offset + $length; $($rest$(($rest_num))? => $rest_length),*); - } - }; - - (@internal $wire_name:ident($($wire_param:ident)*), $offset:expr;) => { - paste! { - #[inline] - pub fn [<$wire_name _ size>]($($wire_param: usize)*) -> usize { - $offset.next_power_of_two() - } - } - }; -} diff --git a/singer-utils/src/structs.rs b/singer-utils/src/structs.rs deleted file mode 100644 index 33b351ee1..000000000 --- a/singer-utils/src/structs.rs +++ /dev/null @@ -1,43 +0,0 @@ -use simple_frontend::structs::ChallengeId; -use strum_macros::EnumIter; -use uint::UInt; - -use crate::{ - constants::{EVM_STACK_BIT_WIDTH, VALUE_BIT_WIDTH}, - uint, -}; - -#[derive(Clone, Debug, Copy, EnumIter)] -pub enum RAMType { - Stack, - Memory, - GlobalState, - Register, -} - -#[derive(Clone, Debug, Copy, EnumIter)] -pub enum ROMType { - Bytecode, - Calldata, - Range, -} - -#[derive(Clone, Copy, Debug, EnumIter)] -pub enum InstOutChipType { - RAMLoad, - RAMStore, - ROMInput, -} - -#[derive(Clone, Copy, Debug)] -pub struct ChipChallenges { - // Challenges for multiple-tuple chip records - pub(super) record_rlc: ChallengeId, - // Challenges for multiple-cell values - pub(super) record_item_rlc: ChallengeId, -} - -pub type UInt64 = UInt<64, VALUE_BIT_WIDTH>; -pub type PCUInt = UInt64; -pub type TSUInt = UInt<48, 48>; -pub type StackUInt = UInt<{ EVM_STACK_BIT_WIDTH }, { VALUE_BIT_WIDTH }>; diff --git a/singer-utils/src/uint.rs b/singer-utils/src/uint.rs deleted file mode 100644 index 649eaf352..000000000 --- a/singer-utils/src/uint.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod arithmetic; -mod cmp; -pub mod constants; -#[allow(clippy::module_inception)] -mod uint; -pub use uint::UInt; -pub mod util; -mod witness_extractors; diff --git a/singer-utils/src/uint/arithmetic.rs b/singer-utils/src/uint/arithmetic.rs deleted file mode 100644 index e908b28cc..000000000 --- a/singer-utils/src/uint/arithmetic.rs +++ /dev/null @@ -1,643 +0,0 @@ -use crate::{ - chip_handler::{ChipHandler, range::RangeChip}, - error::UtilError, - uint::uint::UInt, -}; -use ff::Field; -use ff_ext::ExtensionField; -use simple_frontend::structs::{CellId, CircuitBuilder}; - -impl UInt { - /// Little-endian addition. - /// Assumes users will check the correct range of the result themselves. - // Addition of A + B with limbs [a, b, c] and [d, e, f] respectively - // - // cell_modulo = 2^C - // addend_0 - a b c - // addend_1 - d e f - // -------------------------------------------------- - // result - (a + d) % 2^C (b + e) % 2^C (c + f) % 2^C - // carry - (a + d) // 2^C (b + e) // 2^C (c + f) % 2^C - // - // every limb in addend_0 and addend_1 exists in the range [0, ..., 2^C - 1] - // after summing two limb values, the result exists in [0, ..., 2^(C+1) - 2] - // the carry value is either 0 or 1, - // it cannot be >= 2 as that will require result value >= 2^(C+1) - // - // assuming result range check, there is a unique carry vector that makes all - // constraint pass. - // if a + b > max_cell_value then carry must be set to 1 (if not range check fails) - // if a + b <= max_cell_value then carry must be set to 0 (if not range check fails) - // - // NOTE: this function doesn't perform the required range check! - pub fn add_unsafe( - circuit_builder: &mut CircuitBuilder, - addend_0: &UInt, - addend_1: &UInt, - carry: &[CellId], - ) -> Result, UtilError> { - let result: UInt = circuit_builder - .create_cells(Self::N_OPERAND_CELLS) - .try_into()?; - - for i in 0..Self::N_OPERAND_CELLS { - let (a, b, result) = (addend_0.values[i], addend_1.values[i], result.values[i]); - - // result = a + b - overflow_carry + last_carry - circuit_builder.add(result, a, E::BaseField::ONE); - circuit_builder.add(result, b, E::BaseField::ONE); - Self::handle_carry(result, circuit_builder, i, carry); - } - - Ok(result) - } - - /// Little-endian addition. - pub fn add( - circuit_builder: &mut CircuitBuilder, - chip_handler: &mut ChipHandler, - addend_0: &UInt, - addend_1: &UInt, - witness: &[CellId], - ) -> Result, UtilError> { - let carry = Self::extract_carry_add(witness); - let range_values = Self::extract_range_values(witness); - let computed_result = Self::add_unsafe(circuit_builder, addend_0, addend_1, carry)?; - RangeChip::range_check_uint( - chip_handler, - circuit_builder, - &computed_result, - Some(range_values), - ) - } - - /// Add a constant value to a `UInt` instance - /// Assumes users will check the correct range of the result themselves. - pub fn add_const_unsafe( - circuit_builder: &mut CircuitBuilder, - addend_0: &UInt, - constant: E::BaseField, - carry: &[CellId], - ) -> Result, UtilError> { - let result: UInt = circuit_builder - .create_cells(Self::N_OPERAND_CELLS) - .try_into()?; - - // add constant to the first limb - circuit_builder.add_const(result.values[0], constant); - - // cascade carry - for i in 0..Self::N_OPERAND_CELLS { - let (a, result) = (addend_0.values[i], result.values[i]); - - circuit_builder.add(result, a, E::BaseField::ONE); - Self::handle_carry(result, circuit_builder, i, carry); - } - - Ok(result) - } - - /// Add a constant value to a `UInt` instance - pub fn add_const( - circuit_builder: &mut CircuitBuilder, - chip_handler: &mut ChipHandler, - addend_0: &UInt, - constant: E::BaseField, - witness: &[CellId], - ) -> Result, UtilError> { - let carry = Self::extract_carry_add(witness); - let range_values = Self::extract_range_values(witness); - let computed_result = Self::add_const_unsafe(circuit_builder, addend_0, constant, carry)?; - RangeChip::range_check_uint( - chip_handler, - circuit_builder, - &computed_result, - Some(range_values), - ) - } - - /// Add a constant value to a `UInt` instance - /// Assumes that addition leads to no overflow. - pub fn add_const_no_overflow( - circuit_builder: &mut CircuitBuilder, - chip_handler: &mut ChipHandler, - addend_0: &UInt, - constant: E::BaseField, - witness: &[CellId], - ) -> Result, UtilError> { - let carry = Self::extract_carry_no_overflow_add(witness); - let range_values = Self::extract_range_values_no_overflow(witness); - let computed_result = Self::add_const_unsafe(circuit_builder, addend_0, constant, carry)?; - RangeChip::range_check_uint( - chip_handler, - circuit_builder, - &computed_result, - Some(range_values), - ) - } - - /// Adds a single cell value to a `UInt` instance - /// Assumes users will check the correct range of the result and - pub fn add_cell_unsafe( - circuit_builder: &mut CircuitBuilder, - addend_0: &UInt, - addend_1: CellId, - carry: &[CellId], - ) -> Result, UtilError> { - let result: UInt = circuit_builder - .create_cells(Self::N_OPERAND_CELLS) - .try_into()?; - - // add small_value to the first limb - circuit_builder.add(result.values[0], addend_1, E::BaseField::ONE); - - // cascade carry - for i in 0..Self::N_OPERAND_CELLS { - let (a, result) = (addend_0.values[i], result.values[i]); - - circuit_builder.add(result, a, E::BaseField::ONE); - Self::handle_carry(result, circuit_builder, i, carry); - } - - Ok(result) - } - - /// Adds a single cell value to a `UInt` instance - pub fn add_cell( - circuit_builder: &mut CircuitBuilder, - chip_handler: &mut ChipHandler, - addend_0: &UInt, - addend_1: CellId, - witness: &[CellId], - ) -> Result, UtilError> { - let carry = Self::extract_carry_add(witness); - let range_values = Self::extract_range_values(witness); - let computed_result = Self::add_cell_unsafe(circuit_builder, addend_0, addend_1, carry)?; - RangeChip::range_check_uint( - chip_handler, - circuit_builder, - &computed_result, - Some(range_values), - ) - } - - /// Adds a single cell value to a `UInt` instance - /// Assumes that addition lead to no overflow. - pub fn add_cell_no_overflow( - circuit_builder: &mut CircuitBuilder, - chip_handler: &mut ChipHandler, - addend_0: &UInt, - addend_1: CellId, - witness: &[CellId], - ) -> Result, UtilError> { - let carry = Self::extract_carry_no_overflow_add(witness); - let range_values = Self::extract_range_values_no_overflow(witness); - let computed_result = Self::add_cell_unsafe(circuit_builder, addend_0, addend_1, carry)?; - RangeChip::range_check_uint( - chip_handler, - circuit_builder, - &computed_result, - Some(range_values), - ) - } - - /// Little endian subtraction - /// Assumes users will check the correct range of the result themselves. - pub fn sub_unsafe( - circuit_builder: &mut CircuitBuilder, - minuend: &UInt, - subtrahend: &UInt, - borrow: &[CellId], - ) -> Result, UtilError> { - let result: UInt = circuit_builder - .create_cells(Self::N_OPERAND_CELLS) - .try_into()?; - - for i in 0..Self::N_OPERAND_CELLS { - let (minuend, subtrahend, result) = - (minuend.values[i], subtrahend.values[i], result.values[i]); - - circuit_builder.add(result, minuend, E::BaseField::ONE); - circuit_builder.add(result, subtrahend, -E::BaseField::ONE); - - Self::handle_borrow(result, circuit_builder, i, borrow); - } - - Ok(result) - } - - /// Little endian subtraction - pub fn sub( - circuit_builder: &mut CircuitBuilder, - chip_handler: &mut ChipHandler, - minuend: &UInt, - subtrahend: &UInt, - witness: &[CellId], - ) -> Result, UtilError> { - let borrow = Self::extract_borrow_sub(witness); - let range_values = Self::extract_range_values(witness); - let computed_result = Self::sub_unsafe(circuit_builder, minuend, subtrahend, borrow)?; - RangeChip::range_check_uint( - chip_handler, - circuit_builder, - &computed_result, - Some(range_values), - ) - } - - /// Modify addition result based on carry instructions - fn handle_carry( - result_cell_id: CellId, - circuit_builder: &mut CircuitBuilder, - limb_index: usize, - carry: &[CellId], - ) { - // overflow carry - // represents the portion of the result that should move to the next operation - // inorder to keep the value <= C bits - // carry[i] = (addend_0[i] + addend_1[i]) % 2^C - - // last carry - // represents the carry that was passed from the previous operation - // this carry should be added to the current result - // carry[i - 1] = (addend_0[i - 1] + addend_1[i - 1]) % 2^C - - if limb_index > carry.len() { - return; - } - - // handle overflow carry - // we need to subtract the carry value from the current result - if limb_index < carry.len() { - circuit_builder.add( - result_cell_id, - carry[limb_index], - -E::BaseField::from(1 << C), - ); - } - - // handle last operation carry - // we need to add this to the current result - if limb_index > 0 { - circuit_builder.add(result_cell_id, carry[limb_index - 1], E::BaseField::ONE); - } - } - - /// Modify subtraction result based on borrow instructions - fn handle_borrow( - result_cell_id: CellId, - circuit_builder: &mut CircuitBuilder, - limb_index: usize, - borrow: &[CellId], - ) { - // borrow - // represents the portion of the result that should move from the - // next operation to the current operation i.e. reduce the result - // of the operation to come - // this should be added to the current result - // = borrow[i] - - // last borrow - // represents the portion of the current result that was moved during - // the previous computation - // this should be removed from the current result - - if limb_index > borrow.len() { - return; - } - - // handle borrow - // we need to add borrow units of C to the result - if limb_index < borrow.len() { - circuit_builder.add( - result_cell_id, - borrow[limb_index], - E::BaseField::from(1 << C), - ); - } - - // handle last borrow - // we need to remove this from the current result - if limb_index > 0 { - circuit_builder.add(result_cell_id, borrow[limb_index - 1], -E::BaseField::ONE); - } - } -} - -// #[cfg(test)] -// mod tests { -// use crate::uint::{constants::AddSubConstants, UInt}; -// use gkr::structs::{Circuit, CircuitWitness}; -// use goldilocks::{Goldilocks, GoldilocksExt2}; -// use itertools::Itertools; -// use simple_frontend::structs::CircuitBuilder; - -// #[test] -// fn test_add_unsafe() { -// // UInt<20, 5> (4 limbs) - -// // A (big-endian representation) -// // 01001 | 10100 | 11010 | 11110 - -// // B (big-endian representation) -// // 00101 | 01010 | 10110 | 10000 - -// // A + B -// // big endian and represented as field elements -// // 9 | 20 | 26 | 30 -// // 5 | 10 | 22 | 16 -// // result 14 | 31 | 17 | 14 -// // carry 0 | 0 | 1 | 1 - -// // build the circuit -// type UInt20 = UInt<20, 5>; -// let mut circuit_builder = CircuitBuilder::::new(); - -// // input wires -// // addend_0, addend_1, carry -// let (addend_0_id, addend_0_cells) = -// circuit_builder.create_witness_in(UInt20::N_OPERAND_CELLS); -// let (addend_1_id, addend_1_cells) = -// circuit_builder.create_witness_in(UInt20::N_OPERAND_CELLS); -// let (carry_id, carry_cells) = -// circuit_builder.create_witness_in(AddSubConstants::::N_CARRY_CELLS); - -// let addend_0 = UInt20::try_from(addend_0_cells).expect("should build uint"); -// let addend_1 = UInt20::try_from(addend_1_cells).expect("should build uint"); - -// // update circuit builder with circuit instructions -// let result = -// UInt20::add_unsafe(&mut circuit_builder, &addend_0, &addend_1, &carry_cells).unwrap(); -// circuit_builder.configure(); -// let circuit = Circuit::new(&circuit_builder); - -// // generate witness -// // calling rev() to make things little endian representation -// let addend_0_witness = vec![9, 20, 26, 30] -// .into_iter() -// .rev() -// .map(|v| Goldilocks::from(v)) -// .collect_vec(); -// let addend_1_witness = vec![5, 10, 22, 16] -// .into_iter() -// .rev() -// .map(|v| Goldilocks::from(v)) -// .collect_vec(); -// let carry_witness = vec![0, 0, 1, 1] -// .into_iter() -// .rev() -// .map(|v| Goldilocks::from(v)) -// .collect_vec(); - -// let mut wires_in = vec![vec![]; circuit.n_witness_in]; -// wires_in[addend_0_id as usize] = addend_0_witness; -// wires_in[addend_1_id as usize] = addend_1_witness; -// wires_in[carry_id as usize] = carry_witness; - -// let circuit_witness = { -// let challenges = vec![GoldilocksExt2::from(2)]; -// let mut circuit_witness = CircuitWitness::new(&circuit, challenges); -// circuit_witness.add_instance(&circuit, wires_in); -// circuit_witness -// }; - -// circuit_witness.check_correctness(&circuit); - -// // check the result correctness -// let result_values = circuit_witness.output_layer_witness_ref().instances[0].to_vec(); -// assert_eq!( -// result_values, -// [14, 17, 31, 14] -// .into_iter() -// .map(|v| Goldilocks::from(v)) -// .collect_vec() -// ); -// } - -// #[test] -// fn test_add_constant_unsafe() { -// // UInt<20, 5> (4 limbs) - -// // A + constant -// // A = 14 | 31 | 28 | 14 -// // constant = 200 -// // big endian and represented as field elements -// // 14 | 31 | 28 | 14 -// // | | | 200 -// // result 15 | 0 | 2 | 22 -// // carry 0 | 1 | 1 | 6 - -// type UInt20 = UInt<20, 5>; -// let mut circuit_builder = CircuitBuilder::::new(); - -// // input wires -// // addend_0, carry, constant -// let (addend_0_id, addend_0_cells) = -// circuit_builder.create_witness_in(UInt20::N_OPERAND_CELLS); -// let (carry_id, carry_cells) = -// circuit_builder.create_witness_in(AddSubConstants::::N_CARRY_CELLS); - -// let addend_0 = UInt20::try_from(addend_0_cells).expect("should build uint"); - -// // update circuit builder -// let result = UInt20::add_const_unsafe( -// &mut circuit_builder, -// &addend_0, -// Goldilocks::from(200), -// &carry_cells, -// ) -// .unwrap(); -// circuit_builder.configure(); -// let circuit = Circuit::new(&circuit_builder); - -// // generate witness -// // calling rev() to make things little endian representation -// let addend_0_witness = vec![14, 31, 28, 14] -// .into_iter() -// .rev() -// .map(|v| Goldilocks::from(v)) -// .collect_vec(); -// let carry_witness = vec![0, 1, 1, 6] -// .into_iter() -// .rev() -// .map(|v| Goldilocks::from(v)) -// .collect_vec(); - -// let mut wires_in = vec![vec![]; circuit.n_witness_in]; -// wires_in[addend_0_id as usize] = addend_0_witness; -// wires_in[carry_id as usize] = carry_witness; - -// let circuit_witness = { -// let challenges = vec![GoldilocksExt2::from(2)]; -// let mut circuit_witness = CircuitWitness::new(&circuit, challenges); -// circuit_witness.add_instance(&circuit, wires_in); -// circuit_witness -// }; - -// circuit_witness.check_correctness(&circuit); - -// // check the result correctness -// let result_values = circuit_witness.output_layer_witness_ref().instances[0].to_vec(); -// assert_eq!( -// result_values, -// [22, 2, 0, 15] -// .into_iter() -// .map(|v| Goldilocks::from(v)) -// .collect_vec() -// ); -// } - -// #[test] -// fn test_add_small_unsafe() { -// // UInt<20, 5> (4 limbs) - -// // A + constant -// // A = 14 | 31 | 28 | 14 -// // small = 200 // TODO: fix this should be < 32 -// // big endian and represented as field elements -// // 14 | 31 | 28 | 14 -// // | | | 200 -// // result 15 | 0 | 2 | 22 -// // carry 0 | 1 | 1 | 6 - -// type UInt20 = UInt<20, 5>; -// let mut circuit_builder = CircuitBuilder::::new(); - -// // input wires -// // addend_0, carry, constant -// let (addend_0_id, addend_0_cells) = -// circuit_builder.create_witness_in(UInt20::N_OPERAND_CELLS); -// let (small_value_id, small_value_cell) = circuit_builder.create_witness_in(1); -// let (carry_id, carry_cells) = -// circuit_builder.create_witness_in(AddSubConstants::::N_CARRY_CELLS); - -// let addend_0 = UInt20::try_from(addend_0_cells).expect("should build uint"); - -// // update circuit builder -// let result = UInt20::add_cell_unsafe( -// &mut circuit_builder, -// &addend_0, -// small_value_cell[0], -// &carry_cells, -// ) -// .unwrap(); -// circuit_builder.configure(); -// let circuit = Circuit::new(&circuit_builder); - -// // generate witness -// // calling rev() to make things little endian representation -// let addend_0_witness = vec![14, 31, 28, 14] -// .into_iter() -// .rev() -// .map(|v| Goldilocks::from(v)) -// .collect_vec(); -// let small_value_witness = vec![200] -// .into_iter() -// .map(|v| Goldilocks::from(v)) -// .collect_vec(); -// let carry_witness = vec![0, 1, 1, 6] -// .into_iter() -// .rev() -// .map(|v| Goldilocks::from(v)) -// .collect_vec(); - -// let mut wires_in = vec![vec![]; circuit.n_witness_in]; -// wires_in[addend_0_id as usize] = addend_0_witness; -// wires_in[small_value_id as usize] = small_value_witness; -// wires_in[carry_id as usize] = carry_witness; - -// let circuit_witness = { -// let challenges = vec![GoldilocksExt2::from(2)]; -// let mut circuit_witness = CircuitWitness::new(&circuit, challenges); -// circuit_witness.add_instance(&circuit, wires_in); -// circuit_witness -// }; - -// circuit_witness.check_correctness(&circuit); - -// // check the result correctness -// let result_values = circuit_witness.output_layer_witness_ref().instances[0].to_vec(); -// assert_eq!( -// result_values, -// [22, 2, 0, 15] -// .into_iter() -// .map(|v| Goldilocks::from(v)) -// .collect_vec() -// ); -// } - -// #[test] -// fn test_sub_unsafe() { -// // A - B -// // big endian and represented as field elements -// // 9 | 20 | 26 | 30 -// // 5 | 30 | 28 | 10 -// // result 3 | 21 | 30 | 20 -// // borrow 0 | 1 | 1 | 0 - -// // build the circuit -// type UInt20 = UInt<20, 5>; -// let mut circuit_builder = CircuitBuilder::::new(); - -// // input wires -// // minuend, subtrahend, borrow -// let (minuend_id, minuend_cells) = -// circuit_builder.create_witness_in(UInt20::N_OPERAND_CELLS); -// let (subtrahend_id, subtrahend_cells) = -// circuit_builder.create_witness_in(UInt20::N_OPERAND_CELLS); -// // |Carry| == |Borrow| -// let (borrow_id, borrow_cells) = -// circuit_builder.create_witness_in(AddSubConstants::::N_CARRY_CELLS); - -// let minuend = UInt20::try_from(minuend_cells).expect("should build uint"); -// let subtrahend = UInt20::try_from(subtrahend_cells).expect("should build uint"); - -// // update the circuit builder -// let result = -// UInt20::sub_unsafe(&mut circuit_builder, &minuend, &subtrahend, &borrow_cells).unwrap(); -// circuit_builder.configure(); -// let circuit = Circuit::new(&circuit_builder); - -// // generate witness -// // calling rev() to make things little endian representation -// let minuend_witness = vec![9, 20, 26, 30] -// .into_iter() -// .rev() -// .map(|v| Goldilocks::from(v)) -// .collect_vec(); -// let subtrahend_witness = vec![5, 30, 28, 10] -// .into_iter() -// .rev() -// .map(|v| Goldilocks::from(v)) -// .collect(); -// let borrow_witness = vec![0, 1, 1, 0] -// .into_iter() -// .rev() -// .map(|v| Goldilocks::from(v)) -// .collect_vec(); - -// let mut wires_in = vec![vec![]; circuit.n_witness_in]; -// wires_in[minuend_id as usize] = minuend_witness; -// wires_in[subtrahend_id as usize] = subtrahend_witness; -// wires_in[borrow_id as usize] = borrow_witness; - -// let circuit_witness = { -// let challenges = vec![GoldilocksExt2::from(2)]; -// let mut circuit_witness = CircuitWitness::new(&circuit, challenges); -// circuit_witness.add_instance(&circuit, wires_in); -// circuit_witness -// }; - -// circuit_witness.check_correctness(&circuit); - -// // check the result correctness -// let result_values = circuit_witness.output_layer_witness_ref().instances[0].to_vec(); -// assert_eq!( -// result_values, -// [20, 30, 21, 3] -// .into_iter() -// .map(|v| Goldilocks::from(v)) -// .collect_vec() -// ); -// } -// } diff --git a/singer-utils/src/uint/cmp.rs b/singer-utils/src/uint/cmp.rs deleted file mode 100644 index 5e668fc34..000000000 --- a/singer-utils/src/uint/cmp.rs +++ /dev/null @@ -1,112 +0,0 @@ -use crate::{ - chip_handler::{ChipHandler, range::RangeChip}, - error::UtilError, - uint::{constants::AddSubConstants, uint::UInt}, -}; -use ff::Field; -use ff_ext::ExtensionField; -use simple_frontend::structs::{CellId, CircuitBuilder, MixedCell}; - -impl UInt { - /// Generates the required information for asserting lt and leq - pub fn lt( - circuit_builder: &mut CircuitBuilder, - chip_handler: &mut ChipHandler, - operand_0: &UInt, - operand_1: &UInt, - witness: &[CellId], - ) -> Result<(CellId, UInt), UtilError> { - let borrow = Self::extract_borrow_sub(witness); - let range_values = Self::extract_range_values(witness); - let computed_diff = Self::sub_unsafe(circuit_builder, operand_0, operand_1, borrow)?; - - let diff = RangeChip::range_check_uint( - chip_handler, - circuit_builder, - &computed_diff, - Some(range_values), - )?; - - // if operand_0 < operand_1, the last borrow should equal 1 - if borrow.len() == AddSubConstants::::N_CARRY_CELLS { - Ok((borrow[AddSubConstants::::N_CARRY_CELLS - 1], diff)) - } else { - Ok((circuit_builder.create_cell(), diff)) - } - } - - /// Asserts that operand_0 < operand_1 - pub fn assert_lt( - circuit_builder: &mut CircuitBuilder, - chip_handler: &mut ChipHandler, - operand_0: &UInt, - operand_1: &UInt, - witness: &[CellId], - ) -> Result<(), UtilError> { - let (borrow, _) = Self::lt(circuit_builder, chip_handler, operand_0, operand_1, witness)?; - circuit_builder.assert_const(borrow, 1); - Ok(()) - } - - /// Asserts that operand_0 <= operand_1 - pub fn assert_leq( - circuit_builder: &mut CircuitBuilder, - chip_handler: &mut ChipHandler, - operand_0: &UInt, - operand_1: &UInt, - witness: &[CellId], - ) -> Result<(), UtilError> { - let (borrow, diff) = - Self::lt(circuit_builder, chip_handler, operand_0, operand_1, witness)?; - - // we have two scenarios - // 1. eq - // in this case, borrow = 0 and diff = [0, ..., 0] - // 2. lt - // in this case, borrow = 1 and diff = [..field_elements..] - // we check for both cases with the following - // if borrow == 0 return diff else return 0 - // then assert that the returned item = 0 - - let diff_values = diff.values(); - for d in diff_values.iter() { - let s = circuit_builder.create_cell(); - circuit_builder.sel_mixed( - s, - (*d).into(), - MixedCell::Constant(E::BaseField::ZERO), - borrow, - ); - circuit_builder.assert_const(s, 0); - } - - Ok(()) - } - - /// Asserts that two `UInt` instances represent equal value - pub fn assert_eq( - circuit_builder: &mut CircuitBuilder, - operand_0: &UInt, - operand_1: &UInt, - ) -> Result<(), UtilError> { - let diff = circuit_builder.create_cells(Self::N_OPERAND_CELLS); - let operand_0_cells = operand_0.values(); - let operand_1_cells = operand_1.values(); - for i in 0..Self::N_OPERAND_CELLS { - circuit_builder.add(diff[i], operand_0_cells[i], E::BaseField::ONE); - circuit_builder.add(diff[i], operand_1_cells[i], -E::BaseField::ONE); - circuit_builder.assert_const(diff[i], 0); - } - Ok(()) - } - - /// Asserts that a `UInt` instance and a set of range cells represent equal value - pub fn assert_eq_range_values( - circuit_builder: &mut CircuitBuilder, - operand_0: &UInt, - operand_1: &[CellId], - ) -> Result<(), UtilError> { - let range_as_uint = UInt::from_range_values(circuit_builder, operand_1)?; - Self::assert_eq(circuit_builder, operand_0, &range_as_uint) - } -} diff --git a/singer-utils/src/uint/constants.rs b/singer-utils/src/uint/constants.rs deleted file mode 100644 index 969ac14fe..000000000 --- a/singer-utils/src/uint/constants.rs +++ /dev/null @@ -1,69 +0,0 @@ -use super::uint::UInt; -use crate::{constants::RANGE_CHIP_BIT_WIDTH, uint::util::const_min}; -use std::marker::PhantomData; - -impl UInt { - pub const M: usize = M; - pub const C: usize = C; - - /// Determines the maximum number of bits that should be represented in each cell - /// independent of the cell capacity `C`. - /// If M < C i.e. total bit < cell capacity, the maximum_usable_cell_capacity - /// is actually M. - /// but if M >= C then maximum_usable_cell_capacity = C - pub const MAX_CELL_BIT_WIDTH: usize = const_min(M, C); - - /// `N_OPERAND_CELLS` represent the minimum number of cells each of size `C` needed - /// to hold `M` total bits - pub const N_OPERAND_CELLS: usize = M.div_ceil(Self::MAX_CELL_BIT_WIDTH); - - /// The number of `RANGE_CHIP_BIT_WIDTH` cells needed to represent one cell of size `C` - pub const N_RANGE_CELLS_PER_CELL: usize = - Self::MAX_CELL_BIT_WIDTH.div_ceil(RANGE_CHIP_BIT_WIDTH); - - /// The number of `RANGE_CHIP_BIT_WIDTH` cells needed to represent the entire `UInt` - pub const N_RANGE_CELLS: usize = Self::N_OPERAND_CELLS * Self::N_RANGE_CELLS_PER_CELL; -} - -/// Holds addition specific constants -pub struct AddSubConstants { - _marker: PhantomData, -} - -impl AddSubConstants> { - /// Number of cells required to track carry information for the addition operation. - /// operand_0 = a b c - /// operand_1 = e f g - /// ---------- - /// result = h i j - /// carry = k l m - - /// |Carry| = |Cells| - pub const N_CARRY_CELLS: usize = UInt::::N_OPERAND_CELLS; - - /// Number of cells required to track carry information if we assume the addition - /// operation cannot lead to overflow. - /// operand_0 = a b c - /// operand_1 = e f g - /// ---------- - /// result = h i j - /// carry = l m - - /// |Carry| = |Cells - 1| - const N_CARRY_CELLS_NO_OVERFLOW: usize = Self::N_CARRY_CELLS - 1; - - /// The size of the witness - pub const N_WITNESS_CELLS: usize = UInt::::N_RANGE_CELLS + Self::N_CARRY_CELLS; - - /// The size of the witness assuming carry has no overflow - /// |Range_values| + |Carry - 1| - pub const N_WITNESS_CELLS_NO_CARRY_OVERFLOW: usize = - UInt::::N_RANGE_CELLS + Self::N_CARRY_CELLS_NO_OVERFLOW; - - pub const N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS: usize = Self::N_CARRY_CELLS_NO_OVERFLOW; - - /// The number of `RANGE_CHIP_BIT_WIDTH` cells needed to represent the carry cells, assuming - /// no overflow. - // TODO: if guaranteed no overflow, then we don't need to range check the highest limb - // hence this can be (N_OPERANDS - 1) * N_RANGE_CELLS_PER_CELL - // update this once, range check logic doesn't assume all limbs - pub const N_RANGE_CELLS_NO_OVERFLOW: usize = UInt::::N_RANGE_CELLS; -} diff --git a/singer-utils/src/uint/uint.rs b/singer-utils/src/uint/uint.rs deleted file mode 100644 index af310c395..000000000 --- a/singer-utils/src/uint/uint.rs +++ /dev/null @@ -1,259 +0,0 @@ -use crate::{ - constants::{BYTE_BIT_WIDTH, RANGE_CHIP_BIT_WIDTH}, - error::UtilError, - uint::util::{add_one_to_big_num, convert_decomp, pad_cells}, -}; -use ff_ext::ExtensionField; -use goldilocks::SmallField; -use simple_frontend::structs::{CellId, CircuitBuilder}; -use sumcheck::util::ceil_log2; - -#[derive(Clone)] -/// Unsigned integer with `M` total bits. `C` denotes the cell bit width. -/// Represented in little endian form. -pub struct UInt { - pub values: Vec, -} - -impl UInt { - /// Return the `UInt` underlying cell id's - pub fn values(&self) -> &[CellId] { - &self.values - } - - /// Builds a `UInt` instance from a set of cells that represent `RANGE_VALUES` - /// assumes range_values are represented in little endian form - pub fn from_range_values( - circuit_builder: &mut CircuitBuilder, - range_values: &[CellId], - ) -> Result { - Self::from_different_sized_cell_values( - circuit_builder, - range_values, - RANGE_CHIP_BIT_WIDTH, - true, - ) - } - - /// Builds a `UInt` instance from a set of cells that represent big-endian `BYTE_VALUES` - pub fn from_bytes_big_endian( - circuit_builder: &mut CircuitBuilder, - bytes: &[CellId], - ) -> Result { - Self::from_bytes(circuit_builder, bytes, false) - } - - /// Builds a `UInt` instance from a set of cells that represent little-endian `BYTE_VALUES` - pub fn from_bytes_little_endian( - circuit_builder: &mut CircuitBuilder, - bytes: &[CellId], - ) -> Result { - Self::from_bytes(circuit_builder, bytes, true) - } - - /// Builds a `UInt` instance from a set of cells that represent `BYTE_VALUES` - pub fn from_bytes( - circuit_builder: &mut CircuitBuilder, - bytes: &[CellId], - is_little_endian: bool, - ) -> Result { - Self::from_different_sized_cell_values( - circuit_builder, - bytes, - BYTE_BIT_WIDTH, - is_little_endian, - ) - } - - /// Builds a `UInt` instance from a set of cell values of a certain `CELL_WIDTH` - fn from_different_sized_cell_values( - circuit_builder: &mut CircuitBuilder, - cell_values: &[CellId], - cell_width: usize, - is_little_endian: bool, - ) -> Result { - let mut values = convert_decomp( - circuit_builder, - cell_values, - cell_width, - Self::MAX_CELL_BIT_WIDTH, - is_little_endian, - )?; - debug_assert!(values.len() <= Self::N_OPERAND_CELLS); - pad_cells(circuit_builder, &mut values, Self::N_OPERAND_CELLS); - values.try_into() - } - - /// Generate ((0)_{2^C}, (1)_{2^C}, ..., (size - 1)_{2^C}) - pub fn counter_vector(size: usize) -> Vec> { - let num_vars = ceil_log2(size); - let number_of_limbs = num_vars.div_ceil(C); - let cell_modulo = F::from(1 << C); - - let mut res = vec![vec![F::ZERO; number_of_limbs]]; - - for i in 1..size { - res.push(add_one_to_big_num(cell_modulo, &res[i - 1])); - } - - res - } -} - -/// Construct `UInt` from `Vec` -impl TryFrom> for UInt { - type Error = UtilError; - - fn try_from(values: Vec) -> Result { - if values.len() != Self::N_OPERAND_CELLS { - return Err(UtilError::UIntError(format!( - "cannot construct UInt<{}, {}> from {} cells, requires {} cells", - M, - C, - values.len(), - Self::N_OPERAND_CELLS - ))); - } - - Ok(Self { values }) - } -} - -/// Construct `UInt` from `$[CellId]` -impl TryFrom<&[CellId]> for UInt { - type Error = UtilError; - - fn try_from(values: &[CellId]) -> Result { - values.to_vec().try_into() - } -} - -// #[cfg(test)] -// mod tests { -// use crate::uint::uint::UInt; -// use gkr::structs::{Circuit, CircuitWitness}; -// use goldilocks::{Goldilocks, GoldilocksExt2}; -// use itertools::Itertools; -// use simple_frontend::structs::CircuitBuilder; - -// #[test] -// fn test_uint_from_cell_ids() { -// // 33 total bits and each cells holds just 4 bits -// // to hold all 33 bits without truncations, we'd need 9 cells -// // 9 * 4 = 36 > 33 -// type UInt33 = UInt<33, 4>; -// assert!(UInt33::try_from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).is_ok()); -// assert!(UInt33::try_from(vec![1, 2, 3]).is_err()); -// assert!(UInt33::try_from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).is_err()); -// } - -// #[test] -// fn test_uint_from_different_sized_cell_values() { -// // build circuit -// let mut circuit_builder = CircuitBuilder::::new(); -// let (_, small_values) = circuit_builder.create_witness_in(8); -// type UInt30 = UInt<30, 6>; -// let uint_instance = -// UInt30::from_different_sized_cell_values(&mut circuit_builder, &small_values, 2, true) -// .unwrap(); -// circuit_builder.configure(); -// let circuit = Circuit::new(&circuit_builder); - -// // input -// // we start with cells of bit width 2 (8 of them) -// // 11 00 10 11 01 10 01 01 (bit representation) -// // 3 0 2 3 1 2 1 1 (field representation) -// // -// // repacking into cells of bit width 6 -// // 110010 110110 010100 -// // since total bit = 30 then expect 5 cells ( 30 / 6) -// // since we have 3 cells, we need to pad with 2 more -// // hence expected output: -// // 100011 100111 000101 000000 000000(bit representation) -// // 35 39 5 0 0 - -// let witness_values = vec![3, 0, 2, 3, 1, 2, 1, 1] -// .into_iter() -// .map(|v| Goldilocks::from(v)) -// .collect_vec(); -// let circuit_witness = { -// let challenges = vec![GoldilocksExt2::from(2)]; -// let mut circuit_witness = CircuitWitness::new(&circuit, challenges); -// circuit_witness.add_instance(&circuit, vec![witness_values]); -// circuit_witness -// }; -// circuit_witness.check_correctness(&circuit); - -// let output = circuit_witness.output_layer_witness_ref().instances[0].to_vec(); -// assert_eq!( -// &output[..5], -// vec![35, 39, 5, 0, 0] -// .into_iter() -// .map(|v| Goldilocks::from(v)) -// .collect_vec() -// ); - -// // padding to power of 2 -// assert_eq!( -// &output[5..], -// vec![0, 0, 0] -// .into_iter() -// .map(|v| Goldilocks::from(v)) -// .collect_vec() -// ); -// } - -// #[test] -// fn test_counter_vector() { -// // each limb has 5 bits so all number from 0..3 should require only 1 limb -// type UInt30 = UInt<30, 5>; -// let res = UInt30::counter_vector::(3); -// assert_eq!( -// res, -// vec![ -// vec![Goldilocks::from(0)], -// vec![Goldilocks::from(1)], -// vec![Goldilocks::from(2)] -// ] -// ); - -// // each limb has a single bit, number from 0..5 should require 3 limbs each -// type UInt50 = UInt<50, 1>; -// let res = UInt50::counter_vector::(5); -// assert_eq!( -// res, -// vec![ -// // 0 -// vec![ -// Goldilocks::from(0), -// Goldilocks::from(0), -// Goldilocks::from(0) -// ], -// // 1 -// vec![ -// Goldilocks::from(1), -// Goldilocks::from(0), -// Goldilocks::from(0) -// ], -// // 2 -// vec![ -// Goldilocks::from(0), -// Goldilocks::from(1), -// Goldilocks::from(0) -// ], -// // 3 -// vec![ -// Goldilocks::from(1), -// Goldilocks::from(1), -// Goldilocks::from(0) -// ], -// // 4 -// vec![ -// Goldilocks::from(0), -// Goldilocks::from(0), -// Goldilocks::from(1) -// ], -// ] -// ); -// } -// } diff --git a/singer-utils/src/uint/util.rs b/singer-utils/src/uint/util.rs deleted file mode 100644 index e8044dd5a..000000000 --- a/singer-utils/src/uint/util.rs +++ /dev/null @@ -1,326 +0,0 @@ -use crate::error::UtilError; -use ff::PrimeField; -use ff_ext::ExtensionField; -use goldilocks::SmallField; -use simple_frontend::structs::{CellId, CircuitBuilder}; - -#[allow(clippy::too_long_first_doc_paragraph)] -/// Given some data represented by n small cells of size s -/// this function represents the same data in m big cells of size b -/// where b >= s -/// e.g. -/// information = 1100 -/// represented with 2 small cells of size 2 each -/// small -> 11 | 00 -/// we can pack this into a single big cell of size 4 -/// big -> 1100 -pub fn convert_decomp( - circuit_builder: &mut CircuitBuilder, - small_cells: &[CellId], - small_cell_bit_width: usize, - big_cell_bit_width: usize, - is_little_endian: bool, -) -> Result, UtilError> { - assert!(E::BaseField::NUM_BITS >= big_cell_bit_width as u32); - - if small_cell_bit_width > big_cell_bit_width { - return Err(UtilError::UIntError( - "cannot pack bigger width cells into smaller width cells".to_string(), - )); - } - - if small_cell_bit_width == big_cell_bit_width { - return Ok(small_cells.to_vec()); - } - - // ensure the small cell values are in little endian form - let small_cells = if !is_little_endian { - small_cells.iter().copied().rev().collect() - } else { - small_cells.to_vec() - }; - - // compute the number of small cells that can fit into each big cell - let small_cell_count_per_big_cell = big_cell_bit_width / small_cell_bit_width; - - let mut new_cell_ids = vec![]; - - // iteratively take and pack n small cells into 1 big cell - for values in small_cells.chunks(small_cell_count_per_big_cell) { - let big_cell = circuit_builder.create_cell(); - for (small_chunk_index, small_bit_cell) in values.iter().enumerate() { - let shift_size = small_chunk_index * small_cell_bit_width; - circuit_builder.add( - big_cell, - *small_bit_cell, - E::BaseField::from(1 << shift_size), - ); - } - new_cell_ids.push(big_cell); - } - - Ok(new_cell_ids) -} - -/// Pads a `Vec` with new cells to reach some given size n -pub fn pad_cells( - circuit_builder: &mut CircuitBuilder, - cells: &mut Vec, - size: usize, -) { - if cells.len() < size { - cells.extend(circuit_builder.create_cells(size - cells.len())) - } -} - -/// Compile time evaluated minimum function -/// returns min(a, b) -pub const fn const_min(a: usize, b: usize) -> usize { - if a <= b { a } else { b } -} - -/// Assumes each limb < max_value -/// adds 1 to the big value, while preserving the above constraint -pub fn add_one_to_big_num(limb_modulo: F, limbs: &[F]) -> Vec { - let mut should_add_one = true; - let mut result = vec![]; - - for limb in limbs { - let mut new_limb_value = *limb; - if should_add_one { - new_limb_value += F::ONE; - if new_limb_value == limb_modulo { - new_limb_value = F::ZERO; - } else { - should_add_one = false; - } - } - result.push(new_limb_value); - } - - result -} - -// #[cfg(test)] -// mod tests { -// use crate::uint::util::{add_one_to_big_num, const_min, convert_decomp, pad_cells}; -// use gkr::structs::{Circuit, CircuitWitness}; -// use goldilocks::{Goldilocks, GoldilocksExt2}; -// use itertools::Itertools; -// use multilinear_extensions::mle::IntoMLE; -// use simple_frontend::structs::CircuitBuilder; - -// #[test] -// #[should_panic] -// fn test_pack_big_cells_into_small_cells() { -// let mut circuit_builder = CircuitBuilder::::new(); -// let (_, big_values) = circuit_builder.create_witness_in(5); -// let big_bit_width = 5; -// let small_bit_width = 2; -// let _ = convert_decomp( -// &mut circuit_builder, -// &big_values, -// big_bit_width, -// small_bit_width, -// true, -// ) -// .unwrap(); -// } - -// #[test] -// fn test_pack_same_size_cells() { -// let mut circuit_builder = CircuitBuilder::::new(); -// let (_, initial_values) = circuit_builder.create_witness_in(5); -// let small_bit_width = 2; -// let big_bit_width = 2; -// let new_values = convert_decomp( -// &mut circuit_builder, -// &initial_values, -// small_bit_width, -// big_bit_width, -// true, -// ) -// .unwrap(); -// assert_eq!(initial_values, new_values); -// } - -// #[test] -// fn test_pack_small_cells_into_big_cells() { -// let mut circuit_builder = CircuitBuilder::::new(); -// let (_, small_values) = circuit_builder.create_witness_in(9); -// let small_bit_width = 2; -// let big_bit_width = 6; -// let big_values = convert_decomp( -// &mut circuit_builder, -// &small_values, -// small_bit_width, -// big_bit_width, -// true, -// ) -// .unwrap(); -// assert_eq!(big_values.len(), 3); -// circuit_builder.create_witness_out_from_cells(&big_values); - -// // verify construction against concrete witness values -// circuit_builder.configure(); -// let circuit = Circuit::new(&circuit_builder); - -// // input -// // we start with cells of bit width 2 (9 of them) -// // 11 00 10 11 01 10 01 01 11 (bit representation) -// // 3 0 2 3 1 2 1 1 3 (field representation) -// // -// // expected output -// // repacking into cells of bit width 6 -// // we can only fit three 2-bit cells into a 6 bit cell -// // 100011 100111 110101 (bit representation) -// // 35 39 53 (field representation) - -// let witness_values = vec![3, 0, 2, 3, 1, 2, 1, 1, 3] -// .into_iter() -// .map(|v| Goldilocks::from(v)) -// .collect::>(); -// let circuit_witness = { -// let mut circuit_witness = CircuitWitness::new(&circuit, vec![]); -// circuit_witness.add_instance(&circuit, vec![witness_values]); -// circuit_witness -// }; - -// circuit_witness.check_correctness(&circuit); - -// let output = circuit_witness.output_layer_witness_ref().instances[0].to_vec(); - -// assert_eq!( -// &output[..3], -// vec![35, 39, 53] -// .into_iter() -// .map(|v| Goldilocks::from(v)) -// .collect::>() -// ); - -// // padding to power of 2 -// assert_eq!( -// &output[3..], -// vec![0] -// .into_iter() -// .map(|v| Goldilocks::from(v)) -// .collect_vec() -// ); -// } - -// #[test] -// fn test_pad_cells() { -// let mut circuit_builder = CircuitBuilder::::new(); -// let (_, mut small_values) = circuit_builder.create_witness_in(3); -// // assert before padding -// assert_eq!(small_values, vec![0, 1, 2]); -// // pad -// pad_cells(&mut circuit_builder, &mut small_values, 5); -// // assert after padding -// assert_eq!(small_values, vec![0, 1, 2, 3, 4]); -// } - -// #[test] -// fn test_min_function() { -// assert_eq!(const_min(2, 3), 2); -// assert_eq!(const_min(3, 3), 3); -// assert_eq!(const_min(5, 3), 3); -// } - -// #[test] -// fn test_add_one_big_num() { -// let limb_modulo = Goldilocks::from(2); - -// // 000 -// let initial_limbs = vec![Goldilocks::from(0); 3]; - -// // 100 -// let updated_limbs = add_one_to_big_num(limb_modulo, &initial_limbs); -// assert_eq!( -// updated_limbs, -// vec![ -// Goldilocks::from(1), -// Goldilocks::from(0), -// Goldilocks::from(0) -// ] -// ); - -// // 010 -// let updated_limbs = add_one_to_big_num(limb_modulo, &updated_limbs); -// assert_eq!( -// updated_limbs, -// vec![ -// Goldilocks::from(0), -// Goldilocks::from(1), -// Goldilocks::from(0) -// ] -// ); - -// // 110 -// let updated_limbs = add_one_to_big_num(limb_modulo, &updated_limbs); -// assert_eq!( -// updated_limbs, -// vec![ -// Goldilocks::from(1), -// Goldilocks::from(1), -// Goldilocks::from(0) -// ] -// ); - -// // 001 -// let updated_limbs = add_one_to_big_num(limb_modulo, &updated_limbs); -// assert_eq!( -// updated_limbs, -// vec![ -// Goldilocks::from(0), -// Goldilocks::from(0), -// Goldilocks::from(1) -// ] -// ); - -// // 101 -// let updated_limbs = add_one_to_big_num(limb_modulo, &updated_limbs); -// assert_eq!( -// updated_limbs, -// vec![ -// Goldilocks::from(1), -// Goldilocks::from(0), -// Goldilocks::from(1) -// ] -// ); - -// // 011 -// let updated_limbs = add_one_to_big_num(limb_modulo, &updated_limbs); -// assert_eq!( -// updated_limbs, -// vec![ -// Goldilocks::from(0), -// Goldilocks::from(1), -// Goldilocks::from(1) -// ] -// ); - -// // 111 -// let updated_limbs = add_one_to_big_num(limb_modulo, &updated_limbs); -// assert_eq!( -// updated_limbs, -// vec![ -// Goldilocks::from(1), -// Goldilocks::from(1), -// Goldilocks::from(1) -// ] -// ); - -// // restart cycle -// // 000 -// let updated_limbs = add_one_to_big_num(limb_modulo, &updated_limbs); -// assert_eq!( -// updated_limbs, -// vec![ -// Goldilocks::from(0), -// Goldilocks::from(0), -// Goldilocks::from(0) -// ] -// ); -// } -// } diff --git a/singer-utils/src/uint/witness_extractors.rs b/singer-utils/src/uint/witness_extractors.rs deleted file mode 100644 index e70b2e8cb..000000000 --- a/singer-utils/src/uint/witness_extractors.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::uint::{constants::AddSubConstants, uint::UInt}; -use simple_frontend::structs::CellId; - -// TODO: split this into different impls, constrained by specific contexts -// e.g add_sub, mul, ... -impl UInt { - // witness_structure - // [...range_values..., ...carry_witness...] - - pub fn extract_carry_add(witness: &[CellId]) -> &[CellId] { - &witness[Self::N_RANGE_CELLS..] - } - - pub fn extract_carry_no_overflow_add(witness: &[CellId]) -> &[CellId] { - &witness[AddSubConstants::::N_RANGE_CELLS_NO_OVERFLOW..] - } - - pub fn extract_unsafe_carry_add(witness: &[CellId]) -> &[CellId] { - witness - } - - pub fn extract_borrow_sub(witness: &[CellId]) -> &[CellId] { - &witness[Self::N_RANGE_CELLS..] - } - - pub fn extract_unsafe_borrow_sub(witness: &[CellId]) -> &[CellId] { - witness - } - - pub fn extract_range_values(witness: &[CellId]) -> &[CellId] { - &witness[..Self::N_RANGE_CELLS] - } - - pub fn extract_range_values_no_overflow(witness: &[CellId]) -> &[CellId] { - &witness[..AddSubConstants::::N_RANGE_CELLS_NO_OVERFLOW] - } -} diff --git a/singer/Cargo.toml b/singer/Cargo.toml deleted file mode 100644 index ac71ab4b9..000000000 --- a/singer/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -edition.workspace = true -license.workspace = true -name = "singer" -version.workspace = true - -[dependencies] -ark-std.workspace = true -ff.workspace = true -goldilocks.workspace = true -rayon.workspace = true -serde.workspace = true - -ff_ext = { path = "../ff_ext" } -gkr = { path = "../gkr", features = ["parallel"] } -gkr-graph = { version = "0", path = "../gkr-graph" } -multilinear_extensions = { version = "0", path = "../multilinear_extensions" } -simple-frontend = { version = "0", path = "../simple-frontend" } -singer-utils = { version = "0", path = "../singer-utils" } -transcript = { path = "../transcript" } - -itertools.workspace = true -paste.workspace = true -strum.workspace = true -strum_macros.workspace = true -tracing-flame.workspace = true -tracing-subscriber.workspace = true - -[dev-dependencies] -cfg-if.workspace = true -criterion.workspace = true -pprof.workspace = true -tracing.workspace = true - -[features] -dbg-add-opcode = [] -flamegraph = ["pprof/flamegraph", "pprof/criterion"] -non_pow2_rayon_thread = [] -test-dbg = [] -witness-count = [] - -[[bench]] -harness = false -name = "add" diff --git a/singer/benches/add.rs b/singer/benches/add.rs deleted file mode 100644 index 5984a19b2..000000000 --- a/singer/benches/add.rs +++ /dev/null @@ -1,129 +0,0 @@ -#![allow(clippy::manual_memcpy)] -#![allow(clippy::needless_range_loop)] - -use std::time::{Duration, Instant}; - -use ark_std::test_rng; -use criterion::*; - -use ff_ext::{ExtensionField, ff::Field}; -use goldilocks::GoldilocksExt2; -use itertools::Itertools; - -cfg_if::cfg_if! { - if #[cfg(feature = "flamegraph")] { - criterion_group! { - name = op_add; - config = Criterion::default().warm_up_time(Duration::from_millis(3000)).with_profiler(pprof::criterion::PProfProfiler::new(100, pprof::criterion::Output::Flamegraph(None))); - targets = bench_add - } - } else { - criterion_group! { - name = op_add; - config = Criterion::default().warm_up_time(Duration::from_millis(3000)); - targets = bench_add - } - } -} - -criterion_main!(op_add); - -const NUM_SAMPLES: usize = 10; - -use multilinear_extensions::util::max_usable_threads; -use singer::{ - CircuitWiresIn, SingerGraphBuilder, SingerParams, - instructions::{Instruction, InstructionGraph, SingerCircuitBuilder, add::AddInstruction}, - scheme::GKRGraphProverState, -}; -use singer_utils::structs::ChipChallenges; -use transcript::Transcript; - -fn bench_add(c: &mut Criterion) { - let max_thread_id = max_usable_threads(); - let chip_challenges = ChipChallenges::default(); - let circuit_builder = - SingerCircuitBuilder::::new(chip_challenges).expect("circuit builder failed"); - - for instance_num_vars in 10..14 { - // expand more input size once runtime is acceptable - let mut group = c.benchmark_group(format!("add_op_{}", instance_num_vars)); - group.sample_size(NUM_SAMPLES); - - // Benchmark the proving time - group.bench_function( - BenchmarkId::new("prove_add", format!("prove_add_log2_{}", instance_num_vars)), - |b| { - b.iter_with_setup( - || { - let mut rng = test_rng(); - let singer_builder = SingerGraphBuilder::::default(); - let real_challenges = vec![E::random(&mut rng), E::random(&mut rng)]; - (rng, singer_builder, real_challenges) - }, - |(mut rng,mut singer_builder, real_challenges)| { - let size = AddInstruction::phase0_size(); - let phase0: CircuitWiresIn = vec![(0..(1 << instance_num_vars)) - .map(|_| { - (0..size) - .map(|_| { - ::BaseField::random( - &mut rng, - ) - }) - .collect_vec() - }) - .collect_vec().into(), - ]; - - - let timer = Instant::now(); - - let _ = AddInstruction::construct_graph_and_witness( - &mut singer_builder.graph_builder, - &mut singer_builder.chip_builder, - &circuit_builder.insts_circuits - [>::OPCODE as usize], - vec![phase0], - &real_challenges, - 1 << instance_num_vars, - &SingerParams::default(), - ) - .expect("gkr graph construction failed"); - - let (graph, wit) = singer_builder.graph_builder.finalize_graph_and_witness(); - - println!( - "AddInstruction::construct_graph_and_witness, instance_num_vars = {}, time = {}", - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - - let point = vec![E::random(&mut rng), E::random(&mut rng)]; - let target_evals = graph.target_evals(&wit, &point); - - let prover_transcript = &mut Transcript::new(b"Singer"); - - let timer = Instant::now(); - let _ = GKRGraphProverState::prove( - &graph, - &wit, - &target_evals, - prover_transcript, - (1 << instance_num_vars).min(max_thread_id), - ) - .expect("prove failed"); - println!( - "AddInstruction::prove, instance_num_vars = {}, time = {}", - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - }); - }, - ); - - group.finish(); - } - - type E = GoldilocksExt2; -} diff --git a/singer/examples/add.rs b/singer/examples/add.rs deleted file mode 100644 index 85724baf3..000000000 --- a/singer/examples/add.rs +++ /dev/null @@ -1,204 +0,0 @@ -use std::{collections::BTreeMap, time::Instant}; - -use ark_std::test_rng; -use ff_ext::{ExtensionField, ff::Field}; -use gkr_graph::structs::CircuitGraphAuxInfo; -use goldilocks::{Goldilocks, GoldilocksExt2}; -use itertools::Itertools; - -use simple_frontend::structs::CellId; -use singer::{ - CircuitWiresIn, SingerGraphBuilder, SingerParams, - instructions::{Instruction, InstructionGraph, SingerCircuitBuilder, add::AddInstruction}, - scheme::{GKRGraphProverState, GKRGraphVerifierState}, - u64vec, -}; -use singer_utils::{ - constants::RANGE_CHIP_BIT_WIDTH, - structs::{ChipChallenges, StackUInt, TSUInt}, -}; -use tracing_flame::FlameLayer; -use tracing_subscriber::{EnvFilter, Registry, fmt, layer::SubscriberExt}; -use transcript::Transcript; - -fn get_single_instance_values_map() -> BTreeMap<&'static str, Vec> { - let mut phase0_values_map = BTreeMap::<&'static str, Vec>::new(); - phase0_values_map.insert(AddInstruction::phase0_pc_str(), vec![Goldilocks::from( - 1u64, - )]); - phase0_values_map.insert(AddInstruction::phase0_stack_ts_str(), vec![ - Goldilocks::from(3u64), - ]); - phase0_values_map.insert(AddInstruction::phase0_memory_ts_str(), vec![ - Goldilocks::from(1u64), - ]); - phase0_values_map.insert(AddInstruction::phase0_stack_top_str(), vec![ - Goldilocks::from(100u64), - ]); - phase0_values_map.insert(AddInstruction::phase0_clk_str(), vec![Goldilocks::from( - 1u64, - )]); - phase0_values_map.insert( - AddInstruction::phase0_pc_add_str(), - vec![], // carry is 0, may test carry using larger values in PCUInt - ); - phase0_values_map.insert(AddInstruction::phase0_stack_ts_add_str(), vec![ - Goldilocks::from(4u64), /* first TSUInt::N_RANGE_CHECK_CELLS = 1*(56/16) = 4 - * cells are range values, stack_ts + 1 = 4 */ - Goldilocks::from(0u64), - Goldilocks::from(0u64), - Goldilocks::from(0u64), - // no place for carry - ]); - phase0_values_map.insert(AddInstruction::phase0_old_stack_ts0_str(), vec![ - Goldilocks::from(2u64), - ]); - let m: u64 = (1 << TSUInt::C) - 1; - let range_values = u64vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); - phase0_values_map.insert(AddInstruction::phase0_old_stack_ts_lt0_str(), vec![ - Goldilocks::from(range_values[0]), - Goldilocks::from(range_values[1]), - Goldilocks::from(range_values[2]), - Goldilocks::from(1u64), // borrow - ]); - phase0_values_map.insert(AddInstruction::phase0_old_stack_ts1_str(), vec![ - Goldilocks::from(1u64), - ]); - let m: u64 = (1 << TSUInt::C) - 2; - let range_values = u64vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); - phase0_values_map.insert(AddInstruction::phase0_old_stack_ts_lt1_str(), vec![ - Goldilocks::from(range_values[0]), - Goldilocks::from(range_values[1]), - Goldilocks::from(range_values[2]), - Goldilocks::from(1u64), // borrow - ]); - let m: u64 = (1 << StackUInt::C) - 1; - phase0_values_map.insert(AddInstruction::phase0_addend_0_str(), vec![ - Goldilocks::from(m), - ]); - phase0_values_map.insert(AddInstruction::phase0_addend_1_str(), vec![ - Goldilocks::from(1u64), - ]); - let range_values = u64vec::<{ StackUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m + 1); - let mut wit_phase0_instruction_add: Vec = vec![]; - for value in &range_values[..16] { - wit_phase0_instruction_add.push(Goldilocks::from(*value)); - } - wit_phase0_instruction_add.push(Goldilocks::from(1u64)); // carry is [1, 0, ...] - phase0_values_map.insert( - AddInstruction::phase0_instruction_add_str(), - wit_phase0_instruction_add, - ); - phase0_values_map -} -fn main() { - let max_thread_id = 8; - let instance_num_vars = 13; - type E = GoldilocksExt2; - let chip_challenges = ChipChallenges::default(); - let circuit_builder = - SingerCircuitBuilder::::new(chip_challenges).expect("circuit builder failed"); - let mut singer_builder = SingerGraphBuilder::::default(); - - let mut rng = test_rng(); - let size = AddInstruction::phase0_size(); - let phase0_values_map = get_single_instance_values_map(); - let phase0_idx_map = AddInstruction::phase0_idxes_map(); - - let mut single_witness_in = vec![::BaseField::ZERO; size]; - - for key in phase0_idx_map.keys() { - let range = phase0_idx_map - .get(key) - .unwrap() - .clone() - .collect::>(); - let values = phase0_values_map - .get(key) - .expect(&("unknown key ".to_owned() + key)); - for (value_idx, cell_idx) in range.into_iter().enumerate() { - if value_idx < values.len() { - single_witness_in[cell_idx] = values[value_idx]; - } - } - } - - let phase0: CircuitWiresIn = vec![ - (0..(1 << instance_num_vars)) - .map(|_| single_witness_in.clone()) - .collect_vec() - .into(), - ]; - - let real_challenges = vec![E::random(&mut rng), E::random(&mut rng)]; - - let timer = Instant::now(); - - let _ = AddInstruction::construct_graph_and_witness( - &mut singer_builder.graph_builder, - &mut singer_builder.chip_builder, - &circuit_builder.insts_circuits[>::OPCODE as usize], - vec![phase0], - &real_challenges, - 1 << instance_num_vars, - &SingerParams::default(), - ) - .expect("gkr graph construction failed"); - - let (graph, wit) = singer_builder.graph_builder.finalize_graph_and_witness(); - - println!( - "AddInstruction::construct_graph_and_witness, instance_num_vars = {}, time = {}", - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - - let (flame_layer, _guard) = FlameLayer::with_file("./tracing.folded").unwrap(); - let subscriber = Registry::default() - .with( - fmt::layer() - .compact() - .with_thread_ids(false) - .with_thread_names(false), - ) - .with(EnvFilter::from_default_env()) - .with(flame_layer.with_threads_collapsed(true)); - tracing::subscriber::set_global_default(subscriber).unwrap(); - - let point = vec![E::random(&mut rng), E::random(&mut rng)]; - let target_evals = graph.target_evals(&wit, &point); - - for _ in 0..5 { - let prover_transcript = &mut Transcript::new(b"Singer"); - let timer = Instant::now(); - let proof = GKRGraphProverState::prove( - &graph, - &wit, - &target_evals, - prover_transcript, - (1 << instance_num_vars).min(max_thread_id), - ) - .expect("prove failed"); - println!( - "AddInstruction::prove, instance_num_vars = {}, time = {}", - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - let mut verifier_transcript = Transcript::new(b"Singer"); - GKRGraphVerifierState::verify( - &graph, - &real_challenges, - &target_evals, - proof, - &CircuitGraphAuxInfo { - instance_num_vars: wit - .node_witnesses - .iter() - .map(|witness| witness.instance_num_vars()) - .collect(), - }, - &mut verifier_transcript, - ) - .expect("verify failed"); - } -} diff --git a/singer/examples/push_and_pop.rs b/singer/examples/push_and_pop.rs deleted file mode 100644 index 1dbbbcf4f..000000000 --- a/singer/examples/push_and_pop.rs +++ /dev/null @@ -1,70 +0,0 @@ -use goldilocks::GoldilocksExt2; -use itertools::Itertools; -use singer::{ - SingerAuxInfo, SingerGraphBuilder, SingerParams, SingerWiresIn, - instructions::SingerCircuitBuilder, - scheme::{prover::prove, verifier::verify}, -}; -use singer_utils::structs::ChipChallenges; -use transcript::Transcript; - -fn main() { - let chip_challenges = ChipChallenges::default(); - let circuit_builder = SingerCircuitBuilder::::new(chip_challenges) - .expect("circuit builder failed"); - let singer_builder = SingerGraphBuilder::::default(); - - let bytecode = [0x60_u8, 0x01, 0x50]; - - let mut prover_transcript = Transcript::new(b"Singer"); - - // TODO: Generate the following items. - let singer_wires_in = SingerWiresIn::default(); - let real_challenges = vec![]; - let singer_params = SingerParams::default(); - - let (proof, singer_aux_info, singer_wire_out_values) = { - let real_n_instances = singer_wires_in - .instructions - .iter() - .map(|x| (x.opcode, x.real_n_instances)) - .collect_vec(); - let (circuit, witness, wires_out_id) = singer_builder - .construct_graph_and_witness( - &circuit_builder, - singer_wires_in, - &bytecode, - &[], - &real_challenges, - &singer_params, - ) - .expect("construct failed"); - - let (proof, graph_aux_info, singer_wire_out_values) = - prove(&circuit, &witness, &wires_out_id, &mut prover_transcript).expect("prove failed"); - let aux_info = SingerAuxInfo { - graph_aux_info, - real_n_instances, - singer_params, - bytecode_len: bytecode.len(), - ..Default::default() - }; - (proof, aux_info, singer_wire_out_values) - }; - - // 4. Verify. - let mut verifier_transcript = Transcript::new(b"Singer"); - let singer_builder = SingerGraphBuilder::::default(); - let circuit = singer_builder - .construct_graph(&circuit_builder, &singer_aux_info) - .expect("construct failed"); - verify( - &circuit, - proof, - singer_wire_out_values, - &singer_aux_info, - &real_challenges, - &mut verifier_transcript, - ) - .expect("verify failed"); -} diff --git a/singer/src/error.rs b/singer/src/error.rs deleted file mode 100644 index 04e7be1bf..000000000 --- a/singer/src/error.rs +++ /dev/null @@ -1,22 +0,0 @@ -use gkr_graph::error::GKRGraphError; -use singer_utils::error::UtilError; - -#[derive(Debug)] -pub enum ZKVMError { - CircuitError, - UtilError(UtilError), - GKRGraphError(GKRGraphError), - VerifyError, -} - -impl From for ZKVMError { - fn from(error: GKRGraphError) -> Self { - Self::GKRGraphError(error) - } -} - -impl From for ZKVMError { - fn from(error: UtilError) -> Self { - Self::UtilError(error) - } -} diff --git a/singer/src/instructions.rs b/singer/src/instructions.rs deleted file mode 100644 index dbecf9287..000000000 --- a/singer/src/instructions.rs +++ /dev/null @@ -1,275 +0,0 @@ -use std::{mem, sync::Arc}; - -use ff_ext::ExtensionField; -use gkr::structs::Circuit; -use gkr_graph::structs::{CircuitGraphBuilder, NodeOutputType, PredType}; -use simple_frontend::structs::WitnessId; - -use singer_utils::{chips::SingerChipBuilder, constants::OpcodeType, structs::ChipChallenges}; -use strum_macros::EnumIter; - -use crate::{CircuitWiresIn, SingerParams, error::ZKVMError}; - -use self::{ - add::AddInstruction, calldataload::CalldataloadInstruction, dup::DupInstruction, - gt::GtInstruction, jump::JumpInstruction, jumpdest::JumpdestInstruction, - jumpi::JumpiInstruction, mstore::MstoreInstruction, pop::PopInstruction, push::PushInstruction, - ret::ReturnInstruction, swap::SwapInstruction, -}; - -// arithmetic -pub mod add; - -// bitwise -pub mod gt; - -// control -pub mod jump; -pub mod jumpdest; -pub mod jumpi; -pub mod ret; - -// stack -pub mod dup; -pub mod pop; -pub mod push; -pub mod swap; - -// memory -pub mod mstore; - -// system -pub mod calldataload; - -#[derive(Clone, Debug)] -pub struct SingerCircuitBuilder { - /// Opcode circuits - pub insts_circuits: [Vec>; 256], - pub challenges: ChipChallenges, -} - -impl SingerCircuitBuilder { - pub fn new(challenges: ChipChallenges) -> Result { - let mut insts_circuits = Vec::with_capacity(256); - for opcode in 0..=255 { - insts_circuits.push(construct_instruction_circuits(opcode, challenges)?); - } - let insts_circuits: [Vec>; 256] = insts_circuits - .try_into() - .map_err(|_| ZKVMError::CircuitError)?; - Ok(Self { - insts_circuits, - challenges, - }) - } -} - -/// Construct instruction circuits and its extensions. -pub(crate) fn construct_instruction_circuits( - opcode: u8, - challenges: ChipChallenges, -) -> Result>, ZKVMError> { - match opcode { - 0x01 => AddInstruction::construct_circuits(challenges), - 0x11 => GtInstruction::construct_circuits(challenges), - 0x35 => CalldataloadInstruction::construct_circuits(challenges), - 0x50 => PopInstruction::construct_circuits(challenges), - 0x52 => MstoreInstruction::construct_circuits(challenges), - 0x56 => JumpInstruction::construct_circuits(challenges), - 0x57 => JumpiInstruction::construct_circuits(challenges), - 0x5B => JumpdestInstruction::construct_circuits(challenges), - 0x60 => PushInstruction::<1>::construct_circuits(challenges), - 0x80 => DupInstruction::<1>::construct_circuits(challenges), - 0x81 => DupInstruction::<2>::construct_circuits(challenges), - 0x91 => SwapInstruction::<2>::construct_circuits(challenges), - 0x93 => SwapInstruction::<4>::construct_circuits(challenges), - 0xF3 => ReturnInstruction::construct_circuits(challenges), - _ => Ok(vec![]), // TODO: Add more instructions. - } -} - -#[allow(clippy::too_many_arguments)] -pub(crate) fn construct_inst_graph_and_witness( - opcode: u8, - graph_builder: &mut CircuitGraphBuilder, - chip_builder: &mut SingerChipBuilder, - inst_circuits: &[InstCircuit], - sources: Vec>, - real_challenges: &[E], - real_n_instances: usize, - params: &SingerParams, -) -> Result, ZKVMError> { - let construct_circuit_graph = match opcode { - 0x01 => AddInstruction::construct_graph_and_witness, - 0x11 => GtInstruction::construct_graph_and_witness, - 0x35 => CalldataloadInstruction::construct_graph_and_witness, - 0x50 => PopInstruction::construct_graph_and_witness, - 0x52 => MstoreInstruction::construct_graph_and_witness, - 0x56 => JumpInstruction::construct_graph_and_witness, - 0x57 => JumpiInstruction::construct_graph_and_witness, - 0x5B => JumpdestInstruction::construct_graph_and_witness, - 0x60 => PushInstruction::<1>::construct_graph_and_witness, - 0x80 => DupInstruction::<1>::construct_graph_and_witness, - 0x81 => DupInstruction::<2>::construct_graph_and_witness, - 0x91 => SwapInstruction::<2>::construct_graph_and_witness, - 0x93 => SwapInstruction::<4>::construct_graph_and_witness, - 0xF3 => ReturnInstruction::construct_graph_and_witness, - _ => return Ok(None), // TODO: Add more instructions. - }; - - construct_circuit_graph( - graph_builder, - chip_builder, - inst_circuits, - sources, - real_challenges, - real_n_instances, - params, - ) -} - -pub(crate) fn construct_inst_graph( - opcode: u8, - graph_builder: &mut CircuitGraphBuilder, - chip_builder: &mut SingerChipBuilder, - inst_circuits: &[InstCircuit], - real_n_instances: usize, - params: &SingerParams, -) -> Result, ZKVMError> { - let construct_graph = match opcode { - 0x01 => AddInstruction::construct_graph, - 0x11 => GtInstruction::construct_graph, - 0x35 => CalldataloadInstruction::construct_graph, - 0x50 => PopInstruction::construct_graph, - 0x52 => MstoreInstruction::construct_graph, - 0x56 => JumpInstruction::construct_graph, - 0x57 => JumpiInstruction::construct_graph, - 0x5B => JumpdestInstruction::construct_graph, - 0x60 => PushInstruction::<1>::construct_graph, - 0x80 => DupInstruction::<1>::construct_graph, - 0x81 => DupInstruction::<2>::construct_graph, - 0x91 => SwapInstruction::<2>::construct_graph, - 0x93 => SwapInstruction::<4>::construct_graph, - 0xF3 => ReturnInstruction::construct_graph, - _ => unimplemented!(), - }; - - construct_graph( - graph_builder, - chip_builder, - inst_circuits, - real_n_instances, - params, - ) -} - -#[allow(clippy::enum_variant_names)] -#[derive(Clone, Copy, Debug, EnumIter)] -pub(crate) enum InstOutputType { - RAMLoad, - RAMStore, - ROMInput, -} - -#[derive(Clone, Debug)] -pub struct InstCircuit { - pub(crate) circuit: Arc>, - pub(crate) layout: InstCircuitLayout, -} - -#[derive(Clone, Debug, Default)] -pub struct InstCircuitLayout { - // Will be connected to the chips. - pub(crate) chip_check_wire_id: [Option<(WitnessId, usize)>; 3], - // Target. Especially for return the size of public output. - pub(crate) target_wire_id: Option, - // Will be connected to the accessory circuits. - pub(crate) succ_dup_wires_id: Vec, - pub(crate) succ_ooo_wires_id: Vec, - - /// Wires in index - // TODO(Matthias): check whether we need this field. - #[allow(dead_code)] - pub(crate) phases_wire_id: Vec, - // wire id fetched from pred circuit. - pub(crate) pred_dup_wire_id: Option, - pub(crate) pred_ooo_wire_id: Option, -} - -pub trait Instruction { - const OPCODE: OpcodeType; - const NAME: &'static str; - fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError>; -} - -/// Construct the part of the circuit graph for an instruction. -pub trait InstructionGraph { - type InstType: Instruction; - - /// Construct instruction circuits and its extensions. Mostly there is no - /// extensions. - fn construct_circuits(challenges: ChipChallenges) -> Result>, ZKVMError> { - let circuits = vec![Self::InstType::construct_circuit(challenges)?]; - Ok(circuits) - } - - /// Add instruction circuits, its accessories and corresponding witnesses to - /// the graph. Besides, Generate the tree-structured circuit to compute the - /// product or fraction summation of the chip check wires. - fn construct_graph_and_witness( - graph_builder: &mut CircuitGraphBuilder, - chip_builder: &mut SingerChipBuilder, - inst_circuits: &[InstCircuit], - mut sources: Vec>, - real_challenges: &[E], - real_n_instances: usize, - _: &SingerParams, - ) -> Result, ZKVMError> { - assert_eq!(sources.len(), 1, "unknown source length"); - let inst_circuit = &inst_circuits[0]; - let inst_wires_in = mem::take(&mut sources[0]); - let node_id = graph_builder.add_node_with_witness( - Self::InstType::NAME, - &inst_circuits[0].circuit, - vec![PredType::Source; inst_wires_in.len()], - real_challenges.to_vec(), - inst_wires_in, - real_n_instances.next_power_of_two(), - )?; - - chip_builder.construct_chip_check_graph_and_witness( - graph_builder, - node_id, - &inst_circuit.layout.chip_check_wire_id, - real_challenges, - real_n_instances, - )?; - Ok(None) - } - - /// Add instruction circuits, its accessories and corresponding witnesses to - /// the graph. Besides, Generate the tree-structured circuit to compute the - /// product or fraction summation of the chip check wires. - fn construct_graph( - graph_builder: &mut CircuitGraphBuilder, - chip_builder: &mut SingerChipBuilder, - inst_circuits: &[InstCircuit], - real_n_instances: usize, - _: &SingerParams, - ) -> Result, ZKVMError> { - let inst_circuit = &inst_circuits[0]; - let node_id = graph_builder.add_node( - stringify!(Self::InstType), - &inst_circuits[0].circuit, - vec![PredType::Source; inst_circuit.circuit.n_witness_in], - )?; - - chip_builder.construct_chip_check_graph( - graph_builder, - node_id, - &inst_circuit.layout.chip_check_wire_id, - real_n_instances, - )?; - Ok(None) - } -} diff --git a/singer/src/instructions/add.rs b/singer/src/instructions/add.rs deleted file mode 100644 index edf90738b..000000000 --- a/singer/src/instructions/add.rs +++ /dev/null @@ -1,382 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; -use gkr::structs::Circuit; -use paste::paste; -use simple_frontend::structs::{CircuitBuilder, MixedCell}; -use singer_utils::{ - chip_handler::{ - ChipHandler, bytecode::BytecodeChip, global_state::GlobalStateChip, range::RangeChip, - stack::StackChip, - }, - constants::OpcodeType, - register_witness, - structs::{PCUInt, StackUInt, TSUInt}, - uint::constants::AddSubConstants, -}; -use std::{collections::BTreeMap, sync::Arc}; - -use crate::error::ZKVMError; - -use super::{ChipChallenges, InstCircuit, InstCircuitLayout, Instruction, InstructionGraph}; - -pub struct AddInstruction; - -impl InstructionGraph for AddInstruction { - type InstType = Self; -} - -register_witness!( - AddInstruction, - phase0 { - pc => PCUInt::N_OPERAND_CELLS, - stack_ts => TSUInt::N_OPERAND_CELLS, - memory_ts => TSUInt::N_OPERAND_CELLS, - stack_top => 1, - clk => 1, - - pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, - - old_stack_ts0 => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt0 => AddSubConstants::::N_WITNESS_CELLS, - old_stack_ts1 => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt1 => AddSubConstants::::N_WITNESS_CELLS, - - addend_0 => StackUInt::N_OPERAND_CELLS, - addend_1 => StackUInt::N_OPERAND_CELLS, - instruction_add => AddSubConstants::::N_WITNESS_CELLS - } -); - -impl Instruction for AddInstruction { - const OPCODE: OpcodeType = OpcodeType::ADD; - const NAME: &'static str = "ADD"; - fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { - let mut circuit_builder = CircuitBuilder::default(); - let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); - - let mut chip_handler = ChipHandler::new(challenges); - - // State update - let pc = PCUInt::try_from(&phase0[Self::phase0_pc()])?; - let stack_ts = TSUInt::try_from(&phase0[Self::phase0_stack_ts()])?; - let memory_ts = &phase0[Self::phase0_memory_ts()]; - let stack_top = phase0[Self::phase0_stack_top().start]; - let stack_top_expr = MixedCell::Cell(stack_top); - let clk = phase0[Self::phase0_clk().start]; - let clk_expr = MixedCell::Cell(clk); - GlobalStateChip::state_in( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - stack_ts.values(), - memory_ts, - stack_top, - clk, - ); - - let next_pc = - RangeChip::add_pc_const(&mut circuit_builder, &pc, 1, &phase0[Self::phase0_pc_add()])?; - let next_stack_ts = RangeChip::add_ts_with_const( - &mut chip_handler, - &mut circuit_builder, - &stack_ts, - 1, - &phase0[Self::phase0_stack_ts_add()], - )?; - - GlobalStateChip::state_out( - &mut chip_handler, - &mut circuit_builder, - next_pc.values(), - next_stack_ts.values(), - memory_ts, - stack_top_expr.sub(E::BaseField::from(1)), - clk_expr.add(E::BaseField::ONE), - ); - - // Execution result = addend0 + addend1, with carry. - let addend_0 = (&phase0[Self::phase0_addend_0()]).try_into()?; - let addend_1 = (&phase0[Self::phase0_addend_1()]).try_into()?; - #[cfg(feature = "dbg-add-opcode")] - println!( - "addInstCircuit::phase0_instruction_add: {:?}", - Self::phase0_instruction_add() - ); - let result = StackUInt::add( - &mut circuit_builder, - &mut chip_handler, - &addend_0, - &addend_1, - &phase0[Self::phase0_instruction_add()], - )?; - - // Check the range of stack_top - 2 is within [0, 1 << STACK_TOP_BIT_WIDTH). - RangeChip::range_check_stack_top( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(2)), - )?; - - // Pop two values from stack - let old_stack_ts0 = (&phase0[Self::phase0_old_stack_ts0()]).try_into()?; - TSUInt::assert_lt( - &mut circuit_builder, - &mut chip_handler, - &old_stack_ts0, - &stack_ts, - &phase0[Self::phase0_old_stack_ts_lt0()], - )?; - StackChip::pop( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(1)), - old_stack_ts0.values(), - addend_0.values(), - ); - - let old_stack_ts1 = (&phase0[Self::phase0_old_stack_ts1()]).try_into()?; - TSUInt::assert_lt( - &mut circuit_builder, - &mut chip_handler, - &old_stack_ts1, - &stack_ts, - &phase0[Self::phase0_old_stack_ts_lt1()], - )?; - StackChip::pop( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(2)), - old_stack_ts1.values(), - addend_1.values(), - ); - - // Push one result to stack - StackChip::push( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(2)), - stack_ts.values(), - result.values(), - ); - - // Bytecode check for (pc, add) - BytecodeChip::bytecode_with_pc_opcode( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - >::OPCODE, - ); - - let (ram_load_id, ram_store_id, rom_id) = chip_handler.finalize(&mut circuit_builder); - circuit_builder.configure(); - - let outputs_wire_id = [ram_load_id, ram_store_id, rom_id]; - - Ok(InstCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - layout: InstCircuitLayout { - chip_check_wire_id: outputs_wire_id, - phases_wire_id: vec![phase0_wire_id], - ..Default::default() - }, - }) - } -} - -#[cfg(test)] -mod test { - #[cfg(not(debug_assertions))] - use crate::{ - CircuitWiresIn, SingerGraphBuilder, SingerParams, instructions::InstructionGraph, - instructions::SingerCircuitBuilder, scheme::GKRGraphProverState, - }; - #[cfg(not(debug_assertions))] - use ark_std::test_rng; - #[cfg(not(debug_assertions))] - use ff::Field; - #[cfg(not(debug_assertions))] - use ff_ext::ExtensionField; - #[cfg(not(debug_assertions))] - use transcript::Transcript; - - use goldilocks::{Goldilocks, GoldilocksExt2}; - use singer_utils::{ - constants::RANGE_CHIP_BIT_WIDTH, - structs::{StackUInt, TSUInt}, - }; - use std::collections::BTreeMap; - #[cfg(not(debug_assertions))] - use std::time::Instant; - - use crate::{ - instructions::{AddInstruction, ChipChallenges, Instruction}, - test::{get_uint_params, test_opcode_circuit_v2}, - utils::u64vec, - }; - - #[test] - fn test_add_construct_circuit() { - let challenges = ChipChallenges::default(); - - let phase0_idx_map = AddInstruction::phase0_idxes_map(); - let phase0_witness_size = AddInstruction::phase0_size(); - - #[cfg(feature = "witness-count")] - { - println!("ADD: {:?}", &phase0_idx_map); - println!("ADD witness_size: {:?}", phase0_witness_size); - } - - // initialize general test inputs associated with push1 - let inst_circuit = AddInstruction::construct_circuit(challenges).unwrap(); - - #[cfg(feature = "test-dbg")] - println!("{:?}", inst_circuit); - - let mut phase0_values_map = BTreeMap::<&'static str, Vec>::new(); - phase0_values_map.insert(AddInstruction::phase0_pc_str(), vec![Goldilocks::from( - 1u64, - )]); - phase0_values_map.insert(AddInstruction::phase0_stack_ts_str(), vec![ - Goldilocks::from(3u64), - ]); - phase0_values_map.insert(AddInstruction::phase0_memory_ts_str(), vec![ - Goldilocks::from(1u64), - ]); - phase0_values_map.insert(AddInstruction::phase0_stack_top_str(), vec![ - Goldilocks::from(100u64), - ]); - phase0_values_map.insert(AddInstruction::phase0_clk_str(), vec![Goldilocks::from( - 1u64, - )]); - phase0_values_map.insert( - AddInstruction::phase0_pc_add_str(), - vec![], // carry is 0, may test carry using larger values in PCUInt - ); - phase0_values_map.insert(AddInstruction::phase0_stack_ts_add_str(), vec![ - Goldilocks::from(4u64), /* first TSUInt::N_RANGE_CELLS = 1*(48/16) = 3 cells are - * range values, stack_ts + 1 = 4 */ - Goldilocks::from(0u64), - Goldilocks::from(0u64), - // no place for carry - ]); - phase0_values_map.insert(AddInstruction::phase0_old_stack_ts0_str(), vec![ - Goldilocks::from(2u64), - ]); - let m: u64 = (1 << get_uint_params::().1) - 1; - let range_values = u64vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); - phase0_values_map.insert(AddInstruction::phase0_old_stack_ts_lt0_str(), vec![ - Goldilocks::from(range_values[0]), - Goldilocks::from(range_values[1]), - Goldilocks::from(range_values[2]), - Goldilocks::from(1u64), - ]); - phase0_values_map.insert(AddInstruction::phase0_old_stack_ts1_str(), vec![ - Goldilocks::from(1u64), - ]); - let m: u64 = (1 << get_uint_params::().1) - 2; - let range_values = u64vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); - phase0_values_map.insert(AddInstruction::phase0_old_stack_ts_lt1_str(), vec![ - Goldilocks::from(range_values[0]), - Goldilocks::from(range_values[1]), - Goldilocks::from(range_values[2]), - Goldilocks::from(1u64), - ]); - let m: u64 = (1 << get_uint_params::().1) - 1; - phase0_values_map.insert(AddInstruction::phase0_addend_0_str(), vec![ - Goldilocks::from(m), - ]); - phase0_values_map.insert(AddInstruction::phase0_addend_1_str(), vec![ - Goldilocks::from(1u64), - ]); - let range_values = u64vec::<{ StackUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m + 1); - let mut wit_phase0_instruction_add: Vec = vec![]; - for &value in &range_values[..16] { - wit_phase0_instruction_add.push(Goldilocks::from(value)) - } - wit_phase0_instruction_add.push(Goldilocks::from(1u64)); // carry is [1, 0, ...] - phase0_values_map.insert( - AddInstruction::phase0_instruction_add_str(), - wit_phase0_instruction_add, - ); - - // The actual challenges used is: - // challenges - // { ChallengeConst { challenge: 1, exp: i }: [Goldilocks(c^i)] } - let c = GoldilocksExt2::from(6u64); - let circuit_witness_challenges = vec![c; 3]; - - let _ = test_opcode_circuit_v2( - &inst_circuit, - &phase0_idx_map, - phase0_witness_size, - &phase0_values_map, - circuit_witness_challenges, - ); - } - - #[cfg(not(debug_assertions))] - fn bench_add_instruction_helper(instance_num_vars: usize) { - let chip_challenges = ChipChallenges::default(); - let circuit_builder = - SingerCircuitBuilder::::new(chip_challenges).expect("circuit builder failed"); - let mut singer_builder = SingerGraphBuilder::::default(); - - let mut rng = test_rng(); - let size = AddInstruction::phase0_size(); - let phase0: CircuitWiresIn = vec![ - (0..(1 << instance_num_vars)) - .map(|_| { - (0..size) - .map(|_| E::BaseField::random(&mut rng)) - .collect::>() - }) - .collect::>() - .into(), - ]; - - let real_challenges = vec![E::random(&mut rng), E::random(&mut rng)]; - - let timer = Instant::now(); - - let _ = AddInstruction::construct_graph_and_witness( - &mut singer_builder.graph_builder, - &mut singer_builder.chip_builder, - &circuit_builder.insts_circuits[>::OPCODE as usize], - vec![phase0], - &real_challenges, - 1 << instance_num_vars, - &SingerParams::default(), - ) - .expect("gkr graph construction failed"); - - let (graph, wit) = singer_builder.graph_builder.finalize_graph_and_witness(); - - println!( - "AddInstruction::construct_graph_and_witness, instance_num_vars = {}, time = {}", - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - - let point = vec![E::random(&mut rng), E::random(&mut rng)]; - let target_evals = graph.target_evals(&wit, &point); - - let prover_transcript = &mut Transcript::new(b"Singer"); - - let timer = Instant::now(); - let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, prover_transcript, 1) - .expect("prove failed"); - println!( - "AddInstruction::prove, instance_num_vars = {}, time = {}", - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - } - - #[test] - #[cfg(not(debug_assertions))] - fn bench_add_instruction() { - bench_add_instruction_helper::(10); - } -} diff --git a/singer/src/instructions/calldataload.rs b/singer/src/instructions/calldataload.rs deleted file mode 100644 index 181274a5a..000000000 --- a/singer/src/instructions/calldataload.rs +++ /dev/null @@ -1,331 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; -use gkr::structs::Circuit; -use paste::paste; -use simple_frontend::structs::{CircuitBuilder, MixedCell}; -use singer_utils::{ - chip_handler::{ - ChipHandler, bytecode::BytecodeChip, calldata::CalldataChip, global_state::GlobalStateChip, - range::RangeChip, stack::StackChip, - }, - constants::OpcodeType, - register_witness, - structs::{PCUInt, StackUInt, TSUInt, UInt64}, - uint::constants::AddSubConstants, -}; -use std::{collections::BTreeMap, sync::Arc}; - -use crate::error::ZKVMError; - -use super::{ChipChallenges, InstCircuit, InstCircuitLayout, Instruction, InstructionGraph}; - -impl InstructionGraph for CalldataloadInstruction { - type InstType = Self; -} - -pub struct CalldataloadInstruction; - -register_witness!( - CalldataloadInstruction, - phase0 { - pc => PCUInt::N_OPERAND_CELLS, - stack_ts => TSUInt::N_OPERAND_CELLS, - memory_ts => TSUInt::N_OPERAND_CELLS, - ts => TSUInt::N_OPERAND_CELLS, - stack_top => 1, - clk => 1, - - pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, - - data => StackUInt::N_OPERAND_CELLS, - offset => UInt64::N_OPERAND_CELLS, - old_stack_ts => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt => AddSubConstants::::N_WITNESS_CELLS - } -); - -impl Instruction for CalldataloadInstruction { - const OPCODE: OpcodeType = OpcodeType::CALLDATALOAD; - const NAME: &'static str = "CALLDATALOAD"; - fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { - let mut circuit_builder = CircuitBuilder::default(); - let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); - - // instantiate memory handlers - let mut chip_handler = ChipHandler::new(challenges); - - // State update - let pc = PCUInt::try_from(&phase0[Self::phase0_pc()])?; - let stack_ts = TSUInt::try_from(&phase0[Self::phase0_stack_ts()])?; - let memory_ts = &phase0[Self::phase0_memory_ts()]; - let stack_top = phase0[Self::phase0_stack_top().start]; - let stack_top_expr = MixedCell::Cell(stack_top); - let clk = phase0[Self::phase0_clk().start]; - let clk_expr = MixedCell::Cell(clk); - GlobalStateChip::state_in( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - stack_ts.values(), - memory_ts, - stack_top, - clk, - ); - - let next_pc = - RangeChip::add_pc_const(&mut circuit_builder, &pc, 1, &phase0[Self::phase0_pc_add()])?; - let next_stack_ts = RangeChip::add_ts_with_const( - &mut chip_handler, - &mut circuit_builder, - &stack_ts, - 1, - &phase0[Self::phase0_stack_ts_add()], - )?; - - GlobalStateChip::state_out( - &mut chip_handler, - &mut circuit_builder, - next_pc.values(), - next_stack_ts.values(), - memory_ts, - stack_top_expr, - clk_expr.add(E::BaseField::ONE), - ); - - // Range check for stack top - RangeChip::range_check_stack_top( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(1)), - )?; - - // Stack pop offset from the stack. - let old_stack_ts = TSUInt::try_from(&phase0[Self::phase0_old_stack_ts()])?; - let offset = &phase0[Self::phase0_offset()]; - StackChip::pop( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::ONE), - old_stack_ts.values(), - offset, - ); - TSUInt::assert_lt( - &mut circuit_builder, - &mut chip_handler, - &old_stack_ts, - &stack_ts, - &phase0[Self::phase0_old_stack_ts_lt()], - )?; - - // CallDataLoad check (offset, data) - let data = &phase0[Self::phase0_data()]; - CalldataChip::load(&mut chip_handler, &mut circuit_builder, offset, data); - - // Stack push data to the stack. - StackChip::push( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::ONE), - stack_ts.values(), - data, - ); - - // Bytecode table (pc, CalldataLoad) - BytecodeChip::bytecode_with_pc_opcode( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - >::OPCODE, - ); - - let (ram_load_id, ram_store_id, rom_id) = chip_handler.finalize(&mut circuit_builder); - circuit_builder.configure(); - - let outputs_wire_id = [ram_load_id, ram_store_id, rom_id]; - - Ok(InstCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - layout: InstCircuitLayout { - chip_check_wire_id: outputs_wire_id, - phases_wire_id: vec![phase0_wire_id], - ..Default::default() - }, - }) - } -} - -#[cfg(test)] -mod test { - #[cfg(not(debug_assertions))] - use crate::{ - CircuitWiresIn, SingerGraphBuilder, SingerParams, - instructions::{InstructionGraph, SingerCircuitBuilder}, - scheme::GKRGraphProverState, - }; - #[cfg(not(debug_assertions))] - use ark_std::test_rng; - #[cfg(not(debug_assertions))] - use ff::Field; - #[cfg(not(debug_assertions))] - use ff_ext::ExtensionField; - use goldilocks::{Goldilocks, GoldilocksExt2}; - use singer_utils::{constants::RANGE_CHIP_BIT_WIDTH, structs::TSUInt}; - use std::collections::BTreeMap; - #[cfg(not(debug_assertions))] - use std::time::Instant; - #[cfg(not(debug_assertions))] - use transcript::Transcript; - - #[allow(deprecated)] - use crate::test::test_opcode_circuit; - use crate::{ - instructions::{CalldataloadInstruction, ChipChallenges, Instruction}, - test::get_uint_params, - utils::u64vec, - }; - - #[test] - fn test_calldataload_construct_circuit() { - let challenges = ChipChallenges::default(); - - let phase0_idx_map = CalldataloadInstruction::phase0_idxes_map(); - let phase0_witness_size = CalldataloadInstruction::phase0_size(); - - #[cfg(feature = "witness-count")] - { - println!("CALLDATALOAD: {:?}", &phase0_idx_map); - println!("CALLDATALOAD witness_size: {:?}", phase0_witness_size); - } - - // initialize general test inputs associated with push1 - let inst_circuit = CalldataloadInstruction::construct_circuit(challenges).unwrap(); - - #[cfg(feature = "test-dbg")] - println!("{:?}", inst_circuit); - - let mut phase0_values_map = BTreeMap::>::new(); - phase0_values_map.insert("phase0_pc".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_ts".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_stack_ts".to_string(), vec![Goldilocks::from(3u64)]); - phase0_values_map.insert("phase0_memory_ts".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_stack_top".to_string(), vec![Goldilocks::from( - 100u64, - )]); - phase0_values_map.insert("phase0_clk".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert( - "phase0_pc_add".to_string(), - vec![], // carry is 0, may test carry using larger values in PCUInt - ); - phase0_values_map.insert("phase0_stack_ts_add".to_string(), vec![ - Goldilocks::from(4u64), /* first TSUInt::N_RANGE_CELLS = 1*(56/16) = 4 cells are - * range values, stack_ts + 1 = 4 */ - Goldilocks::from(0u64), - Goldilocks::from(0u64), - Goldilocks::from(0u64), - // no place for carry - ]); - phase0_values_map.insert("phase0_old_stack_ts".to_string(), vec![Goldilocks::from( - 2u64, - )]); - let m: u64 = (1 << get_uint_params::().1) - 1; - let range_values = u64vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); - phase0_values_map.insert("phase0_old_stack_ts_lt".to_string(), vec![ - Goldilocks::from(range_values[0]), - Goldilocks::from(range_values[1]), - Goldilocks::from(range_values[2]), - Goldilocks::from(1u64), // borrow - ]); - phase0_values_map.insert("phase0_data".to_string(), vec![ - Goldilocks::from(7u64), - Goldilocks::from(6u64), - Goldilocks::from(5u64), - Goldilocks::from(4u64), - Goldilocks::from(3u64), - Goldilocks::from(2u64), - Goldilocks::from(1u64), - Goldilocks::from(0u64), - ]); - phase0_values_map.insert("phase0_offset".to_string(), vec![Goldilocks::from(1u64)]); - - let circuit_witness_challenges = vec![ - GoldilocksExt2::from(2), - GoldilocksExt2::from(2), - GoldilocksExt2::from(2), - ]; - - #[allow(deprecated)] - let _circuit_witness = test_opcode_circuit( - &inst_circuit, - &phase0_idx_map, - phase0_witness_size, - &phase0_values_map, - circuit_witness_challenges, - ); - } - - #[cfg(not(debug_assertions))] - fn bench_calldataload_instruction_helper(instance_num_vars: usize) { - let chip_challenges = ChipChallenges::default(); - let circuit_builder = - SingerCircuitBuilder::::new(chip_challenges).expect("circuit builder failed"); - let mut singer_builder = SingerGraphBuilder::::default(); - - let mut rng = test_rng(); - let size = CalldataloadInstruction::phase0_size(); - let phase0: CircuitWiresIn = vec![ - (0..(1 << instance_num_vars)) - .map(|_| { - (0..size) - .map(|_| E::BaseField::random(&mut rng)) - .collect::>() - }) - .collect::>() - .into(), - ]; - - let real_challenges = vec![E::random(&mut rng), E::random(&mut rng)]; - - let timer = Instant::now(); - - let _ = CalldataloadInstruction::construct_graph_and_witness( - &mut singer_builder.graph_builder, - &mut singer_builder.chip_builder, - &circuit_builder.insts_circuits - [>::OPCODE as usize], - vec![phase0], - &real_challenges, - 1 << instance_num_vars, - &SingerParams::default(), - ) - .expect("gkr graph construction failed"); - - let (graph, wit) = singer_builder.graph_builder.finalize_graph_and_witness(); - - println!( - "CalldataloadInstruction::construct_graph_and_witness, instance_num_vars = {}, time = {}", - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - - let point = vec![E::random(&mut rng), E::random(&mut rng)]; - let target_evals = graph.target_evals(&wit, &point); - - let prover_transcript = &mut Transcript::new(b"Singer"); - - let timer = Instant::now(); - let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, prover_transcript, 1) - .expect("prove failed"); - println!( - "CalldataloadInstruction::prove, instance_num_vars = {}, time = {}", - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - } - - #[test] - #[cfg(not(debug_assertions))] - fn bench_calldataload_instruction() { - bench_calldataload_instruction_helper::(10); - } -} diff --git a/singer/src/instructions/dup.rs b/singer/src/instructions/dup.rs deleted file mode 100644 index 246dc1685..000000000 --- a/singer/src/instructions/dup.rs +++ /dev/null @@ -1,350 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; -use gkr::structs::Circuit; -use paste::paste; -use simple_frontend::structs::{CircuitBuilder, MixedCell}; -use singer_utils::{ - chip_handler::{ - ChipHandler, bytecode::BytecodeChip, global_state::GlobalStateChip, range::RangeChip, - stack::StackChip, - }, - constants::OpcodeType, - register_witness, - structs::{PCUInt, StackUInt, TSUInt}, - uint::constants::AddSubConstants, -}; -use std::{collections::BTreeMap, sync::Arc}; - -use crate::error::ZKVMError; - -use super::{ChipChallenges, InstCircuit, InstCircuitLayout, Instruction, InstructionGraph}; - -pub struct DupInstruction; - -impl InstructionGraph for DupInstruction { - type InstType = Self; -} - -register_witness!( - DupInstruction, - phase0 { - pc => PCUInt::N_OPERAND_CELLS, - stack_ts => TSUInt::N_OPERAND_CELLS, - memory_ts => TSUInt::N_OPERAND_CELLS, - stack_top => 1, - clk => 1, - - pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, - - stack_values => StackUInt::N_OPERAND_CELLS, - old_stack_ts => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt => AddSubConstants::::N_WITNESS_CELLS - } -); - -impl Instruction for DupInstruction { - const OPCODE: OpcodeType = match N { - 1 => OpcodeType::DUP1, - 2 => OpcodeType::DUP2, - _ => unimplemented!(), - }; - const NAME: &'static str = match N { - 1 => "DUP1", - 2 => "DUP2", - _ => unimplemented!(), - }; - fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { - let mut circuit_builder = CircuitBuilder::default(); - let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); - - let mut chip_handler = ChipHandler::new(challenges); - - // State update - let pc = PCUInt::try_from(&phase0[Self::phase0_pc()])?; - let stack_ts = TSUInt::try_from(&phase0[Self::phase0_stack_ts()])?; - let memory_ts = &phase0[Self::phase0_memory_ts()]; - let stack_top = phase0[Self::phase0_stack_top().start]; - let stack_top_expr = MixedCell::Cell(stack_top); - let clk = phase0[Self::phase0_clk().start]; - let clk_expr = MixedCell::Cell(clk); - GlobalStateChip::state_in( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - stack_ts.values(), - memory_ts, - stack_top, - clk, - ); - - let next_pc = - RangeChip::add_pc_const(&mut circuit_builder, &pc, 1, &phase0[Self::phase0_pc_add()])?; - let next_stack_ts = RangeChip::add_ts_with_const( - &mut chip_handler, - &mut circuit_builder, - &stack_ts, - 1, - &phase0[Self::phase0_stack_ts_add()], - )?; - - GlobalStateChip::state_out( - &mut chip_handler, - &mut circuit_builder, - next_pc.values(), - next_stack_ts.values(), - memory_ts, - stack_top_expr.add(E::BaseField::from(1)), - clk_expr.add(E::BaseField::ONE), - ); - - // Check the range of stack_top - N is within [0, 1 << STACK_TOP_BIT_WIDTH). - RangeChip::range_check_stack_top( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(N as u64)), - )?; - - // Pop rlc of stack[top - N] from stack - let old_stack_ts = (&phase0[Self::phase0_old_stack_ts()]).try_into()?; - TSUInt::assert_lt( - &mut circuit_builder, - &mut chip_handler, - &old_stack_ts, - &stack_ts, - &phase0[Self::phase0_old_stack_ts_lt()], - )?; - let stack_values = &phase0[Self::phase0_stack_values()]; - StackChip::pop( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(1)), - old_stack_ts.values(), - stack_values, - ); - - // Check the range of stack_top within [0, 1 << STACK_TOP_BIT_WIDTH). - RangeChip::range_check_stack_top( - &mut chip_handler, - &mut circuit_builder, - stack_top.into(), - )?; - // Push stack_values twice to stack - StackChip::push( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(1)), - stack_ts.values(), - stack_values, - ); - StackChip::push( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr, - stack_ts.values(), - stack_values, - ); - - // Bytecode check for (pc, DUP{N}) - BytecodeChip::bytecode_with_pc_opcode( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - >::OPCODE, - ); - - let (ram_load_id, ram_store_id, rom_id) = chip_handler.finalize(&mut circuit_builder); - circuit_builder.configure(); - - let outputs_wire_id = [ram_load_id, ram_store_id, rom_id]; - - Ok(InstCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - layout: InstCircuitLayout { - chip_check_wire_id: outputs_wire_id, - phases_wire_id: vec![phase0_wire_id], - ..Default::default() - }, - }) - } -} - -#[cfg(test)] -mod test { - #[cfg(not(debug_assertions))] - use crate::{ - CircuitWiresIn, SingerGraphBuilder, SingerParams, - instructions::{InstructionGraph, SingerCircuitBuilder}, - scheme::GKRGraphProverState, - }; - #[cfg(not(debug_assertions))] - use ark_std::test_rng; - #[cfg(not(debug_assertions))] - use ff::Field; - #[cfg(not(debug_assertions))] - use ff_ext::ExtensionField; - use goldilocks::{Goldilocks, GoldilocksExt2}; - use singer_utils::{constants::RANGE_CHIP_BIT_WIDTH, structs::TSUInt}; - use std::collections::BTreeMap; - #[cfg(not(debug_assertions))] - use std::time::Instant; - #[cfg(not(debug_assertions))] - use transcript::Transcript; - - #[allow(deprecated)] - use crate::test::test_opcode_circuit; - use crate::{ - instructions::{ChipChallenges, DupInstruction, Instruction}, - test::get_uint_params, - utils::u64vec, - }; - - #[test] - fn test_dup1_construct_circuit() { - let challenges = ChipChallenges::default(); - - let phase0_idx_map = DupInstruction::<1>::phase0_idxes_map(); - let phase0_witness_size = DupInstruction::<1>::phase0_size(); - - #[cfg(feature = "witness-count")] - { - println!("DUP1: {:?}", &phase0_idx_map); - println!("DUP1 witness_size = {:?}", phase0_witness_size); - } - - // initialize general test inputs associated with push1 - let inst_circuit = DupInstruction::<1>::construct_circuit(challenges).unwrap(); - - #[cfg(feature = "test-dbg")] - println!("{:?}", inst_circuit); - - let mut phase0_values_map = BTreeMap::>::new(); - phase0_values_map.insert("phase0_pc".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_stack_ts".to_string(), vec![Goldilocks::from(2u64)]); - phase0_values_map.insert("phase0_memory_ts".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_stack_top".to_string(), vec![Goldilocks::from( - 100u64, - )]); - phase0_values_map.insert("phase0_clk".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert( - "phase0_pc_add".to_string(), - vec![], // carry is 0, may test carry using larger values in PCUInt - ); - phase0_values_map.insert("phase0_stack_ts_add".to_string(), vec![ - Goldilocks::from(3u64), /* first TSUInt::N_RANGE_CELLS = 1*(56/16) = 4 cells are - * range values, stack_ts + 1 = 4 */ - Goldilocks::from(0u64), - Goldilocks::from(0u64), - Goldilocks::from(0u64), - // no place for carry - ]); - phase0_values_map.insert("phase0_stack_values".to_string(), vec![ - Goldilocks::from(7u64), - Goldilocks::from(6u64), - Goldilocks::from(5u64), - Goldilocks::from(4u64), - Goldilocks::from(3u64), - Goldilocks::from(2u64), - Goldilocks::from(1u64), - Goldilocks::from(0u64), - ]); - phase0_values_map.insert("phase0_old_stack_ts".to_string(), vec![Goldilocks::from( - 1u64, - )]); - let m: u64 = (1 << get_uint_params::().1) - 1; - let range_values = u64vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); - phase0_values_map.insert("phase0_old_stack_ts_lt".to_string(), vec![ - Goldilocks::from(range_values[0]), - Goldilocks::from(range_values[1]), - Goldilocks::from(range_values[2]), - Goldilocks::from(1u64), - ]); - - let circuit_witness_challenges = vec![ - GoldilocksExt2::from(2), - GoldilocksExt2::from(2), - GoldilocksExt2::from(2), - ]; - - #[allow(deprecated)] - let _circuit_witness = test_opcode_circuit( - &inst_circuit, - &phase0_idx_map, - phase0_witness_size, - &phase0_values_map, - circuit_witness_challenges, - ); - } - - #[cfg(not(debug_assertions))] - fn bench_dup_instruction_helper(instance_num_vars: usize) { - let chip_challenges = ChipChallenges::default(); - let circuit_builder = - SingerCircuitBuilder::::new(chip_challenges).expect("circuit builder failed"); - let mut singer_builder = SingerGraphBuilder::::default(); - - let mut rng = test_rng(); - let size = DupInstruction::::phase0_size(); - let phase0: CircuitWiresIn = vec![ - (0..(1 << instance_num_vars)) - .map(|_| { - (0..size) - .map(|_| E::BaseField::random(&mut rng)) - .collect::>() - }) - .collect::>() - .into(), - ]; - - let real_challenges = vec![E::random(&mut rng), E::random(&mut rng)]; - - let timer = Instant::now(); - - let _ = DupInstruction::::construct_graph_and_witness( - &mut singer_builder.graph_builder, - &mut singer_builder.chip_builder, - &circuit_builder.insts_circuits[ as Instruction>::OPCODE as usize], - vec![phase0], - &real_challenges, - 1 << instance_num_vars, - &SingerParams::default(), - ) - .expect("gkr graph construction failed"); - - let (graph, wit) = singer_builder.graph_builder.finalize_graph_and_witness(); - - println!( - "Dup{}Instruction::construct_graph_and_witness, instance_num_vars = {}, time = {}", - N, - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - - let point = vec![E::random(&mut rng), E::random(&mut rng)]; - let target_evals = graph.target_evals(&wit, &point); - - let prover_transcript = &mut Transcript::new(b"Singer"); - - let timer = Instant::now(); - let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, prover_transcript, 1) - .expect("prove failed"); - println!( - "Dup{}Instruction::prove, instance_num_vars = {}, time = {}", - N, - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - } - - #[test] - #[cfg(not(debug_assertions))] - fn bench_dup1_instruction() { - bench_dup_instruction_helper::(10); - } - - #[test] - #[cfg(not(debug_assertions))] - fn bench_dup2_instruction() { - bench_dup_instruction_helper::(10); - } -} diff --git a/singer/src/instructions/gt.rs b/singer/src/instructions/gt.rs deleted file mode 100644 index 771d5315d..000000000 --- a/singer/src/instructions/gt.rs +++ /dev/null @@ -1,364 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; -use gkr::structs::Circuit; -use paste::paste; -use simple_frontend::structs::{CircuitBuilder, MixedCell}; -use singer_utils::{ - chip_handler::{ - ChipHandler, bytecode::BytecodeChip, global_state::GlobalStateChip, range::RangeChip, - stack::StackChip, - }, - constants::OpcodeType, - register_witness, - structs::{PCUInt, StackUInt, TSUInt}, - uint::constants::AddSubConstants, -}; -use std::{collections::BTreeMap, sync::Arc}; - -use crate::error::ZKVMError; - -use super::{ChipChallenges, InstCircuit, InstCircuitLayout, Instruction, InstructionGraph}; - -pub struct GtInstruction; - -impl InstructionGraph for GtInstruction { - type InstType = Self; -} - -register_witness!( - GtInstruction, - phase0 { - pc => PCUInt::N_OPERAND_CELLS, - stack_ts => TSUInt::N_OPERAND_CELLS, - memory_ts => TSUInt::N_OPERAND_CELLS, - stack_top => 1, - clk => 1, - - pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, - - old_stack_ts0 => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt0 => AddSubConstants::::N_WITNESS_CELLS, - old_stack_ts1 => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt1 => AddSubConstants::::N_WITNESS_CELLS, - - oprand_0 => StackUInt::N_OPERAND_CELLS, - oprand_1 => StackUInt::N_OPERAND_CELLS, - instruction_gt => AddSubConstants::::N_WITNESS_CELLS - } -); - -impl Instruction for GtInstruction { - const OPCODE: OpcodeType = OpcodeType::GT; - const NAME: &'static str = "GT"; - fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { - let mut circuit_builder = CircuitBuilder::default(); - let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); - - let mut chip_handler = ChipHandler::new(challenges); - - // State update - let pc = PCUInt::try_from(&phase0[Self::phase0_pc()])?; - let stack_ts = TSUInt::try_from(&phase0[Self::phase0_stack_ts()])?; - let memory_ts = &phase0[Self::phase0_memory_ts()]; - let stack_top = phase0[Self::phase0_stack_top().start]; - let stack_top_expr = MixedCell::Cell(stack_top); - let clk = phase0[Self::phase0_clk().start]; - let clk_expr = MixedCell::Cell(clk); - GlobalStateChip::state_in( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - stack_ts.values(), - memory_ts, - stack_top, - clk, - ); - - let next_pc = - RangeChip::add_pc_const(&mut circuit_builder, &pc, 1, &phase0[Self::phase0_pc_add()])?; - let next_stack_ts = RangeChip::add_ts_with_const( - &mut chip_handler, - &mut circuit_builder, - &stack_ts, - 1, - &phase0[Self::phase0_stack_ts_add()], - )?; - - GlobalStateChip::state_out( - &mut chip_handler, - &mut circuit_builder, - next_pc.values(), - next_stack_ts.values(), - memory_ts, - stack_top_expr.sub(E::BaseField::from(1)), - clk_expr.add(E::BaseField::ONE), - ); - - // Execution result = addend0 + addend1, with carry. - let oprand_0 = (&phase0[Self::phase0_oprand_0()]).try_into()?; - let oprand_1 = (&phase0[Self::phase0_oprand_1()]).try_into()?; - let (result, _) = StackUInt::lt( - &mut circuit_builder, - &mut chip_handler, - &oprand_1, - &oprand_0, - &phase0[Self::phase0_instruction_gt()], - )?; - - // Check the range of stack_top - 2 is within [0, 1 << STACK_TOP_BIT_WIDTH). - RangeChip::range_check_stack_top( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(2)), - )?; - - // Pop two values from stack - let old_stack_ts0 = (&phase0[Self::phase0_old_stack_ts0()]).try_into()?; - TSUInt::assert_lt( - &mut circuit_builder, - &mut chip_handler, - &old_stack_ts0, - &stack_ts, - &phase0[Self::phase0_old_stack_ts_lt0()], - )?; - StackChip::pop( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(1)), - old_stack_ts0.values(), - oprand_0.values(), - ); - - let old_stack_ts1 = (&phase0[Self::phase0_old_stack_ts1()]).try_into()?; - TSUInt::assert_lt( - &mut circuit_builder, - &mut chip_handler, - &old_stack_ts1, - &stack_ts, - &phase0[Self::phase0_old_stack_ts_lt1()], - )?; - StackChip::pop( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(2)), - old_stack_ts1.values(), - oprand_1.values(), - ); - - // Push one result to stack. Since values are little-endien, it is - // equivalent to pad result with 0s. - StackChip::push( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(2)), - stack_ts.values(), - &[result], - ); - - // Bytecode check for (pc, gt) - BytecodeChip::bytecode_with_pc_opcode( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - >::OPCODE, - ); - - let (ram_load_id, ram_store_id, rom_id) = chip_handler.finalize(&mut circuit_builder); - circuit_builder.configure(); - - let outputs_wire_id = [ram_load_id, ram_store_id, rom_id]; - - Ok(InstCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - layout: InstCircuitLayout { - chip_check_wire_id: outputs_wire_id, - phases_wire_id: vec![phase0_wire_id], - ..Default::default() - }, - }) - } -} - -#[cfg(test)] -mod test { - #[cfg(not(debug_assertions))] - use crate::{ - CircuitWiresIn, SingerGraphBuilder, SingerParams, instructions::InstructionGraph, - instructions::SingerCircuitBuilder, scheme::GKRGraphProverState, - }; - #[cfg(not(debug_assertions))] - use ark_std::test_rng; - #[cfg(not(debug_assertions))] - use ff::Field; - #[cfg(not(debug_assertions))] - use ff_ext::ExtensionField; - #[cfg(not(debug_assertions))] - use std::time::Instant; - #[cfg(not(debug_assertions))] - use transcript::Transcript; - - use goldilocks::{Goldilocks, GoldilocksExt2}; - use singer_utils::{constants::RANGE_CHIP_BIT_WIDTH, structs::TSUInt}; - use std::collections::BTreeMap; - - #[allow(deprecated)] - use crate::test::test_opcode_circuit; - use crate::{ - instructions::{ChipChallenges, GtInstruction, Instruction}, - test::get_uint_params, - utils::u64vec, - }; - - #[test] - fn test_gt_construct_circuit() { - let challenges = ChipChallenges::default(); - - let phase0_idx_map = GtInstruction::phase0_idxes_map(); - let phase0_witness_size = GtInstruction::phase0_size(); - - #[cfg(feature = "witness-count")] - { - println!("GT {:?}", &phase0_idx_map); - println!("GT witness_size {:?}", &phase0_witness_size); - } - - // initialize general test inputs associated with push1 - let inst_circuit = GtInstruction::construct_circuit(challenges).unwrap(); - - #[cfg(feature = "test-dbg")] - println!("{:?}", inst_circuit); - - let mut phase0_values_map = BTreeMap::>::new(); - phase0_values_map.insert("phase0_pc".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_stack_ts".to_string(), vec![Goldilocks::from(3u64)]); - phase0_values_map.insert("phase0_memory_ts".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_stack_top".to_string(), vec![Goldilocks::from( - 100u64, - )]); - phase0_values_map.insert("phase0_clk".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert( - "phase0_pc_add".to_string(), - vec![], // carry is 0, may test carry using larger values in PCUInt - ); - phase0_values_map.insert("phase0_stack_ts_add".to_string(), vec![ - Goldilocks::from(4u64), /* first TSUInt::N_RANGE_CELLS = 1*(56/16) = 4 cells are - * range values, stack_ts + 1 = 4 */ - Goldilocks::from(0u64), - Goldilocks::from(0u64), - Goldilocks::from(0u64), - // no place for carry - ]); - phase0_values_map.insert("phase0_old_stack_ts0".to_string(), vec![Goldilocks::from( - 2u64, - )]); - let m: u64 = (1 << get_uint_params::().1) - 1; - let range_values = u64vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); - phase0_values_map.insert("phase0_old_stack_ts_lt0".to_string(), vec![ - Goldilocks::from(range_values[0]), - Goldilocks::from(range_values[1]), - Goldilocks::from(range_values[2]), - Goldilocks::from(1u64), - ]); - phase0_values_map.insert("phase0_old_stack_ts1".to_string(), vec![Goldilocks::from( - 1u64, - )]); - let m: u64 = (1 << get_uint_params::().1) - 2; - let range_values = u64vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); - phase0_values_map.insert("phase0_old_stack_ts_lt1".to_string(), vec![ - Goldilocks::from(range_values[0]), - Goldilocks::from(range_values[1]), - Goldilocks::from(range_values[2]), - Goldilocks::from(1u64), - ]); - phase0_values_map.insert("phase0_oprand_0".to_string(), vec![Goldilocks::from(2u64)]); - phase0_values_map.insert("phase0_oprand_1".to_string(), vec![Goldilocks::from(1u64)]); - // given borrow = [1,1,1,1,1,1,1,1] - // oprand_1 - oprand_0 is vec![2^32-1; 8] - // its range value is vec![2^16-1; 16] - let range_values = vec![Goldilocks::from(65535u64); 16]; - let borrow = vec![Goldilocks::from(1u64); 8]; - phase0_values_map.insert( - "phase0_instruction_gt".to_string(), - [range_values.as_slice(), borrow.as_slice()].concat(), - ); - let circuit_witness_challenges = vec![ - GoldilocksExt2::from(2), - GoldilocksExt2::from(2), - GoldilocksExt2::from(2), - ]; - - #[allow(deprecated)] - let _circuit_witness = test_opcode_circuit( - &inst_circuit, - &phase0_idx_map, - phase0_witness_size, - &phase0_values_map, - circuit_witness_challenges, - ); - } - - #[cfg(not(debug_assertions))] - fn bench_gt_instruction_helper(instance_num_vars: usize) { - let chip_challenges = ChipChallenges::default(); - let circuit_builder = - SingerCircuitBuilder::::new(chip_challenges).expect("circuit builder failed"); - let mut singer_builder = SingerGraphBuilder::::default(); - - let mut rng = test_rng(); - let size = GtInstruction::phase0_size(); - let phase0: CircuitWiresIn = vec![ - (0..(1 << instance_num_vars)) - .map(|_| { - (0..size) - .map(|_| E::BaseField::random(&mut rng)) - .collect::>() - }) - .collect::>() - .into(), - ]; - - let real_challenges = vec![E::random(&mut rng), E::random(&mut rng)]; - - let timer = Instant::now(); - - let _ = GtInstruction::construct_graph_and_witness( - &mut singer_builder.graph_builder, - &mut singer_builder.chip_builder, - &circuit_builder.insts_circuits[>::OPCODE as usize], - vec![phase0], - &real_challenges, - 1 << instance_num_vars, - &SingerParams::default(), - ) - .expect("gkr graph construction failed"); - - let (graph, wit) = singer_builder.graph_builder.finalize_graph_and_witness(); - - println!( - "GtInstruction::construct_graph_and_witness, instance_num_vars = {}, time = {}", - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - - let point = vec![E::random(&mut rng), E::random(&mut rng)]; - let target_evals = graph.target_evals(&wit, &point); - - let prover_transcript = &mut Transcript::new(b"Singer"); - - let timer = Instant::now(); - let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, prover_transcript, 1) - .expect("prove failed"); - println!( - "GtInstruction::prove, instance_num_vars = {}, time = {}", - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - } - - #[test] - #[cfg(not(debug_assertions))] - fn bench_gt_instruction() { - bench_gt_instruction_helper::(10); - } -} diff --git a/singer/src/instructions/jump.rs b/singer/src/instructions/jump.rs deleted file mode 100644 index ebd065f1c..000000000 --- a/singer/src/instructions/jump.rs +++ /dev/null @@ -1,289 +0,0 @@ -use std::sync::Arc; - -use ff::Field; -use ff_ext::ExtensionField; -use gkr::structs::Circuit; -use paste::paste; -use simple_frontend::structs::{CircuitBuilder, MixedCell}; -use singer_utils::{ - chip_handler::{ - ChipHandler, bytecode::BytecodeChip, global_state::GlobalStateChip, range::RangeChip, - stack::StackChip, - }, - constants::OpcodeType, - register_witness, - structs::{PCUInt, TSUInt}, - uint::constants::AddSubConstants, -}; -use std::collections::BTreeMap; - -use crate::error::ZKVMError; - -use super::{ChipChallenges, InstCircuit, InstCircuitLayout, Instruction, InstructionGraph}; - -pub struct JumpInstruction; - -impl InstructionGraph for JumpInstruction { - type InstType = Self; -} - -register_witness!( - JumpInstruction, - phase0 { - pc => PCUInt::N_OPERAND_CELLS, - stack_ts => TSUInt::N_OPERAND_CELLS, - memory_ts => TSUInt::N_OPERAND_CELLS, - stack_top => 1, - clk => 1, - - next_pc => PCUInt::N_OPERAND_CELLS, - - old_stack_ts => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt => AddSubConstants::::N_WITNESS_CELLS - } -); - -impl Instruction for JumpInstruction { - const OPCODE: OpcodeType = OpcodeType::JUMP; - const NAME: &'static str = "JUMP"; - fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { - let mut circuit_builder = CircuitBuilder::default(); - let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); - - let mut chip_handler = ChipHandler::new(challenges); - - // State update - let pc = PCUInt::try_from(&phase0[Self::phase0_pc()])?; - let stack_ts = TSUInt::try_from(&phase0[Self::phase0_stack_ts()])?; - let memory_ts = &phase0[Self::phase0_memory_ts()]; - let stack_top = phase0[Self::phase0_stack_top().start]; - let stack_top_expr = MixedCell::Cell(stack_top); - let clk = phase0[Self::phase0_clk().start]; - let clk_expr = MixedCell::Cell(clk); - GlobalStateChip::state_in( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - stack_ts.values(), - memory_ts, - stack_top, - clk, - ); - - // Pop next pc from stack - RangeChip::range_check_stack_top( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::ONE), - )?; - - let next_pc = &phase0[Self::phase0_next_pc()]; - let old_stack_ts = (&phase0[Self::phase0_old_stack_ts()]).try_into()?; - TSUInt::assert_lt( - &mut circuit_builder, - &mut chip_handler, - &old_stack_ts, - &stack_ts, - &phase0[Self::phase0_old_stack_ts_lt()], - )?; - StackChip::pop( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::ONE), - old_stack_ts.values(), - next_pc, - ); - - GlobalStateChip::state_out( - &mut chip_handler, - &mut circuit_builder, - next_pc, - stack_ts.values(), // Because there is no stack push. - memory_ts, - stack_top_expr.sub(E::BaseField::from(1)), - clk_expr.add(E::BaseField::ONE), - ); - - // Bytecode check for (pc, jump) - BytecodeChip::bytecode_with_pc_opcode( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - >::OPCODE, - ); - // Bytecode check for (next_pc, jumpdest) - BytecodeChip::bytecode_with_pc_opcode( - &mut chip_handler, - &mut circuit_builder, - next_pc, - OpcodeType::JUMPDEST, - ); - - let (ram_load_id, ram_store_id, rom_id) = chip_handler.finalize(&mut circuit_builder); - circuit_builder.configure(); - - let outputs_wire_id = [ram_load_id, ram_store_id, rom_id]; - - Ok(InstCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - layout: InstCircuitLayout { - chip_check_wire_id: outputs_wire_id, - phases_wire_id: vec![phase0_wire_id], - ..Default::default() - }, - }) - } -} - -#[cfg(test)] -mod test { - #[allow(deprecated)] - use crate::test::test_opcode_circuit; - #[cfg(not(debug_assertions))] - use crate::{ - CircuitWiresIn, SingerGraphBuilder, SingerParams, - instructions::{InstructionGraph, SingerCircuitBuilder}, - scheme::GKRGraphProverState, - }; - use crate::{ - instructions::{ChipChallenges, Instruction, JumpInstruction}, - test::get_uint_params, - utils::u64vec, - }; - #[cfg(not(debug_assertions))] - use ark_std::test_rng; - #[cfg(not(debug_assertions))] - use ff::Field; - #[cfg(not(debug_assertions))] - use ff_ext::ExtensionField; - use goldilocks::{Goldilocks, GoldilocksExt2}; - use singer_utils::{constants::RANGE_CHIP_BIT_WIDTH, structs::TSUInt}; - use std::collections::BTreeMap; - #[cfg(not(debug_assertions))] - use std::time::Instant; - #[cfg(not(debug_assertions))] - use transcript::Transcript; - - #[test] - fn test_jump_construct_circuit() { - let challenges = ChipChallenges::default(); - - let phase0_idx_map = JumpInstruction::phase0_idxes_map(); - let phase0_witness_size = JumpInstruction::phase0_size(); - - #[cfg(feature = "witness-count")] - { - println!("JUMP {:?}", &phase0_idx_map); - println!("JUMP witness_size = {:?}", phase0_witness_size); - } - - // initialize general test inputs associated with push1 - let inst_circuit = JumpInstruction::construct_circuit(challenges).unwrap(); - - #[cfg(feature = "test-dbg")] - println!("{:?}", inst_circuit); - - let mut phase0_values_map = BTreeMap::>::new(); - phase0_values_map.insert("phase0_pc".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_stack_ts".to_string(), vec![Goldilocks::from(2u64)]); - phase0_values_map.insert("phase0_memory_ts".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_stack_top".to_string(), vec![Goldilocks::from( - 100u64, - )]); - phase0_values_map.insert("phase0_clk".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_next_pc".to_string(), vec![ - Goldilocks::from(127u64), - Goldilocks::from(125u64), - ]); - phase0_values_map.insert("phase0_old_stack_ts".to_string(), vec![Goldilocks::from( - 1u64, - )]); - let m: u64 = (1 << get_uint_params::().1) - 1; - let range_values = u64vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); - phase0_values_map.insert("phase0_old_stack_ts_lt".to_string(), vec![ - Goldilocks::from(range_values[0]), - Goldilocks::from(range_values[1]), - Goldilocks::from(range_values[2]), - Goldilocks::from(1u64), // current length has no cells for borrow - ]); - - let circuit_witness_challenges = vec![ - GoldilocksExt2::from(2), - GoldilocksExt2::from(2), - GoldilocksExt2::from(2), - ]; - - #[allow(deprecated)] - let _circuit_witness = test_opcode_circuit( - &inst_circuit, - &phase0_idx_map, - phase0_witness_size, - &phase0_values_map, - circuit_witness_challenges, - ); - } - - #[cfg(not(debug_assertions))] - fn bench_jump_instruction_helper(instance_num_vars: usize) { - let chip_challenges = ChipChallenges::default(); - let circuit_builder = - SingerCircuitBuilder::::new(chip_challenges).expect("circuit builder failed"); - let mut singer_builder = SingerGraphBuilder::::default(); - - let mut rng = test_rng(); - let size = JumpInstruction::phase0_size(); - let phase0: CircuitWiresIn = vec![ - (0..(1 << instance_num_vars)) - .map(|_| { - (0..size) - .map(|_| E::BaseField::random(&mut rng)) - .collect::>() - }) - .collect::>() - .into(), - ]; - - let real_challenges = vec![E::random(&mut rng), E::random(&mut rng)]; - - let timer = Instant::now(); - - let _ = JumpInstruction::construct_graph_and_witness( - &mut singer_builder.graph_builder, - &mut singer_builder.chip_builder, - &circuit_builder.insts_circuits[>::OPCODE as usize], - vec![phase0], - &real_challenges, - 1 << instance_num_vars, - &SingerParams::default(), - ) - .expect("gkr graph construction failed"); - - let (graph, wit) = singer_builder.graph_builder.finalize_graph_and_witness(); - - println!( - "JumpInstruction::construct_graph_and_witness, instance_num_vars = {}, time = {}", - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - - let point = vec![E::random(&mut rng), E::random(&mut rng)]; - let target_evals = graph.target_evals(&wit, &point); - - let prover_transcript = &mut Transcript::new(b"Singer"); - - let timer = Instant::now(); - let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, prover_transcript, 1) - .expect("prove failed"); - println!( - "JumpInstruction::prove, instance_num_vars = {}, time = {}", - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - } - - #[test] - #[cfg(not(debug_assertions))] - fn bench_jump_instruction() { - bench_jump_instruction_helper::(10); - } -} diff --git a/singer/src/instructions/jumpdest.rs b/singer/src/instructions/jumpdest.rs deleted file mode 100644 index 38145791b..000000000 --- a/singer/src/instructions/jumpdest.rs +++ /dev/null @@ -1,239 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; -use gkr::structs::Circuit; -use paste::paste; -use simple_frontend::structs::{CircuitBuilder, MixedCell}; -use singer_utils::{ - chip_handler::{ - ChipHandler, bytecode::BytecodeChip, global_state::GlobalStateChip, range::RangeChip, - }, - constants::OpcodeType, - register_witness, - structs::{PCUInt, TSUInt}, - uint::constants::AddSubConstants, -}; -use std::{collections::BTreeMap, sync::Arc}; - -use crate::error::ZKVMError; - -use super::{ChipChallenges, InstCircuit, InstCircuitLayout, Instruction, InstructionGraph}; - -pub struct JumpdestInstruction; - -impl InstructionGraph for JumpdestInstruction { - type InstType = Self; -} - -register_witness!( - JumpdestInstruction, - phase0 { - pc => PCUInt::N_OPERAND_CELLS , - stack_ts=> TSUInt::N_OPERAND_CELLS, - memory_ts => TSUInt::N_OPERAND_CELLS, - stack_top => 1, - clk => 1, - - pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS - } -); - -impl Instruction for JumpdestInstruction { - const OPCODE: OpcodeType = OpcodeType::JUMPDEST; - const NAME: &'static str = "JUMPDEST"; - fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { - let mut circuit_builder = CircuitBuilder::default(); - let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); - - let mut chip_handler = ChipHandler::new(challenges); - - // State update - let pc = PCUInt::try_from(&phase0[Self::phase0_pc()])?; - let stack_ts = TSUInt::try_from(&phase0[Self::phase0_stack_ts()])?; - let memory_ts = &phase0[Self::phase0_memory_ts()]; - let stack_top = phase0[Self::phase0_stack_top().start]; - let clk = phase0[Self::phase0_clk().start]; - let clk_expr = MixedCell::Cell(clk); - GlobalStateChip::state_in( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - stack_ts.values(), - memory_ts, - stack_top, - clk, - ); - - let next_pc = - RangeChip::add_pc_const(&mut circuit_builder, &pc, 1, &phase0[Self::phase0_pc_add()])?; - GlobalStateChip::state_out( - &mut chip_handler, - &mut circuit_builder, - next_pc.values(), - stack_ts.values(), // Because there is no stack push. - memory_ts, - stack_top.into(), - clk_expr.add(E::BaseField::ONE), - ); - - // Bytecode check for (pc, jump) - BytecodeChip::bytecode_with_pc_opcode( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - >::OPCODE, - ); - - let (ram_load_id, ram_store_id, rom_id) = chip_handler.finalize(&mut circuit_builder); - circuit_builder.configure(); - - let outputs_wire_id = [ram_load_id, ram_store_id, rom_id]; - - Ok(InstCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - layout: InstCircuitLayout { - chip_check_wire_id: outputs_wire_id, - phases_wire_id: vec![phase0_wire_id], - ..Default::default() - }, - }) - } -} - -#[cfg(test)] -mod test { - #[cfg(not(debug_assertions))] - use crate::{ - CircuitWiresIn, SingerGraphBuilder, SingerParams, - instructions::{InstructionGraph, SingerCircuitBuilder}, - scheme::GKRGraphProverState, - }; - #[cfg(not(debug_assertions))] - use ark_std::test_rng; - #[cfg(not(debug_assertions))] - use ff::Field; - #[cfg(not(debug_assertions))] - use ff_ext::ExtensionField; - use goldilocks::{Goldilocks, GoldilocksExt2}; - use std::collections::BTreeMap; - #[cfg(not(debug_assertions))] - use std::time::Instant; - #[cfg(not(debug_assertions))] - use transcript::Transcript; - - use crate::instructions::{ChipChallenges, Instruction, JumpdestInstruction}; - #[allow(deprecated)] - use crate::test::test_opcode_circuit; - - #[test] - fn test_jumpdest_construct_circuit() { - let challenges = ChipChallenges::default(); - - let phase0_idx_map = JumpdestInstruction::phase0_idxes_map(); - let phase0_witness_size = JumpdestInstruction::phase0_size(); - - #[cfg(feature = "witness-count")] - { - println!("JUMPDEST {:?}", &phase0_idx_map); - println!("JUMPDEST witness_size = {:?}", phase0_witness_size); - } - - // initialize general test inputs associated with push1 - let inst_circuit = JumpdestInstruction::construct_circuit(challenges).unwrap(); - - #[cfg(feature = "test-dbg")] - println!("{:?}", inst_circuit); - - let mut phase0_values_map = BTreeMap::>::new(); - phase0_values_map.insert("phase0_pc".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_stack_ts".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_memory_ts".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_stack_top".to_string(), vec![Goldilocks::from( - 100u64, - )]); - phase0_values_map.insert("phase0_clk".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert( - "phase0_pc_add".to_string(), - vec![], // carry is 0, may test carry using larger values in PCUInt - ); - - let circuit_witness_challenges = vec![ - GoldilocksExt2::from(2), - GoldilocksExt2::from(2), - GoldilocksExt2::from(2), - ]; - - #[allow(deprecated)] - let _circuit_witness = test_opcode_circuit( - &inst_circuit, - &phase0_idx_map, - phase0_witness_size, - &phase0_values_map, - circuit_witness_challenges, - ); - } - - #[cfg(not(debug_assertions))] - fn bench_jumpdest_instruction_helper(instance_num_vars: usize) { - let chip_challenges = ChipChallenges::default(); - let circuit_builder = - SingerCircuitBuilder::::new(chip_challenges).expect("circuit builder failed"); - let mut singer_builder = SingerGraphBuilder::::default(); - - let mut rng = test_rng(); - let size = JumpdestInstruction::phase0_size(); - let phase0: CircuitWiresIn = vec![ - (0..(1 << instance_num_vars)) - .map(|_| { - (0..size) - .map(|_| E::BaseField::random(&mut rng)) - .collect::>() - }) - .collect::>() - .into(), - ]; - - let real_challenges = vec![E::random(&mut rng), E::random(&mut rng)]; - - let timer = Instant::now(); - - let _ = JumpdestInstruction::construct_graph_and_witness( - &mut singer_builder.graph_builder, - &mut singer_builder.chip_builder, - &circuit_builder.insts_circuits - [>::OPCODE as usize], - vec![phase0], - &real_challenges, - 1 << instance_num_vars, - &SingerParams::default(), - ) - .expect("gkr graph construction failed"); - - let (graph, wit) = singer_builder.graph_builder.finalize_graph_and_witness(); - - println!( - "JumpdestInstruction::construct_graph_and_witness, instance_num_vars = {}, time = {}", - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - - let point = vec![E::random(&mut rng), E::random(&mut rng)]; - let target_evals = graph.target_evals(&wit, &point); - - let prover_transcript = &mut Transcript::new(b"Singer"); - - let timer = Instant::now(); - let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, prover_transcript, 1) - .expect("prove failed"); - println!( - "JumpdestInstruction::prove, instance_num_vars = {}, time = {}", - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - } - - #[test] - #[cfg(not(debug_assertions))] - fn bench_jumpdest_instruction() { - bench_jumpdest_instruction_helper::(10); - } -} diff --git a/singer/src/instructions/jumpi.rs b/singer/src/instructions/jumpi.rs deleted file mode 100644 index 5da02a5af..000000000 --- a/singer/src/instructions/jumpi.rs +++ /dev/null @@ -1,210 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; -use gkr::structs::Circuit; -use itertools::izip; -use paste::paste; -use simple_frontend::structs::{CircuitBuilder, MixedCell}; -use singer_utils::{ - chip_handler::{ - ChipHandler, bytecode::BytecodeChip, global_state::GlobalStateChip, range::RangeChip, - stack::StackChip, - }, - constants::OpcodeType, - register_witness, - structs::{PCUInt, StackUInt, TSUInt}, - uint::constants::AddSubConstants, -}; -use std::{collections::BTreeMap, sync::Arc}; - -use crate::error::ZKVMError; - -use super::{ChipChallenges, InstCircuit, InstCircuitLayout, Instruction, InstructionGraph}; - -pub struct JumpiInstruction; - -impl InstructionGraph for JumpiInstruction { - type InstType = Self; -} - -register_witness!( - JumpiInstruction, - phase0 { - pc => PCUInt::N_OPERAND_CELLS , - stack_ts => TSUInt::N_OPERAND_CELLS, - memory_ts => TSUInt::N_OPERAND_CELLS, - stack_top => 1, - clk => 1, - - old_stack_ts_dest => TSUInt::N_OPERAND_CELLS, - old_stack_ts_dest_lt => AddSubConstants::::N_WITNESS_CELLS, - old_stack_ts_cond => TSUInt::N_OPERAND_CELLS, - old_stack_ts_cond_lt => AddSubConstants::::N_WITNESS_CELLS, - - dest_values => StackUInt::N_OPERAND_CELLS, - cond_values => StackUInt::N_OPERAND_CELLS, - cond_values_inv => StackUInt::N_OPERAND_CELLS, - cond_non_zero_or_inv => 1, - - pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - pc_plus_1_opcode => 1 - } -); - -impl Instruction for JumpiInstruction { - const OPCODE: OpcodeType = OpcodeType::JUMPI; - const NAME: &'static str = "JUMPI"; - fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { - let mut circuit_builder = CircuitBuilder::default(); - let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); - - let mut chip_handler = ChipHandler::new(challenges); - - // State update - let pc = PCUInt::try_from(&phase0[Self::phase0_pc()])?; - let stack_ts = TSUInt::try_from(&phase0[Self::phase0_stack_ts()])?; - let memory_ts = &phase0[Self::phase0_memory_ts()]; - let stack_top = phase0[Self::phase0_stack_top().start]; - let stack_top_expr = MixedCell::Cell(stack_top); - let clk = phase0[Self::phase0_clk().start]; - let clk_expr = MixedCell::Cell(clk); - GlobalStateChip::state_in( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - stack_ts.values(), - memory_ts, - stack_top, - clk, - ); - - // Range check stack_top - 2 - RangeChip::range_check_stack_top( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(2)), - )?; - - // Pop the destination pc from stack. - let dest_values = &phase0[Self::phase0_dest_values()]; - let dest_stack_addr = stack_top_expr.sub(E::BaseField::ONE); - - let old_stack_ts_dest = (&phase0[Self::phase0_old_stack_ts_dest()]).try_into()?; - TSUInt::assert_lt( - &mut circuit_builder, - &mut chip_handler, - &old_stack_ts_dest, - &stack_ts, - &phase0[Self::phase0_old_stack_ts_dest_lt()], - )?; - StackChip::pop( - &mut chip_handler, - &mut circuit_builder, - dest_stack_addr, - old_stack_ts_dest.values(), - dest_values, - ); - - // Pop the condition from stack. - let cond_values = &phase0[Self::phase0_cond_values()]; - let old_stack_ts_cond = (&phase0[Self::phase0_old_stack_ts_cond()]).try_into()?; - TSUInt::assert_lt( - &mut circuit_builder, - &mut chip_handler, - &old_stack_ts_cond, - &stack_ts, - &phase0[Self::phase0_old_stack_ts_cond_lt()], - )?; - - StackChip::pop( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(2)), - old_stack_ts_cond.values(), - cond_values, - ); - - // Execution, cond_values_non_zero[i] = [cond_values[i] != 0] - let cond_values_inv = &phase0[Self::phase0_cond_values_inv()]; - let mut cond_values_non_zero = Vec::new(); - for (val, wit) in izip!(cond_values, cond_values_inv) { - cond_values_non_zero.push(RangeChip::non_zero( - &mut chip_handler, - &mut circuit_builder, - *val, - *wit, - )?); - } - // cond_non_zero = [summation of cond_values_non_zero[i] != 0] - let non_zero_or = circuit_builder.create_cell(); - cond_values_non_zero - .iter() - .for_each(|x| circuit_builder.add(non_zero_or, *x, E::BaseField::ONE)); - let cond_non_zero_or_inv = phase0[Self::phase0_cond_non_zero_or_inv().start]; - let cond_non_zero = RangeChip::non_zero( - &mut chip_handler, - &mut circuit_builder, - non_zero_or, - cond_non_zero_or_inv, - )?; - - // If cond_non_zero, next_pc = dest, otherwise, pc = pc + 1 - let pc_add_1 = &phase0[Self::phase0_pc_add()]; - let pc_plus_1 = RangeChip::add_pc_const(&mut circuit_builder, &pc, 1, pc_add_1)?; - let pc_plus_1 = pc_plus_1.values(); - let next_pc = circuit_builder.create_cells(PCUInt::N_OPERAND_CELLS); - for i in 0..PCUInt::N_OPERAND_CELLS { - circuit_builder.select(next_pc[i], pc_plus_1[i], dest_values[i], cond_non_zero); - } - - // State out - GlobalStateChip::state_out( - &mut chip_handler, - &mut circuit_builder, - &next_pc, - stack_ts.values(), // Because there is no stack push. - memory_ts, - stack_top_expr.sub(E::BaseField::from(2)), - clk_expr.add(E::BaseField::ONE), - ); - - // Bytecode check for (pc, jumpi) - BytecodeChip::bytecode_with_pc_opcode( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - >::OPCODE, - ); - - // If cond_non_zero, next_opcode = JUMPDEST, otherwise, opcode = pc + 1 opcode - let pc_plus_1_opcode = phase0[Self::phase0_pc_plus_1_opcode().start]; - let next_opcode = circuit_builder.create_cell(); - circuit_builder.sel_mixed( - next_opcode, - pc_plus_1_opcode.into(), - MixedCell::Constant(E::BaseField::from(OpcodeType::JUMPDEST as u64)), - cond_non_zero, - ); - - // Bytecode check for (next_pc, next_opcode) - BytecodeChip::bytecode_with_pc_byte( - &mut chip_handler, - &mut circuit_builder, - &next_pc, - next_opcode, - ); - - let (ram_load_id, ram_store_id, rom_id) = chip_handler.finalize(&mut circuit_builder); - circuit_builder.configure(); - - let outputs_wire_id = [ram_load_id, ram_store_id, rom_id]; - - Ok(InstCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - layout: InstCircuitLayout { - chip_check_wire_id: outputs_wire_id, - phases_wire_id: vec![phase0_wire_id], - ..Default::default() - }, - }) - } -} diff --git a/singer/src/instructions/mstore.rs b/singer/src/instructions/mstore.rs deleted file mode 100644 index f9865f970..000000000 --- a/singer/src/instructions/mstore.rs +++ /dev/null @@ -1,578 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; -use gkr::structs::Circuit; -use gkr_graph::structs::{CircuitGraphBuilder, NodeOutputType, PredType}; -use paste::paste; -use simple_frontend::structs::{CircuitBuilder, MixedCell}; -use singer_utils::{ - chip_handler::{ - ChipHandler, bytecode::BytecodeChip, global_state::GlobalStateChip, memory::MemoryChip, - range::RangeChip, stack::StackChip, - }, - chips::SingerChipBuilder, - constants::{EVM_STACK_BYTE_WIDTH, OpcodeType}, - register_witness, - structs::{PCUInt, StackUInt, TSUInt}, - uint::constants::AddSubConstants, -}; -use std::{collections::BTreeMap, mem, sync::Arc}; - -use crate::{CircuitWiresIn, SingerParams, error::ZKVMError, utils::add_assign_each_cell}; - -use super::{ChipChallenges, InstCircuit, InstCircuitLayout, Instruction, InstructionGraph}; - -pub struct MstoreInstruction; - -impl InstructionGraph for MstoreInstruction { - type InstType = Self; - - fn construct_circuits(challenges: ChipChallenges) -> Result>, ZKVMError> { - let circuits = vec![ - MstoreInstruction::construct_circuit(challenges)?, - MstoreAccessory::construct_circuit(challenges)?, - ]; - Ok(circuits) - } - - fn construct_graph_and_witness( - graph_builder: &mut CircuitGraphBuilder, - chip_builder: &mut SingerChipBuilder, - inst_circuits: &[InstCircuit], - mut sources: Vec>, - real_challenges: &[E], - real_n_instances: usize, - _: &SingerParams, - ) -> Result, ZKVMError> { - // Add the instruction circuit to the graph. - let inst_circuit = &inst_circuits[0]; - let n_witness_in = inst_circuit.circuit.n_witness_in; - let inst_node_id = graph_builder.add_node_with_witness( - stringify!(ReturnInstruction), - &inst_circuit.circuit, - vec![PredType::Source; n_witness_in], - real_challenges.to_vec(), - mem::take(&mut sources[0]), - real_n_instances, - )?; - chip_builder.construct_chip_check_graph_and_witness( - graph_builder, - inst_node_id, - &inst_circuit.layout.chip_check_wire_id, - real_challenges, - real_n_instances, - )?; - - let mstore_acc_circuit = &inst_circuits[1]; - let n_witness_in = mstore_acc_circuit.circuit.n_witness_in; - let mut preds = vec![PredType::Source; n_witness_in]; - // The order is consistent with the order of creating wires in. - preds[mstore_acc_circuit.layout.pred_dup_wire_id.unwrap() as usize] = PredType::PredWireDup( - NodeOutputType::WireOut(inst_node_id, inst_circuit.layout.succ_dup_wires_id[0]), - ); - preds[mstore_acc_circuit.layout.pred_ooo_wire_id.unwrap() as usize] = PredType::PredWire( - NodeOutputType::WireOut(inst_node_id, inst_circuit.layout.succ_ooo_wires_id[0]), - ); - let mstore_acc_node_id = graph_builder.add_node_with_witness( - stringify!(MstoreAccessory), - &mstore_acc_circuit.circuit, - preds, - real_challenges.to_vec(), - mem::take(&mut sources[1]), - real_n_instances * EVM_STACK_BYTE_WIDTH, - )?; - chip_builder.construct_chip_check_graph_and_witness( - graph_builder, - mstore_acc_node_id, - &mstore_acc_circuit.layout.chip_check_wire_id, - real_challenges, - real_n_instances * EVM_STACK_BYTE_WIDTH, - )?; - Ok(None) - } - - fn construct_graph( - graph_builder: &mut CircuitGraphBuilder, - chip_builder: &mut SingerChipBuilder, - inst_circuits: &[InstCircuit], - real_n_instances: usize, - _: &SingerParams, - ) -> Result, ZKVMError> { - // Add the instruction circuit to the graph. - let inst_circuit = &inst_circuits[0]; - let n_witness_in = inst_circuit.circuit.n_witness_in; - let inst_node_id = graph_builder.add_node( - stringify!(ReturnInstruction), - &inst_circuit.circuit, - vec![PredType::Source; n_witness_in], - )?; - chip_builder.construct_chip_check_graph( - graph_builder, - inst_node_id, - &inst_circuit.layout.chip_check_wire_id, - real_n_instances, - )?; - - let mstore_acc_circuit = &inst_circuits[1]; - let n_witness_in = mstore_acc_circuit.circuit.n_witness_in; - let mut preds = vec![PredType::Source; n_witness_in]; - // The order is consistent with the order of creating wires in. - preds[mstore_acc_circuit.layout.pred_dup_wire_id.unwrap() as usize] = PredType::PredWireDup( - NodeOutputType::WireOut(inst_node_id, inst_circuit.layout.succ_dup_wires_id[0]), - ); - preds[mstore_acc_circuit.layout.pred_ooo_wire_id.unwrap() as usize] = PredType::PredWire( - NodeOutputType::WireOut(inst_node_id, inst_circuit.layout.succ_ooo_wires_id[0]), - ); - let mstore_acc_node_id = graph_builder.add_node( - stringify!(MstoreAccessory), - &mstore_acc_circuit.circuit, - preds, - )?; - chip_builder.construct_chip_check_graph( - graph_builder, - mstore_acc_node_id, - &mstore_acc_circuit.layout.chip_check_wire_id, - real_n_instances * EVM_STACK_BYTE_WIDTH, - )?; - Ok(None) - } -} - -register_witness!( - MstoreInstruction, - phase0 { - pc => PCUInt::N_OPERAND_CELLS, - stack_ts => TSUInt::N_OPERAND_CELLS, - memory_ts => TSUInt::N_OPERAND_CELLS, - stack_top => 1, - clk => 1, - - pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - memory_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, - - offset => StackUInt::N_OPERAND_CELLS, - mem_bytes => EVM_STACK_BYTE_WIDTH, - old_stack_ts_offset => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt_offset => AddSubConstants::::N_WITNESS_CELLS, - old_stack_ts_value => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt_value => AddSubConstants::::N_WITNESS_CELLS - } -); - -impl Instruction for MstoreInstruction { - const OPCODE: OpcodeType = OpcodeType::MSTORE; - const NAME: &'static str = "MSTORE"; - fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { - let mut circuit_builder = CircuitBuilder::default(); - let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); - - let mut chip_handler = ChipHandler::new(challenges); - - // State update - let pc = PCUInt::try_from(&phase0[Self::phase0_pc()])?; - let stack_ts = TSUInt::try_from(&phase0[Self::phase0_stack_ts()])?; - let memory_ts = TSUInt::try_from(&phase0[Self::phase0_memory_ts()])?; - let stack_top = phase0[Self::phase0_stack_top().start]; - let stack_top_expr = MixedCell::Cell(stack_top); - let clk = phase0[Self::phase0_clk().start]; - let clk_expr = MixedCell::Cell(clk); - GlobalStateChip::state_in( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - stack_ts.values(), - memory_ts.values(), - stack_top, - clk, - ); - - let next_pc = - RangeChip::add_pc_const(&mut circuit_builder, &pc, 1, &phase0[Self::phase0_pc_add()])?; - let next_memory_ts = RangeChip::add_ts_with_const( - &mut chip_handler, - &mut circuit_builder, - &memory_ts, - 1, - &phase0[Self::phase0_memory_ts_add()], - )?; - GlobalStateChip::state_out( - &mut chip_handler, - &mut circuit_builder, - next_pc.values(), - stack_ts.values(), - next_memory_ts.values(), - stack_top_expr, - clk_expr.add(E::BaseField::ONE), - ); - - RangeChip::range_check_stack_top( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(2)), - )?; - - // Pop offset from stack - let offset = StackUInt::try_from(&phase0[Self::phase0_offset()])?; - let old_stack_ts_offset = TSUInt::try_from(&phase0[Self::phase0_old_stack_ts_offset()])?; - TSUInt::assert_lt( - &mut circuit_builder, - &mut chip_handler, - &old_stack_ts_offset, - &stack_ts, - &phase0[Self::phase0_old_stack_ts_lt_offset()], - )?; - StackChip::pop( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::ONE), - old_stack_ts_offset.values(), - offset.values(), - ); - - // Pop mem_bytes from stack - let mem_bytes = &phase0[Self::phase0_mem_bytes()]; - RangeChip::range_check_bytes(&mut chip_handler, &mut circuit_builder, mem_bytes)?; - - let mem_value = StackUInt::from_bytes_big_endian(&mut circuit_builder, mem_bytes)?; - let old_stack_ts_value = TSUInt::try_from(&phase0[Self::phase0_old_stack_ts_value()])?; - TSUInt::assert_lt( - &mut circuit_builder, - &mut chip_handler, - &old_stack_ts_value, - &stack_ts, - &phase0[Self::phase0_old_stack_ts_lt_value()], - )?; - StackChip::pop( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(2)), - old_stack_ts_value.values(), - mem_value.values(), - ); - - // Bytecode check for (pc, mstore) - BytecodeChip::bytecode_with_pc_opcode( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - >::OPCODE, - ); - - // To accessory - let (to_acc_dup_id, to_acc_dup) = - circuit_builder.create_witness_out(MstoreAccessory::pred_dup_size()); - add_assign_each_cell( - &mut circuit_builder, - &to_acc_dup[MstoreAccessory::pred_dup_memory_ts()], - next_memory_ts.values(), - ); - add_assign_each_cell( - &mut circuit_builder, - &to_acc_dup[MstoreAccessory::pred_dup_offset()], - offset.values(), - ); - - let (to_acc_ooo_id, to_acc_ooo) = circuit_builder - .create_witness_out(MstoreAccessory::pred_ooo_size() * EVM_STACK_BYTE_WIDTH); - add_assign_each_cell(&mut circuit_builder, &to_acc_ooo, mem_bytes); - - let (ram_load_id, ram_store_id, rom_id) = chip_handler.finalize(&mut circuit_builder); - circuit_builder.configure(); - - let outputs_wire_id = [ram_load_id, ram_store_id, rom_id]; - - Ok(InstCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - layout: InstCircuitLayout { - chip_check_wire_id: outputs_wire_id, - phases_wire_id: vec![phase0_wire_id], - succ_dup_wires_id: vec![to_acc_dup_id], - succ_ooo_wires_id: vec![to_acc_ooo_id], - ..Default::default() - }, - }) - } -} - -pub struct MstoreAccessory; - -register_witness!( - MstoreAccessory, - pred_dup { - memory_ts => TSUInt::N_OPERAND_CELLS, - offset => StackUInt::N_OPERAND_CELLS - }, - pred_ooo { - mem_bytes => 1 - }, - phase0 { - old_memory_ts => TSUInt::N_OPERAND_CELLS, - old_memory_ts_lt => AddSubConstants::::N_WITNESS_CELLS, - - offset_add_delta => AddSubConstants::::N_WITNESS_CELLS, - prev_mem_bytes => 1 - } -); - -impl MstoreAccessory { - fn construct_circuit( - challenges: ChipChallenges, - ) -> Result, ZKVMError> { - let mut circuit_builder = CircuitBuilder::default(); - - // From predesessor circuit. - let (pred_dup_wire_id, pred_dup) = circuit_builder.create_witness_in(Self::pred_dup_size()); - let (pred_ooo_wire_id, pred_ooo) = circuit_builder.create_witness_in(Self::pred_ooo_size()); - - // From witness. - let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); - - let mut chip_handler = ChipHandler::new(challenges); - - // Compute offset, offset + 1, ..., offset + EVM_STACK_BYTE_WIDTH - 1. - // Load previous memory bytes. - let memory_ts = TSUInt::try_from(&pred_dup[Self::pred_dup_memory_ts()])?; - let old_memory_ts = TSUInt::try_from(&phase0[Self::phase0_old_memory_ts()])?; - let old_memory_ts_lt = &phase0[Self::phase0_old_memory_ts_lt()]; - let offset = StackUInt::try_from(&pred_dup[Self::pred_dup_offset()])?; - let offset_add_delta = &phase0[Self::phase0_offset_add_delta()]; - let delta = circuit_builder.create_counter_in(0)[0]; - let offset_plus_delta = StackUInt::add_cell( - &mut circuit_builder, - &mut chip_handler, - &offset, - delta, - offset_add_delta, - )?; - TSUInt::assert_lt( - &mut circuit_builder, - &mut chip_handler, - &old_memory_ts, - &memory_ts, - old_memory_ts_lt, - )?; - - let mem_byte = pred_ooo[Self::pred_ooo_mem_bytes().start]; - let prev_mem_byte = phase0[Self::phase0_prev_mem_bytes().start]; - MemoryChip::write( - &mut chip_handler, - &mut circuit_builder, - offset_plus_delta.values(), - old_memory_ts.values(), - memory_ts.values(), - prev_mem_byte, - mem_byte, - ); - - let (ram_load_id, ram_store_id, rom_id) = chip_handler.finalize(&mut circuit_builder); - circuit_builder.configure(); - - let outputs_wire_id = [ram_load_id, ram_store_id, rom_id]; - - Ok(InstCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - layout: InstCircuitLayout { - chip_check_wire_id: outputs_wire_id, - phases_wire_id: vec![phase0_wire_id], - pred_dup_wire_id: Some(pred_dup_wire_id), - pred_ooo_wire_id: Some(pred_ooo_wire_id), - ..Default::default() - }, - }) - } -} - -#[cfg(test)] -mod test { - #[cfg(not(debug_assertions))] - use super::MstoreAccessory; - use crate::utils::u64vec; - #[cfg(not(debug_assertions))] - use crate::{ - CircuitWiresIn, SingerGraphBuilder, SingerParams, - instructions::{InstructionGraph, SingerCircuitBuilder}, - scheme::GKRGraphProverState, - }; - #[cfg(not(debug_assertions))] - use ark_std::test_rng; - #[cfg(not(debug_assertions))] - use ff::Field; - #[cfg(not(debug_assertions))] - use ff_ext::ExtensionField; - use goldilocks::GoldilocksExt2; - #[cfg(not(debug_assertions))] - use multilinear_extensions::mle::DenseMultilinearExtension; - use singer_utils::structs::ChipChallenges; - #[cfg(not(debug_assertions))] - use std::time::Instant; - #[cfg(not(debug_assertions))] - use transcript::Transcript; - - use crate::instructions::{Instruction, mstore::MstoreInstruction}; - - use crate::test::get_uint_params; - #[allow(deprecated)] - use crate::test::test_opcode_circuit; - use goldilocks::Goldilocks; - use singer_utils::{constants::RANGE_CHIP_BIT_WIDTH, structs::TSUInt}; - use std::collections::BTreeMap; - - #[test] - fn test_mstore_construct_circuit() { - let challenges = ChipChallenges::default(); - - let phase0_idx_map = MstoreInstruction::phase0_idxes_map(); - let phase0_witness_size = MstoreInstruction::phase0_size(); - - #[cfg(feature = "witness-count")] - { - println!("MSTORE: {:?}", &phase0_idx_map); - println!("MSTORE witness_size: {:?}", phase0_witness_size); - } - - // initialize general test inputs associated with opcode - let inst_circuit = MstoreInstruction::construct_circuit(challenges).unwrap(); - - #[cfg(feature = "test-dbg")] - println!("{:?}", inst_circuit); - - let mut phase0_values_map = BTreeMap::>::new(); - phase0_values_map.insert("phase0_pc".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_stack_ts".to_string(), vec![Goldilocks::from(3u64)]); - phase0_values_map.insert("phase0_memory_ts".to_string(), vec![Goldilocks::from(3u64)]); - phase0_values_map.insert("phase0_stack_top".to_string(), vec![Goldilocks::from( - 100u64, - )]); - phase0_values_map.insert("phase0_clk".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert( - "phase0_pc_add".to_string(), - vec![], // carry is 0, may test carry using larger values in PCUInt - ); - phase0_values_map.insert("phase0_memory_ts_add".to_string(), vec![ - Goldilocks::from(4u64), /* first TSUInt::N_RANGE_CELLS = 1*(56/16) = 4 cells are - * range values, memory_ts + 1 = 4 */ - Goldilocks::from(0u64), - Goldilocks::from(0u64), - Goldilocks::from(0u64), - // no place for carry - ]); - phase0_values_map.insert("phase0_offset".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_old_stack_ts_offset".to_string(), vec![ - Goldilocks::from(2u64), - ]); - let m: u64 = (1 << get_uint_params::().1) - 1; - let range_values = u64vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); - phase0_values_map.insert("phase0_old_stack_ts_lt_offset".to_string(), vec![ - Goldilocks::from(range_values[0]), - Goldilocks::from(range_values[1]), - Goldilocks::from(range_values[2]), - Goldilocks::from(1u64), // borrow - ]); - phase0_values_map.insert( - "phase0_mem_bytes".to_string(), - vec![], // use 32-byte 0 for mem_bytes - ); - phase0_values_map.insert("phase0_old_stack_ts_value".to_string(), vec![ - Goldilocks::from(1u64), - ]); - let m: u64 = (1 << get_uint_params::().1) - 2; - let range_values = u64vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); - phase0_values_map.insert("phase0_old_stack_ts_lt_value".to_string(), vec![ - Goldilocks::from(range_values[0]), - Goldilocks::from(range_values[1]), - Goldilocks::from(range_values[2]), - Goldilocks::from(1u64), // borrow - ]); - - let circuit_witness_challenges = vec![ - GoldilocksExt2::from(2), - GoldilocksExt2::from(2), - GoldilocksExt2::from(2), - ]; - - #[allow(deprecated)] - let _circuit_witness = test_opcode_circuit( - &inst_circuit, - &phase0_idx_map, - phase0_witness_size, - &phase0_values_map, - circuit_witness_challenges, - ); - } - - #[cfg(not(debug_assertions))] - fn bench_mstore_instruction_helper(instance_num_vars: usize) { - let chip_challenges = ChipChallenges::default(); - let circuit_builder = - SingerCircuitBuilder::::new(chip_challenges).expect("circuit builder failed"); - let mut singer_builder = SingerGraphBuilder::::default(); - - let mut rng = test_rng(); - let inst_phase0_size = MstoreInstruction::phase0_size(); - let inst_wit: CircuitWiresIn = vec![ - (0..(1 << instance_num_vars)) - .map(|_| { - (0..inst_phase0_size) - .map(|_| E::BaseField::random(&mut rng)) - .collect::>() - }) - .collect::>() - .into(), - ]; - let acc_phase0_size = MstoreAccessory::phase0_size(); - let acc_wit: CircuitWiresIn = vec![ - DenseMultilinearExtension::default(), - DenseMultilinearExtension::default(), - (0..(1 << instance_num_vars) * 32) - .map(|_| { - (0..acc_phase0_size) - .map(|_| E::BaseField::random(&mut rng)) - .collect::>() - }) - .collect::>() - .into(), - ]; - - let real_challenges = vec![E::random(&mut rng), E::random(&mut rng)]; - - let timer = Instant::now(); - - let _ = MstoreInstruction::construct_graph_and_witness( - &mut singer_builder.graph_builder, - &mut singer_builder.chip_builder, - &circuit_builder.insts_circuits[>::OPCODE as usize], - vec![inst_wit, acc_wit], - &real_challenges, - 1 << instance_num_vars, - &SingerParams::default(), - ) - .expect("gkr graph construction failed"); - - let (graph, wit) = singer_builder.graph_builder.finalize_graph_and_witness(); - - println!( - "MstoreInstruction::construct_graph_and_witness, instance_num_vars = {}, time = {}", - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - - let point = vec![E::random(&mut rng), E::random(&mut rng)]; - let target_evals = graph.target_evals(&wit, &point); - - let prover_transcript = &mut Transcript::new(b"Singer"); - - let timer = Instant::now(); - let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, prover_transcript, 1) - .expect("prove failed"); - println!( - "MstoreInstruction::prove, instance_num_vars = {}, time = {}", - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - } - - #[test] - #[cfg(not(debug_assertions))] - fn bench_mstore_instruction() { - bench_mstore_instruction_helper::(5); - } -} diff --git a/singer/src/instructions/pop.rs b/singer/src/instructions/pop.rs deleted file mode 100644 index d7aeb2e4e..000000000 --- a/singer/src/instructions/pop.rs +++ /dev/null @@ -1,294 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; -use gkr::structs::Circuit; -use paste::paste; -use simple_frontend::structs::{CircuitBuilder, MixedCell}; -use singer_utils::{ - chip_handler::{ - ChipHandler, bytecode::BytecodeChip, global_state::GlobalStateChip, range::RangeChip, - stack::StackChip, - }, - constants::OpcodeType, - register_witness, - structs::{PCUInt, StackUInt, TSUInt}, - uint::constants::AddSubConstants, -}; -use std::{collections::BTreeMap, sync::Arc}; - -use crate::error::ZKVMError; - -use super::{ChipChallenges, InstCircuit, InstCircuitLayout, Instruction, InstructionGraph}; -pub struct PopInstruction; - -impl InstructionGraph for PopInstruction { - type InstType = Self; -} - -register_witness!( - PopInstruction, - phase0 { - pc => PCUInt::N_OPERAND_CELLS, - stack_ts => TSUInt::N_OPERAND_CELLS, - memory_ts => TSUInt::N_OPERAND_CELLS, - stack_top => 1, - clk => 1, - - pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - - old_stack_ts => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt => AddSubConstants::::N_WITNESS_CELLS, - stack_values => StackUInt::N_OPERAND_CELLS - } -); - -impl Instruction for PopInstruction { - const OPCODE: OpcodeType = OpcodeType::POP; - const NAME: &'static str = "POP"; - fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { - let mut circuit_builder = CircuitBuilder::default(); - let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); - - let mut chip_handler = ChipHandler::new(challenges); - - // State update - let pc = PCUInt::try_from(&phase0[Self::phase0_pc()])?; - let stack_ts = TSUInt::try_from(&phase0[Self::phase0_stack_ts()])?; - let memory_ts = &phase0[Self::phase0_memory_ts()]; - let stack_top = phase0[Self::phase0_stack_top().start]; - let stack_top_expr = MixedCell::Cell(stack_top); - let clk = phase0[Self::phase0_clk().start]; - let clk_expr = MixedCell::Cell(clk); - GlobalStateChip::state_in( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - stack_ts.values(), - memory_ts, - stack_top, - clk, - ); - - let next_pc = - RangeChip::add_pc_const(&mut circuit_builder, &pc, 1, &phase0[Self::phase0_pc_add()])?; - GlobalStateChip::state_out( - &mut chip_handler, - &mut circuit_builder, - next_pc.values(), - stack_ts.values(), - memory_ts, - stack_top_expr.sub(E::BaseField::from(1)), - clk_expr.add(E::BaseField::ONE), - ); - - // Check the range of stack_top - 1 is within [0, 1 << STACK_TOP_BIT_WIDTH). - RangeChip::range_check_stack_top( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::ONE), - )?; - - // Pop rlc from stack - let old_stack_ts = (&phase0[Self::phase0_old_stack_ts()]).try_into()?; - TSUInt::assert_lt( - &mut circuit_builder, - &mut chip_handler, - &old_stack_ts, - &stack_ts, - &phase0[Self::phase0_old_stack_ts_lt()], - )?; - let stack_values = &phase0[Self::phase0_stack_values()]; - StackChip::pop( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(1)), - old_stack_ts.values(), - stack_values, - ); - - // Bytecode check for (pc, POP) - BytecodeChip::bytecode_with_pc_opcode( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - >::OPCODE, - ); - - let (ram_load_id, ram_store_id, rom_id) = chip_handler.finalize(&mut circuit_builder); - circuit_builder.configure(); - - let outputs_wire_id = [ram_load_id, ram_store_id, rom_id]; - - Ok(InstCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - layout: InstCircuitLayout { - chip_check_wire_id: outputs_wire_id, - phases_wire_id: vec![phase0_wire_id], - ..Default::default() - }, - }) - } -} - -#[cfg(test)] -mod test { - #[cfg(not(debug_assertions))] - use crate::{ - CircuitWiresIn, SingerGraphBuilder, SingerParams, - instructions::{InstructionGraph, SingerCircuitBuilder}, - scheme::GKRGraphProverState, - }; - #[cfg(not(debug_assertions))] - use ark_std::test_rng; - #[cfg(not(debug_assertions))] - use ff::Field; - #[cfg(not(debug_assertions))] - use ff_ext::ExtensionField; - use goldilocks::{Goldilocks, GoldilocksExt2}; - use std::collections::BTreeMap; - #[cfg(not(debug_assertions))] - use std::time::Instant; - #[cfg(not(debug_assertions))] - use transcript::Transcript; - - #[allow(deprecated)] - use crate::test::test_opcode_circuit; - use crate::{ - instructions::{ChipChallenges, Instruction, PopInstruction}, - test::get_uint_params, - utils::u64vec, - }; - use singer_utils::{constants::RANGE_CHIP_BIT_WIDTH, structs::TSUInt}; - - #[test] - fn test_pop_construct_circuit() { - let challenges = ChipChallenges::default(); - - let phase0_idx_map = PopInstruction::phase0_idxes_map(); - let phase0_witness_size = PopInstruction::phase0_size(); - - #[cfg(feature = "witness-count")] - { - println!("POP {:?}", &phase0_idx_map); - println!("POP witness_size = {:?}", phase0_witness_size); - } - - // initialize general test inputs associated with push1 - let inst_circuit = PopInstruction::construct_circuit(challenges).unwrap(); - - #[cfg(feature = "test-dbg")] - println!("{:?}", inst_circuit); - - let mut phase0_values_map = BTreeMap::>::new(); - phase0_values_map.insert("phase0_pc".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_stack_ts".to_string(), vec![Goldilocks::from(2u64)]); - phase0_values_map.insert("phase0_memory_ts".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_stack_top".to_string(), vec![Goldilocks::from( - 100u64, - )]); - phase0_values_map.insert("phase0_clk".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert( - "phase0_pc_add".to_string(), - vec![], // carry is 0, may test carry using larger values in PCUInt - ); - phase0_values_map.insert("phase0_old_stack_ts".to_string(), vec![Goldilocks::from( - 1u64, - )]); - let m: u64 = (1 << get_uint_params::().1) - 1; - let range_values = u64vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); - phase0_values_map.insert("phase0_old_stack_ts_lt".to_string(), vec![ - Goldilocks::from(range_values[0]), - Goldilocks::from(range_values[1]), - Goldilocks::from(range_values[2]), - Goldilocks::from(1u64), // current length has no cells for borrow - ]); - phase0_values_map.insert("phase0_stack_values".to_string(), vec![ - Goldilocks::from(7u64), - Goldilocks::from(6u64), - Goldilocks::from(5u64), - Goldilocks::from(4u64), - Goldilocks::from(3u64), - Goldilocks::from(2u64), - Goldilocks::from(1u64), - Goldilocks::from(0u64), - ]); - - let circuit_witness_challenges = vec![ - GoldilocksExt2::from(2), - GoldilocksExt2::from(2), - GoldilocksExt2::from(2), - ]; - - #[allow(deprecated)] - let _circuit_witness = test_opcode_circuit( - &inst_circuit, - &phase0_idx_map, - phase0_witness_size, - &phase0_values_map, - circuit_witness_challenges, - ); - } - - #[cfg(not(debug_assertions))] - fn bench_pop_instruction_helper(instance_num_vars: usize) { - let chip_challenges = ChipChallenges::default(); - let circuit_builder = - SingerCircuitBuilder::::new(chip_challenges).expect("circuit builder failed"); - let mut singer_builder = SingerGraphBuilder::::default(); - - let mut rng = test_rng(); - let size = PopInstruction::phase0_size(); - let phase0: CircuitWiresIn = vec![ - (0..(1 << instance_num_vars)) - .map(|_| { - (0..size) - .map(|_| E::BaseField::random(&mut rng)) - .collect::>() - }) - .collect::>() - .into(), - ]; - - let real_challenges = vec![E::random(&mut rng), E::random(&mut rng)]; - - let timer = Instant::now(); - - let _ = PopInstruction::construct_graph_and_witness( - &mut singer_builder.graph_builder, - &mut singer_builder.chip_builder, - &circuit_builder.insts_circuits[>::OPCODE as usize], - vec![phase0], - &real_challenges, - 1 << instance_num_vars, - &SingerParams::default(), - ) - .expect("gkr graph construction failed"); - - let (graph, wit) = singer_builder.graph_builder.finalize_graph_and_witness(); - - println!( - "PopInstruction::construct_graph_and_witness, instance_num_vars = {}, time = {}", - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - - let point = vec![E::random(&mut rng), E::random(&mut rng)]; - let target_evals = graph.target_evals(&wit, &point); - - let prover_transcript = &mut Transcript::new(b"Singer"); - - let timer = Instant::now(); - let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, prover_transcript, 1) - .expect("prove failed"); - println!( - "PopInstruction::prove, instance_num_vars = {}, time = {}", - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - } - - #[test] - #[cfg(not(debug_assertions))] - fn bench_pop_instruction() { - bench_pop_instruction_helper::(10); - } -} diff --git a/singer/src/instructions/push.rs b/singer/src/instructions/push.rs deleted file mode 100644 index 92af4021d..000000000 --- a/singer/src/instructions/push.rs +++ /dev/null @@ -1,307 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; -use gkr::structs::Circuit; -use paste::paste; -use simple_frontend::structs::{CircuitBuilder, MixedCell}; -use singer_utils::{ - chip_handler::{ - ChipHandler, bytecode::BytecodeChip, global_state::GlobalStateChip, range::RangeChip, - stack::StackChip, - }, - constants::OpcodeType, - register_witness, - structs::{PCUInt, StackUInt, TSUInt}, - uint::constants::AddSubConstants, -}; -use std::{collections::BTreeMap, sync::Arc}; - -use crate::error::ZKVMError; - -use super::{ChipChallenges, InstCircuit, InstCircuitLayout, Instruction, InstructionGraph}; - -pub struct PushInstruction; - -impl InstructionGraph for PushInstruction { - type InstType = Self; -} - -register_witness!( - PushInstruction, - phase0 { - pc => PCUInt::N_OPERAND_CELLS, - stack_ts => TSUInt::N_OPERAND_CELLS, - memory_ts => TSUInt::N_OPERAND_CELLS, - stack_top => 1, - clk => 1, - - pc_add_i_plus_1 => N * AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, - - stack_bytes => N - } -); - -impl Instruction for PushInstruction { - const OPCODE: OpcodeType = match N { - 1 => OpcodeType::PUSH1, - _ => unimplemented!(), - }; - const NAME: &'static str = match N { - 1 => "PUSH1", - _ => unimplemented!(), - }; - fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { - let mut circuit_builder = CircuitBuilder::default(); - let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); - - let mut chip_handler = ChipHandler::new(challenges); - - // State update - let pc = PCUInt::try_from(&phase0[Self::phase0_pc()])?; - let stack_ts = TSUInt::try_from(&phase0[Self::phase0_stack_ts()])?; - let memory_ts = &phase0[Self::phase0_memory_ts()]; - let stack_top = phase0[Self::phase0_stack_top().start]; - let stack_top_expr = MixedCell::Cell(stack_top); - let clk = phase0[Self::phase0_clk().start]; - let clk_expr = MixedCell::Cell(clk); - GlobalStateChip::state_in( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - stack_ts.values(), - memory_ts, - stack_top, - clk, - ); - let next_pc = RangeChip::add_pc_const( - &mut circuit_builder, - &pc, - N as i64 + 1, - &phase0[Self::phase0_pc_add_i_plus_1()], - )?; - let next_stack_ts = RangeChip::add_ts_with_const( - &mut chip_handler, - &mut circuit_builder, - &stack_ts, - 1, - &phase0[Self::phase0_stack_ts_add()], - )?; - - GlobalStateChip::state_out( - &mut chip_handler, - &mut circuit_builder, - next_pc.values(), - next_stack_ts.values(), - memory_ts, - stack_top_expr.add(E::BaseField::from(1)), - clk_expr.add(E::BaseField::ONE), - ); - - // Check the range of stack_top is within [0, 1 << STACK_TOP_BIT_WIDTH). - RangeChip::range_check_stack_top(&mut chip_handler, &mut circuit_builder, stack_top_expr)?; - - let stack_bytes = &phase0[Self::phase0_stack_bytes()]; - let stack_values = StackUInt::from_bytes_big_endian(&mut circuit_builder, stack_bytes)?; - // Push value to stack - StackChip::push( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr, - stack_ts.values(), - stack_values.values(), - ); - - // Bytecode check for (pc, PUSH{N}), (pc + 1, byte[0]), ..., (pc + N, byte[N - 1]) - BytecodeChip::bytecode_with_pc_opcode( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - >::OPCODE, - ); - for (i, pc_add_i_plus_1) in phase0[Self::phase0_pc_add_i_plus_1()] - .chunks(AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS) - .enumerate() - { - let next_pc = - RangeChip::add_pc_const(&mut circuit_builder, &pc, i as i64 + 1, pc_add_i_plus_1)?; - BytecodeChip::bytecode_with_pc_byte( - &mut chip_handler, - &mut circuit_builder, - next_pc.values(), - stack_bytes[i], - ); - } - - let (ram_load_id, ram_store_id, rom_id) = chip_handler.finalize(&mut circuit_builder); - circuit_builder.configure(); - - let outputs_wire_id = [ram_load_id, ram_store_id, rom_id]; - - Ok(InstCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - layout: InstCircuitLayout { - chip_check_wire_id: outputs_wire_id, - phases_wire_id: vec![phase0_wire_id], - ..Default::default() - }, - }) - } -} - -#[cfg(test)] -mod test { - #[cfg(not(debug_assertions))] - use crate::{ - CircuitWiresIn, SingerGraphBuilder, SingerParams, - instructions::{InstructionGraph, SingerCircuitBuilder}, - scheme::GKRGraphProverState, - }; - #[cfg(not(debug_assertions))] - use ark_std::test_rng; - #[cfg(not(debug_assertions))] - use ff::Field; - #[cfg(not(debug_assertions))] - use ff_ext::ExtensionField; - use goldilocks::{Goldilocks, GoldilocksExt2}; - use std::collections::BTreeMap; - #[cfg(not(debug_assertions))] - use std::time::Instant; - #[cfg(not(debug_assertions))] - use transcript::Transcript; - - use crate::instructions::{ChipChallenges, Instruction, PushInstruction}; - #[allow(deprecated)] - use crate::test::test_opcode_circuit; - - #[test] - fn test_push1_construct_circuit() { - let challenges = ChipChallenges::default(); - - let phase0_idx_map = PushInstruction::<1>::phase0_idxes_map(); - let phase0_witness_size = PushInstruction::<1>::phase0_size(); - - #[cfg(feature = "witness-count")] - { - println!("PUSH1 {:?}", &phase0_idx_map); - println!("PUSH1 witness_size = {:?}", phase0_witness_size); - } - // initialize general test inputs associated with push1 - let inst_circuit = PushInstruction::<1>::construct_circuit(challenges).unwrap(); - - #[cfg(feature = "test-dbg")] - println!("{:?}", inst_circuit); - - let mut phase0_values_map = BTreeMap::>::new(); - phase0_values_map.insert("phase0_pc".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_stack_ts".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_memory_ts".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_stack_top".to_string(), vec![Goldilocks::from( - 100u64, - )]); - phase0_values_map.insert("phase0_clk".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert( - "phase0_pc_add_i_plus_1".to_string(), - vec![], // carry is 0, may test carry using larger values in PCUInt - ); - phase0_values_map.insert("phase0_stack_ts_add".to_string(), vec![ - Goldilocks::from(2u64), /* first TSUInt::N_RANGE_CELLS = 1*(56/16) = 4 cells are - * range values, stack_ts + 1 = 4 */ - Goldilocks::from(0u64), - Goldilocks::from(0u64), - Goldilocks::from(0u64), - // no place for carry - ]); - phase0_values_map.insert("phase0_stack_bytes".to_string(), vec![ - Goldilocks::from(0u64), - Goldilocks::from(1u64), - Goldilocks::from(2u64), - Goldilocks::from(3u64), - Goldilocks::from(4u64), - Goldilocks::from(5u64), - Goldilocks::from(6u64), - Goldilocks::from(7u64), - ]); - - let circuit_witness_challenges = vec![ - GoldilocksExt2::from(2), - GoldilocksExt2::from(2), - GoldilocksExt2::from(2), - ]; - - #[allow(deprecated)] - let _circuit_witness = test_opcode_circuit( - &inst_circuit, - &phase0_idx_map, - phase0_witness_size, - &phase0_values_map, - circuit_witness_challenges, - ); - } - - #[cfg(not(debug_assertions))] - fn bench_push_instruction_helper(instance_num_vars: usize) { - let chip_challenges = ChipChallenges::default(); - let circuit_builder = - SingerCircuitBuilder::::new(chip_challenges).expect("circuit builder failed"); - let mut singer_builder = SingerGraphBuilder::::default(); - - let mut rng = test_rng(); - let size = PushInstruction::::phase0_size(); - let phase0: CircuitWiresIn = vec![ - (0..(1 << instance_num_vars)) - .map(|_| { - (0..size) - .map(|_| E::BaseField::random(&mut rng)) - .collect::>() - }) - .collect::>() - .into(), - ]; - - let real_challenges = vec![E::random(&mut rng), E::random(&mut rng)]; - - let timer = Instant::now(); - - let _ = PushInstruction::::construct_graph_and_witness( - &mut singer_builder.graph_builder, - &mut singer_builder.chip_builder, - &circuit_builder.insts_circuits - [ as Instruction>::OPCODE as usize], - vec![phase0], - &real_challenges, - 1 << instance_num_vars, - &SingerParams::default(), - ) - .expect("gkr graph construction failed"); - - let (graph, wit) = singer_builder.graph_builder.finalize_graph_and_witness(); - - println!( - "Push{}Instruction::construct_graph_and_witness, instance_num_vars = {}, time = {}", - N, - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - - let point = vec![E::random(&mut rng), E::random(&mut rng)]; - let target_evals = graph.target_evals(&wit, &point); - - let prover_transcript = &mut Transcript::new(b"Singer"); - - let timer = Instant::now(); - let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, prover_transcript, 1) - .expect("prove failed"); - println!( - "Push{}Instruction::prove, instance_num_vars = {}, time = {}", - N, - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - } - - #[test] - #[cfg(not(debug_assertions))] - fn bench_push1_instruction() { - bench_push_instruction_helper::(10); - } -} diff --git a/singer/src/instructions/ret.rs b/singer/src/instructions/ret.rs deleted file mode 100644 index 7f95fcea7..000000000 --- a/singer/src/instructions/ret.rs +++ /dev/null @@ -1,604 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; -use gkr::structs::Circuit; -use gkr_graph::structs::{CircuitGraphBuilder, NodeOutputType, PredType}; -use paste::paste; -use simple_frontend::structs::{CircuitBuilder, MixedCell}; -use singer_utils::{ - chip_handler::{ - ChipHandler, bytecode::BytecodeChip, global_state::GlobalStateChip, - ram_handler::RAMHandler, range::RangeChip, stack::StackChip, - }, - chips::SingerChipBuilder, - constants::OpcodeType, - register_witness, - structs::{PCUInt, StackUInt, TSUInt}, - uint::constants::AddSubConstants, -}; -use std::{cell::RefCell, collections::BTreeMap, mem, rc::Rc, sync::Arc}; - -use crate::{CircuitWiresIn, SingerParams, error::ZKVMError, utils::add_assign_each_cell}; - -use super::{ChipChallenges, InstCircuit, InstCircuitLayout, Instruction, InstructionGraph}; - -/// This circuit is to pop offset and public output size from stack. -pub struct ReturnInstruction; -/// This circuit is to load public output from memory, which is a data-parallel -/// circuit load one element in each sub-circuit. -pub struct ReturnPublicOutLoad; -/// This circuit is to load the remaining elmeents after the program execution -/// from memory, which is a data-parallel circuit load one element in each -/// sub-circuit. -pub struct ReturnRestMemLoad; -/// This circuit is to initialize the memory with 0 at the beginning. It can -/// only touches the used addresses. -pub struct ReturnRestMemStore; - -impl InstructionGraph for ReturnInstruction { - type InstType = Self; - - fn construct_circuits(challenges: ChipChallenges) -> Result>, ZKVMError> { - let circuits = vec![ - ReturnInstruction::construct_circuit(challenges)?, - ReturnPublicOutLoad::construct_circuit(challenges)?, - ReturnRestMemLoad::construct_circuit(challenges)?, - ReturnRestMemStore::construct_circuit(challenges)?, - ReturnRestStackPop::construct_circuit(challenges)?, - ]; - Ok(circuits) - } - - fn construct_graph_and_witness( - graph_builder: &mut CircuitGraphBuilder, - chip_builder: &mut SingerChipBuilder, - inst_circuits: &[InstCircuit], - mut sources: Vec>, - real_challenges: &[E], - _: usize, - params: &SingerParams, - ) -> Result, ZKVMError> { - // Add the instruction circuit to the graph. - let inst_circuit = &inst_circuits[0]; - let n_witness_in = inst_circuit.circuit.n_witness_in; - let inst_node_id = graph_builder.add_node_with_witness( - stringify!(ReturnInstruction), - &inst_circuit.circuit, - vec![PredType::Source; n_witness_in], - real_challenges.to_vec(), - mem::take(&mut sources[0]), - 1, - )?; - chip_builder.construct_chip_check_graph_and_witness( - graph_builder, - inst_node_id, - &inst_circuit.layout.chip_check_wire_id, - real_challenges, - 1, - )?; - - // Add the public output load circuit to the graph. - let pub_out_load_circuit = &inst_circuits[1]; - let n_witness_in = pub_out_load_circuit.circuit.n_witness_in; - let mut preds = vec![PredType::Source; n_witness_in]; - preds[pub_out_load_circuit.layout.pred_dup_wire_id.unwrap() as usize] = - PredType::PredWireDup(NodeOutputType::WireOut( - inst_node_id, - inst_circuit.layout.succ_dup_wires_id[0], - )); - let pub_out_load_node_id = graph_builder.add_node_with_witness( - stringify!(ReturnPublicOutLoad), - &pub_out_load_circuit.circuit, - preds, - real_challenges.to_vec(), - mem::take(&mut sources[1]), - params.n_public_output_bytes, - )?; - chip_builder.construct_chip_check_graph_and_witness( - graph_builder, - pub_out_load_node_id, - &pub_out_load_circuit.layout.chip_check_wire_id, - real_challenges, - params.n_public_output_bytes, - )?; - - // Add the rest memory load circuit to the graph. - let rest_mem_load_circuit = &inst_circuits[2]; - let n_witness_in = rest_mem_load_circuit.circuit.n_witness_in; - let rest_mem_load_node_id = graph_builder.add_node_with_witness( - stringify!(ReturnRestMemLoad), - &rest_mem_load_circuit.circuit, - vec![PredType::Source; n_witness_in], - real_challenges.to_vec(), - mem::take(&mut sources[2]), - params.n_mem_finalize, - )?; - chip_builder.construct_chip_check_graph_and_witness( - graph_builder, - rest_mem_load_node_id, - &rest_mem_load_circuit.layout.chip_check_wire_id, - real_challenges, - params.n_mem_finalize, - )?; - - // Add the rest memory store circuit to the graph. - let rest_mem_store_circuit = &inst_circuits[3]; - let n_witness_in = rest_mem_store_circuit.circuit.n_witness_in; - let rest_mem_store_node_id = graph_builder.add_node_with_witness( - stringify!(ReturnRestMemStore), - &rest_mem_store_circuit.circuit, - vec![PredType::Source; n_witness_in], - real_challenges.to_vec(), - mem::take(&mut sources[3]), - params.n_mem_initialize, - )?; - chip_builder.construct_chip_check_graph_and_witness( - graph_builder, - rest_mem_store_node_id, - &rest_mem_store_circuit.layout.chip_check_wire_id, - real_challenges, - params.n_mem_initialize, - )?; - - // Add the rest stack pop circuit to the graph. - let rest_stack_pop_circuit = &inst_circuits[4]; - let n_witness_in = rest_stack_pop_circuit.circuit.n_witness_in; - let rest_stack_pop_node_id = graph_builder.add_node_with_witness( - stringify!(ReturnRestStackPop), - &rest_stack_pop_circuit.circuit, - vec![PredType::Source; n_witness_in], - real_challenges.to_vec(), - mem::take(&mut sources[4]), - params.n_stack_finalize, - )?; - chip_builder.construct_chip_check_graph_and_witness( - graph_builder, - rest_stack_pop_node_id, - &rest_stack_pop_circuit.layout.chip_check_wire_id, - real_challenges, - params.n_stack_finalize, - )?; - - Ok(inst_circuit - .layout - .target_wire_id - .map(|target_wire_id| NodeOutputType::WireOut(inst_node_id, target_wire_id))) - } - - fn construct_graph( - graph_builder: &mut CircuitGraphBuilder, - chip_builder: &mut SingerChipBuilder, - inst_circuits: &[InstCircuit], - _real_n_instances: usize, - params: &SingerParams, - ) -> Result, ZKVMError> { - // Add the instruction circuit to the graph. - let inst_circuit = &inst_circuits[0]; - let n_witness_in = inst_circuit.circuit.n_witness_in; - let inst_node_id = graph_builder.add_node( - stringify!(ReturnInstruction), - &inst_circuit.circuit, - vec![PredType::Source; n_witness_in], - )?; - chip_builder.construct_chip_check_graph( - graph_builder, - inst_node_id, - &inst_circuit.layout.chip_check_wire_id, - 1, - )?; - - // Add the public output load circuit to the graph. - let pub_out_load_circuit = &inst_circuits[1]; - let n_witness_in = pub_out_load_circuit.circuit.n_witness_in; - let mut preds = vec![PredType::Source; n_witness_in]; - preds[pub_out_load_circuit.layout.pred_dup_wire_id.unwrap() as usize] = - PredType::PredWireDup(NodeOutputType::WireOut( - inst_node_id, - inst_circuit.layout.succ_dup_wires_id[0], - )); - let pub_out_load_node_id = graph_builder.add_node( - stringify!(ReturnPublicOutLoad), - &pub_out_load_circuit.circuit, - preds, - )?; - chip_builder.construct_chip_check_graph( - graph_builder, - pub_out_load_node_id, - &pub_out_load_circuit.layout.chip_check_wire_id, - params.n_public_output_bytes, - )?; - - // Add the rest memory load circuit to the graph. - let rest_mem_load_circuit = &inst_circuits[2]; - let n_witness_in = rest_mem_load_circuit.circuit.n_witness_in; - let rest_mem_load_node_id = graph_builder.add_node( - stringify!(ReturnRestMemLoad), - &rest_mem_load_circuit.circuit, - vec![PredType::Source; n_witness_in], - )?; - chip_builder.construct_chip_check_graph( - graph_builder, - rest_mem_load_node_id, - &rest_mem_load_circuit.layout.chip_check_wire_id, - params.n_mem_finalize, - )?; - - // Add the rest memory store circuit to the graph. - let rest_mem_store_circuit = &inst_circuits[3]; - let n_witness_in = rest_mem_store_circuit.circuit.n_witness_in; - let rest_mem_store_node_id = graph_builder.add_node( - stringify!(ReturnRestMemStore), - &rest_mem_store_circuit.circuit, - vec![PredType::Source; n_witness_in], - )?; - chip_builder.construct_chip_check_graph( - graph_builder, - rest_mem_store_node_id, - &rest_mem_store_circuit.layout.chip_check_wire_id, - params.n_mem_initialize, - )?; - - // Add the rest stack pop circuit to the graph. - let rest_stack_pop_circuit = &inst_circuits[4]; - let n_witness_in = rest_stack_pop_circuit.circuit.n_witness_in; - let rest_stack_pop_node_id = graph_builder.add_node( - stringify!(ReturnRestStackPop), - &rest_stack_pop_circuit.circuit, - vec![PredType::Source; n_witness_in], - )?; - chip_builder.construct_chip_check_graph( - graph_builder, - rest_stack_pop_node_id, - &rest_stack_pop_circuit.layout.chip_check_wire_id, - params.n_stack_finalize, - )?; - - Ok(inst_circuit - .layout - .target_wire_id - .map(|target_wire_id| NodeOutputType::WireOut(inst_node_id, target_wire_id))) - } -} - -register_witness!( - ReturnInstruction, - phase0 { - pc => PCUInt::N_OPERAND_CELLS, - stack_ts => TSUInt::N_OPERAND_CELLS, - memory_ts => TSUInt::N_OPERAND_CELLS, - stack_top => 1, - clk => 1, - - old_stack_ts0 => TSUInt::N_OPERAND_CELLS, - old_stack_ts1 => TSUInt::N_OPERAND_CELLS, - - offset => StackUInt::N_OPERAND_CELLS, - mem_length => StackUInt::N_OPERAND_CELLS - } -); - -impl Instruction for ReturnInstruction { - const OPCODE: OpcodeType = OpcodeType::RETURN; - const NAME: &'static str = "RETURN"; - fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { - let mut circuit_builder = CircuitBuilder::default(); - let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); - - let mut chip_handler = ChipHandler::new(challenges); - - // State update - let pc = PCUInt::try_from(&phase0[Self::phase0_pc()])?; - let stack_ts = TSUInt::try_from(&phase0[Self::phase0_stack_ts()])?; - let memory_ts = &phase0[Self::phase0_memory_ts()]; - let stack_top = phase0[Self::phase0_stack_top().start]; - let stack_top_expr = MixedCell::Cell(stack_top); - let clk = phase0[Self::phase0_clk().start]; - GlobalStateChip::state_in( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - stack_ts.values(), - memory_ts, - stack_top, - clk, - ); - - // Check the range of stack_top - 2 is within [0, 1 << STACK_TOP_BIT_WIDTH). - RangeChip::range_check_stack_top( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(2)), - )?; - - // Pop offset and mem_size from stack - let old_stack_ts0 = TSUInt::try_from(&phase0[Self::phase0_old_stack_ts0()])?; - let offset = StackUInt::try_from(&phase0[Self::phase0_offset()])?; - StackChip::pop( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(1)), - old_stack_ts0.values(), - offset.values(), - ); - - let old_stack_ts1 = TSUInt::try_from(&phase0[Self::phase0_old_stack_ts1()])?; - let length = StackUInt::try_from(&phase0[Self::phase0_mem_length()])?; - StackChip::pop( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(2)), - old_stack_ts1.values(), - length.values(), - ); - - // Bytecode check for (pc, ret) - BytecodeChip::bytecode_with_pc_opcode( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - >::OPCODE, - ); - - let (ram_load_id, ram_store_id, rom_id) = chip_handler.finalize(&mut circuit_builder); - circuit_builder.configure(); - - let outputs_wire_id = [ram_load_id, ram_store_id, rom_id]; - - // Copy length to the target wire. - let (target_wire_id, target) = - circuit_builder.create_witness_out(StackUInt::N_OPERAND_CELLS); - let length = length.values(); - for &len in &length[1..] { - circuit_builder.assert_const(len, 0); - } - circuit_builder.add(target[0], length[0], E::BaseField::ONE); - - // println!("target: {:?}", target); - - // Copy offset to wires of public output load circuit. - let (pub_out_wire_id, pub_out) = - circuit_builder.create_witness_out(ReturnPublicOutLoad::pred_size()); - let pub_out_offset = &pub_out[ReturnPublicOutLoad::pred_offset()]; - let offset = offset.values(); - add_assign_each_cell(&mut circuit_builder, pub_out_offset, offset); - - // println!("pub_out: {:?}", pub_out); - - circuit_builder.configure(); - - Ok(InstCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - layout: InstCircuitLayout { - chip_check_wire_id: outputs_wire_id, - phases_wire_id: vec![phase0_wire_id], - target_wire_id: Some(target_wire_id), - succ_dup_wires_id: vec![pub_out_wire_id], - ..Default::default() - }, - }) - } -} - -register_witness!( - ReturnPublicOutLoad, - pred { - offset => StackUInt::N_OPERAND_CELLS - }, - public_io { - byte => 1 - }, - phase0 { - old_memory_ts => TSUInt::N_OPERAND_CELLS, - - offset_add => AddSubConstants::::N_WITNESS_CELLS - } -); - -impl ReturnPublicOutLoad { - fn construct_circuit( - challenges: ChipChallenges, - ) -> Result, ZKVMError> { - let mut circuit_builder = CircuitBuilder::default(); - let (pred_wire_id, pred) = circuit_builder.create_witness_in(Self::pred_size()); - let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); - - let mut chip_handler = ChipHandler::new(challenges); - - // Compute offset + counter - let delta = circuit_builder.create_counter_in(0); - let offset = StackUInt::try_from(&pred[Self::pred_offset()])?; - let offset_add_delta_witness = &phase0[Self::phase0_offset_add()]; - let new_offset = StackUInt::add_cell( - &mut circuit_builder, - &mut chip_handler, - &offset, - delta[0], - offset_add_delta_witness, - )?; - - // Load from memory - let mem_byte = pred[Self::public_io_byte().start]; - let old_memory_ts = TSUInt::try_from(&phase0[Self::phase0_old_memory_ts()])?; - chip_handler.ram_handler.read_oam( - &mut circuit_builder, - new_offset.values(), - old_memory_ts.values(), - &[mem_byte], - ); - - let (ram_load_id, ram_store_id, rom_id) = chip_handler.finalize(&mut circuit_builder); - circuit_builder.configure(); - - let outputs_wire_id = [ram_load_id, ram_store_id, rom_id]; - - Ok(InstCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - layout: InstCircuitLayout { - chip_check_wire_id: outputs_wire_id, - phases_wire_id: vec![phase0_wire_id], - pred_dup_wire_id: Some(pred_wire_id), - ..Default::default() - }, - }) - } - - // TODO(Matthias): Check whether we need this function. - #[allow(dead_code)] - fn name() -> &'static str { - "ReturnPublicOutLoad" - } -} - -register_witness!( - ReturnRestMemLoad, - phase0 { - mem_byte => 1, - offset => StackUInt::N_OPERAND_CELLS, - old_memory_ts => TSUInt::N_OPERAND_CELLS - } -); - -impl ReturnRestMemLoad { - fn construct_circuit( - challenges: ChipChallenges, - ) -> Result, ZKVMError> { - let mut circuit_builder = CircuitBuilder::default(); - let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); - - let ram_handler = Rc::new(RefCell::new(RAMHandler::new(challenges))); - - // Load from memory - let offset = &phase0[Self::phase0_offset()]; - let mem_byte = phase0[Self::phase0_mem_byte().start]; - let old_memory_ts = TSUInt::try_from(&phase0[Self::phase0_old_memory_ts()])?; - ram_handler - .borrow_mut() - .read_oam(&mut circuit_builder, offset, old_memory_ts.values(), &[ - mem_byte, - ]); - - let (ram_load_id, ram_store_id) = ram_handler.borrow_mut().finalize(&mut circuit_builder); - circuit_builder.configure(); - - let outputs_wire_id = [ram_load_id, ram_store_id, None]; - - Ok(InstCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - layout: InstCircuitLayout { - chip_check_wire_id: outputs_wire_id, - phases_wire_id: vec![phase0_wire_id], - ..Default::default() - }, - }) - } - - // TODO(Matthias): Check whether we need this function. - #[allow(dead_code)] - fn name() -> &'static str { - "ReturnRestMemLoad" - } -} - -register_witness!( - ReturnRestMemStore, - phase0 { - mem_byte => 1, - offset => StackUInt::N_OPERAND_CELLS - } -); - -impl ReturnRestMemStore { - fn construct_circuit( - challenges: ChipChallenges, - ) -> Result, ZKVMError> { - let mut circuit_builder = CircuitBuilder::default(); - let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); - - let ram_handler = Rc::new(RefCell::new(RAMHandler::new(challenges))); - - // Load from memory - let offset = &phase0[Self::phase0_offset()]; - let mem_byte = phase0[Self::phase0_mem_byte().start]; - let memory_ts = circuit_builder.create_cells(StackUInt::N_OPERAND_CELLS); - ram_handler - .borrow_mut() - .write_oam(&mut circuit_builder, offset, &memory_ts, &[mem_byte]); - let memory_ts = circuit_builder.create_cells(StackUInt::N_OPERAND_CELLS); - ram_handler - .borrow_mut() - .write_oam(&mut circuit_builder, offset, &memory_ts, &[mem_byte]); - - let (ram_load_id, ram_store_id) = ram_handler.borrow_mut().finalize(&mut circuit_builder); - circuit_builder.configure(); - - let outputs_wire_id = [ram_load_id, ram_store_id, None]; - - Ok(InstCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - layout: InstCircuitLayout { - chip_check_wire_id: outputs_wire_id, - phases_wire_id: vec![phase0_wire_id], - ..Default::default() - }, - }) - } - - // TODO(Matthias): Check whether we need this function. - #[allow(dead_code)] - fn name() -> &'static str { - "ReturnRestMemStore" - } -} - -pub struct ReturnRestStackPop; - -register_witness!( - ReturnRestStackPop, - phase0 { - old_stack_ts => TSUInt::N_OPERAND_CELLS, - stack_values => StackUInt::N_OPERAND_CELLS - } -); - -impl ReturnRestStackPop { - fn construct_circuit( - challenges: ChipChallenges, - ) -> Result, ZKVMError> { - let mut circuit_builder = CircuitBuilder::default(); - let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); - - let mut chip_handler = ChipHandler::new(challenges); - - // Pop from stack - let stack_top = circuit_builder.create_counter_in(0); - let stack_values = &phase0[Self::phase0_stack_values()]; - - let old_stack_ts = TSUInt::try_from(&phase0[Self::phase0_old_stack_ts()])?; - StackChip::pop( - &mut chip_handler, - &mut circuit_builder, - stack_top[0].into(), - old_stack_ts.values(), - stack_values, - ); - - let (ram_load_id, ram_store_id) = chip_handler.ram_handler.finalize(&mut circuit_builder); - circuit_builder.configure(); - - let outputs_wire_id = [ram_load_id, ram_store_id, None]; - - Ok(InstCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - layout: InstCircuitLayout { - chip_check_wire_id: outputs_wire_id, - phases_wire_id: vec![phase0_wire_id], - ..Default::default() - }, - }) - } - - // TODO(Matthias): Check whether we need this function. - #[allow(dead_code)] - fn name() -> &'static str { - "ReturnRestStackPop" - } -} diff --git a/singer/src/instructions/swap.rs b/singer/src/instructions/swap.rs deleted file mode 100644 index cacc8d0dd..000000000 --- a/singer/src/instructions/swap.rs +++ /dev/null @@ -1,389 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; -use gkr::structs::Circuit; -use paste::paste; -use simple_frontend::structs::{CircuitBuilder, MixedCell}; -use singer_utils::{ - chip_handler::{ - ChipHandler, bytecode::BytecodeChip, global_state::GlobalStateChip, range::RangeChip, - stack::StackChip, - }, - constants::OpcodeType, - register_witness, - structs::{PCUInt, StackUInt, TSUInt}, - uint::constants::AddSubConstants, -}; -use std::{collections::BTreeMap, sync::Arc}; - -use crate::error::ZKVMError; - -use super::{ChipChallenges, InstCircuit, InstCircuitLayout, Instruction, InstructionGraph}; -pub struct SwapInstruction; - -impl InstructionGraph for SwapInstruction { - type InstType = Self; -} - -register_witness!( - SwapInstruction, - phase0 { - pc => PCUInt::N_OPERAND_CELLS, - stack_ts => TSUInt::N_OPERAND_CELLS, - memory_ts => TSUInt::N_OPERAND_CELLS, - stack_top => 1, - clk => 1, - - pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, - - old_stack_ts_1 => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt_1 => AddSubConstants::::N_WITNESS_CELLS, - old_stack_ts_n_plus_1 => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt_n_plus_1 => AddSubConstants::::N_WITNESS_CELLS, - stack_values_1 => StackUInt::N_OPERAND_CELLS, - stack_values_n_plus_1 => StackUInt::N_OPERAND_CELLS - } -); - -impl Instruction for SwapInstruction { - const OPCODE: OpcodeType = match N { - 1 => OpcodeType::SWAP1, - 2 => OpcodeType::SWAP2, - 4 => OpcodeType::SWAP4, - _ => unimplemented!(), - }; - const NAME: &'static str = match N { - 1 => "SWAP1", - 2 => "SWAP2", - 4 => "SWAP4", - _ => unimplemented!(), - }; - fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { - let mut circuit_builder = CircuitBuilder::default(); - let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); - - let mut chip_handler = ChipHandler::new(challenges); - - // State update - let pc = PCUInt::try_from(&phase0[Self::phase0_pc()])?; - let stack_ts = TSUInt::try_from(&phase0[Self::phase0_stack_ts()])?; - let memory_ts = &phase0[Self::phase0_memory_ts()]; - let stack_top = phase0[Self::phase0_stack_top().start]; - let stack_top_expr = MixedCell::Cell(stack_top); - let clk = phase0[Self::phase0_clk().start]; - let clk_expr = MixedCell::Cell(clk); - GlobalStateChip::state_in( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - stack_ts.values(), - memory_ts, - stack_top, - clk, - ); - - let next_pc = - RangeChip::add_pc_const(&mut circuit_builder, &pc, 1, &phase0[Self::phase0_pc_add()])?; - let next_stack_ts = RangeChip::add_ts_with_const( - &mut chip_handler, - &mut circuit_builder, - &stack_ts, - 1, - &phase0[Self::phase0_stack_ts_add()], - )?; - - GlobalStateChip::state_out( - &mut chip_handler, - &mut circuit_builder, - next_pc.values(), - next_stack_ts.values(), - memory_ts, - stack_top_expr, - clk_expr.add(E::BaseField::ONE), - ); - - // Check the range of stack_top - (N + 1) is within [0, 1 << STACK_TOP_BIT_WIDTH). - RangeChip::range_check_stack_top( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(N as u64 + 1)), - )?; - - // Pop rlc of stack[top - (N + 1)] from stack - let old_stack_ts_n_plus_1 = (&phase0[Self::phase0_old_stack_ts_n_plus_1()]).try_into()?; - TSUInt::assert_lt( - &mut circuit_builder, - &mut chip_handler, - &old_stack_ts_n_plus_1, - &stack_ts, - &phase0[Self::phase0_old_stack_ts_lt_n_plus_1()], - )?; - let stack_values_n_plus_1 = &phase0[Self::phase0_stack_values_n_plus_1()]; - StackChip::pop( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(N as u64 + 1)), - old_stack_ts_n_plus_1.values(), - stack_values_n_plus_1, - ); - - // Pop rlc of stack[top - 1] from stack - let old_stack_ts_1 = (&phase0[Self::phase0_old_stack_ts_1()]).try_into()?; - TSUInt::assert_lt( - &mut circuit_builder, - &mut chip_handler, - &old_stack_ts_1, - &stack_ts, - &phase0[Self::phase0_old_stack_ts_lt_1()], - )?; - let stack_values_1 = &phase0[Self::phase0_stack_values_1()]; - StackChip::pop( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::ONE), - old_stack_ts_1.values(), - stack_values_1, - ); - - // Push stack_1 to the stack at top - (N + 1) - StackChip::push( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::from(N as u64 + 1)), - stack_ts.values(), - stack_values_1, - ); - // Push stack_n_plus_1 to the stack at top - 1 - StackChip::push( - &mut chip_handler, - &mut circuit_builder, - stack_top_expr.sub(E::BaseField::ONE), - stack_ts.values(), - stack_values_n_plus_1, - ); - - // Bytecode check for (pc, SWAP{N}). - BytecodeChip::bytecode_with_pc_opcode( - &mut chip_handler, - &mut circuit_builder, - pc.values(), - >::OPCODE, - ); - - let (ram_load_id, ram_store_id, rom_id) = chip_handler.finalize(&mut circuit_builder); - circuit_builder.configure(); - - let outputs_wire_id = [ram_load_id, ram_store_id, rom_id]; - - Ok(InstCircuit { - circuit: Arc::new(Circuit::new(&circuit_builder)), - layout: InstCircuitLayout { - chip_check_wire_id: outputs_wire_id, - phases_wire_id: vec![phase0_wire_id], - ..Default::default() - }, - }) - } -} - -#[cfg(test)] -mod test { - #[cfg(not(debug_assertions))] - use crate::{ - CircuitWiresIn, SingerGraphBuilder, SingerParams, - instructions::{InstructionGraph, SingerCircuitBuilder}, - scheme::GKRGraphProverState, - }; - #[cfg(not(debug_assertions))] - use ark_std::test_rng; - #[cfg(not(debug_assertions))] - use ff::Field; - #[cfg(not(debug_assertions))] - use ff_ext::ExtensionField; - use goldilocks::{Goldilocks, GoldilocksExt2}; - use singer_utils::{constants::RANGE_CHIP_BIT_WIDTH, structs::TSUInt}; - use std::collections::BTreeMap; - #[cfg(not(debug_assertions))] - use std::time::Instant; - #[cfg(not(debug_assertions))] - use transcript::Transcript; - - #[allow(deprecated)] - use crate::test::test_opcode_circuit; - use crate::{ - instructions::{ChipChallenges, Instruction, SwapInstruction}, - test::get_uint_params, - utils::u64vec, - }; - - #[test] - fn test_swap2_construct_circuit() { - let challenges = ChipChallenges::default(); - - let phase0_idx_map = SwapInstruction::<2>::phase0_idxes_map(); - let phase0_witness_size = SwapInstruction::<2>::phase0_size(); - - #[cfg(feature = "witness-count")] - { - println!("SWAP2 {:?}", &phase0_idx_map); - println!("SWAP2 witness_size = {:?}", phase0_witness_size); - } - - // initialize general test inputs associated with push1 - let inst_circuit = SwapInstruction::<2>::construct_circuit(challenges).unwrap(); - - #[cfg(feature = "test-dbg")] - println!("{:?}", inst_circuit); - - let mut phase0_values_map = BTreeMap::>::new(); - phase0_values_map.insert("phase0_pc".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_stack_ts".to_string(), vec![Goldilocks::from(4u64)]); - phase0_values_map.insert("phase0_memory_ts".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert("phase0_stack_top".to_string(), vec![Goldilocks::from( - 100u64, - )]); - phase0_values_map.insert("phase0_clk".to_string(), vec![Goldilocks::from(1u64)]); - phase0_values_map.insert( - "phase0_pc_add".to_string(), - vec![], // carry is 0, may test carry using larger values in PCUInt - ); - phase0_values_map.insert("phase0_stack_ts_add".to_string(), vec![ - Goldilocks::from(5u64), /* first TSUInt::N_RANGE_CELLS = 1*(56/16) = 4 cells are - * range values, stack_ts + 1 = 4 */ - Goldilocks::from(0u64), - Goldilocks::from(0u64), - Goldilocks::from(0u64), - // no place for carry - ]); - phase0_values_map.insert("phase0_old_stack_ts_1".to_string(), vec![Goldilocks::from( - 3u64, - )]); - let m: u64 = (1 << get_uint_params::().1) - 1; - let range_values = u64vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); - phase0_values_map.insert("phase0_old_stack_ts_lt_1".to_string(), vec![ - Goldilocks::from(range_values[0]), - Goldilocks::from(range_values[1]), - Goldilocks::from(range_values[2]), - Goldilocks::from(1u64), // current length has no cells for borrow - ]); - phase0_values_map.insert("phase0_old_stack_ts_n_plus_1".to_string(), vec![ - Goldilocks::from(1u64), - ]); - let m: u64 = (1 << get_uint_params::().1) - 3; - let range_values = u64vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); - phase0_values_map.insert("phase0_old_stack_ts_lt_n_plus_1".to_string(), vec![ - Goldilocks::from(range_values[0]), - Goldilocks::from(range_values[1]), - Goldilocks::from(range_values[2]), - Goldilocks::from(1u64), // current length has no cells for borrow - ]); - phase0_values_map.insert("phase0_stack_values_1".to_string(), vec![ - Goldilocks::from(7u64), - Goldilocks::from(6u64), - Goldilocks::from(5u64), - Goldilocks::from(4u64), - Goldilocks::from(3u64), - Goldilocks::from(2u64), - Goldilocks::from(1u64), - Goldilocks::from(0u64), - ]); - phase0_values_map.insert("phase0_stack_values_n_plus_1".to_string(), vec![ - Goldilocks::from(0u64), - Goldilocks::from(1u64), - Goldilocks::from(2u64), - Goldilocks::from(3u64), - Goldilocks::from(4u64), - Goldilocks::from(5u64), - Goldilocks::from(6u64), - Goldilocks::from(7u64), - ]); - - let circuit_witness_challenges = vec![ - GoldilocksExt2::from(2), - GoldilocksExt2::from(2), - GoldilocksExt2::from(2), - ]; - - #[allow(deprecated)] - let _circuit_witness = test_opcode_circuit( - &inst_circuit, - &phase0_idx_map, - phase0_witness_size, - &phase0_values_map, - circuit_witness_challenges, - ); - } - - #[cfg(not(debug_assertions))] - fn bench_swap_instruction_helper(instance_num_vars: usize) { - let chip_challenges = ChipChallenges::default(); - let circuit_builder = - SingerCircuitBuilder::::new(chip_challenges).expect("circuit builder failed"); - let mut singer_builder = SingerGraphBuilder::::default(); - - let mut rng = test_rng(); - let size = SwapInstruction::::phase0_size(); - let phase0: CircuitWiresIn = vec![ - (0..(1 << instance_num_vars)) - .map(|_| { - (0..size) - .map(|_| E::BaseField::random(&mut rng)) - .collect::>() - }) - .collect::>() - .into(), - ]; - - let real_challenges = vec![E::random(&mut rng), E::random(&mut rng)]; - - let timer = Instant::now(); - - let _ = SwapInstruction::::construct_graph_and_witness( - &mut singer_builder.graph_builder, - &mut singer_builder.chip_builder, - &circuit_builder.insts_circuits - [ as Instruction>::OPCODE as usize], - vec![phase0], - &real_challenges, - 1 << instance_num_vars, - &SingerParams::default(), - ) - .expect("gkr graph construction failed"); - - let (graph, wit) = singer_builder.graph_builder.finalize_graph_and_witness(); - - println!( - "Swap{}Instruction::construct_graph_and_witness, instance_num_vars = {}, time = {}", - N, - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - - let point = vec![E::random(&mut rng), E::random(&mut rng)]; - let target_evals = graph.target_evals(&wit, &point); - - let prover_transcript = &mut Transcript::new(b"Singer"); - - let timer = Instant::now(); - let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, prover_transcript, 1) - .expect("prove failed"); - println!( - "Swap{}Instruction::prove, instance_num_vars = {}, time = {}", - N, - instance_num_vars, - timer.elapsed().as_secs_f64() - ); - } - - #[test] - #[cfg(not(debug_assertions))] - fn bench_swap2_instruction() { - bench_swap_instruction_helper::(10); - } - - #[test] - #[cfg(not(debug_assertions))] - fn bench_swap4_instruction() { - bench_swap_instruction_helper::(10); - } -} diff --git a/singer/src/lib.rs b/singer/src/lib.rs deleted file mode 100644 index 228fbab6e..000000000 --- a/singer/src/lib.rs +++ /dev/null @@ -1,240 +0,0 @@ -#![allow(incomplete_features)] -#![feature(generic_const_exprs)] - -use error::ZKVMError; -use ff_ext::ExtensionField; -use gkr_graph::structs::{ - CircuitGraph, CircuitGraphAuxInfo, CircuitGraphBuilder, CircuitGraphWitness, NodeOutputType, -}; -use instructions::{ - InstOutputType, SingerCircuitBuilder, construct_inst_graph, construct_inst_graph_and_witness, -}; -use multilinear_extensions::{ - mle::DenseMultilinearExtension, virtual_poly_v2::ArcMultilinearExtension, -}; -use singer_utils::chips::SingerChipBuilder; -use std::mem; - -pub mod error; -pub mod instructions; -pub mod scheme; -#[cfg(test)] -pub mod test; -pub use utils::u64vec; -mod utils; - -// Process sketch: -// 1. Construct instruction circuits and circuit gadgets => circuit gadgets -// 2. (bytecode + input) => Run revm interpreter, generate all wires in -// 2.1 phase 0 wire in + commitment -// 2.2 phase 1 wire in + commitment -// 2.3 phase 2 wire in + commitment -// 3. (circuit gadgets + wires in) => gkr graph + gkr witness -// 4. (gkr graph + gkr witness) => (gkr proof + point) -// 5. (commitments + point) => pcs proof - -#[allow(clippy::too_long_first_doc_paragraph)] -/// Circuit graph builder for Singer. `output_wires_id` is indexed by -/// InstOutputType, corresponding to the product of summation of the chip check -/// records. `public_output_size` is the wire id stores the size of public -/// output. -#[derive(Default)] -pub struct SingerGraphBuilder<'a, E: ExtensionField> { - pub graph_builder: CircuitGraphBuilder<'a, E>, - pub chip_builder: SingerChipBuilder, - pub public_output_size: Option, -} - -impl<'a, E: ExtensionField> SingerGraphBuilder<'a, E> { - pub fn construct_graph_and_witness( - mut self, - circuit_builder: &SingerCircuitBuilder, - singer_wires_in: SingerWiresIn, - bytecode: &[u8], - program_input: &[u8], - real_challenges: &[E], - params: &SingerParams, - ) -> Result<(SingerCircuit, SingerWitness<'a, E>, SingerWiresOutID), ZKVMError> { - // Add instruction and its extension (if any) circuits to the graph. - for inst_wires_in in singer_wires_in.instructions.into_iter() { - let InstWiresIn { - opcode, - real_n_instances, - wires_in, - } = inst_wires_in; - let inst_circuits = &circuit_builder.insts_circuits[opcode as usize]; - let pub_out_id = construct_inst_graph_and_witness( - opcode, - &mut self.graph_builder, - &mut self.chip_builder, - inst_circuits, - wires_in, - real_challenges, - real_n_instances, - params, - )?; - if pub_out_id.is_some() { - self.public_output_size = pub_out_id; - } - } - - // Construct tables for lookup arguments, including bytecode, range and - // calldata. - let table_out_node_id = self.chip_builder.construct_lookup_table_graph_and_witness( - &mut self.graph_builder, - bytecode, - program_input, - singer_wires_in.table_count, - &circuit_builder.challenges, - real_challenges, - )?; - - let SingerGraphBuilder { - graph_builder, - chip_builder, - public_output_size, - } = self; - - let mut output_wires_id = chip_builder.output_wires_id; - - let singer_wire_out_id = SingerWiresOutID { - ram_load: mem::take(&mut output_wires_id[InstOutputType::RAMLoad as usize]), - ram_store: mem::take(&mut output_wires_id[InstOutputType::RAMStore as usize]), - rom_input: mem::take(&mut output_wires_id[InstOutputType::ROMInput as usize]), - rom_table: table_out_node_id, - - public_output_size, - }; - - let (graph, graph_witness) = - graph_builder.finalize_graph_and_witness_with_targets(&singer_wire_out_id.to_vec()); - Ok(( - SingerCircuit(graph), - SingerWitness(graph_witness), - singer_wire_out_id, - )) - } - - pub fn construct_graph( - mut self, - circuit_builder: &SingerCircuitBuilder, - aux_info: &SingerAuxInfo, - ) -> Result, ZKVMError> { - // Add instruction and its extension (if any) circuits to the graph. - for (opcode, real_n_instances) in aux_info.real_n_instances.iter() { - let inst_circuits = &circuit_builder.insts_circuits[*opcode as usize]; - let pub_out_id = construct_inst_graph( - *opcode, - &mut self.graph_builder, - &mut self.chip_builder, - inst_circuits, - *real_n_instances, - &aux_info.singer_params, - )?; - if pub_out_id.is_some() { - self.public_output_size = pub_out_id; - } - } - - // Construct tables for lookup arguments, including bytecode, range and - // calldata. - let table_out_node_id = self.chip_builder.construct_lookup_table_graph( - &mut self.graph_builder, - aux_info.bytecode_len, - aux_info.program_input_len, - &circuit_builder.challenges, - )?; - - let SingerGraphBuilder { - graph_builder, - chip_builder, - public_output_size, - } = self; - - let mut output_wires_id = chip_builder.output_wires_id; - - let singer_wire_out_id = SingerWiresOutID { - ram_load: mem::take(&mut output_wires_id[InstOutputType::RAMLoad as usize]), - ram_store: mem::take(&mut output_wires_id[InstOutputType::RAMStore as usize]), - rom_input: mem::take(&mut output_wires_id[InstOutputType::ROMInput as usize]), - rom_table: table_out_node_id, - - public_output_size, - }; - - let graph = graph_builder.finalize_graph_with_targets(&singer_wire_out_id.to_vec()); - Ok(SingerCircuit(graph)) - } -} - -pub struct SingerCircuit(CircuitGraph); - -pub struct SingerWitness<'a, E: ExtensionField>(pub CircuitGraphWitness<'a, E>); - -#[derive(Clone, Debug, Default)] -pub struct SingerWiresIn { - pub instructions: Vec>, - pub table_count: Vec>, -} - -#[derive(Clone, Debug, Default)] -pub struct SingerParams { - pub n_public_output_bytes: usize, - pub n_mem_initialize: usize, - pub n_mem_finalize: usize, - pub n_stack_finalize: usize, -} -#[derive(Clone, Debug)] -pub struct SingerWiresOutID { - ram_load: Vec, - ram_store: Vec, - rom_input: Vec, - rom_table: Vec, - - public_output_size: Option, -} - -#[derive(Clone)] -pub struct SingerWiresOutValues<'a, E: ExtensionField> { - ram_load: Vec>, - ram_store: Vec>, - rom_input: Vec>, - rom_table: Vec>, - - public_output_size: Option>, -} - -impl SingerWiresOutID { - pub fn to_vec(&self) -> Vec { - let mut res = [ - self.ram_load.clone(), - self.ram_store.clone(), - self.rom_input.clone(), - ] - .concat(); - if let Some(public_output_size) = self.public_output_size { - res.push(public_output_size); - } - res - } -} - -#[derive(Clone, Debug, Default)] -pub struct SingerAuxInfo { - pub graph_aux_info: CircuitGraphAuxInfo, - pub real_n_instances: Vec<(u8, usize)>, - pub singer_params: SingerParams, - pub bytecode_len: usize, - pub program_input_len: usize, - pub program_output_len: usize, -} - -// Indexed by 1. wires_in id (or phase); 2. instance id || wire id. -pub type CircuitWiresIn = Vec>; - -#[derive(Clone, Debug, Default)] -pub struct InstWiresIn { - pub opcode: u8, - pub real_n_instances: usize, - pub wires_in: Vec>, -} diff --git a/singer/src/scheme.rs b/singer/src/scheme.rs deleted file mode 100644 index 6c5448e2e..000000000 --- a/singer/src/scheme.rs +++ /dev/null @@ -1,15 +0,0 @@ -use ff_ext::ExtensionField; - -// TODO: to be changed to a real PCS scheme. - -pub mod prover; -pub mod verifier; - -pub type GKRGraphProof = gkr_graph::structs::IOPProof; -pub type GKRGraphProverState = gkr_graph::structs::IOPProverState; -pub type GKRGraphVerifierState = gkr_graph::structs::IOPVerifierState; - -pub struct SingerProof { - // TODO: restore and implement `commitment_phase_proof` and `open_phase_proof` - gkr_phase_proof: GKRGraphProof, -} diff --git a/singer/src/scheme/prover.rs b/singer/src/scheme/prover.rs deleted file mode 100644 index 0f48de865..000000000 --- a/singer/src/scheme/prover.rs +++ /dev/null @@ -1,78 +0,0 @@ -use ff_ext::ExtensionField; -use gkr_graph::structs::{CircuitGraphAuxInfo, NodeOutputType}; -use itertools::Itertools; -use multilinear_extensions::virtual_poly_v2::ArcMultilinearExtension; -use transcript::Transcript; - -use crate::{ - SingerCircuit, SingerWiresOutID, SingerWiresOutValues, SingerWitness, error::ZKVMError, -}; - -use super::{GKRGraphProverState, SingerProof}; - -pub fn prove<'a, E: ExtensionField>( - vm_circuit: &SingerCircuit, - vm_witness: &SingerWitness<'a, E>, - vm_out_id: &SingerWiresOutID, - transcript: &mut Transcript, -) -> Result< - ( - SingerProof, - CircuitGraphAuxInfo, - SingerWiresOutValues<'a, E>, - ), - ZKVMError, -> { - // TODO: Add PCS. - let point = (0..2 * ::DEGREE) - .map(|_| { - transcript - .get_and_append_challenge(b"output point") - .elements - }) - .collect_vec(); - - let singer_out_evals = { - let target_wits = |node_out_ids: &[NodeOutputType]| -> Vec> { - node_out_ids - .iter() - .map(|node| match node { - NodeOutputType::OutputLayer(node_id) => vm_witness.0.node_witnesses[*node_id] - .output_layer_witness_ref() - .clone(), - NodeOutputType::WireOut(node_id, wit_id) => { - vm_witness.0.node_witnesses[*node_id].witness_out_ref()[*wit_id as usize] - .clone() - } - }) - .collect_vec() - }; - let ram_load = target_wits(&vm_out_id.ram_load); - let ram_store = target_wits(&vm_out_id.ram_store); - let rom_input = target_wits(&vm_out_id.rom_input); - let rom_table = target_wits(&vm_out_id.rom_table); - SingerWiresOutValues { - ram_load, - ram_store, - rom_input, - rom_table, - public_output_size: vm_out_id - .public_output_size - .map(|node| target_wits(&[node])[0].clone()), - } - }; - - let aux_info = CircuitGraphAuxInfo { - instance_num_vars: vm_witness - .0 - .node_witnesses - .iter() - .map(|witness| witness.instance_num_vars()) - .collect(), - }; - - let target_evals = vm_circuit.0.target_evals(&vm_witness.0, &point); - let gkr_phase_proof = - GKRGraphProverState::prove(&vm_circuit.0, &vm_witness.0, &target_evals, transcript, 1)?; - Ok((SingerProof { gkr_phase_proof }, aux_info, singer_out_evals)) -} diff --git a/singer/src/scheme/verifier.rs b/singer/src/scheme/verifier.rs deleted file mode 100644 index b61c76eb3..000000000 --- a/singer/src/scheme/verifier.rs +++ /dev/null @@ -1,105 +0,0 @@ -use ff_ext::ExtensionField; -use gkr::structs::PointAndEval; -use gkr_graph::structs::TargetEvaluations; -use itertools::{Itertools, chain}; -use transcript::Transcript; - -use crate::{SingerAuxInfo, SingerCircuit, SingerWiresOutValues, error::ZKVMError}; - -use super::{GKRGraphVerifierState, SingerProof}; - -pub fn verify( - vm_circuit: &SingerCircuit, - vm_proof: SingerProof, - singer_out_evals: SingerWiresOutValues<'_, E>, - aux_info: &SingerAuxInfo, - challenges: &[E], - transcript: &mut Transcript, -) -> Result<(), ZKVMError> { - // TODO: Add PCS. - let point = (0..2 * ::DEGREE) - .map(|_| { - transcript - .get_and_append_challenge(b"output point") - .elements - }) - .collect_vec(); - - let SingerWiresOutValues { - ram_load, - ram_store, - rom_input, - rom_table, - public_output_size, - } = singer_out_evals; - - let ram_load_product: E = ram_load - .iter() - .map(|x| E::from_limbs(x.get_base_field_vec())) - .product(); - let ram_store_product = ram_store - .iter() - .map(|x| E::from_limbs(x.get_base_field_vec())) - .product(); - if ram_load_product != ram_store_product { - return Err(ZKVMError::VerifyError); - } - - let rom_input_sum = rom_input - .iter() - .map(|x| { - let l = x.get_base_field_vec().len(); - let (den, num) = x.get_base_field_vec().split_at(l / 2); - (E::from_limbs(den), E::from_limbs(num)) - }) - .fold((E::ONE, E::ZERO), |acc, x| { - (acc.0 * x.0, acc.0 * x.1 + acc.1 * x.0) - }); - let rom_table_sum = rom_table - .iter() - .map(|x| { - let l = x.get_base_field_vec().len(); - let (den, num) = x.get_base_field_vec().split_at(l / 2); - (E::from_limbs(den), E::from_limbs(num)) - }) - .fold((E::ONE, E::ZERO), |acc, x| { - (acc.0 * x.0, acc.0 * x.1 + acc.1 * x.0) - }); - if rom_input_sum.0 * rom_table_sum.1 != rom_input_sum.1 * rom_table_sum.0 { - return Err(ZKVMError::VerifyError); - } - - let mut target_evals = TargetEvaluations( - chain![ram_load, ram_store, rom_input, rom_table,] - .map(|x| { - PointAndEval::new( - point[..x.num_vars()].to_vec(), - x.evaluate(&point[..x.num_vars()]), - ) - }) - .collect_vec(), - ); - - if let Some(output) = &public_output_size { - let f = output; - target_evals.0.push(PointAndEval::new( - point[..f.num_vars()].to_vec(), - f.evaluate(&point[..f.num_vars()]), - )); - assert_eq!( - output.get_base_field_vec()[0], - E::BaseField::from(aux_info.program_output_len as u64) - ) - } - - GKRGraphVerifierState::verify( - &vm_circuit.0, - challenges, - &target_evals, - vm_proof.gkr_phase_proof, - &aux_info.graph_aux_info, - transcript, - )?; - - Ok(()) -} diff --git a/singer/src/test.rs b/singer/src/test.rs deleted file mode 100644 index 3a588fd7a..000000000 --- a/singer/src/test.rs +++ /dev/null @@ -1,166 +0,0 @@ -use core::ops::Range; -use ff::Field; -use ff_ext::ExtensionField; -use gkr::structs::CircuitWitness; -use multilinear_extensions::mle::IntoMLE; -use simple_frontend::structs::CellId; -use singer_utils::uint::UInt; -use std::collections::BTreeMap; - -use crate::instructions::InstCircuit; - -pub(crate) trait UIntParams { - const BITS: usize; - const CELL_BIT_WIDTH: usize; -} - -impl UIntParams for UInt { - const BITS: usize = M; - const CELL_BIT_WIDTH: usize = C; -} - -pub(crate) fn get_uint_params() -> (usize, usize) { - (T::BITS, T::CELL_BIT_WIDTH) -} - -pub(crate) fn test_opcode_circuit_v2<'a, Ext: ExtensionField>( - inst_circuit: &InstCircuit, - phase0_idx_map: &BTreeMap<&'static str, Range>, - phase0_witness_size: usize, - phase0_values_map: &BTreeMap<&'static str, Vec>, - circuit_witness_challenges: Vec, -) -> CircuitWitness<'a, Ext> { - // configure circuit - let circuit = inst_circuit.circuit.as_ref(); - - #[cfg(feature = "test-dbg")] - println!("{:?}", circuit); - - // get indexes for circuit inputs and wire_in - // only phase0 - let inputs_idxes = &inst_circuit.layout.phases_wire_id; - let phase0_input_idx = inputs_idxes[0]; - - // assign witnesses to circuit - let n_witness_in = circuit.n_witness_in; - let mut witness_in = vec![vec![]; n_witness_in]; - witness_in[phase0_input_idx as usize] = vec![Ext::BaseField::ZERO; phase0_witness_size]; - - for key in phase0_idx_map.keys() { - let range = phase0_idx_map - .get(key) - .unwrap() - .clone() - .collect::>(); - let values = phase0_values_map - .get(key) - .expect(&("unknown key ".to_owned() + key)); - for (value_idx, cell_idx) in range.into_iter().enumerate() { - if value_idx < values.len() { - witness_in[phase0_input_idx as usize][cell_idx] = values[value_idx]; - } - } - } - - #[cfg(feature = "test-dbg")] - println!("{:?}", witness_in); - - let witness_in = witness_in.into_iter().map(|w_in| w_in.into_mle()).collect(); - - let circuit_witness = { - let mut circuit_witness = CircuitWitness::new(circuit, circuit_witness_challenges); - circuit_witness.add_instance(circuit, witness_in); - circuit_witness - }; - - #[cfg(feature = "test-dbg")] - println!("{:?}", circuit_witness); - // dbg!(&circuit_witness); - - circuit_witness.check_correctness(circuit); - - circuit_witness - - // let instance_num_vars = circuit_witness.instance_num_vars(); - // let (proof, output_num_vars, output_eval) = { - // let mut prover_transcript = Transcript::::new(b"example"); - // let output_num_vars = instance_num_vars + circuit.first_layer_ref().num_vars(); - // let output_point = (0..output_num_vars) - // .map(|_| { - // prover_transcript - // .get_and_append_challenge(b"output point") - // .elements - // }) - // .collect_vec(); - // let output_eval = circuit_witness - // .layer_poly(0, circuit.first_layer_ref().num_vars()) - // .evaluate(&output_point); - // ( - // IOPProverState::prove_parallel( - // &circuit, - // &circuit_witness, - // &[(output_point, output_eval)], - // &[], - // &mut prover_transcript, - // ), - // output_num_vars, - // output_eval, - // ) - // }; - // let gkr_input_claims = { - // let mut verifier_transcript = &mut Transcript::::new(b"example"); - // let output_point = (0..output_num_vars) - // .map(|_| { - // verifier_transcript - // .get_and_append_challenge(b"output point") - // .elements - // }) - // .collect_vec(); - // IOPVerifierState::verify_parallel( - // &circuit, - // circuit_witness.challenges(), - // &[(output_point, output_eval)], - // &[], - // &proof, - // instance_num_vars, - // &mut verifier_transcript, - // ) - // .expect("verification failed") - // }; - // let expected_values = circuit_witness - // .witness_in_ref() - // .iter() - // .map(|witness| { - // witness - // .instances - // .as_slice() - // .mle(circuit.max_wit_in_num_vars.expect("REASON"), instance_num_vars) - // .evaluate(&gkr_input_claims.point_and_evals) - // }) - // .collect_vec(); - // for i in 0..gkr_input_claims.point_and_evals.len() { - // assert_eq!(expected_values[i], gkr_input_claims.point_and_evals[i]); - // } - // println!("verification succeeded"); -} - -#[deprecated(note = "deprecated and use test_opcode_circuit_v2 instead")] -pub(crate) fn test_opcode_circuit<'a, Ext: ExtensionField>( - inst_circuit: &InstCircuit, - phase0_idx_map: &BTreeMap<&'static str, Range>, - phase0_witness_size: usize, - phase0_values_map: &BTreeMap>, - circuit_witness_challenges: Vec, -) -> CircuitWitness<'a, Ext> { - let phase0_values_map = phase0_values_map - .iter() - .map(|(key, value)| (key.clone().leak() as &'static str, value.clone())) - .collect::>>(); - test_opcode_circuit_v2( - inst_circuit, - phase0_idx_map, - phase0_witness_size, - &phase0_values_map, - circuit_witness_challenges, - ) -} diff --git a/singer/src/utils.rs b/singer/src/utils.rs deleted file mode 100644 index c9cca791c..000000000 --- a/singer/src/utils.rs +++ /dev/null @@ -1,28 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; -use itertools::izip; -use simple_frontend::structs::{CellId, CircuitBuilder}; - -pub(crate) fn add_assign_each_cell( - circuit_builder: &mut CircuitBuilder, - dest: &[CellId], - src: &[CellId], -) { - assert_eq!(dest.len(), src.len()); - for (dest, src) in izip!(dest, src) { - circuit_builder.add(*dest, *src, E::BaseField::ONE); - } -} - -// split single u64 value into W slices, each slice got C bits. -// all the rest slices will be filled with 0 if W x C > 64 -pub fn u64vec(x: u64) -> [u64; W] { - assert!(C <= 64); - let mut x = x; - let mut ret = [0; W]; - for item in &mut ret { - *item = x & ((1 << C) - 1); - x >>= C; - } - ret -}