diff --git a/.gitignore b/.gitignore index 879b665c9..1ff2d1b5e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target .vscode -*.log \ No newline at end of file +*.log +log.txt diff --git a/Cargo.lock b/Cargo.lock index 7e5201052..1aaab2229 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1256,6 +1256,9 @@ name = "singer" version = "0.1.0" dependencies = [ "ark-std", + "cfg-if", + "const_env", + "criterion", "ff", "ff_ext", "gkr", @@ -1265,6 +1268,7 @@ dependencies = [ "mpcs", "multilinear_extensions", "paste", + "pprof", "rayon", "serde", "simple-frontend", @@ -1299,6 +1303,7 @@ dependencies = [ name = "singer-utils" version = "0.1.0" dependencies = [ + "ark-std", "ff", "ff_ext", "gkr", @@ -1309,6 +1314,7 @@ dependencies = [ "strum 0.26.1", "strum_macros 0.26.1", "sumcheck", + "transcript", ] [[package]] diff --git a/gkr-graph/examples/series_connection_alt.rs b/gkr-graph/examples/series_connection_alt.rs index a98f73503..713798a32 100644 --- a/gkr-graph/examples/series_connection_alt.rs +++ b/gkr-graph/examples/series_connection_alt.rs @@ -246,6 +246,7 @@ fn main() -> Result<(), GKRGraphError> { &circuit_witness, &TargetEvaluations(vec![PointAndEval::new(output_point, output_eval)]), &mut prover_transcript, + 1, )?; // ============= diff --git a/gkr-graph/src/circuit_builder.rs b/gkr-graph/src/circuit_builder.rs index 632f677b3..acf215f9a 100644 --- a/gkr-graph/src/circuit_builder.rs +++ b/gkr-graph/src/circuit_builder.rs @@ -13,6 +13,7 @@ impl CircuitGraph { witness: &CircuitGraphWitness, point: &Point, ) -> TargetEvaluations { + // println!("targets: {:?}, point: {:?}", self.targets, point); let target_evals = self .targets .iter() @@ -29,7 +30,9 @@ impl CircuitGraph { .as_slice() .original_mle(), }; - PointAndEval::new(point[..poly.num_vars].to_vec(), poly.evaluate(point)) + // 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 index 81f4b8bb9..d12d54c41 100644 --- a/gkr-graph/src/circuit_graph_builder.rs +++ b/gkr-graph/src/circuit_graph_builder.rs @@ -36,6 +36,10 @@ impl CircuitGraphBuilder { 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()); diff --git a/gkr-graph/src/prover.rs b/gkr-graph/src/prover.rs index 1b1c298d9..df7ef8f26 100644 --- a/gkr-graph/src/prover.rs +++ b/gkr-graph/src/prover.rs @@ -18,6 +18,7 @@ impl IOPProverState { 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()); @@ -35,14 +36,25 @@ impl IOPProverState { let gkr_proofs = izip!(&circuit.nodes, &circuit_witness.node_witnesses) .rev() .map(|(node, witness)| { + // println!("expected_max_thread_id {:?}", expected_max_thread_id); + let max_thread_id = witness.n_instances().min(expected_max_thread_id); + // println!("max_thread_id {:?}", max_thread_id); + let timer = std::time::Instant::now(); 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]), - 1, + 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() diff --git a/gkr-graph/src/structs.rs b/gkr-graph/src/structs.rs index 50a0f2226..5a13d6784 100644 --- a/gkr-graph/src/structs.rs +++ b/gkr-graph/src/structs.rs @@ -22,7 +22,7 @@ pub struct IOPVerifierState { marker: PhantomData, } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub(crate) enum NodeInputType { WireIn(usize, WitnessId), } @@ -43,6 +43,7 @@ pub enum PredType { PredWireDup(NodeOutputType), } +#[derive(Clone, Debug)] pub struct CircuitNode { pub(crate) id: usize, pub(crate) label: &'static str, @@ -51,7 +52,7 @@ pub struct CircuitNode { pub(crate) preds: Vec, } -#[derive(Default)] +#[derive(Clone, Debug, Default)] pub struct CircuitGraph { pub(crate) nodes: Vec>, pub(crate) targets: Vec, diff --git a/gkr/src/circuit/circuit_layout.rs b/gkr/src/circuit/circuit_layout.rs index b95e2cbb9..3fb7d7787 100644 --- a/gkr/src/circuit/circuit_layout.rs +++ b/gkr/src/circuit/circuit_layout.rs @@ -1,4 +1,4 @@ -use core::fmt; +use core::{fmt, panic}; use std::collections::{BTreeMap, HashMap}; use ff_ext::ExtensionField; @@ -247,6 +247,9 @@ impl Circuit { 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 { diff --git a/gkr/src/circuit/circuit_witness.rs b/gkr/src/circuit/circuit_witness.rs index 8153e9edb..66f423004 100644 --- a/gkr/src/circuit/circuit_witness.rs +++ b/gkr/src/circuit/circuit_witness.rs @@ -148,11 +148,13 @@ impl CircuitWitness { wit_out.resize(length, F::ZERO); wits_out[wit_id].instances[instance_id] = wit_out; }); - circuit.assert_consts.iter().for_each(|gate| { - if let ConstantType::Field(constant) = gate.scalar { - assert_eq!(layer_wits[0].instances[instance_id][gate.idx_out], constant); - } - }); + + // #[cfg(debug_assertions)] + // circuit.assert_consts.iter().for_each(|gate| { + // if let ConstantType::Field(constant) = gate.scalar { + // assert_eq!(layer_wits[0].instances[instance_id][gate.idx_out], constant); + // } + // }); } (layer_wits, wits_out) } diff --git a/gkr/src/utils.rs b/gkr/src/utils.rs index 4627f3c40..284e9f09d 100644 --- a/gkr/src/utils.rs +++ b/gkr/src/utils.rs @@ -1,6 +1,5 @@ use ff::Field; use ff_ext::ExtensionField; -use rayon::{iter::ParallelIterator, slice::ParallelSlice}; use sumcheck::util::{ceil_log2, is_power_of_2}; use std::{iter, sync::Arc}; diff --git a/singer-pro/src/basic_block.rs b/singer-pro/src/basic_block.rs index 34e8d5f09..c9ea30eba 100644 --- a/singer-pro/src/basic_block.rs +++ b/singer-pro/src/basic_block.rs @@ -244,6 +244,7 @@ impl BasicBlock { )?; let mut to_succ = &bb_start_circuit.layout.to_succ_inst; + let mut next_pc = None; let mut local_stack = BasicBlockStack::initialize(self.info.clone(), bb_start_node_id, to_succ); let mut pred_node_id = bb_start_node_id; @@ -282,6 +283,9 @@ impl BasicBlock { } pred_node_id = node_id[0]; to_succ = &inst_circuit.layout.to_succ_inst; + if let Some(to_bb_final) = inst_circuit.layout.to_bb_final { + next_pc = Some(NodeOutputType::WireOut(pred_node_id, to_bb_final)); + } local_stack.push_node_outputs(stack, mode); } @@ -304,6 +308,7 @@ impl BasicBlock { memory_ts, stack_top, clk, + next_pc, ); let bb_final_node_id = graph_builder.add_node_with_witness( "BB final", @@ -378,6 +383,7 @@ impl BasicBlock { )?; let mut to_succ = &bb_start_circuit.layout.to_succ_inst; + let mut next_pc = None; let mut local_stack = BasicBlockStack::initialize(self.info.clone(), bb_start_node_id, to_succ); let mut pred_node_id = bb_start_node_id; @@ -413,6 +419,9 @@ impl BasicBlock { } pred_node_id = node_id[0]; to_succ = &inst_circuit.layout.to_succ_inst; + if let Some(op_to_bb_final) = inst_circuit.layout.to_bb_final { + next_pc = Some(NodeOutputType::WireOut(pred_node_id, op_to_bb_final)); + } local_stack.push_node_outputs(stack, mode); } @@ -435,6 +444,7 @@ impl BasicBlock { memory_ts, stack_top, clk, + next_pc, ); let bb_final_node_id = graph_builder.add_node("BB final", &bb_final_circuit.circuit, preds)?; @@ -466,3 +476,143 @@ impl BasicBlock { Ok(public_output_size) } } + +#[cfg(test)] +mod test { + use crate::{ + basic_block::{ + bb_final::BasicBlockFinal, bb_start::BasicBlockStart, BasicBlock, BasicBlockInfo, + }, + instructions::{add::AddInstruction, SingerInstCircuitBuilder}, + scheme::GKRGraphProverState, + BasicBlockWiresIn, SingerParams, + }; + use ark_std::test_rng; + use ff::Field; + use ff_ext::ExtensionField; + use gkr::structs::LayerWitness; + use gkr_graph::structs::CircuitGraphBuilder; + use goldilocks::GoldilocksExt2; + use itertools::Itertools; + use singer_utils::{ + chips::SingerChipBuilder, + constants::OpcodeType, + structs::{ChipChallenges, PCUInt}, + }; + use std::time::Instant; + use transcript::Transcript; + + // A benchmark containing `n_adds_in_bb` ADD instructions in a basic block. + fn bench_bb_helper(instance_num_vars: usize, n_adds_in_bb: usize) { + let chip_challenges = ChipChallenges::default(); + let circuit_builder = + SingerInstCircuitBuilder::::new(chip_challenges).expect("circuit builder failed"); + + let bytecode = vec![vec![OpcodeType::ADD as u8; n_adds_in_bb]]; + + let mut rng = test_rng(); + let real_challenges = vec![E::random(&mut rng), E::random(&mut rng)]; + let n_instances = 1 << instance_num_vars; + + let timer = Instant::now(); + + let mut random_matrix = |n, m| { + (0..n) + .map(|_| (0..m).map(|_| E::BaseField::random(&mut rng)).collect()) + .collect() + }; + let bb_witness = BasicBlockWiresIn { + bb_start: vec![LayerWitness { + instances: random_matrix( + n_instances, + BasicBlockStart::phase0_size(n_adds_in_bb + 1), + ), + }], + bb_final: vec![ + LayerWitness { + instances: random_matrix(n_instances, BasicBlockFinal::phase0_size()), + }, + LayerWitness::default(), + LayerWitness::default(), + LayerWitness::default(), + LayerWitness { + instances: random_matrix(n_instances, PCUInt::N_OPRAND_CELLS), + }, + LayerWitness::default(), + LayerWitness::default(), + ], + bb_accs: vec![], + opcodes: (0..n_adds_in_bb) + .map(|_| { + vec![vec![ + LayerWitness { + instances: random_matrix(n_instances, AddInstruction::phase0_size()), + }, + LayerWitness::default(), + LayerWitness::default(), + LayerWitness::default(), + ]] + }) + .collect_vec(), + real_n_instance: n_instances, + }; + + let info = BasicBlockInfo { + pc_start: 0, + bb_start_stack_top_offsets: (-((n_adds_in_bb + 1) as i64)..0).collect_vec(), + bb_final_stack_top_offsets: vec![-1], + delta_stack_top: -(n_adds_in_bb as i64), + }; + let bb_start_circuit = BasicBlockStart::construct_circuit(&info, chip_challenges) + .expect("construct circuit failed"); + let bb_final_circuit = BasicBlockFinal::construct_circuit(&info, chip_challenges) + .expect("construct circuit failed"); + let bb = BasicBlock { + bytecode: bytecode[0].clone(), + info, + bb_start_circuit, + bb_final_circuit, + bb_acc_circuits: vec![], + }; + + let mut graph_builder = CircuitGraphBuilder::::new(); + let mut chip_builder = SingerChipBuilder::::new(); + let _ = bb + .construct_graph_and_witness( + &mut graph_builder, + &mut chip_builder, + &circuit_builder, + bb_witness, + &real_challenges, + &SingerParams::default(), + ) + .expect("gkr graph construction failed"); + + let (graph, wit) = graph_builder.finalize_graph_and_witness(); + + println!( + "AddInstruction::construct_graph_and_witness, instance_num_vars = {}, time = {}s", + instance_num_vars, + timer.elapsed().as_secs_f64() + ); + + let point = (0..20).map(|_| E::random(&mut rng)).collect_vec(); + let target_evals = graph.target_evals(&wit, &point); + + let mut prover_transcript = &mut Transcript::new(b"Singer"); + + let timer = Instant::now(); + let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, &mut prover_transcript, 1) + .expect("prove failed"); + println!( + "AddInstruction::prove, instance_num_vars = {}, time = {}s", + instance_num_vars, + timer.elapsed().as_secs_f64() + ); + } + + #[test] + fn bench_bb() { + bench_bb_helper::(10, 5); + } +} diff --git a/singer-pro/src/basic_block/bb_final.rs b/singer-pro/src/basic_block/bb_final.rs index bf97b69d6..8bcd88f69 100644 --- a/singer-pro/src/basic_block/bb_final.rs +++ b/singer-pro/src/basic_block/bb_final.rs @@ -11,7 +11,7 @@ use singer_utils::{ }, chips::IntoEnumIterator, register_witness, - structs::{ChipChallenges, InstOutChipType, PCUInt, RAMHandler, ROMHandler, TSUInt}, + structs::{ChipChallenges, InstOutChipType, PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, uint::UIntAddSub, }; use std::sync::Arc; @@ -91,7 +91,8 @@ impl BasicBlockFinal { let stack_operand_ids = stack_top_offsets .iter() .map(|offset| { - let (stack_from_insts_id, stack_from_insts) = circuit_builder.create_witness_in(1); + let (stack_from_insts_id, stack_from_insts) = + circuit_builder.create_witness_in(StackUInt::N_OPRAND_CELLS); ram_handler.stack_push( &mut circuit_builder, stack_top_expr.add(i64_to_base_field::(*offset)), diff --git a/singer-pro/src/component.rs b/singer-pro/src/component.rs index 2f7fef051..12492f855 100644 --- a/singer-pro/src/component.rs +++ b/singer-pro/src/component.rs @@ -100,6 +100,7 @@ impl BBFinalLayout { memory_ts: NodeOutputType, stack_top: NodeOutputType, clk: NodeOutputType, + next_pc: Option, ) -> Vec { let mut input = vec![PredType::Source; n_wires_in]; self.from_pred_inst @@ -113,6 +114,10 @@ impl BBFinalLayout { input[self.from_pred_inst.memory_ts_id as usize] = PredType::PredWire(memory_ts); input[self.from_bb_start.stack_top_id as usize] = PredType::PredWire(stack_top); input[self.from_bb_start.clk_id as usize] = PredType::PredWire(clk); + // TODO: Incorrect + if let (Some(next_pc_id), Some(next_pc)) = (self.next_pc_id.as_ref(), next_pc) { + input[*next_pc_id as usize] = PredType::PredWire(next_pc); + } input } } diff --git a/singer-pro/src/instructions.rs b/singer-pro/src/instructions.rs index 0b5cfd6eb..9a55475a5 100644 --- a/singer-pro/src/instructions.rs +++ b/singer-pro/src/instructions.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, mem}; use ff_ext::ExtensionField; use gkr_graph::structs::{CircuitGraphBuilder, NodeOutputType, PredType}; use itertools::Itertools; -use singer_utils::{chips::SingerChipBuilder, structs::ChipChallenges}; +use singer_utils::{chips::SingerChipBuilder, constants::OpcodeType, structs::ChipChallenges}; use crate::{ component::{AccessoryCircuit, InstCircuit}, @@ -127,6 +127,8 @@ pub(crate) fn construct_inst_graph( } pub(crate) trait Instruction { + const OPCODE: OpcodeType; + const NAME: &'static str; fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError>; } @@ -157,7 +159,7 @@ pub(crate) trait InstructionGraph { _params: &SingerParams, ) -> Result<(Vec, Vec, Option), ZKVMError> { let node_id = graph_builder.add_node_with_witness( - stringify!(Self::InstType), + >::NAME, &inst_circuit.circuit, preds, real_challenges.to_vec(), @@ -193,8 +195,11 @@ pub(crate) trait InstructionGraph { real_n_instances: usize, _params: &SingerParams, ) -> Result<(Vec, Vec, Option), ZKVMError> { - let node_id = - graph_builder.add_node(stringify!(Self::InstType), &inst_circuit.circuit, preds)?; + let node_id = graph_builder.add_node( + >::NAME, + &inst_circuit.circuit, + preds, + )?; let stack = inst_circuit .layout .to_succ_inst diff --git a/singer-pro/src/instructions/add.rs b/singer-pro/src/instructions/add.rs index a950d23c7..e136a8744 100644 --- a/singer-pro/src/instructions/add.rs +++ b/singer-pro/src/instructions/add.rs @@ -5,6 +5,7 @@ use simple_frontend::structs::CircuitBuilder; use singer_utils::{ chip_handler::ROMOperations, chips::IntoEnumIterator, + constants::OpcodeType, register_witness, structs::{ChipChallenges, InstOutChipType, ROMHandler, StackUInt, TSUInt}, uint::UIntAddSub, @@ -34,6 +35,8 @@ register_witness!( ); 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::new(); diff --git a/singer-pro/src/instructions/calldataload.rs b/singer-pro/src/instructions/calldataload.rs index 8db5e284d..2d9436c32 100644 --- a/singer-pro/src/instructions/calldataload.rs +++ b/singer-pro/src/instructions/calldataload.rs @@ -5,6 +5,7 @@ use simple_frontend::structs::CircuitBuilder; use singer_utils::{ chip_handler::{CalldataChipOperations, ROMOperations}, chips::IntoEnumIterator, + constants::OpcodeType, register_witness, structs::{ChipChallenges, InstOutChipType, ROMHandler, StackUInt, TSUInt, UInt64}, }; @@ -32,6 +33,8 @@ register_witness!( ); 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::new(); diff --git a/singer-pro/src/instructions/gt.rs b/singer-pro/src/instructions/gt.rs index a3b1f0e43..99099070b 100644 --- a/singer-pro/src/instructions/gt.rs +++ b/singer-pro/src/instructions/gt.rs @@ -5,6 +5,7 @@ use simple_frontend::structs::CircuitBuilder; use singer_utils::{ chip_handler::ROMOperations, chips::IntoEnumIterator, + constants::OpcodeType, register_witness, structs::{ChipChallenges, InstOutChipType, ROMHandler, StackUInt, TSUInt}, uint::UIntCmp, @@ -32,6 +33,8 @@ register_witness!( ); 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::new(); diff --git a/singer-pro/src/instructions/jump.rs b/singer-pro/src/instructions/jump.rs index 4fd99361e..e297230f9 100644 --- a/singer-pro/src/instructions/jump.rs +++ b/singer-pro/src/instructions/jump.rs @@ -3,6 +3,7 @@ use gkr::structs::Circuit; use simple_frontend::structs::CircuitBuilder; use singer_utils::{ chips::IntoEnumIterator, + constants::OpcodeType, structs::{ChipChallenges, InstOutChipType, StackUInt, TSUInt}, }; use std::sync::Arc; @@ -21,6 +22,8 @@ impl InstructionGraph for JumpInstruction { } impl Instruction for JumpInstruction { + const OPCODE: OpcodeType = OpcodeType::JUMP; + const NAME: &'static str = "JUMP"; fn construct_circuit(_: ChipChallenges) -> Result, ZKVMError> { let mut circuit_builder = CircuitBuilder::new(); // From predesessor instruction diff --git a/singer-pro/src/instructions/jumpi.rs b/singer-pro/src/instructions/jumpi.rs index fe0f76854..3ab9ef735 100644 --- a/singer-pro/src/instructions/jumpi.rs +++ b/singer-pro/src/instructions/jumpi.rs @@ -38,6 +38,8 @@ register_witness!( ); 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::new(); diff --git a/singer-pro/src/instructions/mstore.rs b/singer-pro/src/instructions/mstore.rs index c45bb114d..dcc290c7e 100644 --- a/singer-pro/src/instructions/mstore.rs +++ b/singer-pro/src/instructions/mstore.rs @@ -7,7 +7,7 @@ use simple_frontend::structs::CircuitBuilder; use singer_utils::{ chip_handler::{MemoryChipOperations, ROMOperations, RangeChipOperations}, chips::{IntoEnumIterator, SingerChipBuilder}, - constants::EVM_STACK_BYTE_WIDTH, + constants::{OpcodeType, EVM_STACK_BYTE_WIDTH}, register_witness, structs::{ChipChallenges, InstOutChipType, RAMHandler, ROMHandler, StackUInt, TSUInt}, uint::{UIntAddSub, UIntCmp}, @@ -117,6 +117,8 @@ register_witness!( ); 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::::new(); // From witness diff --git a/singer-pro/src/instructions/ret.rs b/singer-pro/src/instructions/ret.rs index 899c74a69..605475560 100644 --- a/singer-pro/src/instructions/ret.rs +++ b/singer-pro/src/instructions/ret.rs @@ -6,6 +6,7 @@ use simple_frontend::structs::CircuitBuilder; use singer_utils::{ chip_handler::{OAMOperations, ROMOperations}, chips::{IntoEnumIterator, SingerChipBuilder}, + constants::OpcodeType, register_witness, structs::{ChipChallenges, InstOutChipType, RAMHandler, ROMHandler, StackUInt, TSUInt}, uint::UIntAddSub, @@ -46,7 +47,7 @@ impl InstructionGraph for ReturnInstruction { // Add the instruction circuit to the graph. let node_id = graph_builder.add_node_with_witness( - stringify!(ReturnInstruction), + >::NAME, &inst_circuit.circuit, preds, real_challenges.to_vec(), @@ -68,6 +69,39 @@ impl InstructionGraph for ReturnInstruction { Err(ZKVMError::CircuitError) } } + + fn construct_graph( + graph_builder: &mut CircuitGraphBuilder, + chip_builder: &mut SingerChipBuilder, + inst_circuit: &InstCircuit, + _acc_circuits: &[AccessoryCircuit], + preds: Vec, + real_n_instances: usize, + _: &SingerParams, + ) -> Result<(Vec, Vec, Option), ZKVMError> { + let public_output_size = + preds[inst_circuit.layout.from_pred_inst.stack_operand_ids[1] as usize].clone(); + + // Add the instruction circuit to the graph. + let node_id = graph_builder.add_node( + >::NAME, + &inst_circuit.circuit, + preds, + )?; + + chip_builder.construct_chip_check_graph( + graph_builder, + node_id, + &inst_circuit.layout.to_chip_ids, + real_n_instances, + )?; + + if let PredType::PredWire(out) = public_output_size { + Ok((vec![node_id], vec![], Some(out))) + } else { + Err(ZKVMError::CircuitError) + } + } } register_witness!( @@ -82,6 +116,8 @@ register_witness!( ); 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::new(); diff --git a/singer-pro/src/instructions/unknown.rs b/singer-pro/src/instructions/unknown.rs index b399b32f6..bf09444b6 100644 --- a/singer-pro/src/instructions/unknown.rs +++ b/singer-pro/src/instructions/unknown.rs @@ -1,4 +1,5 @@ use ff_ext::ExtensionField; +use singer_utils::constants::OpcodeType; use singer_utils::structs::ChipChallenges; use crate::{component::InstCircuit, error::ZKVMError}; @@ -7,6 +8,8 @@ use super::{Instruction, InstructionGraph}; pub struct UnknownInstruction; impl Instruction for UnknownInstruction { + const OPCODE: OpcodeType = OpcodeType::UNKNOWN; + const NAME: &'static str = "UNKNOWN"; fn construct_circuit(_: ChipChallenges) -> Result, ZKVMError> { Err(ZKVMError::CircuitError) } diff --git a/singer-pro/src/scheme/prover.rs b/singer-pro/src/scheme/prover.rs index 3248ac5f3..82f500aba 100644 --- a/singer-pro/src/scheme/prover.rs +++ b/singer-pro/src/scheme/prover.rs @@ -77,7 +77,7 @@ pub fn prove( 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)?; + GKRGraphProverState::prove(&vm_circuit.0, &vm_witness.0, &target_evals, transcript, 1)?; Ok(( SingerProof { gkr_phase_proof, diff --git a/singer-utils/Cargo.toml b/singer-utils/Cargo.toml index c7aea5f37..ae921df4e 100644 --- a/singer-utils/Cargo.toml +++ b/singer-utils/Cargo.toml @@ -7,6 +7,7 @@ license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +ark-std.workspace = true ff_ext = { path = "../ff_ext" } ff.workspace = true goldilocks.workspace = true @@ -18,3 +19,4 @@ gkr-graph = { version = "0.1.0", path = "../gkr-graph" } sumcheck = { version = "0.1.0", path = "../sumcheck" } strum = "0.26.1" strum_macros = "0.26.1" +transcript = { version = "0.1.0", path = "../transcript" } diff --git a/singer-utils/src/chip_handler.rs b/singer-utils/src/chip_handler.rs index fac94f232..c9268a116 100644 --- a/singer-utils/src/chip_handler.rs +++ b/singer-utils/src/chip_handler.rs @@ -69,6 +69,8 @@ pub trait RangeChipOperations: ROMOperations { circuit_builder: &mut CircuitBuilder, bytes: &[CellId], ) -> Result<(), UtilError>; + + fn range_check_table_item(&mut self, circuit_builder: &mut CircuitBuilder, item: CellId); } pub trait MemoryChipOperations: RAMOperations { @@ -237,25 +239,10 @@ impl ChipChallenges { record_item_rlc, } } - pub fn bytecode(&self) -> ChallengeId { - self.record_rlc - } - pub fn stack(&self) -> ChallengeId { - self.record_rlc - } - pub fn global_state(&self) -> ChallengeId { - self.record_rlc - } - pub fn mem(&self) -> ChallengeId { - self.record_rlc - } - pub fn range(&self) -> ChallengeId { - self.record_rlc - } - pub fn calldata(&self) -> ChallengeId { - self.record_rlc - } pub fn record_item_rlc(&self) -> ChallengeId { self.record_item_rlc } + pub fn record_rlc(&self) -> ChallengeId { + self.record_rlc + } } diff --git a/singer-utils/src/chip_handler/ram_handler.rs b/singer-utils/src/chip_handler/ram_handler.rs index 2d9925af6..a3d8126ef 100644 --- a/singer-utils/src/chip_handler/ram_handler.rs +++ b/singer-utils/src/chip_handler/ram_handler.rs @@ -24,11 +24,14 @@ impl OAMOperations for RAMHandler { key: &[CellId], value: &[CellId], ) { - let out = circuit_builder.create_ext_cell(); + let item_rlc = circuit_builder.create_ext_cell(); let mut items = old_ts.to_vec(); items.extend(key.to_vec()); items.extend(value.to_vec()); - circuit_builder.rlc(&out, &items, self.challenge.record_rlc); + 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.rd_records.push(out); } @@ -39,11 +42,14 @@ impl OAMOperations for RAMHandler { key: &[MixedCell], value: &[MixedCell], ) { - let out = circuit_builder.create_ext_cell(); + let item_rlc = circuit_builder.create_ext_cell(); let mut items = old_ts.to_vec(); items.extend(key.to_vec()); items.extend(value.to_vec()); - circuit_builder.rlc_mixed(&out, &items, self.challenge.record_rlc); + 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.rd_records.push(out); } @@ -54,11 +60,14 @@ impl OAMOperations for RAMHandler { key: &[CellId], value: &[CellId], ) { - let out = circuit_builder.create_ext_cell(); + let item_rlc = circuit_builder.create_ext_cell(); let mut items = cur_ts.to_vec(); items.extend(key.to_vec()); items.extend(value.to_vec()); - circuit_builder.rlc(&out, &items, self.challenge.record_rlc); + 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.wt_records.push(out); } @@ -69,11 +78,14 @@ impl OAMOperations for RAMHandler { key: &[MixedCell], value: &[MixedCell], ) { - let out = circuit_builder.create_ext_cell(); + let item_rlc = circuit_builder.create_ext_cell(); let mut items = cur_ts.to_vec(); items.extend(key.to_vec()); items.extend(value.to_vec()); - circuit_builder.rlc_mixed(&out, &items, self.challenge.record_rlc); + 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.wt_records.push(out); } diff --git a/singer-utils/src/chip_handler/range.rs b/singer-utils/src/chip_handler/range.rs index 98fe08456..fdc6081b8 100644 --- a/singer-utils/src/chip_handler/range.rs +++ b/singer-utils/src/chip_handler/range.rs @@ -9,7 +9,7 @@ use crate::{ uint::UIntAddSub, }; -use super::RangeChipOperations; +use super::{ROMOperations, RangeChipOperations}; impl RangeChipOperations for ROMHandler { fn range_check_stack_top( @@ -69,6 +69,10 @@ impl RangeChipOperations for ROMHandler { } Ok(()) } + + fn range_check_table_item(&mut self, circuit_builder: &mut CircuitBuilder, item: CellId) { + self.rom_load(circuit_builder, &[], &[item]); + } } impl ROMHandler { @@ -81,10 +85,8 @@ impl ROMHandler { if bit_width > RANGE_CHIP_BIT_WIDTH { return Err(UtilError::ChipHandlerError); } - let out = circuit_builder.create_ext_cell(); let items = [value.mul(E::BaseField::from(1 << (RANGE_CHIP_BIT_WIDTH - bit_width)))]; - circuit_builder.rlc_mixed(&out, &items, self.challenge.record_rlc); - self.records.push(out); + self.rom_load_mixed(circuit_builder, &[], &items); Ok(()) } } @@ -112,13 +114,13 @@ impl ROMHandler { constant: i64, witness: &[CellId], ) -> Result { - let carry = UIntAddSub::::extract_unsafe_carry(witness); + //let carry = UIntAddSub::::extract_unsafe_carry(witness); UIntAddSub::::add_const( circuit_builder, self, &ts, i64_to_base_field::(constant), - carry, + witness, ) } diff --git a/singer-utils/src/chip_handler/rom_handler.rs b/singer-utils/src/chip_handler/rom_handler.rs index 3b1617d5c..ec138e71a 100644 --- a/singer-utils/src/chip_handler/rom_handler.rs +++ b/singer-utils/src/chip_handler/rom_handler.rs @@ -22,9 +22,12 @@ impl ROMOperations for ROMHandler { key: &[CellId], value: &[CellId], ) { - let out = circuit_builder.create_ext_cell(); + let item_rlc = circuit_builder.create_ext_cell(); let items = [key.to_vec(), value.to_vec()].concat(); - circuit_builder.rlc(&out, &items, self.challenge.record_rlc); + 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); } @@ -34,9 +37,12 @@ impl ROMOperations for ROMHandler { key: &[MixedCell], value: &[MixedCell], ) { - let out = circuit_builder.create_ext_cell(); + let item_rlc = circuit_builder.create_ext_cell(); let items = [key.to_vec(), value.to_vec()].concat(); - circuit_builder.rlc_mixed(&out, &items, self.challenge.record_rlc); + 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); } diff --git a/singer-utils/src/chips.rs b/singer-utils/src/chips.rs index bd9b41775..f57459c38 100644 --- a/singer-utils/src/chips.rs +++ b/singer-utils/src/chips.rs @@ -80,7 +80,7 @@ impl SingerChipBuilder { preds, &leaf.circuit, inner, - vec![], + vec![LayerWitness::default(); 2], real_challenges, instance_num_vars, ) @@ -89,6 +89,8 @@ impl SingerChipBuilder { // 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, @@ -102,6 +104,8 @@ impl SingerChipBuilder { // 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, diff --git a/singer-utils/src/chips/bytecode.rs b/singer-utils/src/chips/bytecode.rs index 29baf88fe..20ef83126 100644 --- a/singer-utils/src/chips/bytecode.rs +++ b/singer-utils/src/chips/bytecode.rs @@ -8,12 +8,26 @@ use simple_frontend::structs::{CircuitBuilder, MixedCell}; use sumcheck::util::ceil_log2; use crate::{ + chip_handler::{BytecodeChipOperations, ROMOperations}, error::UtilError, - structs::{ChipChallenges, PCUInt, ROMType}, + structs::{ChipChallenges, PCUInt, ROMHandler}, }; use super::ChipCircuitGadgets; +fn construct_circuit(challenges: &ChipChallenges) -> Arc> { + let mut circuit_builder = CircuitBuilder::::new(); + let (_, pc_cells) = circuit_builder.create_witness_in(PCUInt::N_OPRAND_CELLS); + let (_, bytecode_cells) = circuit_builder.create_witness_in(1); + + let mut rom_handler = ROMHandler::new(&challenges); + rom_handler.bytecode_with_pc_byte(&mut circuit_builder, &pc_cells, bytecode_cells[0]); + let _ = rom_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( @@ -22,25 +36,7 @@ pub(crate) fn construct_bytecode_table_and_witness( challenges: &ChipChallenges, real_challenges: &[E], ) -> Result<(PredType, PredType, usize), UtilError> { - let mut circuit_builder = CircuitBuilder::::new(); - let (_, pc_cells) = circuit_builder.create_witness_in(PCUInt::N_OPRAND_CELLS); - let (_, bytecode_cells) = circuit_builder.create_witness_in(1); - - let rlc = circuit_builder.create_ext_cell(); - let mut items = vec![MixedCell::Constant(E::BaseField::from( - ROMType::Bytecode as u64, - ))]; - items.extend(pc_cells.iter().map(|x| MixedCell::Cell(*x)).collect_vec()); - items.extend( - bytecode_cells - .iter() - .map(|x| MixedCell::Cell(*x)) - .collect_vec(), - ); - circuit_builder.rlc_mixed(&rlc, &items, challenges.bytecode()); - - circuit_builder.configure(); - let bytecode_circuit = Arc::new(Circuit::new(&circuit_builder)); + let bytecode_circuit = construct_circuit(challenges); let selector = ChipCircuitGadgets::construct_prefix_selector(bytecode.len(), 1); let selector_node_id = builder.add_node_with_witness( @@ -90,25 +86,7 @@ pub(crate) fn construct_bytecode_table( bytecode_len: usize, challenges: &ChipChallenges, ) -> Result<(PredType, PredType, usize), UtilError> { - let mut circuit_builder = CircuitBuilder::::new(); - let (_, pc_cells) = circuit_builder.create_witness_in(PCUInt::N_OPRAND_CELLS); - let (_, bytecode_cells) = circuit_builder.create_witness_in(1); - - let rlc = circuit_builder.create_ext_cell(); - let mut items = vec![MixedCell::Constant(E::BaseField::from( - ROMType::Bytecode as u64, - ))]; - items.extend(pc_cells.iter().map(|x| MixedCell::Cell(*x)).collect_vec()); - items.extend( - bytecode_cells - .iter() - .map(|x| MixedCell::Cell(*x)) - .collect_vec(), - ); - circuit_builder.rlc_mixed(&rlc, &items, challenges.bytecode()); - - circuit_builder.configure(); - let bytecode_circuit = Arc::new(Circuit::new(&circuit_builder)); + let bytecode_circuit = construct_circuit(challenges); let selector = ChipCircuitGadgets::construct_prefix_selector(bytecode_len, 1); let selector_node_id = diff --git a/singer-utils/src/chips/calldata.rs b/singer-utils/src/chips/calldata.rs index e9940ae40..cc2f8f187 100644 --- a/singer-utils/src/chips/calldata.rs +++ b/singer-utils/src/chips/calldata.rs @@ -1,8 +1,9 @@ use std::sync::Arc; use crate::{ + chip_handler::{CalldataChipOperations, ROMOperations}, error::UtilError, - structs::{ChipChallenges, ROMType, StackUInt}, + structs::{ChipChallenges, ROMHandler, StackUInt, UInt64}, }; use super::ChipCircuitGadgets; @@ -10,9 +11,21 @@ use ff_ext::ExtensionField; use gkr::structs::{Circuit, LayerWitness}; use gkr_graph::structs::{CircuitGraphBuilder, NodeOutputType, PredType}; use itertools::Itertools; -use simple_frontend::structs::{CircuitBuilder, MixedCell}; +use simple_frontend::structs::CircuitBuilder; use sumcheck::util::ceil_log2; +fn construct_circuit(challenges: &ChipChallenges) -> Arc> { + let mut circuit_builder = CircuitBuilder::::new(); + let (_, id_cells) = circuit_builder.create_witness_in(UInt64::N_OPRAND_CELLS); + let (_, calldata_cells) = circuit_builder.create_witness_in(StackUInt::N_OPRAND_CELLS); + let mut rom_handler = ROMHandler::new(&challenges); + rom_handler.calldataload(&mut circuit_builder, &id_cells, &calldata_cells); + let _ = rom_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( @@ -21,25 +34,7 @@ pub(crate) fn construct_calldata_table_and_witness( challenges: &ChipChallenges, real_challenges: &[E], ) -> Result<(PredType, PredType, usize), UtilError> { - let mut circuit_builder = CircuitBuilder::::new(); - let (_, id_cells) = circuit_builder.create_witness_in(1); - let (_, calldata_cells) = circuit_builder.create_witness_in(StackUInt::N_OPRAND_CELLS); - - let rlc = circuit_builder.create_ext_cell(); - let mut items = vec![MixedCell::Constant(E::BaseField::from( - ROMType::Calldata as u64, - ))]; - items.extend(id_cells.iter().map(|x| MixedCell::Cell(*x)).collect_vec()); - items.extend( - calldata_cells - .iter() - .map(|x| MixedCell::Cell(*x)) - .collect_vec(), - ); - circuit_builder.rlc_mixed(&rlc, &items, challenges.calldata()); - - circuit_builder.configure(); - let calldata_circuit = Arc::new(Circuit::new(&circuit_builder)); + 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( @@ -98,25 +93,7 @@ pub(crate) fn construct_calldata_table( program_input_len: usize, challenges: &ChipChallenges, ) -> Result<(PredType, PredType, usize), UtilError> { - let mut circuit_builder = CircuitBuilder::::new(); - let (_, id_cells) = circuit_builder.create_witness_in(1); - let (_, calldata_cells) = circuit_builder.create_witness_in(StackUInt::N_OPRAND_CELLS); - - let rlc = circuit_builder.create_ext_cell(); - let mut items = vec![MixedCell::Constant(E::BaseField::from( - ROMType::Calldata as u64, - ))]; - items.extend(id_cells.iter().map(|x| MixedCell::Cell(*x)).collect_vec()); - items.extend( - calldata_cells - .iter() - .map(|x| MixedCell::Cell(*x)) - .collect_vec(), - ); - circuit_builder.rlc_mixed(&rlc, &items, challenges.calldata()); - - circuit_builder.configure(); - let calldata_circuit = Arc::new(Circuit::new(&circuit_builder)); + let calldata_circuit = construct_circuit(challenges); let selector = ChipCircuitGadgets::construct_prefix_selector(program_input_len, 1); let selector_node_id = diff --git a/singer-utils/src/chips/circuit_gadgets.rs b/singer-utils/src/chips/circuit_gadgets.rs index f6b5c6e38..a6f881168 100644 --- a/singer-utils/src/chips/circuit_gadgets.rs +++ b/singer-utils/src/chips/circuit_gadgets.rs @@ -70,7 +70,7 @@ impl ChipCircuitGadgets { let mut circuit_builder = CircuitBuilder::::new(); 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); + 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); @@ -109,7 +109,7 @@ impl ChipCircuitGadgets { 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_witness_out(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); @@ -154,7 +154,7 @@ impl ChipCircuitGadgets { let mut circuit_builder = CircuitBuilder::::new(); 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_witness_out(2); + let output = circuit_builder.create_ext_cells(2); // denominator circuit_builder.mul2_ext( &output[0], // output_den @@ -194,7 +194,7 @@ impl ChipCircuitGadgets { pub(crate) fn construct_frac_sum_inner() -> Arc> { let mut circuit_builder = CircuitBuilder::::new(); let (_, input) = circuit_builder.create_ext_witness_in(4); - let (_, output) = circuit_builder.create_ext_witness_out(2); + let output = circuit_builder.create_ext_cells(2); // denominator circuit_builder.mul2_ext( &output[0], // output_den @@ -226,7 +226,7 @@ impl ChipCircuitGadgets { let mut circuit_builder = CircuitBuilder::::new(); 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_witness_out(1); + 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); @@ -251,9 +251,10 @@ impl ChipCircuitGadgets { pub(crate) fn construct_product_inner() -> Arc> { let mut circuit_builder = CircuitBuilder::::new(); let (_, input) = circuit_builder.create_ext_witness_in(2); - let (_, output) = circuit_builder.create_ext_witness_out(1); + 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 index d5201cc91..78c141909 100644 --- a/singer-utils/src/chips/range.rs +++ b/singer-utils/src/chips/range.rs @@ -3,14 +3,27 @@ use std::sync::Arc; use ff_ext::ExtensionField; use gkr::structs::Circuit; use gkr_graph::structs::{CircuitGraphBuilder, NodeOutputType, PredType}; -use simple_frontend::structs::{CircuitBuilder, MixedCell}; +use simple_frontend::structs::CircuitBuilder; use crate::{ + chip_handler::{ROMOperations, RangeChipOperations}, constants::RANGE_CHIP_BIT_WIDTH, error::UtilError, - structs::{ChipChallenges, ROMType}, + structs::{ChipChallenges, ROMHandler}, }; +fn construct_circuit(challenges: &ChipChallenges) -> Arc> { + let mut circuit_builder = CircuitBuilder::::new(); + let cells = circuit_builder.create_counter_in(0); + + let mut rom_handler = ROMHandler::new(&challenges); + rom_handler.range_check_table_item(&mut circuit_builder, cells[0]); + let _ = rom_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( @@ -19,16 +32,7 @@ pub(crate) fn construct_range_table_and_witness( challenges: &ChipChallenges, real_challenges: &[E], ) -> Result<(PredType, usize), UtilError> { - let mut circuit_builder = CircuitBuilder::::new(); - let cells = circuit_builder.create_counter_in(0); - let items = [ - MixedCell::Constant(E::BaseField::from(ROMType::Range as u64)), - MixedCell::Cell(cells[0]), - ]; - let rlc = circuit_builder.create_ext_cell(); - circuit_builder.rlc_mixed(&rlc, &items, challenges.range()); - circuit_builder.configure(); - let range_circuit = Arc::new(Circuit::new(&circuit_builder)); + let range_circuit = construct_circuit(challenges); let table_node_id = builder.add_node_with_witness( "range table circuit", @@ -51,16 +55,7 @@ pub(crate) fn construct_range_table( bit_with: usize, challenges: &ChipChallenges, ) -> Result<(PredType, usize), UtilError> { - let mut circuit_builder = CircuitBuilder::::new(); - let cells = circuit_builder.create_counter_in(0); - let items = [ - MixedCell::Constant(E::BaseField::from(ROMType::Range as u64)), - MixedCell::Cell(cells[0]), - ]; - let rlc = circuit_builder.create_ext_cell(); - circuit_builder.rlc_mixed(&rlc, &items, challenges.range()); - circuit_builder.configure(); - let range_circuit = Arc::new(Circuit::new(&circuit_builder)); + let range_circuit = construct_circuit(challenges); let table_node_id = builder.add_node("range table circuit", &range_circuit, vec![])?; Ok(( diff --git a/singer-utils/src/constants.rs b/singer-utils/src/constants.rs index 9a40eece5..c6d78b49e 100644 --- a/singer-utils/src/constants.rs +++ b/singer-utils/src/constants.rs @@ -1,3 +1,5 @@ +use strum_macros::EnumIter; + pub const STACK_TOP_BIT_WIDTH: usize = 10; pub const RANGE_CHIP_BIT_WIDTH: usize = 16; @@ -6,7 +8,9 @@ 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, diff --git a/singer-utils/src/macros.rs b/singer-utils/src/macros.rs index 46afb9c2a..612de1966 100644 --- a/singer-utils/src/macros.rs +++ b/singer-utils/src/macros.rs @@ -67,8 +67,8 @@ macro_rules! register_multi_witness { (@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] - fn [<$wire_name _ $name>](idx: usize$(, $wire_param: usize)*) -> std::ops::Range { - $offset + $length * (idx - 1)..$offset + $length * idx + 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),*); } @@ -77,7 +77,7 @@ macro_rules! register_multi_witness { (@internal $wire_name:ident($($wire_param:ident)*), $offset:expr; $name:ident => $length:expr $(, $rest:ident$(($rest_num:expr))? => $rest_length:expr)*) => { paste! { #[inline] - fn [<$wire_name _ $name>]($($wire_param: usize)*) -> std::ops::Range { + 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),*); @@ -87,7 +87,7 @@ macro_rules! register_multi_witness { (@internal $wire_name:ident($($wire_param:ident)*), $offset:expr;) => { paste! { #[inline] - fn [<$wire_name _ size>]($($wire_param: usize)*) -> usize { + 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 index 20367d985..e21b432bf 100644 --- a/singer-utils/src/structs.rs +++ b/singer-utils/src/structs.rs @@ -28,9 +28,9 @@ pub enum InstOutChipType { #[derive(Clone, Copy, Debug)] pub struct ChipChallenges { // Challenges for multiple-tuple chip records - pub record_rlc: ChallengeId, + pub(super) record_rlc: ChallengeId, // Challenges for multiple-cell values - pub record_item_rlc: ChallengeId, + pub(super) record_item_rlc: ChallengeId, } #[derive(Clone, Debug)] diff --git a/singer-utils/src/uint.rs b/singer-utils/src/uint.rs index f9eb6a914..09dade8f2 100644 --- a/singer-utils/src/uint.rs +++ b/singer-utils/src/uint.rs @@ -15,7 +15,11 @@ impl TryFrom<&[usize]> for UInt { type Error = UtilError; fn try_from(values: &[usize]) -> Result { if values.len() != Self::N_OPRAND_CELLS { - return Err(UtilError::UIntError); + panic!( + "expected = {}, got = {}", + Self::N_OPRAND_CELLS, + values.len() + ); } Ok(Self { values: values.to_vec(), @@ -37,9 +41,9 @@ impl UInt { const N_CARRY_CELLS: usize = Self::N_OPRAND_CELLS; const N_CARRY_NO_OVERFLOW_CELLS: usize = Self::N_OPRAND_CELLS - 1; pub const N_RANGE_CHECK_CELLS: usize = - Self::N_OPRAND_CELLS * (C + RANGE_CHIP_BIT_WIDTH - 1) / RANGE_CHIP_BIT_WIDTH; + Self::N_OPRAND_CELLS * ((C + RANGE_CHIP_BIT_WIDTH - 1) / RANGE_CHIP_BIT_WIDTH); pub const N_RANGE_CHECK_NO_OVERFLOW_CELLS: usize = - (Self::N_OPRAND_CELLS - 1) * (C + RANGE_CHIP_BIT_WIDTH - 1) / RANGE_CHIP_BIT_WIDTH; + (Self::N_OPRAND_CELLS - 1) * ((C + RANGE_CHIP_BIT_WIDTH - 1) / RANGE_CHIP_BIT_WIDTH); pub fn values(&self) -> &[CellId] { &self.values @@ -64,11 +68,15 @@ impl UInt { circuit_builder: &mut CircuitBuilder, bytes: &[CellId], ) -> Result { - if C <= M { - convert_decomp(circuit_builder, bytes, 8, C, true).try_into() + let mut values = if C <= M { + convert_decomp(circuit_builder, bytes, 8, C, true) } else { - convert_decomp(circuit_builder, bytes, 8, M, true).try_into() + convert_decomp(circuit_builder, bytes, 8, M, true) + }; + while values.len() < Self::N_OPRAND_CELLS { + values.push(circuit_builder.create_cell()); } + Self::try_from(values) } pub fn assert_eq( @@ -165,7 +173,7 @@ fn convert_decomp( circuit_builder.add( tmp, small_values[k], - E::BaseField::from((1 as u64) << k - j * small_bit_width), + E::BaseField::from((1 as u64) << (k - j) * small_bit_width), ); } tmp @@ -178,15 +186,14 @@ fn convert_decomp( mod test { use crate::uint::convert_decomp; + use super::UInt; use gkr::structs::{Circuit, CircuitWitness}; use goldilocks::{Goldilocks, GoldilocksExt2}; use simple_frontend::structs::CircuitBuilder; #[test] fn test_convert_decomp() { - // use of convert_decomp must ensure that - // small_len * small_bit_width does not exceed - // the field's max bit size (64 for Goldlilocks) + // test case 1 let mut circuit_builder = CircuitBuilder::::new(); let big_bit_width = 3; let small_bit_width = 2; @@ -221,5 +228,98 @@ mod test { for i in 1..16 { assert_eq!(result_values.instances[0][i], Goldilocks::from(0u64)); } + // test case 2 + let mut circuit_builder = CircuitBuilder::::new(); + let big_bit_width = 32; + let small_bit_width = 16; + let (small_values_wire_in_id, small_values) = circuit_builder.create_witness_in(4); + let values = convert_decomp( + &mut circuit_builder, + &small_values, + small_bit_width, + big_bit_width, + true, + ); + assert_eq!(values.len(), 2); + circuit_builder.configure(); + let circuit = Circuit::new(&circuit_builder); + let n_witness_in = circuit.n_witness_in; + let mut wires_in = vec![vec![]; n_witness_in]; + wires_in[small_values_wire_in_id as usize] = vec![ + Goldilocks::from(0u64), + Goldilocks::from(0u64), + Goldilocks::from(1u64), + Goldilocks::from(0u64), + ]; + 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 + }; + #[cfg(feature = "test-dbg")] + println!("{:?}", circuit_witness); + circuit_witness.check_correctness(&circuit); + // check the result + let result_values = circuit_witness.output_layer_witness_ref(); + assert_eq!( + result_values.instances[0], + vec![Goldilocks::from(0u64), Goldilocks::from(1u64)] + ); + } + + #[test] + fn test_from_range_values() { + let mut circuit_builder = CircuitBuilder::::new(); + let (range_values_wire_in_id, range_values) = circuit_builder.create_witness_in(16); + let range_value = + UInt::<256, 32>::from_range_values(&mut circuit_builder, &range_values).unwrap(); + assert_eq!(range_value.values.len(), 8); + circuit_builder.configure(); + let circuit = Circuit::new(&circuit_builder); + let n_witness_in = circuit.n_witness_in; + let mut wires_in = vec![vec![]; n_witness_in]; + wires_in[range_values_wire_in_id as usize] = vec![ + Goldilocks::from(0u64), + Goldilocks::from(0u64), + Goldilocks::from(1u64), + Goldilocks::from(0u64), + Goldilocks::from(0u64), + Goldilocks::from(0u64), + Goldilocks::from(0u64), + Goldilocks::from(0u64), + Goldilocks::from(0u64), + Goldilocks::from(0u64), + Goldilocks::from(0u64), + Goldilocks::from(0u64), + Goldilocks::from(0u64), + Goldilocks::from(0u64), + Goldilocks::from(0u64), + Goldilocks::from(0u64), + ]; + 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 + }; + #[cfg(feature = "test-dbg")] + println!("{:?}", circuit_witness); + circuit_witness.check_correctness(&circuit); + // check the result + let result_values = circuit_witness.output_layer_witness_ref(); + assert_eq!( + result_values.instances[0], + vec![ + Goldilocks::from(0), + Goldilocks::from(1), + Goldilocks::from(0), + Goldilocks::from(0), + Goldilocks::from(0), + Goldilocks::from(0), + Goldilocks::from(0), + Goldilocks::from(0), + ] + ); } } diff --git a/singer/Cargo.toml b/singer/Cargo.toml index bf55d3c1c..ea3a9dd08 100644 --- a/singer/Cargo.toml +++ b/singer/Cargo.toml @@ -26,3 +26,22 @@ itertools = "0.12.0" strum = "0.25.0" strum_macros = "0.25.3" paste = "1.0.14" + + +[dev-dependencies] +pprof = { version = "0.13", features = ["flamegraph"]} +criterion = { version = "0.5", features = ["html_reports"] } +cfg-if = "1.0.0" +const_env = "0.1.2" + +[features] +witness-count = [] +test-dbg = [] +dbg-add-opcode = [] + +[[bench]] +name = "add" +harness = false + +[profile.bench] +opt-level = 0 \ No newline at end of file diff --git a/singer/benches/add.rs b/singer/benches/add.rs new file mode 100644 index 000000000..b55578198 --- /dev/null +++ b/singer/benches/add.rs @@ -0,0 +1,161 @@ +#![allow(clippy::manual_memcpy)] +#![allow(clippy::needless_range_loop)] + +use std::time::{Duration, Instant}; + +use ark_std::test_rng; +use const_env::from_env; +use criterion::*; + +use ff_ext::ff::Field; +use ff_ext::ExtensionField; +use gkr::structs::LayerWitness; +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; +#[from_env] +const RAYON_NUM_THREADS: usize = 8; + +use singer::{ + instructions::{add::AddInstruction, Instruction, InstructionGraph, SingerCircuitBuilder}, + scheme::GKRGraphProverState, + CircuitWiresIn, SingerGraphBuilder, SingerParams, +}; +use singer_utils::structs::ChipChallenges; +use transcript::Transcript; + +pub fn is_power_of_2(x: usize) -> bool { + (x != 0) && ((x & (x - 1)) == 0) +} + +fn bench_add(c: &mut Criterion) { + let max_thread_id = { + if !is_power_of_2(RAYON_NUM_THREADS) { + #[cfg(not(feature = "non_pow2_rayon_thread"))] + { + panic!("add --features non_pow2_rayon_thread to enable unsafe feature which support non pow of 2 rayon thread pool"); + } + + #[cfg(feature = "non_pow2_rayon_thread")] + { + use sumcheck::local_thread_pool::create_local_pool_once; + use sumcheck::util::ceil_log2; + let max_thread_id = 1 << ceil_log2(RAYON_NUM_THREADS); + create_local_pool_once(1 << ceil_log2(RAYON_NUM_THREADS), true); + max_thread_id + } + } else { + RAYON_NUM_THREADS + } + }; + let chip_challenges = ChipChallenges::default(); + let circuit_builder = + SingerCircuitBuilder::::new(chip_challenges).expect("circuit builder failed"); + + for instance_num_vars in 11..12 { + // 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_keccak256", format!("keccak256_log2_{}", instance_num_vars)), + |b| { + b.iter_with_setup( + || { + let mut rng = test_rng(); + let singer_builder = SingerGraphBuilder::::new(); + + 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< + ::BaseField, + > = vec![LayerWitness { + instances: (0..(1 << instance_num_vars)) + .map(|_| { + (0..size) + .map(|_| { + ::BaseField::random( + &mut rng, + ) + }) + .collect_vec() + }) + .collect_vec(), + }]; + + + 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 mut prover_transcript = &mut Transcript::new(b"Singer"); + + let timer = Instant::now(); + let _ = GKRGraphProverState::prove( + &graph, + &wit, + &target_evals, + &mut 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 new file mode 100644 index 000000000..454deb914 --- /dev/null +++ b/singer/examples/add.rs @@ -0,0 +1,89 @@ +use std::time::{Duration, Instant}; + +use ark_std::test_rng; +use const_env::from_env; +use criterion::*; + +use ff_ext::ff::Field; +use ff_ext::ExtensionField; +use gkr::structs::LayerWitness; +use goldilocks::GoldilocksExt2; +use itertools::Itertools; + +const NUM_SAMPLES: usize = 10; +#[from_env] +const RAYON_NUM_THREADS: usize = 8; + +use singer::{ + instructions::{add::AddInstruction, Instruction, InstructionGraph, SingerCircuitBuilder}, + scheme::GKRGraphProverState, + CircuitWiresIn, SingerGraphBuilder, SingerParams, +}; +use singer_utils::structs::ChipChallenges; +use transcript::Transcript; + +fn main() { + let max_thread_id = 1; + let instance_num_vars = 9; + type E = GoldilocksExt2; + let chip_challenges = ChipChallenges::default(); + let circuit_builder = + SingerCircuitBuilder::::new(chip_challenges).expect("circuit builder failed"); + let mut singer_builder = SingerGraphBuilder::::new(); + + let mut rng = test_rng(); + let size = AddInstruction::phase0_size(); + let phase0: CircuitWiresIn<::BaseField> = + vec![LayerWitness { + instances: (0..(1 << instance_num_vars)) + .map(|_| { + (0..size) + .map(|_| ::BaseField::random(&mut rng)) + .collect_vec() + }) + .collect_vec(), + }]; + + 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 mut prover_transcript = &mut Transcript::new(b"Singer"); + + let timer = Instant::now(); + let _ = GKRGraphProverState::prove( + &graph, + &wit, + &target_evals, + &mut 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() + ); +} diff --git a/singer/src/instructions.rs b/singer/src/instructions.rs index bc1f6443c..863fcc3ef 100644 --- a/singer/src/instructions.rs +++ b/singer/src/instructions.rs @@ -5,7 +5,7 @@ use gkr::structs::Circuit; use gkr_graph::structs::{CircuitGraphBuilder, NodeOutputType, PredType}; use simple_frontend::structs::WitnessId; -use singer_utils::{chips::SingerChipBuilder, structs::ChipChallenges}; +use singer_utils::{chips::SingerChipBuilder, constants::OpcodeType, structs::ChipChallenges}; use strum_macros::EnumIter; use crate::{error::ZKVMError, CircuitWiresIn, SingerParams}; @@ -44,8 +44,8 @@ pub mod calldataload; #[derive(Clone, Debug)] pub struct SingerCircuitBuilder { /// Opcode circuits - pub(crate) insts_circuits: [Vec>; 256], - pub(crate) challenges: ChipChallenges, + pub insts_circuits: [Vec>; 256], + pub challenges: ChipChallenges, } impl SingerCircuitBuilder { @@ -84,7 +84,7 @@ pub(crate) fn construct_instruction_circuits( 0x91 => SwapInstruction::<2>::construct_circuits(challenges), 0x93 => SwapInstruction::<4>::construct_circuits(challenges), 0xF3 => ReturnInstruction::construct_circuits(challenges), - _ => unimplemented!(), + _ => Ok(vec![]), // TODO: Add more instructions. } } @@ -113,7 +113,7 @@ pub(crate) fn construct_inst_graph_and_witness( 0x91 => SwapInstruction::<2>::construct_graph_and_witness, 0x93 => SwapInstruction::<4>::construct_graph_and_witness, 0xF3 => ReturnInstruction::construct_graph_and_witness, - _ => unimplemented!(), + _ => return Ok(None), // TODO: Add more instructions. }; construct_circuit_graph( @@ -192,12 +192,14 @@ pub struct InstCircuitLayout { pub(crate) pred_ooo_wire_id: Option, } -pub(crate) trait Instruction { +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(crate) trait InstructionGraph { +pub trait InstructionGraph { type InstType: Instruction; /// Construct instruction circuits and its extensions. Mostly there is no @@ -222,7 +224,7 @@ pub(crate) trait InstructionGraph { let inst_circuit = &inst_circuits[0]; let inst_wires_in = mem::take(&mut sources[0]); let node_id = graph_builder.add_node_with_witness( - stringify!(Self::InstType), + Self::InstType::NAME, &inst_circuits[0].circuit, vec![PredType::Source; inst_wires_in.len()], real_challenges.to_vec(), diff --git a/singer/src/instructions/add.rs b/singer/src/instructions/add.rs index 8843964b2..a042de2c5 100644 --- a/singer/src/instructions/add.rs +++ b/singer/src/instructions/add.rs @@ -38,9 +38,9 @@ register_witness!( stack_ts_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_CELLS, old_stack_ts0 => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt0 => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_lt0 => UIntCmp::::N_WITNESS_CELLS, old_stack_ts1 => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt1 => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_lt1 => UIntCmp::::N_WITNESS_CELLS, addend_0 => StackUInt::N_OPRAND_CELLS, addend_1 => StackUInt::N_OPRAND_CELLS, @@ -48,11 +48,9 @@ register_witness!( } ); -impl AddInstruction { - const OPCODE: OpcodeType = OpcodeType::ADD; -} - 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::new(); let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); @@ -97,6 +95,11 @@ impl Instruction for AddInstruction { // 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 = UIntAddSub::::add( &mut circuit_builder, &mut rom_handler, @@ -151,7 +154,11 @@ impl Instruction for AddInstruction { ); // Bytecode check for (pc, add) - rom_handler.bytecode_with_pc_opcode(&mut circuit_builder, pc.values(), Self::OPCODE); + rom_handler.bytecode_with_pc_opcode( + &mut circuit_builder, + pc.values(), + >::OPCODE, + ); let (ram_load_id, ram_store_id) = ram_handler.finalize(&mut circuit_builder); let rom_id = rom_handler.finalize(&mut circuit_builder); @@ -169,3 +176,245 @@ impl Instruction for AddInstruction { }) } } + +#[cfg(test)] +mod test { + use ark_std::test_rng; + use core::ops::Range; + use ff::Field; + use ff_ext::ExtensionField; + use gkr::structs::LayerWitness; + use goldilocks::{Goldilocks, GoldilocksExt2}; + use itertools::Itertools; + use simple_frontend::structs::CellId; + use singer_utils::constants::RANGE_CHIP_BIT_WIDTH; + use singer_utils::structs::{StackUInt, TSUInt}; + use std::collections::BTreeMap; + use std::time::Instant; + use transcript::Transcript; + + use crate::instructions::{ + AddInstruction, ChipChallenges, Instruction, InstructionGraph, SingerCircuitBuilder, + }; + use crate::scheme::GKRGraphProverState; + use crate::test::{get_uint_params, test_opcode_circuit, u2vec}; + use crate::{CircuitWiresIn, SingerGraphBuilder, SingerParams}; + + impl AddInstruction { + #[inline] + fn phase0_idxes_map() -> BTreeMap> { + let mut map = BTreeMap::new(); + map.insert("phase0_pc".to_string(), Self::phase0_pc()); + map.insert("phase0_stack_ts".to_string(), Self::phase0_stack_ts()); + map.insert("phase0_memory_ts".to_string(), Self::phase0_memory_ts()); + map.insert("phase0_stack_top".to_string(), Self::phase0_stack_top()); + map.insert("phase0_clk".to_string(), Self::phase0_clk()); + map.insert("phase0_pc_add".to_string(), Self::phase0_pc_add()); + map.insert( + "phase0_stack_ts_add".to_string(), + Self::phase0_stack_ts_add(), + ); + map.insert( + "phase0_old_stack_ts0".to_string(), + Self::phase0_old_stack_ts0(), + ); + map.insert( + "phase0_old_stack_ts_lt0".to_string(), + Self::phase0_old_stack_ts_lt0(), + ); + map.insert( + "phase0_old_stack_ts1".to_string(), + Self::phase0_old_stack_ts1(), + ); + map.insert( + "phase0_old_stack_ts_lt1".to_string(), + Self::phase0_old_stack_ts_lt1(), + ); + map.insert("phase0_addend_0".to_string(), Self::phase0_addend_0()); + map.insert("phase0_addend_1".to_string(), Self::phase0_addend_1()); + map.insert( + "phase0_instruction_add".to_string(), + Self::phase0_instruction_add(), + ); + + map + } + } + + #[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::>::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_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( + "phase0_old_stack_ts0".to_string(), + vec![Goldilocks::from(2u64)], + ); + let m: u64 = (1 << get_uint_params::().1) - 1; + let range_values = u2vec::<{ TSUInt::N_RANGE_CHECK_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(range_values[3]), + Goldilocks::from(1u64), // borrow + ], + ); + 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 = u2vec::<{ TSUInt::N_RANGE_CHECK_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(range_values[3]), + Goldilocks::from(1u64), // borrow + ], + ); + let m: u64 = (1 << get_uint_params::().1) - 1; + phase0_values_map.insert("phase0_addend_0".to_string(), vec![Goldilocks::from(m)]); + phase0_values_map.insert("phase0_addend_1".to_string(), vec![Goldilocks::from(1u64)]); + let range_values = u2vec::<{ StackUInt::N_RANGE_CHECK_CELLS }, RANGE_CHIP_BIT_WIDTH>(m + 1); + let mut wit_phase0_instruction_add: Vec = vec![]; + for i in 0..16 { + wit_phase0_instruction_add.push(Goldilocks::from(range_values[i])) + } + wit_phase0_instruction_add.push(Goldilocks::from(1u64)); // carry is [1, 0, ...] + phase0_values_map.insert( + "phase0_instruction_add".to_string(), + wit_phase0_instruction_add, + ); + + // The actual challenges used is: + // challenges + // { ChallengeConst { challenge: 1, exp: i }: [Goldilocks(c^i)] } + let c: u64 = 6; + let circuit_witness_challenges = vec![ + GoldilocksExt2::from(c), + GoldilocksExt2::from(c), + GoldilocksExt2::from(c), + ]; + + let circuit_witness = test_opcode_circuit( + &inst_circuit, + &phase0_idx_map, + phase0_witness_size, + &phase0_values_map, + circuit_witness_challenges, + ); + + // check the correctness of add operation + // stack_push = RLC([stack_ts=3, RAMType::Stack=0, stack_top=98, result=0,1,0,0,0,0,0,0, len=11]) + // = 3 (stack_ts) + c^2 * 98 (stack_top) + c^4 * 1 + c^11 + let add_stack_push_wire_id = inst_circuit.layout.chip_check_wire_id[1].unwrap().0; + let add_stack_push = + &circuit_witness.witness_out_ref()[add_stack_push_wire_id as usize].instances[0][1]; + let add_stack_push_value: u64 = 3 + c.pow(2_u32) * 98 + c.pow(4u32) * 1 + c.pow(11_u32); + assert_eq!(*add_stack_push, Goldilocks::from(add_stack_push_value)); + } + + 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::::new(); + + let mut rng = test_rng(); + let size = AddInstruction::phase0_size(); + let phase0: CircuitWiresIn = vec![LayerWitness { + instances: (0..(1 << instance_num_vars)) + .map(|_| { + (0..size) + .map(|_| E::BaseField::random(&mut rng)) + .collect_vec() + }) + .collect_vec(), + }]; + + 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 mut prover_transcript = &mut Transcript::new(b"Singer"); + + let timer = Instant::now(); + let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, &mut prover_transcript, 1) + .expect("prove failed"); + println!( + "AddInstruction::prove, instance_num_vars = {}, time = {}", + instance_num_vars, + timer.elapsed().as_secs_f64() + ); + } + + #[test] + fn bench_add_instruction() { + bench_add_instruction_helper::(10); + } +} diff --git a/singer/src/instructions/calldataload.rs b/singer/src/instructions/calldataload.rs index 5935eeefa..de1ee19d5 100644 --- a/singer/src/instructions/calldataload.rs +++ b/singer/src/instructions/calldataload.rs @@ -41,15 +41,13 @@ register_witness!( data => StackUInt::N_OPRAND_CELLS, offset => UInt64::N_OPRAND_CELLS, old_stack_ts => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS + old_stack_ts_lt => UIntCmp::::N_WITNESS_CELLS } ); -impl CalldataloadInstruction { - const OPCODE: OpcodeType = OpcodeType::CALLDATALOAD; -} - 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::new(); let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); @@ -127,7 +125,11 @@ impl Instruction for CalldataloadInstruction { ); // Bytecode table (pc, CalldataLoad) - rom_handler.bytecode_with_pc_opcode(&mut circuit_builder, pc.values(), Self::OPCODE); + rom_handler.bytecode_with_pc_opcode( + &mut circuit_builder, + pc.values(), + >::OPCODE, + ); let (ram_load_id, ram_store_id) = ram_handler.finalize(&mut circuit_builder); let rom_id = rom_handler.finalize(&mut circuit_builder); @@ -145,3 +147,210 @@ impl Instruction for CalldataloadInstruction { }) } } + +#[cfg(test)] +mod test { + use ark_std::test_rng; + use core::ops::Range; + use ff::Field; + use ff_ext::ExtensionField; + use gkr::structs::LayerWitness; + use goldilocks::{Goldilocks, GoldilocksExt2}; + use itertools::Itertools; + use simple_frontend::structs::CellId; + use singer_utils::constants::RANGE_CHIP_BIT_WIDTH; + use singer_utils::structs::TSUInt; + use std::collections::BTreeMap; + use std::time::Instant; + use transcript::Transcript; + + use crate::instructions::{ + CalldataloadInstruction, ChipChallenges, Instruction, InstructionGraph, + SingerCircuitBuilder, + }; + use crate::scheme::GKRGraphProverState; + use crate::test::{get_uint_params, test_opcode_circuit, u2vec}; + use crate::{CircuitWiresIn, SingerGraphBuilder, SingerParams}; + + impl CalldataloadInstruction { + #[inline] + fn phase0_idxes_map() -> BTreeMap> { + let mut map = BTreeMap::new(); + + map.insert("phase0_pc".to_string(), Self::phase0_pc()); + map.insert("phase0_stack_ts".to_string(), Self::phase0_stack_ts()); + map.insert("phase0_memory_ts".to_string(), Self::phase0_memory_ts()); + map.insert("phase0_ts".to_string(), Self::phase0_ts()); + map.insert("phase0_stack_top".to_string(), Self::phase0_stack_top()); + map.insert("phase0_clk".to_string(), Self::phase0_clk()); + map.insert("phase0_pc_add".to_string(), Self::phase0_pc_add()); + map.insert( + "phase0_stack_ts_add".to_string(), + Self::phase0_stack_ts_add(), + ); + map.insert("phase0_data".to_string(), Self::phase0_data()); + map.insert("phase0_offset".to_string(), Self::phase0_offset()); + map.insert( + "phase0_old_stack_ts".to_string(), + Self::phase0_old_stack_ts(), + ); + map.insert( + "phase0_old_stack_ts_lt".to_string(), + Self::phase0_old_stack_ts_lt(), + ); + + map + } + } + + #[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_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( + "phase0_old_stack_ts".to_string(), + vec![Goldilocks::from(2u64)], + ); + let m: u64 = (1 << get_uint_params::().1) - 1; + let range_values = u2vec::<{ TSUInt::N_RANGE_CHECK_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(range_values[3]), + 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), + ]; + + let _circuit_witness = test_opcode_circuit( + &inst_circuit, + &phase0_idx_map, + phase0_witness_size, + &phase0_values_map, + circuit_witness_challenges, + ); + } + + 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::::new(); + + let mut rng = test_rng(); + let size = CalldataloadInstruction::phase0_size(); + let phase0: CircuitWiresIn = vec![LayerWitness { + instances: (0..(1 << instance_num_vars)) + .map(|_| { + (0..size) + .map(|_| E::BaseField::random(&mut rng)) + .collect_vec() + }) + .collect_vec(), + }]; + + 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 mut prover_transcript = &mut Transcript::new(b"Singer"); + + let timer = Instant::now(); + let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, &mut prover_transcript, 1) + .expect("prove failed"); + println!( + "CalldataloadInstruction::prove, instance_num_vars = {}, time = {}", + instance_num_vars, + timer.elapsed().as_secs_f64() + ); + } + + #[test] + fn bench_calldataload_instruction() { + bench_calldataload_instruction_helper::(10); + } +} diff --git a/singer/src/instructions/dup.rs b/singer/src/instructions/dup.rs index 5de39a5b6..6f4dc7e92 100644 --- a/singer/src/instructions/dup.rs +++ b/singer/src/instructions/dup.rs @@ -39,19 +39,21 @@ register_witness!( stack_values => StackUInt::N_OPRAND_CELLS, old_stack_ts => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS + old_stack_ts_lt => UIntCmp::::N_WITNESS_CELLS } ); -impl DupInstruction { +impl Instruction for DupInstruction { const OPCODE: OpcodeType = match N { 1 => OpcodeType::DUP1, 2 => OpcodeType::DUP2, _ => unimplemented!(), }; -} - -impl Instruction for DupInstruction { + const NAME: &'static str = match N { + 1 => "DUP1", + 2 => "DUP2", + _ => unimplemented!(), + }; fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { let mut circuit_builder = CircuitBuilder::new(); let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); @@ -133,7 +135,11 @@ impl Instruction for DupInstruction { ); // Bytecode check for (pc, DUP{N}) - rom_handler.bytecode_with_pc_opcode(&mut circuit_builder, pc.values(), Self::OPCODE); + rom_handler.bytecode_with_pc_opcode( + &mut circuit_builder, + pc.values(), + >::OPCODE, + ); let (ram_load_id, ram_store_id) = ram_handler.finalize(&mut circuit_builder); let rom_id = rom_handler.finalize(&mut circuit_builder); @@ -151,3 +157,213 @@ impl Instruction for DupInstruction { }) } } + +#[cfg(test)] +mod test { + use ark_std::test_rng; + use core::ops::Range; + use ff::Field; + use ff_ext::ExtensionField; + use gkr::structs::LayerWitness; + use goldilocks::{Goldilocks, GoldilocksExt2}; + use itertools::Itertools; + use simple_frontend::structs::CellId; + use singer_utils::constants::RANGE_CHIP_BIT_WIDTH; + use singer_utils::structs::TSUInt; + use std::collections::BTreeMap; + use std::time::Instant; + use transcript::Transcript; + + use crate::instructions::{ + ChipChallenges, DupInstruction, Instruction, InstructionGraph, SingerCircuitBuilder, + }; + use crate::scheme::GKRGraphProverState; + use crate::test::{get_uint_params, test_opcode_circuit, u2vec}; + use crate::{CircuitWiresIn, SingerGraphBuilder, SingerParams}; + + impl DupInstruction { + #[inline] + fn phase0_idxes_map() -> BTreeMap> { + let mut map = BTreeMap::new(); + map.insert("phase0_pc".to_string(), Self::phase0_pc()); + map.insert("phase0_stack_ts".to_string(), Self::phase0_stack_ts()); + map.insert("phase0_memory_ts".to_string(), Self::phase0_memory_ts()); + map.insert("phase0_stack_top".to_string(), Self::phase0_stack_top()); + map.insert("phase0_clk".to_string(), Self::phase0_clk()); + map.insert("phase0_pc_add".to_string(), Self::phase0_pc_add()); + map.insert( + "phase0_stack_ts_add".to_string(), + Self::phase0_stack_ts_add(), + ); + map.insert( + "phase0_stack_values".to_string(), + Self::phase0_stack_values(), + ); + map.insert( + "phase0_old_stack_ts".to_string(), + Self::phase0_old_stack_ts(), + ); + map.insert( + "phase0_old_stack_ts_lt".to_string(), + Self::phase0_old_stack_ts_lt(), + ); + + map + } + } + + #[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_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( + "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 = u2vec::<{ TSUInt::N_RANGE_CHECK_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(range_values[3]), + Goldilocks::from(1u64), + ], + ); + + let circuit_witness_challenges = vec![ + GoldilocksExt2::from(2), + GoldilocksExt2::from(2), + GoldilocksExt2::from(2), + ]; + + let _circuit_witness = test_opcode_circuit( + &inst_circuit, + &phase0_idx_map, + phase0_witness_size, + &phase0_values_map, + circuit_witness_challenges, + ); + } + + 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::::new(); + + let mut rng = test_rng(); + let size = DupInstruction::::phase0_size(); + let phase0: CircuitWiresIn = vec![LayerWitness { + instances: (0..(1 << instance_num_vars)) + .map(|_| { + (0..size) + .map(|_| E::BaseField::random(&mut rng)) + .collect_vec() + }) + .collect_vec(), + }]; + + 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 mut prover_transcript = &mut Transcript::new(b"Singer"); + + let timer = Instant::now(); + let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, &mut prover_transcript, 1) + .expect("prove failed"); + println!( + "Dup{}Instruction::prove, instance_num_vars = {}, time = {}", + N, + instance_num_vars, + timer.elapsed().as_secs_f64() + ); + } + + #[test] + fn bench_dup1_instruction() { + bench_dup_instruction_helper::(10); + } + + #[test] + fn bench_dup2_instruction() { + bench_dup_instruction_helper::(10); + } +} diff --git a/singer/src/instructions/gt.rs b/singer/src/instructions/gt.rs index 252a05239..2b0165531 100644 --- a/singer/src/instructions/gt.rs +++ b/singer/src/instructions/gt.rs @@ -38,9 +38,9 @@ register_witness!( stack_ts_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_CELLS, old_stack_ts0 => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt0 => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_lt0 => UIntCmp::::N_WITNESS_CELLS, old_stack_ts1 => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt1 => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_lt1 => UIntCmp::::N_WITNESS_CELLS, oprand_0 => StackUInt::N_OPRAND_CELLS, oprand_1 => StackUInt::N_OPRAND_CELLS, @@ -48,11 +48,9 @@ register_witness!( } ); -impl GtInstruction { - const OPCODE: OpcodeType = OpcodeType::GT; -} - 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::new(); let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); @@ -152,7 +150,11 @@ impl Instruction for GtInstruction { ); // Bytecode check for (pc, gt) - rom_handler.bytecode_with_pc_opcode(&mut circuit_builder, pc.values(), Self::OPCODE); + rom_handler.bytecode_with_pc_opcode( + &mut circuit_builder, + pc.values(), + >::OPCODE, + ); let (ram_load_id, ram_store_id) = ram_handler.finalize(&mut circuit_builder); let rom_id = rom_handler.finalize(&mut circuit_builder); @@ -170,3 +172,229 @@ impl Instruction for GtInstruction { }) } } + +#[cfg(test)] +mod test { + use ark_std::test_rng; + use core::ops::Range; + use ff::Field; + use ff_ext::ExtensionField; + use gkr::structs::LayerWitness; + use goldilocks::{Goldilocks, GoldilocksExt2}; + use itertools::Itertools; + use simple_frontend::structs::CellId; + use singer_utils::constants::RANGE_CHIP_BIT_WIDTH; + use singer_utils::structs::TSUInt; + use std::collections::BTreeMap; + use std::time::Instant; + use transcript::Transcript; + + use crate::instructions::{ + ChipChallenges, GtInstruction, Instruction, InstructionGraph, SingerCircuitBuilder, + }; + use crate::scheme::GKRGraphProverState; + use crate::test::{get_uint_params, test_opcode_circuit, u2vec}; + use crate::{CircuitWiresIn, SingerGraphBuilder, SingerParams}; + + impl GtInstruction { + #[inline] + fn phase0_idxes_map() -> BTreeMap> { + let mut map = BTreeMap::new(); + map.insert("phase0_pc".to_string(), Self::phase0_pc()); + map.insert("phase0_stack_ts".to_string(), Self::phase0_stack_ts()); + map.insert("phase0_memory_ts".to_string(), Self::phase0_memory_ts()); + map.insert("phase0_stack_top".to_string(), Self::phase0_stack_top()); + map.insert("phase0_clk".to_string(), Self::phase0_clk()); + map.insert("phase0_pc_add".to_string(), Self::phase0_pc_add()); + map.insert( + "phase0_stack_ts_add".to_string(), + Self::phase0_stack_ts_add(), + ); + map.insert( + "phase0_old_stack_ts0".to_string(), + Self::phase0_old_stack_ts0(), + ); + map.insert( + "phase0_old_stack_ts_lt0".to_string(), + Self::phase0_old_stack_ts_lt0(), + ); + map.insert( + "phase0_old_stack_ts1".to_string(), + Self::phase0_old_stack_ts1(), + ); + map.insert( + "phase0_old_stack_ts_lt1".to_string(), + Self::phase0_old_stack_ts_lt1(), + ); + map.insert("phase0_oprand_0".to_string(), Self::phase0_oprand_0()); + map.insert("phase0_oprand_1".to_string(), Self::phase0_oprand_1()); + map.insert( + "phase0_instruction_gt".to_string(), + Self::phase0_instruction_gt(), + ); + + map + } + } + + #[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_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( + "phase0_old_stack_ts0".to_string(), + vec![Goldilocks::from(2u64)], + ); + let m: u64 = (1 << get_uint_params::().1) - 1; + let range_values = u2vec::<{ TSUInt::N_RANGE_CHECK_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(range_values[3]), + 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 = u2vec::<{ TSUInt::N_RANGE_CHECK_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(range_values[3]), + 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), + ]; + + let _circuit_witness = test_opcode_circuit( + &inst_circuit, + &phase0_idx_map, + phase0_witness_size, + &phase0_values_map, + circuit_witness_challenges, + ); + } + + 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::::new(); + + let mut rng = test_rng(); + let size = GtInstruction::phase0_size(); + let phase0: CircuitWiresIn = vec![LayerWitness { + instances: (0..(1 << instance_num_vars)) + .map(|_| { + (0..size) + .map(|_| E::BaseField::random(&mut rng)) + .collect_vec() + }) + .collect_vec(), + }]; + + 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 mut prover_transcript = &mut Transcript::new(b"Singer"); + + let timer = Instant::now(); + let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, &mut prover_transcript, 1) + .expect("prove failed"); + println!( + "GtInstruction::prove, instance_num_vars = {}, time = {}", + instance_num_vars, + timer.elapsed().as_secs_f64() + ); + } + + #[test] + fn bench_gt_instruction() { + bench_gt_instruction_helper::(10); + } +} diff --git a/singer/src/instructions/jump.rs b/singer/src/instructions/jump.rs index 57baac5aa..35c1175a4 100644 --- a/singer/src/instructions/jump.rs +++ b/singer/src/instructions/jump.rs @@ -42,11 +42,9 @@ register_witness!( } ); -impl JumpInstruction { - const OPCODE: OpcodeType = OpcodeType::JUMP; -} - 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::new(); let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); @@ -100,7 +98,11 @@ impl Instruction for JumpInstruction { ); // Bytecode check for (pc, jump) - rom_handler.bytecode_with_pc_opcode(&mut circuit_builder, pc.values(), Self::OPCODE); + rom_handler.bytecode_with_pc_opcode( + &mut circuit_builder, + pc.values(), + >::OPCODE, + ); // Bytecode check for (next_pc, jumpdest) rom_handler.bytecode_with_pc_opcode(&mut circuit_builder, &next_pc, OpcodeType::JUMPDEST); @@ -123,16 +125,25 @@ impl Instruction for JumpInstruction { #[cfg(test)] mod test { - use core::ops::Range; - use std::collections::BTreeMap; - use crate::instructions::{ChipChallenges, Instruction, JumpInstruction}; use crate::test::{get_uint_params, test_opcode_circuit, u2vec}; - + use ark_std::test_rng; + use core::ops::Range; + use ff::Field; + use ff_ext::ExtensionField; + use gkr::structs::LayerWitness; use goldilocks::{Goldilocks, GoldilocksExt2}; + use itertools::Itertools; use simple_frontend::structs::CellId; use singer_utils::constants::RANGE_CHIP_BIT_WIDTH; use singer_utils::structs::TSUInt; + use std::collections::BTreeMap; + use std::time::Instant; + use transcript::Transcript; + + use crate::instructions::{InstructionGraph, SingerCircuitBuilder}; + use crate::scheme::GKRGraphProverState; + use crate::{CircuitWiresIn, SingerGraphBuilder, SingerParams}; impl JumpInstruction { #[inline] @@ -212,7 +223,7 @@ mod test { GoldilocksExt2::from(2), ]; - test_opcode_circuit( + let _circuit_witness = test_opcode_circuit( &inst_circuit, &phase0_idx_map, phase0_witness_size, @@ -220,4 +231,65 @@ mod test { circuit_witness_challenges, ); } + + 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::::new(); + + let mut rng = test_rng(); + let size = JumpInstruction::phase0_size(); + let phase0: CircuitWiresIn = vec![LayerWitness { + instances: (0..(1 << instance_num_vars)) + .map(|_| { + (0..size) + .map(|_| E::BaseField::random(&mut rng)) + .collect_vec() + }) + .collect_vec(), + }]; + + 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 mut prover_transcript = &mut Transcript::new(b"Singer"); + + let timer = Instant::now(); + let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, &mut prover_transcript, 1) + .expect("prove failed"); + println!( + "JumpInstruction::prove, instance_num_vars = {}, time = {}", + instance_num_vars, + timer.elapsed().as_secs_f64() + ); + } + + #[test] + fn bench_jump_instruction() { + bench_jump_instruction_helper::(10); + } } diff --git a/singer/src/instructions/jumpdest.rs b/singer/src/instructions/jumpdest.rs index f5a2a74de..99cd73343 100644 --- a/singer/src/instructions/jumpdest.rs +++ b/singer/src/instructions/jumpdest.rs @@ -37,11 +37,9 @@ register_witness!( } ); -impl JumpdestInstruction { - pub const OPCODE: OpcodeType = OpcodeType::JUMPDEST; -} - 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::new(); let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); @@ -76,7 +74,11 @@ impl Instruction for JumpdestInstruction { ); // Bytecode check for (pc, jump) - rom_handler.bytecode_with_pc_opcode(&mut circuit_builder, pc.values(), Self::OPCODE); + rom_handler.bytecode_with_pc_opcode( + &mut circuit_builder, + pc.values(), + >::OPCODE, + ); let (ram_load_id, ram_store_id) = ram_handler.finalize(&mut circuit_builder); let rom_id = rom_handler.finalize(&mut circuit_builder); @@ -94,3 +96,150 @@ impl Instruction for JumpdestInstruction { }) } } + +#[cfg(test)] +mod test { + use ark_std::test_rng; + use core::ops::Range; + use ff::Field; + use ff_ext::ExtensionField; + use gkr::structs::LayerWitness; + use goldilocks::{Goldilocks, GoldilocksExt2}; + use itertools::Itertools; + use simple_frontend::structs::CellId; + use std::collections::BTreeMap; + use std::time::Instant; + use transcript::Transcript; + + use crate::instructions::{ + ChipChallenges, Instruction, InstructionGraph, JumpdestInstruction, SingerCircuitBuilder, + }; + use crate::scheme::GKRGraphProverState; + use crate::test::test_opcode_circuit; + use crate::{CircuitWiresIn, SingerGraphBuilder, SingerParams}; + + impl JumpdestInstruction { + #[inline] + fn phase0_idxes_map() -> BTreeMap> { + let mut map = BTreeMap::new(); + map.insert("phase0_pc".to_string(), Self::phase0_pc()); + map.insert("phase0_stack_ts".to_string(), Self::phase0_stack_ts()); + map.insert("phase0_memory_ts".to_string(), Self::phase0_memory_ts()); + map.insert("phase0_stack_top".to_string(), Self::phase0_stack_top()); + map.insert("phase0_clk".to_string(), Self::phase0_clk()); + map.insert("phase0_pc_add".to_string(), Self::phase0_pc_add()); + + map + } + } + + #[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), + ]; + + let _circuit_witness = test_opcode_circuit( + &inst_circuit, + &phase0_idx_map, + phase0_witness_size, + &phase0_values_map, + circuit_witness_challenges, + ); + } + + 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::::new(); + + let mut rng = test_rng(); + let size = JumpdestInstruction::phase0_size(); + let phase0: CircuitWiresIn = vec![LayerWitness { + instances: (0..(1 << instance_num_vars)) + .map(|_| { + (0..size) + .map(|_| E::BaseField::random(&mut rng)) + .collect_vec() + }) + .collect_vec(), + }]; + + 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 mut prover_transcript = &mut Transcript::new(b"Singer"); + + let timer = Instant::now(); + let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, &mut prover_transcript, 1) + .expect("prove failed"); + println!( + "JumpdestInstruction::prove, instance_num_vars = {}, time = {}", + instance_num_vars, + timer.elapsed().as_secs_f64() + ); + } + + #[test] + fn bench_jumpdest_instruction() { + bench_jumpdest_instruction_helper::(10); + } +} diff --git a/singer/src/instructions/jumpi.rs b/singer/src/instructions/jumpi.rs index 3c2a99866..d3d4c4103 100644 --- a/singer/src/instructions/jumpi.rs +++ b/singer/src/instructions/jumpi.rs @@ -50,11 +50,9 @@ register_witness!( } ); -impl JumpiInstruction { - const OPCODE: OpcodeType = OpcodeType::JUMPI; -} - 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::new(); let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); @@ -156,7 +154,11 @@ impl Instruction for JumpiInstruction { ); // Bytecode check for (pc, jumpi) - rom_handler.bytecode_with_pc_opcode(&mut circuit_builder, pc.values(), Self::OPCODE); + rom_handler.bytecode_with_pc_opcode( + &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]; diff --git a/singer/src/instructions/mstore.rs b/singer/src/instructions/mstore.rs index 8cebb0c4c..7185454d9 100644 --- a/singer/src/instructions/mstore.rs +++ b/singer/src/instructions/mstore.rs @@ -152,17 +152,15 @@ register_witness!( offset => StackUInt::N_OPRAND_CELLS, mem_bytes => EVM_STACK_BYTE_WIDTH, old_stack_ts_offset => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt_offset => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_lt_offset => UIntCmp::::N_WITNESS_CELLS, old_stack_ts_value => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt_value => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS + old_stack_ts_lt_value => UIntCmp::::N_WITNESS_CELLS } ); -impl MstoreInstruction { - const OPCODE: OpcodeType = OpcodeType::MSTORE; -} - 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::new(); let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); @@ -246,7 +244,11 @@ impl Instruction for MstoreInstruction { ); // Bytecode check for (pc, mstore) - rom_handler.bytecode_with_pc_opcode(&mut circuit_builder, pc.values(), Self::OPCODE); + rom_handler.bytecode_with_pc_opcode( + &mut circuit_builder, + pc.values(), + >::OPCODE, + ); // To accessory let (to_acc_dup_id, to_acc_dup) = @@ -305,8 +307,10 @@ register_witness!( } ); -impl Instruction for MstoreAccessory { - fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { +impl MstoreAccessory { + fn construct_circuit( + challenges: ChipChallenges, + ) -> Result, ZKVMError> { let mut circuit_builder = CircuitBuilder::new(); // From predesessor circuit. @@ -371,3 +375,241 @@ impl Instruction for MstoreAccessory { }) } } + +#[cfg(test)] +mod test { + use crate::{instructions::InstructionGraph, scheme::GKRGraphProverState, SingerParams}; + use ark_std::test_rng; + use ff::Field; + use ff_ext::ExtensionField; + use gkr::structs::LayerWitness; + use goldilocks::GoldilocksExt2; + use itertools::Itertools; + use singer_utils::structs::ChipChallenges; + use std::time::Instant; + use transcript::Transcript; + + use crate::{ + instructions::{ + mstore::{MstoreAccessory, MstoreInstruction}, + Instruction, SingerCircuitBuilder, + }, + CircuitWiresIn, SingerGraphBuilder, + }; + + use crate::test::{get_uint_params, test_opcode_circuit, u2vec}; + use core::ops::Range; + use goldilocks::Goldilocks; + use simple_frontend::structs::CellId; + use singer_utils::constants::RANGE_CHIP_BIT_WIDTH; + use singer_utils::structs::TSUInt; + use std::collections::BTreeMap; + + impl MstoreInstruction { + #[inline] + fn phase0_idxes_map() -> BTreeMap> { + let mut map = BTreeMap::new(); + + map.insert("phase0_pc".to_string(), Self::phase0_pc()); + map.insert("phase0_stack_ts".to_string(), Self::phase0_stack_ts()); + map.insert("phase0_memory_ts".to_string(), Self::phase0_memory_ts()); + map.insert("phase0_stack_top".to_string(), Self::phase0_stack_top()); + map.insert("phase0_clk".to_string(), Self::phase0_clk()); + map.insert("phase0_pc_add".to_string(), Self::phase0_pc_add()); + map.insert( + "phase0_memory_ts_add".to_string(), + Self::phase0_memory_ts_add(), + ); + map.insert("phase0_offset".to_string(), Self::phase0_offset()); + map.insert("phase0_mem_bytes".to_string(), Self::phase0_mem_bytes()); + map.insert( + "phase0_old_stack_ts_offset".to_string(), + Self::phase0_old_stack_ts_offset(), + ); + map.insert( + "phase0_old_stack_ts_lt_offset".to_string(), + Self::phase0_old_stack_ts_lt_offset(), + ); + map.insert( + "phase0_old_stack_ts_value".to_string(), + Self::phase0_old_stack_ts_value(), + ); + map.insert( + "phase0_old_stack_ts_lt_value".to_string(), + Self::phase0_old_stack_ts_lt_value(), + ); + + map + } + } + + #[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_CHECK_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 = u2vec::<{ TSUInt::N_RANGE_CHECK_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(range_values[3]), + 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 = u2vec::<{ TSUInt::N_RANGE_CHECK_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(range_values[3]), + Goldilocks::from(1u64), // borrow + ], + ); + + let circuit_witness_challenges = vec![ + GoldilocksExt2::from(2), + GoldilocksExt2::from(2), + GoldilocksExt2::from(2), + ]; + + let _circuit_witness = test_opcode_circuit( + &inst_circuit, + &phase0_idx_map, + phase0_witness_size, + &phase0_values_map, + circuit_witness_challenges, + ); + } + + 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::::new(); + + let mut rng = test_rng(); + let inst_phase0_size = MstoreInstruction::phase0_size(); + let inst_wit: CircuitWiresIn = vec![LayerWitness { + instances: (0..(1 << instance_num_vars)) + .map(|_| { + (0..inst_phase0_size) + .map(|_| E::BaseField::random(&mut rng)) + .collect_vec() + }) + .collect_vec(), + }]; + let acc_phase0_size = MstoreAccessory::phase0_size(); + let acc_wit: CircuitWiresIn = vec![ + LayerWitness { instances: vec![] }, + LayerWitness { instances: vec![] }, + LayerWitness { + instances: (0..(1 << instance_num_vars) * 32) + .map(|_| { + (0..acc_phase0_size) + .map(|_| E::BaseField::random(&mut rng)) + .collect_vec() + }) + .collect_vec(), + }, + ]; + + 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 mut prover_transcript = &mut Transcript::new(b"Singer"); + + let timer = Instant::now(); + let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, &mut prover_transcript, 1) + .expect("prove failed"); + println!( + "MstoreInstruction::prove, instance_num_vars = {}, time = {}", + instance_num_vars, + timer.elapsed().as_secs_f64() + ); + } + + #[test] + fn bench_mstore_instruction() { + bench_mstore_instruction_helper::(5); + } +} diff --git a/singer/src/instructions/pop.rs b/singer/src/instructions/pop.rs index a83710525..32f083671 100644 --- a/singer/src/instructions/pop.rs +++ b/singer/src/instructions/pop.rs @@ -41,11 +41,9 @@ register_witness!( } ); -impl PopInstruction { - const OPCODE: OpcodeType = OpcodeType::POP; -} - 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::new(); let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); @@ -102,7 +100,11 @@ impl Instruction for PopInstruction { ); // Bytecode check for (pc, POP) - rom_handler.bytecode_with_pc_opcode(&mut circuit_builder, pc.values(), Self::OPCODE); + rom_handler.bytecode_with_pc_opcode( + &mut circuit_builder, + pc.values(), + >::OPCODE, + ); let (ram_load_id, ram_store_id) = ram_handler.finalize(&mut circuit_builder); let rom_id = rom_handler.finalize(&mut circuit_builder); @@ -123,15 +125,26 @@ impl Instruction for PopInstruction { #[cfg(test)] mod test { + use ark_std::test_rng; use core::ops::Range; + use ff::Field; + use ff_ext::ExtensionField; + use gkr::structs::LayerWitness; + use goldilocks::{Goldilocks, GoldilocksExt2}; + use itertools::Itertools; use std::collections::BTreeMap; use crate::instructions::{ChipChallenges, Instruction, PopInstruction}; use crate::test::{get_uint_params, test_opcode_circuit, u2vec}; - use goldilocks::{Goldilocks, GoldilocksExt2}; use simple_frontend::structs::CellId; use singer_utils::constants::RANGE_CHIP_BIT_WIDTH; use singer_utils::structs::TSUInt; + use std::time::Instant; + use transcript::Transcript; + + use crate::instructions::{InstructionGraph, SingerCircuitBuilder}; + use crate::scheme::GKRGraphProverState; + use crate::{CircuitWiresIn, SingerGraphBuilder, SingerParams}; impl PopInstruction { #[inline] @@ -228,7 +241,7 @@ mod test { GoldilocksExt2::from(2), ]; - test_opcode_circuit( + let _circuit_witness = test_opcode_circuit( &inst_circuit, &phase0_idx_map, phase0_witness_size, @@ -236,4 +249,65 @@ mod test { circuit_witness_challenges, ); } + + 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::::new(); + + let mut rng = test_rng(); + let size = PopInstruction::phase0_size(); + let phase0: CircuitWiresIn = vec![LayerWitness { + instances: (0..(1 << instance_num_vars)) + .map(|_| { + (0..size) + .map(|_| E::BaseField::random(&mut rng)) + .collect_vec() + }) + .collect_vec(), + }]; + + 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 mut prover_transcript = &mut Transcript::new(b"Singer"); + + let timer = Instant::now(); + let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, &mut prover_transcript, 1) + .expect("prove failed"); + println!( + "PopInstruction::prove, instance_num_vars = {}, time = {}", + instance_num_vars, + timer.elapsed().as_secs_f64() + ); + } + + #[test] + fn bench_pop_instruction() { + bench_pop_instruction_helper::(10); + } } diff --git a/singer/src/instructions/push.rs b/singer/src/instructions/push.rs index 02ba265c5..6dfe7084f 100644 --- a/singer/src/instructions/push.rs +++ b/singer/src/instructions/push.rs @@ -41,14 +41,15 @@ register_witness!( } ); -impl PushInstruction { +impl Instruction for PushInstruction { const OPCODE: OpcodeType = match N { 1 => OpcodeType::PUSH1, _ => unimplemented!(), }; -} - -impl Instruction for PushInstruction { + const NAME: &'static str = match N { + 1 => "PUSH1", + _ => unimplemented!(), + }; fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { let mut circuit_builder = CircuitBuilder::new(); let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); @@ -107,7 +108,11 @@ impl Instruction for PushInstruction { ); // Bytecode check for (pc, PUSH{N}), (pc + 1, byte[0]), ..., (pc + N, byte[N - 1]) - rom_handler.bytecode_with_pc_opcode(&mut circuit_builder, pc.values(), Self::OPCODE); + rom_handler.bytecode_with_pc_opcode( + &mut circuit_builder, + pc.values(), + >::OPCODE, + ); for (i, pc_add_i_plus_1) in phase0[Self::phase0_pc_add_i_plus_1()] .chunks(UIntAddSub::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS) .enumerate() @@ -137,3 +142,182 @@ impl Instruction for PushInstruction { }) } } + +#[cfg(test)] +mod test { + use ark_std::test_rng; + use core::ops::Range; + use ff::Field; + use ff_ext::ExtensionField; + use gkr::structs::LayerWitness; + use goldilocks::{Goldilocks, GoldilocksExt2}; + use itertools::Itertools; + use simple_frontend::structs::CellId; + use std::collections::BTreeMap; + use std::time::Instant; + use transcript::Transcript; + + use crate::instructions::{ + ChipChallenges, Instruction, InstructionGraph, PushInstruction, SingerCircuitBuilder, + }; + use crate::scheme::GKRGraphProverState; + use crate::test::test_opcode_circuit; + use crate::{CircuitWiresIn, SingerGraphBuilder, SingerParams}; + + impl PushInstruction { + #[inline] + fn phase0_idxes_map() -> BTreeMap> { + let mut map = BTreeMap::new(); + map.insert("phase0_pc".to_string(), Self::phase0_pc()); + map.insert("phase0_stack_ts".to_string(), Self::phase0_stack_ts()); + map.insert("phase0_memory_ts".to_string(), Self::phase0_memory_ts()); + map.insert("phase0_stack_top".to_string(), Self::phase0_stack_top()); + map.insert("phase0_clk".to_string(), Self::phase0_clk()); + map.insert( + "phase0_pc_add_i_plus_1".to_string(), + Self::phase0_pc_add_i_plus_1(), + ); + map.insert( + "phase0_stack_ts_add".to_string(), + Self::phase0_stack_ts_add(), + ); + map.insert("phase0_stack_bytes".to_string(), Self::phase0_stack_bytes()); + + map + } + } + + #[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_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( + "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), + ]; + + let _circuit_witness = test_opcode_circuit( + &inst_circuit, + &phase0_idx_map, + phase0_witness_size, + &phase0_values_map, + circuit_witness_challenges, + ); + } + + 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::::new(); + + let mut rng = test_rng(); + let size = PushInstruction::::phase0_size(); + let phase0: CircuitWiresIn = vec![LayerWitness { + instances: (0..(1 << instance_num_vars)) + .map(|_| { + (0..size) + .map(|_| E::BaseField::random(&mut rng)) + .collect_vec() + }) + .collect_vec(), + }]; + + 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 mut prover_transcript = &mut Transcript::new(b"Singer"); + + let timer = Instant::now(); + let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, &mut prover_transcript, 1) + .expect("prove failed"); + println!( + "Push{}Instruction::prove, instance_num_vars = {}, time = {}", + N, + instance_num_vars, + timer.elapsed().as_secs_f64() + ); + } + + #[test] + fn bench_push1_instruction() { + bench_push_instruction_helper::(10); + } +} diff --git a/singer/src/instructions/ret.rs b/singer/src/instructions/ret.rs index 9d75d39d6..ac24c2efa 100644 --- a/singer/src/instructions/ret.rs +++ b/singer/src/instructions/ret.rs @@ -276,11 +276,9 @@ register_witness!( } ); -impl ReturnInstruction { - const OPCODE: OpcodeType = OpcodeType::RETURN; -} - 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::new(); let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); @@ -310,7 +308,7 @@ impl Instruction for ReturnInstruction { )?; // Pop offset and mem_size from stack - let old_stack_ts0 = StackUInt::try_from(&phase0[Self::phase0_old_stack_ts0()])?; + let old_stack_ts0 = TSUInt::try_from(&phase0[Self::phase0_old_stack_ts0()])?; let offset = StackUInt::try_from(&phase0[Self::phase0_offset()])?; ram_handler.stack_pop( &mut circuit_builder, @@ -319,7 +317,7 @@ impl Instruction for ReturnInstruction { offset.values(), ); - let old_stack_ts1 = StackUInt::try_from(&phase0[Self::phase0_old_stack_ts1()])?; + let old_stack_ts1 = TSUInt::try_from(&phase0[Self::phase0_old_stack_ts1()])?; let length = StackUInt::try_from(&phase0[Self::phase0_mem_length()])?; ram_handler.stack_pop( &mut circuit_builder, @@ -329,7 +327,11 @@ impl Instruction for ReturnInstruction { ); // Bytecode check for (pc, ret) - rom_handler.bytecode_with_pc_opcode(&mut circuit_builder, pc.values(), Self::OPCODE); + rom_handler.bytecode_with_pc_opcode( + &mut circuit_builder, + pc.values(), + >::OPCODE, + ); let (ram_load_id, ram_store_id) = ram_handler.finalize(&mut circuit_builder); let rom_id = rom_handler.finalize(&mut circuit_builder); @@ -346,6 +348,8 @@ impl Instruction for ReturnInstruction { } 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()); @@ -353,6 +357,10 @@ impl Instruction for ReturnInstruction { 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 { @@ -381,8 +389,10 @@ register_witness!( } ); -impl Instruction for ReturnPublicOutLoad { - fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { +impl ReturnPublicOutLoad { + fn construct_circuit( + challenges: ChipChallenges, + ) -> Result, ZKVMError> { let mut circuit_builder = CircuitBuilder::new(); 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()); @@ -427,6 +437,10 @@ impl Instruction for ReturnPublicOutLoad { }, }) } + + fn name() -> &'static str { + "ReturnPublicOutLoad" + } } register_witness!( @@ -438,8 +452,10 @@ register_witness!( } ); -impl Instruction for ReturnRestMemLoad { - fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { +impl ReturnRestMemLoad { + fn construct_circuit( + challenges: ChipChallenges, + ) -> Result, ZKVMError> { let mut circuit_builder = CircuitBuilder::new(); let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); let mut ram_handler = RAMHandler::new(&challenges); @@ -469,6 +485,10 @@ impl Instruction for ReturnRestMemLoad { }, }) } + + fn name() -> &'static str { + "ReturnRestMemLoad" + } } register_witness!( @@ -479,8 +499,10 @@ register_witness!( } ); -impl Instruction for ReturnRestMemStore { - fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { +impl ReturnRestMemStore { + fn construct_circuit( + challenges: ChipChallenges, + ) -> Result, ZKVMError> { let mut circuit_builder = CircuitBuilder::new(); let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); let mut ram_handler = RAMHandler::new(&challenges); @@ -505,6 +527,10 @@ impl Instruction for ReturnRestMemStore { }, }) } + + fn name() -> &'static str { + "ReturnRestMemStore" + } } pub struct ReturnRestStackPop; @@ -517,8 +543,10 @@ register_witness!( } ); -impl Instruction for ReturnRestStackPop { - fn construct_circuit(challenges: ChipChallenges) -> Result, ZKVMError> { +impl ReturnRestStackPop { + fn construct_circuit( + challenges: ChipChallenges, + ) -> Result, ZKVMError> { let mut circuit_builder = CircuitBuilder::new(); let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); let mut ram_handler = RAMHandler::new(&challenges); @@ -549,4 +577,8 @@ impl Instruction for ReturnRestStackPop { }, }) } + + fn name() -> &'static str { + "ReturnRestStackPop" + } } diff --git a/singer/src/instructions/swap.rs b/singer/src/instructions/swap.rs index efd282d38..5c944652a 100644 --- a/singer/src/instructions/swap.rs +++ b/singer/src/instructions/swap.rs @@ -37,24 +37,27 @@ register_witness!( stack_ts_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_CELLS, old_stack_ts_1 => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt_1 => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_lt_1 => UIntCmp::::N_WITNESS_CELLS, old_stack_ts_n_plus_1 => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt_n_plus_1 => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_lt_n_plus_1 => UIntCmp::::N_WITNESS_CELLS, stack_values_1 => StackUInt::N_OPRAND_CELLS, stack_values_n_plus_1 => StackUInt::N_OPRAND_CELLS } ); -impl SwapInstruction { +impl Instruction for SwapInstruction { const OPCODE: OpcodeType = match N { 1 => OpcodeType::SWAP1, 2 => OpcodeType::SWAP2, 4 => OpcodeType::SWAP4, _ => unimplemented!(), }; -} - -impl Instruction for SwapInstruction { + 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::new(); let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); @@ -152,7 +155,11 @@ impl Instruction for SwapInstruction { ); // Bytecode check for (pc, SWAP{N}). - rom_handler.bytecode_with_pc_opcode(&mut circuit_builder, pc.values(), Self::OPCODE); + rom_handler.bytecode_with_pc_opcode( + &mut circuit_builder, + pc.values(), + >::OPCODE, + ); let (ram_load_id, ram_store_id) = ram_handler.finalize(&mut circuit_builder); let rom_id = rom_handler.finalize(&mut circuit_builder); @@ -170,3 +177,255 @@ impl Instruction for SwapInstruction { }) } } + +#[cfg(test)] +mod test { + use ark_std::test_rng; + use core::ops::Range; + use ff::Field; + use ff_ext::ExtensionField; + use gkr::structs::LayerWitness; + use goldilocks::{Goldilocks, GoldilocksExt2}; + use itertools::Itertools; + use simple_frontend::structs::CellId; + use singer_utils::constants::RANGE_CHIP_BIT_WIDTH; + use singer_utils::structs::TSUInt; + use std::collections::BTreeMap; + use std::time::Instant; + use transcript::Transcript; + + use crate::instructions::{ + ChipChallenges, Instruction, InstructionGraph, SingerCircuitBuilder, SwapInstruction, + }; + use crate::scheme::GKRGraphProverState; + use crate::test::{get_uint_params, test_opcode_circuit, u2vec}; + use crate::{CircuitWiresIn, SingerGraphBuilder, SingerParams}; + + impl SwapInstruction { + #[inline] + fn phase0_idxes_map() -> BTreeMap> { + let mut map = BTreeMap::new(); + map.insert("phase0_pc".to_string(), Self::phase0_pc()); + map.insert("phase0_stack_ts".to_string(), Self::phase0_stack_ts()); + map.insert("phase0_memory_ts".to_string(), Self::phase0_memory_ts()); + map.insert("phase0_stack_top".to_string(), Self::phase0_stack_top()); + map.insert("phase0_clk".to_string(), Self::phase0_clk()); + map.insert("phase0_pc_add".to_string(), Self::phase0_pc_add()); + map.insert( + "phase0_stack_ts_add".to_string(), + Self::phase0_stack_ts_add(), + ); + map.insert( + "phase0_old_stack_ts_1".to_string(), + Self::phase0_old_stack_ts_1(), + ); + map.insert( + "phase0_old_stack_ts_lt_1".to_string(), + Self::phase0_old_stack_ts_lt_1(), + ); + map.insert( + "phase0_old_stack_ts_n_plus_1".to_string(), + Self::phase0_old_stack_ts_n_plus_1(), + ); + map.insert( + "phase0_old_stack_ts_lt_n_plus_1".to_string(), + Self::phase0_old_stack_ts_lt_n_plus_1(), + ); + map.insert( + "phase0_stack_values_1".to_string(), + Self::phase0_stack_values_1(), + ); + map.insert( + "phase0_stack_values_n_plus_1".to_string(), + Self::phase0_stack_values_n_plus_1(), + ); + + map + } + } + + #[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_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( + "phase0_old_stack_ts_1".to_string(), + vec![Goldilocks::from(3u64)], + ); + let m: u64 = (1 << get_uint_params::().1) - 1; + let range_values = u2vec::<{ TSUInt::N_RANGE_CHECK_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(range_values[3]), + 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 = u2vec::<{ TSUInt::N_RANGE_CHECK_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(range_values[3]), + 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), + ]; + + let _circuit_witness = test_opcode_circuit( + &inst_circuit, + &phase0_idx_map, + phase0_witness_size, + &phase0_values_map, + circuit_witness_challenges, + ); + } + + 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::::new(); + + let mut rng = test_rng(); + let size = SwapInstruction::::phase0_size(); + let phase0: CircuitWiresIn = vec![LayerWitness { + instances: (0..(1 << instance_num_vars)) + .map(|_| { + (0..size) + .map(|_| E::BaseField::random(&mut rng)) + .collect_vec() + }) + .collect_vec(), + }]; + + 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 mut prover_transcript = &mut Transcript::new(b"Singer"); + + let timer = Instant::now(); + let _ = GKRGraphProverState::prove(&graph, &wit, &target_evals, &mut prover_transcript, 1) + .expect("prove failed"); + println!( + "Swap{}Instruction::prove, instance_num_vars = {}, time = {}", + N, + instance_num_vars, + timer.elapsed().as_secs_f64() + ); + } + + #[test] + fn bench_swap2_instruction() { + bench_swap_instruction_helper::(10); + } + + #[test] + fn bench_swap4_instruction() { + bench_swap_instruction_helper::(10); + } +} diff --git a/singer/src/lib.rs b/singer/src/lib.rs index b4fb203ca..614ae606f 100644 --- a/singer/src/lib.rs +++ b/singer/src/lib.rs @@ -16,6 +16,7 @@ use std::mem; pub mod error; pub mod instructions; pub mod scheme; +#[cfg(test)] pub mod test; mod utils; @@ -34,9 +35,9 @@ mod utils; /// records. `public_output_size` is the wire id stores the size of public /// output. pub struct SingerGraphBuilder { - graph_builder: CircuitGraphBuilder, - chip_builder: SingerChipBuilder, - public_output_size: Option, + pub graph_builder: CircuitGraphBuilder, + pub chip_builder: SingerChipBuilder, + pub public_output_size: Option, } impl SingerGraphBuilder { @@ -239,7 +240,7 @@ pub struct SingerAuxInfo { } // Indexed by 1. wires_in id (or phase); 2. instance id; 3. wire id. -pub(crate) type CircuitWiresIn = Vec>; +pub type CircuitWiresIn = Vec>; #[derive(Clone, Debug, Default)] pub struct InstWiresIn { diff --git a/singer/src/scheme/prover.rs b/singer/src/scheme/prover.rs index 3248ac5f3..82f500aba 100644 --- a/singer/src/scheme/prover.rs +++ b/singer/src/scheme/prover.rs @@ -77,7 +77,7 @@ pub fn prove( 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)?; + GKRGraphProverState::prove(&vm_circuit.0, &vm_witness.0, &target_evals, transcript, 1)?; Ok(( SingerProof { gkr_phase_proof, diff --git a/singer/src/test.rs b/singer/src/test.rs index 02f9ad7d9..849189379 100644 --- a/singer/src/test.rs +++ b/singer/src/test.rs @@ -42,7 +42,7 @@ pub(crate) fn test_opcode_circuit( phase0_witness_size: usize, phase0_values_map: &BTreeMap>, circuit_witness_challenges: Vec, -) { +) -> CircuitWitness<::BaseField> { // configure circuit let circuit = inst_circuit.circuit.as_ref(); @@ -87,6 +87,8 @@ pub(crate) fn test_opcode_circuit( 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");