diff --git a/singer-pro/src/basic_block.rs b/singer-pro/src/basic_block.rs index c9ea30eba..ee1fb8c35 100644 --- a/singer-pro/src/basic_block.rs +++ b/singer-pro/src/basic_block.rs @@ -536,7 +536,7 @@ mod test { LayerWitness::default(), LayerWitness::default(), LayerWitness { - instances: random_matrix(n_instances, PCUInt::N_OPRAND_CELLS), + instances: random_matrix(n_instances, PCUInt::N_OPERAND_CELLS), }, LayerWitness::default(), LayerWitness::default(), diff --git a/singer-pro/src/basic_block/bb_final.rs b/singer-pro/src/basic_block/bb_final.rs index 8bcd88f69..7d3b1fef6 100644 --- a/singer-pro/src/basic_block/bb_final.rs +++ b/singer-pro/src/basic_block/bb_final.rs @@ -4,6 +4,7 @@ use gkr::structs::Circuit; use itertools::Itertools; use paste::paste; use simple_frontend::structs::{CircuitBuilder, MixedCell}; +use singer_utils::uint::constants::AddSubConstants; use singer_utils::{ chip_handler::{ GlobalStateChipOperations, OAMOperations, ROMOperations, RangeChipOperations, @@ -12,7 +13,6 @@ use singer_utils::{ chips::IntoEnumIterator, register_witness, structs::{ChipChallenges, InstOutChipType, PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::UIntAddSub, }; use std::sync::Arc; @@ -28,7 +28,7 @@ pub struct BasicBlockFinal; register_witness!(BasicBlockFinal, phase0 { // State in related - stack_ts_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_CELLS + stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW }); impl BasicBlockFinal { @@ -49,12 +49,12 @@ impl BasicBlockFinal { let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); // From BB start - let (stack_ts_id, stack_ts) = circuit_builder.create_witness_in(TSUInt::N_OPRAND_CELLS); + let (stack_ts_id, stack_ts) = circuit_builder.create_witness_in(TSUInt::N_OPERAND_CELLS); let (stack_top_id, stack_top) = circuit_builder.create_witness_in(1); let (clk_id, clk) = circuit_builder.create_witness_in(1); // From inst pc. - let (next_pc_id, next_pc) = circuit_builder.create_witness_in(PCUInt::N_OPRAND_CELLS); + let (next_pc_id, next_pc) = circuit_builder.create_witness_in(PCUInt::N_OPERAND_CELLS); let mut ram_handler = RAMHandler::new(&challenges); let mut rom_handler = ROMHandler::new(&challenges); @@ -68,7 +68,7 @@ impl BasicBlockFinal { stack_ts_add_witness, )?; - let (memory_ts_id, memory_ts) = circuit_builder.create_witness_in(TSUInt::N_OPRAND_CELLS); + let (memory_ts_id, memory_ts) = circuit_builder.create_witness_in(TSUInt::N_OPERAND_CELLS); let stack_top_expr = MixedCell::Cell(stack_top[0]); let clk_expr = MixedCell::Cell(clk[0]); ram_handler.state_out( @@ -92,7 +92,7 @@ impl BasicBlockFinal { .iter() .map(|offset| { let (stack_from_insts_id, stack_from_insts) = - circuit_builder.create_witness_in(StackUInt::N_OPRAND_CELLS); + circuit_builder.create_witness_in(StackUInt::N_OPERAND_CELLS); ram_handler.stack_push( &mut circuit_builder, stack_top_expr.add(i64_to_base_field::(*offset)), diff --git a/singer-pro/src/basic_block/bb_ret.rs b/singer-pro/src/basic_block/bb_ret.rs index 18a55fd9c..2eafaab73 100644 --- a/singer-pro/src/basic_block/bb_ret.rs +++ b/singer-pro/src/basic_block/bb_ret.rs @@ -46,7 +46,7 @@ impl BasicBlockReturn { let n_stack_items = stack_top_offsets.len(); // From BB Start - let (stack_ts_id, stack_ts) = circuit_builder.create_witness_in(TSUInt::N_OPRAND_CELLS); + let (stack_ts_id, stack_ts) = circuit_builder.create_witness_in(TSUInt::N_OPERAND_CELLS); let (stack_top_id, stack_top) = circuit_builder.create_witness_in(1); let (clk_id, _) = circuit_builder.create_witness_in(1); @@ -62,7 +62,7 @@ impl BasicBlockReturn { rom_handler.range_check_stack_top(&mut circuit_builder, stack_top_r)?; // From predesessor instruction - let (memory_ts_id, _) = circuit_builder.create_witness_in(TSUInt::N_OPRAND_CELLS); + let (memory_ts_id, _) = circuit_builder.create_witness_in(TSUInt::N_OPERAND_CELLS); let stack_operand_ids = stack_top_offsets .iter() .map(|offset| { @@ -113,8 +113,8 @@ register_witness!( BBReturnRestMemLoad, phase0 { mem_byte => 1, - old_memory_ts => TSUInt::N_OPRAND_CELLS, - offset => StackUInt::N_OPRAND_CELLS + old_memory_ts => TSUInt::N_OPERAND_CELLS, + offset => StackUInt::N_OPERAND_CELLS } ); @@ -160,7 +160,7 @@ register_witness!( BBReturnRestMemStore, phase0 { mem_byte => 1, - offset => StackUInt::N_OPRAND_CELLS + offset => StackUInt::N_OPERAND_CELLS } ); @@ -176,7 +176,7 @@ impl BBReturnRestMemStore { let offset = &phase0[Self::phase0_offset()]; let mem_byte = phase0[Self::phase0_mem_byte().start]; // memory_ts is zero. - let memory_ts = circuit_builder.create_cells(StackUInt::N_OPRAND_CELLS); + let memory_ts = circuit_builder.create_cells(StackUInt::N_OPERAND_CELLS); ram_handler.oam_store(&mut circuit_builder, offset, &memory_ts, &[mem_byte]); let (ram_load_id, ram_store_id) = ram_handler.finalize(&mut circuit_builder); @@ -206,8 +206,8 @@ pub struct BBReturnRestStackPop; register_witness!( BBReturnRestStackPop, phase0 { - old_stack_ts => TSUInt::N_OPRAND_CELLS, - stack_values => StackUInt::N_OPRAND_CELLS + old_stack_ts => TSUInt::N_OPERAND_CELLS, + stack_values => StackUInt::N_OPERAND_CELLS } ); diff --git a/singer-pro/src/basic_block/bb_start.rs b/singer-pro/src/basic_block/bb_start.rs index f6a63a18b..2616b9076 100644 --- a/singer-pro/src/basic_block/bb_start.rs +++ b/singer-pro/src/basic_block/bb_start.rs @@ -3,6 +3,7 @@ use ff_ext::ExtensionField; use gkr::structs::Circuit; use paste::paste; use simple_frontend::structs::{CircuitBuilder, MixedCell}; +use singer_utils::uint::constants::AddSubConstants; use singer_utils::{ chip_handler::{ GlobalStateChipOperations, OAMOperations, ROMOperations, RangeChipOperations, @@ -11,7 +12,6 @@ use singer_utils::{ chips::IntoEnumIterator, register_multi_witness, structs::{ChipChallenges, InstOutChipType, PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::UIntCmp, }; use std::sync::Arc; @@ -27,16 +27,16 @@ pub(crate) struct BasicBlockStart; register_multi_witness!(BasicBlockStart, phase0(n_stack_items) { // State in related - pc => PCUInt::N_OPRAND_CELLS, - stack_ts => TSUInt::N_OPRAND_CELLS, - memory_ts => TSUInt::N_OPRAND_CELLS, + pc => PCUInt::N_OPERAND_CELLS, + stack_ts => TSUInt::N_OPERAND_CELLS, + memory_ts => TSUInt::N_OPERAND_CELLS, stack_top => 1, clk => 1, // Stack values - old_stack_values(n_stack_items) => StackUInt::N_OPRAND_CELLS, - old_stack_ts(n_stack_items) => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt(n_stack_items) => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS + old_stack_values(n_stack_items) => StackUInt::N_OPERAND_CELLS, + old_stack_ts(n_stack_items) => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt(n_stack_items) => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW }); impl BasicBlockStart { @@ -83,7 +83,7 @@ impl BasicBlockStart { for (i, offset) in stack_top_offsets.iter().enumerate() { let old_stack_ts = TSUInt::try_from(&phase0[Self::phase0_old_stack_ts(i, n_stack_items)])?; - UIntCmp::::assert_lt( + TSUInt::assert_lt( &mut circuit_builder, &mut rom_handler, &old_stack_ts, @@ -102,9 +102,9 @@ impl BasicBlockStart { let mut stack_result_ids = Vec::with_capacity(n_stack_items); for i in 0..n_stack_items { let (stack_operand_id, stack_operand) = - circuit_builder.create_witness_out(StackUInt::N_OPRAND_CELLS); + circuit_builder.create_witness_out(StackUInt::N_OPERAND_CELLS); let old_stack = &phase0[Self::phase0_old_stack_values(i, n_stack_items)]; - for j in 0..StackUInt::N_OPRAND_CELLS { + for j in 0..StackUInt::N_OPERAND_CELLS { circuit_builder.add(stack_operand[j], old_stack[j], E::BaseField::ONE); } stack_result_ids.push(stack_operand_id); @@ -114,7 +114,7 @@ impl BasicBlockStart { // To BB final let (out_stack_ts_id, out_stack_ts) = - circuit_builder.create_witness_out(TSUInt::N_OPRAND_CELLS); + circuit_builder.create_witness_out(TSUInt::N_OPERAND_CELLS); add_assign_each_cell(&mut circuit_builder, &out_stack_ts, stack_ts.values()); let (out_stack_top_id, out_stack_top) = circuit_builder.create_witness_out(1); circuit_builder.add(out_stack_top[0], stack_top, E::BaseField::ONE); diff --git a/singer-pro/src/instructions/add.rs b/singer-pro/src/instructions/add.rs index e136a8744..b9fa98d25 100644 --- a/singer-pro/src/instructions/add.rs +++ b/singer-pro/src/instructions/add.rs @@ -2,13 +2,13 @@ use ff_ext::ExtensionField; use gkr::structs::Circuit; use paste::paste; use simple_frontend::structs::CircuitBuilder; +use singer_utils::uint::constants::AddSubConstants; use singer_utils::{ chip_handler::ROMOperations, chips::IntoEnumIterator, constants::OpcodeType, register_witness, structs::{ChipChallenges, InstOutChipType, ROMHandler, StackUInt, TSUInt}, - uint::UIntAddSub, }; use std::sync::Arc; @@ -30,7 +30,7 @@ register_witness!( AddInstruction, phase0 { // Witness for addend_0 + addend_1 - instruction_add => UIntAddSub::::N_WITNESS_CELLS + instruction_add => AddSubConstants::::N_WITNESS_CELLS } ); @@ -44,16 +44,16 @@ impl Instruction for AddInstruction { let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); // From predesessor instruction - let (memory_ts_id, memory_ts) = circuit_builder.create_witness_in(TSUInt::N_OPRAND_CELLS); - let (addend_0_id, addend_0) = circuit_builder.create_witness_in(StackUInt::N_OPRAND_CELLS); - let (addend_1_id, addend_1) = circuit_builder.create_witness_in(StackUInt::N_OPRAND_CELLS); + let (memory_ts_id, memory_ts) = circuit_builder.create_witness_in(TSUInt::N_OPERAND_CELLS); + let (addend_0_id, addend_0) = circuit_builder.create_witness_in(StackUInt::N_OPERAND_CELLS); + let (addend_1_id, addend_1) = circuit_builder.create_witness_in(StackUInt::N_OPERAND_CELLS); let mut rom_handler = ROMHandler::new(&challenges); // Execution result = addend0 + addend1, with carry. let addend_0 = addend_0.try_into()?; let addend_1 = addend_1.try_into()?; - let result = UIntAddSub::::add( + let result = StackUInt::add( &mut circuit_builder, &mut rom_handler, &addend_0, @@ -63,7 +63,7 @@ impl Instruction for AddInstruction { // To successor instruction let stack_result_id = circuit_builder.create_witness_out_from_cells(result.values()); let (next_memory_ts_id, next_memory_ts) = - circuit_builder.create_witness_out(TSUInt::N_OPRAND_CELLS); + circuit_builder.create_witness_out(TSUInt::N_OPERAND_CELLS); add_assign_each_cell(&mut circuit_builder, &next_memory_ts, &memory_ts); // To chips diff --git a/singer-pro/src/instructions/calldataload.rs b/singer-pro/src/instructions/calldataload.rs index 2d9436c32..0da68e250 100644 --- a/singer-pro/src/instructions/calldataload.rs +++ b/singer-pro/src/instructions/calldataload.rs @@ -28,7 +28,7 @@ pub struct CalldataloadInstruction; register_witness!( CalldataloadInstruction, phase0 { - data => StackUInt::N_OPRAND_CELLS + data => StackUInt::N_OPERAND_CELLS } ); @@ -41,8 +41,8 @@ impl Instruction for CalldataloadInstruction { // From witness let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); // From predesessor instruction - let (memory_ts_id, memory_ts) = circuit_builder.create_witness_in(TSUInt::N_OPRAND_CELLS); - let (offset_id, offset) = circuit_builder.create_witness_in(UInt64::N_OPRAND_CELLS); + let (memory_ts_id, memory_ts) = circuit_builder.create_witness_in(TSUInt::N_OPERAND_CELLS); + let (offset_id, offset) = circuit_builder.create_witness_in(UInt64::N_OPERAND_CELLS); let mut rom_handler = ROMHandler::new(&challenges); @@ -54,7 +54,7 @@ impl Instruction for CalldataloadInstruction { let (data_copy_id, data_copy) = circuit_builder.create_witness_out(data.len()); add_assign_each_cell(&mut circuit_builder, &data_copy, &data); let (next_memory_ts_id, next_memory_ts) = - circuit_builder.create_witness_out(TSUInt::N_OPRAND_CELLS); + circuit_builder.create_witness_out(TSUInt::N_OPERAND_CELLS); add_assign_each_cell(&mut circuit_builder, &next_memory_ts, &memory_ts); // To chips diff --git a/singer-pro/src/instructions/gt.rs b/singer-pro/src/instructions/gt.rs index 99099070b..ea55a3dc6 100644 --- a/singer-pro/src/instructions/gt.rs +++ b/singer-pro/src/instructions/gt.rs @@ -2,13 +2,13 @@ use ff_ext::ExtensionField; use gkr::structs::Circuit; use paste::paste; use simple_frontend::structs::CircuitBuilder; +use singer_utils::uint::constants::AddSubConstants; use singer_utils::{ chip_handler::ROMOperations, chips::IntoEnumIterator, constants::OpcodeType, register_witness, structs::{ChipChallenges, InstOutChipType, ROMHandler, StackUInt, TSUInt}, - uint::UIntCmp, }; use std::sync::Arc; @@ -28,7 +28,7 @@ register_witness!( GtInstruction, phase0 { // Witness for operand_0 > operand_1 - instruction_gt => UIntCmp::::N_WITNESS_CELLS + instruction_gt => AddSubConstants::::N_WITNESS_CELLS } ); @@ -42,18 +42,18 @@ impl Instruction for GtInstruction { let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); // From predesessor instruction - let (memory_ts_id, memory_ts) = circuit_builder.create_witness_in(TSUInt::N_OPRAND_CELLS); + let (memory_ts_id, memory_ts) = circuit_builder.create_witness_in(TSUInt::N_OPERAND_CELLS); let (operand_0_id, operand_0) = - circuit_builder.create_witness_in(StackUInt::N_OPRAND_CELLS); + circuit_builder.create_witness_in(StackUInt::N_OPERAND_CELLS); let (operand_1_id, operand_1) = - circuit_builder.create_witness_in(StackUInt::N_OPRAND_CELLS); + circuit_builder.create_witness_in(StackUInt::N_OPERAND_CELLS); let mut rom_handler = ROMHandler::new(&challenges); // Execution operand_1 > operand_0. let operand_0 = operand_0.try_into()?; let operand_1 = operand_1.try_into()?; - let (result, _) = UIntCmp::::lt( + let (result, _) = StackUInt::lt( &mut circuit_builder, &mut rom_handler, &operand_0, @@ -62,13 +62,13 @@ impl Instruction for GtInstruction { )?; let result = [ vec![result], - circuit_builder.create_cells(StackUInt::N_OPRAND_CELLS - 1), + circuit_builder.create_cells(StackUInt::N_OPERAND_CELLS - 1), ] .concat(); // To successor instruction let stack_result_id = circuit_builder.create_witness_out_from_cells(&result); let (next_memory_ts_id, next_memory_ts) = - circuit_builder.create_witness_out(TSUInt::N_OPRAND_CELLS); + circuit_builder.create_witness_out(TSUInt::N_OPERAND_CELLS); add_assign_each_cell(&mut circuit_builder, &next_memory_ts, &memory_ts); // To chips diff --git a/singer-pro/src/instructions/jump.rs b/singer-pro/src/instructions/jump.rs index e297230f9..fd3919de9 100644 --- a/singer-pro/src/instructions/jump.rs +++ b/singer-pro/src/instructions/jump.rs @@ -27,8 +27,8 @@ impl Instruction for JumpInstruction { fn construct_circuit(_: ChipChallenges) -> Result, ZKVMError> { let mut circuit_builder = CircuitBuilder::new(); // From predesessor instruction - let (memory_ts_id, memory_ts) = circuit_builder.create_witness_in(TSUInt::N_OPRAND_CELLS); - let (next_pc_id, next_pc) = circuit_builder.create_witness_in(StackUInt::N_OPRAND_CELLS); + let (memory_ts_id, memory_ts) = circuit_builder.create_witness_in(TSUInt::N_OPERAND_CELLS); + let (next_pc_id, next_pc) = circuit_builder.create_witness_in(StackUInt::N_OPERAND_CELLS); // To BB final let (next_pc_copy_id, next_pc_copy) = circuit_builder.create_witness_out(next_pc.len()); @@ -36,7 +36,7 @@ impl Instruction for JumpInstruction { // To Succesor instruction let (next_memory_ts_id, next_memory_ts) = - circuit_builder.create_witness_out(TSUInt::N_OPRAND_CELLS); + circuit_builder.create_witness_out(TSUInt::N_OPERAND_CELLS); add_assign_each_cell(&mut circuit_builder, &next_memory_ts, &memory_ts); // To chips diff --git a/singer-pro/src/instructions/jumpi.rs b/singer-pro/src/instructions/jumpi.rs index 3ab9ef735..a9855e4d4 100644 --- a/singer-pro/src/instructions/jumpi.rs +++ b/singer-pro/src/instructions/jumpi.rs @@ -30,9 +30,9 @@ impl InstructionGraph for JumpiInstruction { register_witness!( JumpiInstruction, phase0 { - pc_plus_1 => PCUInt::N_OPRAND_CELLS, + pc_plus_1 => PCUInt::N_OPERAND_CELLS, pc_plus_1_opcode => 1, - cond_values_inv => StackUInt::N_OPRAND_CELLS, + cond_values_inv => StackUInt::N_OPERAND_CELLS, cond_non_zero_or_inv => 1 } ); @@ -47,10 +47,10 @@ impl Instruction for JumpiInstruction { let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); // From predesessor instruction - let (memory_ts_id, memory_ts) = circuit_builder.create_witness_in(TSUInt::N_OPRAND_CELLS); - let (dest_id, dest) = circuit_builder.create_witness_in(StackUInt::N_OPRAND_CELLS); + let (memory_ts_id, memory_ts) = circuit_builder.create_witness_in(TSUInt::N_OPERAND_CELLS); + let (dest_id, dest) = circuit_builder.create_witness_in(StackUInt::N_OPERAND_CELLS); let (cond_values_id, cond_values) = - circuit_builder.create_witness_in(StackUInt::N_OPRAND_CELLS); + circuit_builder.create_witness_in(StackUInt::N_OPERAND_CELLS); let mut rom_handler = ROMHandler::new(&challenges); @@ -71,8 +71,8 @@ impl Instruction for JumpiInstruction { // If cond_non_zero, next_pc = dest, otherwise, pc = pc + 1 let pc_plus_1 = &phase0[Self::phase0_pc_plus_1()]; - let (next_pc_id, next_pc) = circuit_builder.create_witness_out(PCUInt::N_OPRAND_CELLS); - for i in 0..PCUInt::N_OPRAND_CELLS { + let (next_pc_id, next_pc) = circuit_builder.create_witness_out(PCUInt::N_OPERAND_CELLS); + for i in 0..PCUInt::N_OPERAND_CELLS { circuit_builder.select(next_pc[i], pc_plus_1[i], dest[i], cond_non_zero); } @@ -90,7 +90,7 @@ impl Instruction for JumpiInstruction { // To successor instruction let (next_memory_ts_id, next_memory_ts) = - circuit_builder.create_witness_out(TSUInt::N_OPRAND_CELLS); + circuit_builder.create_witness_out(TSUInt::N_OPERAND_CELLS); add_assign_each_cell(&mut circuit_builder, &next_memory_ts, &memory_ts); let rom_id = rom_handler.finalize(&mut circuit_builder); diff --git a/singer-pro/src/instructions/mstore.rs b/singer-pro/src/instructions/mstore.rs index dcc290c7e..31110f067 100644 --- a/singer-pro/src/instructions/mstore.rs +++ b/singer-pro/src/instructions/mstore.rs @@ -10,7 +10,7 @@ use singer_utils::{ constants::{OpcodeType, EVM_STACK_BYTE_WIDTH}, register_witness, structs::{ChipChallenges, InstOutChipType, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::{UIntAddSub, UIntCmp}, + uint::constants::AddSubConstants, }; use std::{mem, sync::Arc}; @@ -111,7 +111,7 @@ impl InstructionGraph for MstoreInstruction { register_witness!( MstoreInstruction, phase0 { - memory_ts_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_CELLS, + memory_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, mem_bytes => EVM_STACK_BYTE_WIDTH } ); @@ -125,10 +125,10 @@ impl Instruction for MstoreInstruction { let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); // From predesessor instruction - let (memory_ts_id, memory_ts) = circuit_builder.create_witness_in(TSUInt::N_OPRAND_CELLS); - let (offset_id, offset) = circuit_builder.create_witness_in(StackUInt::N_OPRAND_CELLS); + let (memory_ts_id, memory_ts) = circuit_builder.create_witness_in(TSUInt::N_OPERAND_CELLS); + let (offset_id, offset) = circuit_builder.create_witness_in(StackUInt::N_OPERAND_CELLS); let (mem_value_id, mem_values) = - circuit_builder.create_witness_in(StackUInt::N_OPRAND_CELLS); + circuit_builder.create_witness_in(StackUInt::N_OPERAND_CELLS); let mut rom_handler = ROMHandler::new(&challenges); @@ -150,8 +150,8 @@ impl Instruction for MstoreInstruction { let mem_values = StackUInt::try_from(mem_values.as_slice())?; let mem_values_from_bytes = - StackUInt::from_bytes_big_endien(&mut circuit_builder, &mem_bytes)?; - UIntCmp::::assert_eq(&mut circuit_builder, &mem_values_from_bytes, &mem_values)?; + StackUInt::from_bytes_big_endian(&mut circuit_builder, &mem_bytes)?; + StackUInt::assert_eq(&mut circuit_builder, &mem_values_from_bytes, &mem_values)?; // To chips. let rom_id = rom_handler.finalize(&mut circuit_builder); @@ -211,17 +211,17 @@ pub struct MstoreAccessory; register_witness!( MstoreAccessory, pred_dup { - memory_ts => TSUInt::N_OPRAND_CELLS, - offset => StackUInt::N_OPRAND_CELLS + memory_ts => TSUInt::N_OPERAND_CELLS, + offset => StackUInt::N_OPERAND_CELLS }, pred_ooo { mem_byte => 1 }, phase0 { - old_memory_ts => TSUInt::N_OPRAND_CELLS, - old_memory_ts_lt => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS, + old_memory_ts => TSUInt::N_OPERAND_CELLS, + old_memory_ts_lt => AddSubConstants::::N_WITNESS_CELLS, - offset_add_delta => UIntAddSub::::N_WITNESS_CELLS, + offset_add_delta => AddSubConstants::::N_WITNESS_CELLS, prev_mem_byte => 1 } ); @@ -250,14 +250,14 @@ impl MstoreAccessory { let offset = StackUInt::try_from(&pred_dup[Self::pred_dup_offset()])?; let offset_add_delta = &phase0[Self::phase0_offset_add_delta()]; let delta = circuit_builder.create_counter_in(0)[0]; - let offset_plus_delta = UIntAddSub::::add_small( + let offset_plus_delta = StackUInt::add_cell( &mut circuit_builder, &mut rom_handler, &offset, delta, offset_add_delta, )?; - UIntCmp::::assert_lt( + TSUInt::assert_lt( &mut circuit_builder, &mut rom_handler, &old_memory_ts, diff --git a/singer-pro/src/instructions/ret.rs b/singer-pro/src/instructions/ret.rs index 605475560..84ea49685 100644 --- a/singer-pro/src/instructions/ret.rs +++ b/singer-pro/src/instructions/ret.rs @@ -3,13 +3,13 @@ use gkr::structs::Circuit; use gkr_graph::structs::{CircuitGraphBuilder, NodeOutputType, PredType}; use paste::paste; use simple_frontend::structs::CircuitBuilder; +use singer_utils::uint::constants::AddSubConstants; use singer_utils::{ chip_handler::{OAMOperations, ROMOperations}, chips::{IntoEnumIterator, SingerChipBuilder}, constants::OpcodeType, register_witness, structs::{ChipChallenges, InstOutChipType, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::UIntAddSub, }; use std::{mem, sync::Arc}; @@ -110,8 +110,8 @@ register_witness!( byte => 1 }, phase0 { - old_memory_ts => TSUInt::N_OPRAND_CELLS, - offset_add => UIntAddSub::::N_WITNESS_CELLS + old_memory_ts => TSUInt::N_OPERAND_CELLS, + offset_add => AddSubConstants::::N_WITNESS_CELLS } ); @@ -128,8 +128,8 @@ impl Instruction for ReturnInstruction { let (phase0_wire_id, phase0) = circuit_builder.create_witness_in(Self::phase0_size()); // From predesessor instruction - let (memory_ts_id, memory_ts) = circuit_builder.create_witness_in(TSUInt::N_OPRAND_CELLS); - let (offset_id, offset) = circuit_builder.create_witness_in(StackUInt::N_OPRAND_CELLS); + let (memory_ts_id, memory_ts) = circuit_builder.create_witness_in(TSUInt::N_OPERAND_CELLS); + let (offset_id, offset) = circuit_builder.create_witness_in(StackUInt::N_OPERAND_CELLS); let mut rom_handler = ROMHandler::new(&challenges); let mut ram_handler = RAMHandler::new(&challenges); @@ -138,7 +138,7 @@ impl Instruction for ReturnInstruction { let delta = circuit_builder.create_counter_in(0)[0]; let offset = StackUInt::try_from(offset.as_slice())?; let offset_add_delta = &phase0[Self::phase0_offset_add()]; - let offset_plus_delta = UIntAddSub::::add_small( + let offset_plus_delta = StackUInt::add_cell( &mut circuit_builder, &mut rom_handler, &offset, @@ -160,7 +160,7 @@ impl Instruction for ReturnInstruction { // To successor instruction let (next_memory_ts_id, next_memory_ts) = - circuit_builder.create_witness_out(TSUInt::N_OPRAND_CELLS); + circuit_builder.create_witness_out(TSUInt::N_OPERAND_CELLS); add_assign_each_cell(&mut circuit_builder, &next_memory_ts, memory_ts.values()); let (ram_load_id, ram_store_id) = ram_handler.finalize(&mut circuit_builder); diff --git a/singer-utils/src/chip_handler.rs b/singer-utils/src/chip_handler.rs index c9268a116..b8cb096ad 100644 --- a/singer-utils/src/chip_handler.rs +++ b/singer-utils/src/chip_handler.rs @@ -1,11 +1,7 @@ use ff_ext::ExtensionField; use simple_frontend::structs::{CellId, ChallengeId, CircuitBuilder, MixedCell, WitnessId}; -use crate::{ - constants::OpcodeType, - error::UtilError, - structs::{ChipChallenges, UInt}, -}; +use crate::{constants::OpcodeType, error::UtilError, structs::ChipChallenges, uint::UInt}; pub mod bytecode; pub mod calldata; diff --git a/singer-utils/src/chip_handler/range.rs b/singer-utils/src/chip_handler/range.rs index fdc6081b8..a1a68e0ab 100644 --- a/singer-utils/src/chip_handler/range.rs +++ b/singer-utils/src/chip_handler/range.rs @@ -5,8 +5,8 @@ use simple_frontend::structs::{CellId, CircuitBuilder, MixedCell}; use crate::{ constants::{RANGE_CHIP_BIT_WIDTH, STACK_TOP_BIT_WIDTH}, error::UtilError, - structs::{PCUInt, ROMHandler, TSUInt, UInt}, - uint::UIntAddSub, + structs::{PCUInt, ROMHandler, TSUInt}, + uint::UInt, }; use super::{ROMOperations, RangeChipOperations}; @@ -40,7 +40,7 @@ impl RangeChipOperations for ROMHandler { Ok((*uint).clone()) } else if let Some(range_values) = range_value_witness { let range_value = UInt::::from_range_values(circuit_builder, range_values)?; - uint.assert_eq(circuit_builder, &range_value); + UInt::::assert_eq(circuit_builder, uint, &range_value)?; let b: usize = M.min(C); let chunk_size = (b + RANGE_CHIP_BIT_WIDTH - 1) / RANGE_CHIP_BIT_WIDTH; for chunk in range_values.chunks(chunk_size) { @@ -98,8 +98,8 @@ impl ROMHandler { constant: i64, witness: &[CellId], ) -> Result { - let carry = UIntAddSub::::extract_unsafe_carry(witness); - UIntAddSub::::add_const_unsafe( + let carry = PCUInt::extract_unsafe_carry_add(witness); + PCUInt::add_const_unsafe( circuit_builder, &pc, i64_to_base_field::(constant), @@ -114,8 +114,7 @@ impl ROMHandler { constant: i64, witness: &[CellId], ) -> Result { - //let carry = UIntAddSub::::extract_unsafe_carry(witness); - UIntAddSub::::add_const( + TSUInt::add_const( circuit_builder, self, &ts, diff --git a/singer-utils/src/chips/bytecode.rs b/singer-utils/src/chips/bytecode.rs index 20ef83126..314b3afa1 100644 --- a/singer-utils/src/chips/bytecode.rs +++ b/singer-utils/src/chips/bytecode.rs @@ -17,7 +17,7 @@ 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 (_, pc_cells) = circuit_builder.create_witness_in(PCUInt::N_OPERAND_CELLS); let (_, bytecode_cells) = circuit_builder.create_witness_in(1); let mut rom_handler = ROMHandler::new(&challenges); @@ -51,16 +51,8 @@ pub(crate) fn construct_bytecode_table_and_witness( let wits_in = vec![ LayerWitness { instances: PCUInt::counter_vector::(bytecode.len().next_power_of_two()) - .into_iter() - .map(|x| vec![x]) - .collect_vec(), - }, - LayerWitness { - instances: bytecode - .iter() - .map(|x| vec![E::BaseField::from(*x as u64)]) - .collect_vec(), - }, + }; + 2 ]; let table_node_id = builder.add_node_with_witness( diff --git a/singer-utils/src/chips/calldata.rs b/singer-utils/src/chips/calldata.rs index cc2f8f187..2968ea0f2 100644 --- a/singer-utils/src/chips/calldata.rs +++ b/singer-utils/src/chips/calldata.rs @@ -16,8 +16,8 @@ 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 (_, id_cells) = circuit_builder.create_witness_in(UInt64::N_OPERAND_CELLS); + let (_, calldata_cells) = circuit_builder.create_witness_in(StackUInt::N_OPERAND_CELLS); let mut rom_handler = ROMHandler::new(&challenges); rom_handler.calldataload(&mut circuit_builder, &id_cells, &calldata_cells); let _ = rom_handler.finalize(&mut circuit_builder); @@ -58,9 +58,9 @@ pub(crate) fn construct_calldata_table_and_witness( }, LayerWitness { instances: (0..calldata.len()) - .step_by(StackUInt::N_OPRAND_CELLS) + .step_by(StackUInt::N_OPERAND_CELLS) .map(|i| { - calldata[i..(i + StackUInt::N_OPRAND_CELLS).min(calldata.len())] + calldata[i..(i + StackUInt::N_OPERAND_CELLS).min(calldata.len())] .iter() .cloned() .rev() diff --git a/singer-utils/src/constants.rs b/singer-utils/src/constants.rs index c6d78b49e..7427f4a62 100644 --- a/singer-utils/src/constants.rs +++ b/singer-utils/src/constants.rs @@ -2,6 +2,7 @@ use strum_macros::EnumIter; pub const STACK_TOP_BIT_WIDTH: usize = 10; +pub const BYTE_BIT_WIDTH: usize = 8; pub const RANGE_CHIP_BIT_WIDTH: usize = 16; pub const VALUE_BIT_WIDTH: usize = 32; pub const EVM_STACK_BIT_WIDTH: usize = 256; diff --git a/singer-utils/src/error.rs b/singer-utils/src/error.rs index 91832a141..2c17c10b0 100644 --- a/singer-utils/src/error.rs +++ b/singer-utils/src/error.rs @@ -4,7 +4,8 @@ use gkr_graph::error::GKRGraphError; pub enum UtilError { ChipError, ChipHandlerError, - UIntError, + // TODO: consider splitting this into smaller errors + UIntError(String), GKRGraphError(GKRGraphError), } diff --git a/singer-utils/src/structs.rs b/singer-utils/src/structs.rs index e21b432bf..6730710f4 100644 --- a/singer-utils/src/structs.rs +++ b/singer-utils/src/structs.rs @@ -1,8 +1,10 @@ use ff_ext::ExtensionField; use simple_frontend::structs::{CellId, ChallengeId, ExtCellId}; use strum_macros::EnumIter; +use uint::UInt; use crate::constants::{EVM_STACK_BIT_WIDTH, VALUE_BIT_WIDTH}; +use crate::uint; #[derive(Clone, Debug, Copy, EnumIter)] pub enum RAMType { @@ -46,13 +48,7 @@ pub struct ROMHandler { pub(crate) challenge: ChipChallenges, } -/// Unsigned integer with `M` bits. C denotes the cell bit width. -#[derive(Clone, Debug)] -pub struct UInt { - pub(crate) values: Vec, -} - pub type UInt64 = UInt<64, VALUE_BIT_WIDTH>; pub type PCUInt = UInt64; -pub type TSUInt = UInt<56, 56>; +pub type TSUInt = UInt<48, 48>; pub type StackUInt = UInt<{ EVM_STACK_BIT_WIDTH as usize }, { VALUE_BIT_WIDTH as usize }>; diff --git a/singer-utils/src/uint.rs b/singer-utils/src/uint.rs index 09dade8f2..1bf0190f0 100644 --- a/singer-utils/src/uint.rs +++ b/singer-utils/src/uint.rs @@ -1,325 +1,7 @@ -use ff::Field; -use ff_ext::ExtensionField; -use goldilocks::SmallField; -use itertools::Itertools; -use simple_frontend::structs::{CellId, CircuitBuilder}; -use std::marker::PhantomData; -use sumcheck::util::ceil_log2; - -use crate::{constants::RANGE_CHIP_BIT_WIDTH, error::UtilError, structs::UInt}; - -pub mod add_sub; -pub mod cmp; - -impl TryFrom<&[usize]> for UInt { - type Error = UtilError; - fn try_from(values: &[usize]) -> Result { - if values.len() != Self::N_OPRAND_CELLS { - panic!( - "expected = {}, got = {}", - Self::N_OPRAND_CELLS, - values.len() - ); - } - Ok(Self { - values: values.to_vec(), - }) - } -} - -impl TryFrom> for UInt { - type Error = UtilError; - fn try_from(values: Vec) -> Result { - let values = values.as_slice().try_into()?; - Ok(values) - } -} - -impl UInt { - pub const N_OPRAND_CELLS: usize = (M + C - 1) / C; - - 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); - pub const N_RANGE_CHECK_NO_OVERFLOW_CELLS: usize = - (Self::N_OPRAND_CELLS - 1) * ((C + RANGE_CHIP_BIT_WIDTH - 1) / RANGE_CHIP_BIT_WIDTH); - - pub fn values(&self) -> &[CellId] { - &self.values - } - - pub fn from_range_values( - circuit_builder: &mut CircuitBuilder, - range_values: &[CellId], - ) -> Result { - let mut values = if C <= M { - convert_decomp(circuit_builder, range_values, RANGE_CHIP_BIT_WIDTH, C, true) - } else { - convert_decomp(circuit_builder, range_values, RANGE_CHIP_BIT_WIDTH, M, true) - }; - while values.len() < Self::N_OPRAND_CELLS { - values.push(circuit_builder.create_cell()); - } - Self::try_from(values) - } - - pub fn from_bytes_big_endien( - circuit_builder: &mut CircuitBuilder, - bytes: &[CellId], - ) -> Result { - let mut values = if C <= M { - convert_decomp(circuit_builder, bytes, 8, C, true) - } else { - 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( - &self, - circuit_builder: &mut CircuitBuilder, - other: &Self, - ) { - for i in 0..self.values.len() { - let diff = circuit_builder.create_cell(); - circuit_builder.add(diff, self.values[i], E::BaseField::ONE); - circuit_builder.add(diff, other.values[i], -E::BaseField::ONE); - circuit_builder.assert_const(diff, 0); - } - } - - pub fn assert_eq_range_values( - &self, - circuit_builder: &mut CircuitBuilder, - range_values: &[CellId], - ) { - let values = if C <= M { - convert_decomp(circuit_builder, range_values, RANGE_CHIP_BIT_WIDTH, C, true) - } else { - convert_decomp(circuit_builder, range_values, RANGE_CHIP_BIT_WIDTH, M, true) - }; - let length = self.values.len().min(values.len()); - for i in 0..length { - let diff = circuit_builder.create_cell(); - circuit_builder.add(diff, self.values[i], E::BaseField::ONE); - circuit_builder.add(diff, values[i], -E::BaseField::ONE); - circuit_builder.assert_const(diff, 0); - } - for i in length..values.len() { - circuit_builder.assert_const(values[i], 0); - } - for i in length..self.values.len() { - circuit_builder.assert_const(self.values[i], 0); - } - } - - /// Generate (0, 1, ..., size) - pub fn counter_vector(size: usize) -> Vec { - let num_vars = ceil_log2(size); - let tensor = |a: &[F], b: Vec| { - let mut res = vec![F::ZERO; a.len() * b.len()]; - for i in 0..b.len() { - for j in 0..a.len() { - res[i * a.len() + j] = b[i] * a[j]; - } - } - res - }; - let counter = (0..(1 << C)).map(|x| F::from(x as u64)).collect_vec(); - let (di, mo) = (num_vars / C, num_vars % C); - let mut res = (0..(1 << mo)).map(|x| F::from(x as u64)).collect_vec(); - for _ in 0..di { - res = tensor(&counter, res); - } - res - } -} - -pub struct UIntAddSub { - _phantom: PhantomData, -} -pub struct UIntCmp { - _phantom: PhantomData, -} - -/// Big-endian bytes to little-endien field values. We don't require -/// `BIG_BIT_WIDTH` % `SMALL_BIT_WIDTH` == 0 because we assume `small_values` -/// can be splitted into chunks with size ceil(BIG_BIT_WIDTH / SMALL_BIT_WIDTH). -/// Each chunk is converted to a value with BIG_BIT_WIDTH bits. -fn convert_decomp( - circuit_builder: &mut CircuitBuilder, - small_values: &[CellId], - small_bit_width: usize, - big_bit_width: usize, - is_little_endian: bool, -) -> Vec { - let small_values = if is_little_endian { - small_values.to_vec() - } else { - small_values.iter().rev().map(|x: &usize| *x).collect_vec() - }; - let chunk_size = (big_bit_width + small_bit_width - 1) / small_bit_width; - let small_len = small_values.len(); - let values = (0..small_len) - .step_by(chunk_size) - .map(|j| { - let tmp = circuit_builder.create_cell(); - for k in j..(j + chunk_size).min(small_len) { - let k = k as usize; - circuit_builder.add( - tmp, - small_values[k], - E::BaseField::from((1 as u64) << (k - j) * small_bit_width), - ); - } - tmp - }) - .collect_vec(); - values -} - -#[cfg(test)] -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() { - // test case 1 - let mut circuit_builder = CircuitBuilder::::new(); - let big_bit_width = 3; - let small_bit_width = 2; - let (small_values_wire_in_id, small_values) = circuit_builder.create_witness_in(31); - let values = convert_decomp( - &mut circuit_builder, - &small_values, - small_bit_width, - big_bit_width, - true, - ); - assert_eq!(values.len(), 16); - 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(1u64), Goldilocks::from(1u64)]; - wires_in[small_values_wire_in_id as usize].extend(vec![Goldilocks::from(0u64); 29]); - 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][0], Goldilocks::from(5u64)); - 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), - ] - ); - } -} +mod arithmetic; +mod cmp; +pub mod constants; +mod uint; +pub use uint::UInt; +pub mod util; +mod witness_extractors; diff --git a/singer-utils/src/uint/add_sub.rs b/singer-utils/src/uint/add_sub.rs deleted file mode 100644 index 48e02ce3f..000000000 --- a/singer-utils/src/uint/add_sub.rs +++ /dev/null @@ -1,341 +0,0 @@ -use ff::Field; -use ff_ext::ExtensionField; -use simple_frontend::structs::{CellId, CircuitBuilder}; - -use crate::{chip_handler::RangeChipOperations, error::UtilError, structs::UInt}; - -use super::UIntAddSub; - -impl UIntAddSub> { - pub const N_NO_OVERFLOW_WITNESS_CELLS: usize = - UInt::::N_RANGE_CHECK_CELLS + UInt::::N_CARRY_NO_OVERFLOW_CELLS; - pub const N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS: usize = UInt::::N_CARRY_NO_OVERFLOW_CELLS; - - pub const N_WITNESS_UNSAFE_CELLS: usize = UInt::::N_CARRY_CELLS; - pub const N_WITNESS_CELLS: usize = - UInt::::N_RANGE_CHECK_CELLS + UInt::::N_CARRY_CELLS; - - pub fn extract_range_values(witness: &[CellId]) -> &[CellId] { - &witness[..UInt::::N_RANGE_CHECK_CELLS] - } - - pub fn extract_range_values_no_overflow(witness: &[CellId]) -> &[CellId] { - &witness[..UInt::::N_RANGE_CHECK_NO_OVERFLOW_CELLS] - } - - pub fn extract_carry_no_overflow(witness: &[CellId]) -> &[CellId] { - &witness[UInt::::N_RANGE_CHECK_NO_OVERFLOW_CELLS..] - } - - pub fn extract_carry(witness: &[CellId]) -> &[CellId] { - &witness[UInt::::N_RANGE_CHECK_CELLS..] - } - - pub fn extract_unsafe_carry(witness: &[CellId]) -> &[CellId] { - witness - } - - /// Little-endian addition. Assume users to check the correct range of the - /// result by themselves. - pub fn add_unsafe( - circuit_builder: &mut CircuitBuilder, - addend_0: &UInt, - addend_1: &UInt, - carry: &[CellId], - ) -> Result, UtilError> { - let result: UInt = circuit_builder - .create_cells(UInt::::N_OPRAND_CELLS) - .try_into()?; - for i in 0..UInt::::N_OPRAND_CELLS { - let (a, b, result) = (addend_0.values[i], addend_1.values[i], result.values[i]); - // result = addend_0 + addend_1 + last_carry - carry * (1 << VALUE_BIT_WIDTH) - circuit_builder.add(result, a, Ext::BaseField::ONE); - circuit_builder.add(result, b, Ext::BaseField::ONE); - // It is equivalent to pad carry with 0s. - if i < carry.len() { - circuit_builder.add(result, carry[i], -Ext::BaseField::from(1 << C)); - } - if i > 0 && i - 1 < carry.len() { - circuit_builder.add(result, carry[i - 1], Ext::BaseField::ONE); - } - } - Ok(result) - } - - /// Little-endian addition. - pub fn add>( - circuit_builder: &mut CircuitBuilder, - range_chip_handler: &mut H, - addend_0: &UInt, - addend_1: &UInt, - witness: &[CellId], - ) -> Result, UtilError> { - let carry = Self::extract_carry(witness); - let range_values = Self::extract_range_values(witness); - let computed_result = Self::add_unsafe(circuit_builder, addend_0, addend_1, carry)?; - range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) - } - - /// Little-endian addition with a constant. Assume users to check the - /// correct range of the result by themselves. - pub fn add_const_unsafe( - circuit_builder: &mut CircuitBuilder, - addend_0: &UInt, - constant: Ext::BaseField, - carry: &[CellId], - ) -> Result, UtilError> { - let result: UInt = circuit_builder - .create_cells(UInt::::N_OPRAND_CELLS) - .try_into()?; - for i in 0..result.values.len() { - let (a, result) = (addend_0.values[i], result.values[i]); - // result = addend_0 + addend_1 + last_carry - carry * (256 << BYTE_WIDTH) - circuit_builder.add(result, a, Ext::BaseField::ONE); - circuit_builder.add_const(result, constant); - // It is equivalent to pad carry with 0s. - if i < carry.len() { - circuit_builder.add(result, carry[i], -Ext::BaseField::from(1 << C)); - } - if i > 0 && i - 1 < carry.len() { - circuit_builder.add(result, carry[i - 1], Ext::BaseField::ONE); - } - } - Ok(result) - } - - /// Little-endian addition with a constant. - pub fn add_const>( - circuit_builder: &mut CircuitBuilder, - range_chip_handler: &mut H, - addend_0: &UInt, - constant: Ext::BaseField, - witness: &[CellId], - ) -> Result, UtilError> { - let carry = Self::extract_carry(witness); - let range_values = Self::extract_range_values(witness); - let computed_result = Self::add_const_unsafe(circuit_builder, addend_0, constant, carry)?; - range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) - } - - /// Little-endian addition with a constant, guaranteed no overflow. - pub fn add_const_no_overflow>( - circuit_builder: &mut CircuitBuilder, - range_chip_handler: &mut H, - addend_0: &UInt, - constant: Ext::BaseField, - witness: &[CellId], - ) -> Result, UtilError> { - let carry = Self::extract_carry_no_overflow(witness); - let range_values = Self::extract_range_values_no_overflow(witness); - let computed_result = Self::add_const_unsafe(circuit_builder, addend_0, constant, carry)?; - range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) - } - - /// Little-endian addition with a small number. Notice that the user should - /// guarantee addend_1 < 1 << C. - pub fn add_small_unsafe( - circuit_builder: &mut CircuitBuilder, - addend_0: &UInt, - addend_1: CellId, - carry: &[CellId], - ) -> Result, UtilError> { - let result: UInt = circuit_builder - .create_cells(UInt::::N_OPRAND_CELLS) - .try_into()?; - for i in 0..result.values.len() { - let (a, result) = (addend_0.values[i], result.values[i]); - // result = addend_0 + addend_1 + last_carry - carry * (256 << BYTE_WIDTH) - circuit_builder.add(result, a, E::BaseField::ONE); - circuit_builder.add(result, addend_1, E::BaseField::ONE); - // It is equivalent to pad carry with 0s. - if i < carry.len() { - circuit_builder.add(result, carry[i], -E::BaseField::from(1 << C)); - } - if i > 0 && i - 1 < carry.len() { - circuit_builder.add(result, carry[i - 1], E::BaseField::ONE); - } - } - Ok(result) - } - - /// Little-endian addition with a small number. Notice that the user should - /// guarantee addend_1 < 1 << C. - pub fn add_small>( - circuit_builder: &mut CircuitBuilder, - range_chip_handler: &mut H, - addend_0: &UInt, - addend_1: CellId, - witness: &[CellId], - ) -> Result, UtilError> { - let carry = Self::extract_carry(witness); - let range_values = Self::extract_range_values(witness); - let computed_result = Self::add_small_unsafe(circuit_builder, addend_0, addend_1, carry)?; - range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) - } - - /// Little-endian addition with a small number, guaranteed no overflow. - /// Notice that the user should guarantee addend_1 < 1 << C. - pub fn add_small_no_overflow>( - circuit_builder: &mut CircuitBuilder, - range_chip_handler: &mut H, - addend_0: &UInt, - addend_1: CellId, - witness: &[CellId], - ) -> Result, UtilError> { - let carry = Self::extract_carry_no_overflow(witness); - let range_values = Self::extract_range_values_no_overflow(witness); - let computed_result = Self::add_small_unsafe(circuit_builder, addend_0, addend_1, carry)?; - range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) - } - - /// Little-endian subtraction. Assume users to check the correct range of - /// the result by themselves. - pub fn sub_unsafe( - circuit_builder: &mut CircuitBuilder, - minuend: &UInt, - subtrahend: &UInt, - borrow: &[CellId], - ) -> Result, UtilError> { - let result: UInt = circuit_builder - .create_cells(UInt::::N_OPRAND_CELLS) - .try_into()?; - // result = minuend - subtrahend + borrow * (1 << BIT_WIDTH) - last_borrow - for i in 0..result.values.len() { - let (minuend, subtrahend, result) = - (minuend.values[i], subtrahend.values[i], result.values[i]); - circuit_builder.add(result, minuend, E::BaseField::ONE); - circuit_builder.add(result, subtrahend, -E::BaseField::ONE); - if i < borrow.len() { - circuit_builder.add(result, borrow[i], E::BaseField::from(1 << C)); - } - if i > 0 && i - 1 < borrow.len() { - circuit_builder.add(result, borrow[i - 1], -E::BaseField::ONE); - } - } - Ok(result) - } -} - -#[cfg(test)] -mod test { - use super::{UInt, UIntAddSub}; - use gkr::structs::{Circuit, CircuitWitness}; - use goldilocks::{Goldilocks, GoldilocksExt2}; - use simple_frontend::structs::CircuitBuilder; - - #[test] - fn test_add_unsafe() { - type Uint256_8 = UInt<256, 8>; - assert_eq!(Uint256_8::N_OPRAND_CELLS, 32); - let mut circuit_builder: CircuitBuilder = CircuitBuilder::new(); - - // configure circuit with cells for addend_0, addend_1 and carry as wire_in - let (addend_0_wire_in_id, addend_0_wire_in_cells) = - circuit_builder.create_witness_in(Uint256_8::N_OPRAND_CELLS); - let (addend_1_wire_in_id, addend_1_wire_in_cells) = - circuit_builder.create_witness_in(Uint256_8::N_OPRAND_CELLS); - let (carry_wire_in_id, carry_wire_in_cells) = - circuit_builder.create_witness_in(Uint256_8::N_OPRAND_CELLS); - let addend_0 = Uint256_8::try_from(addend_0_wire_in_cells); - let addend_1 = Uint256_8::try_from(addend_1_wire_in_cells); - let result = UIntAddSub::::add_unsafe( - &mut circuit_builder, - &addend_0.unwrap(), - &addend_1.unwrap(), - &carry_wire_in_cells, - ); - assert_eq!(result.unwrap().values(), (96..128).collect::>()); - circuit_builder.configure(); - let circuit = Circuit::new(&circuit_builder); - //println!("add unsafe circuit {:?}", circuit); - - // generate witnesses for addend_0, addend_1 and carry - // must pad each witness to the size of N_OPERAND_CELLS - let n_witness_in = circuit.n_witness_in; - let mut wires_in = vec![vec![]; n_witness_in]; - wires_in[addend_0_wire_in_id as usize] = - vec![Goldilocks::from(255u64), Goldilocks::from(255u64)]; - wires_in[addend_0_wire_in_id as usize] - .extend(vec![Goldilocks::from(0u64); Uint256_8::N_OPRAND_CELLS - 2]); - wires_in[addend_1_wire_in_id as usize] = - vec![Goldilocks::from(255u64), Goldilocks::from(254u64)]; - wires_in[addend_1_wire_in_id as usize] - .extend(vec![Goldilocks::from(0u64); Uint256_8::N_OPRAND_CELLS - 2]); - wires_in[carry_wire_in_id as usize] = vec![Goldilocks::from(1u64), Goldilocks::from(1u64)]; - wires_in[carry_wire_in_id as usize] - .extend(vec![Goldilocks::from(0u64); Uint256_8::N_OPRAND_CELLS - 2]); - 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 - }; - //println!("{:?}", circuit_witness); - circuit_witness.check_correctness(&circuit); - - // check the result - let result_values = circuit_witness.output_layer_witness_ref(); - //println!("{:?}", result_values[0]); - assert_eq!(result_values.instances[0][0], Goldilocks::from(254u64)); - assert_eq!(result_values.instances[0][1], Goldilocks::from(254u64)); - assert_eq!(result_values.instances[0][2], Goldilocks::from(1u64)); - } - - #[test] - fn test_sub_unsafe() { - type Uint256_8 = UInt<256, 8>; - assert_eq!(Uint256_8::N_OPRAND_CELLS, 32); - let mut circuit_builder = CircuitBuilder::::new(); - - // configure circuit with cells for minuend, subtrend and borrow as wire_in - let (minuend_wire_in_id, minuend_wire_in_cells) = - circuit_builder.create_witness_in(Uint256_8::N_OPRAND_CELLS); - let (subtrend_wire_in_id, subtrend_wire_in_cells) = - circuit_builder.create_witness_in(Uint256_8::N_OPRAND_CELLS); - let (borrow_wire_in_id, borrow_wire_in_cells) = - circuit_builder.create_witness_in(Uint256_8::N_OPRAND_CELLS); - let minuend = Uint256_8::try_from(minuend_wire_in_cells); - let subtrend = Uint256_8::try_from(subtrend_wire_in_cells); - let result = UIntAddSub::::sub_unsafe( - &mut circuit_builder, - &minuend.unwrap(), - &subtrend.unwrap(), - &borrow_wire_in_cells, - ); - assert_eq!(result.unwrap().values(), (96..128).collect::>()); - circuit_builder.configure(); - let circuit = Circuit::new(&circuit_builder); - //println!("add unsafe circuit {:?}", circuit); - - // generate witnesses for addend_0, addend_1 and carry - // must pad each witness to the size of N_OPERAND_CELLS - let n_witness_in = circuit.n_witness_in; - let mut wires_in = vec![vec![]; n_witness_in]; - wires_in[minuend_wire_in_id as usize] = - vec![Goldilocks::from(1u64), Goldilocks::from(1u64)]; - wires_in[minuend_wire_in_id as usize] - .extend(vec![Goldilocks::from(0u64); Uint256_8::N_OPRAND_CELLS - 2]); - wires_in[subtrend_wire_in_id as usize] = - vec![Goldilocks::from(255u64), Goldilocks::from(254u64)]; - wires_in[subtrend_wire_in_id as usize] - .extend(vec![Goldilocks::from(0u64); Uint256_8::N_OPRAND_CELLS - 2]); - wires_in[borrow_wire_in_id as usize] = vec![Goldilocks::from(1u64), Goldilocks::from(1u64)]; - wires_in[borrow_wire_in_id as usize] - .extend(vec![Goldilocks::from(0u64); Uint256_8::N_OPRAND_CELLS - 2]); - 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 - }; - //println!("{:?}", circuit_witness); - circuit_witness.check_correctness(&circuit); - - // check the result - let result_values = circuit_witness.output_layer_witness_ref(); - //println!("{:?}", result_values[0]); - assert_eq!(result_values.instances[0][0], Goldilocks::from(2u64)); - assert_eq!(result_values.instances[0][1], Goldilocks::from(2u64)); - assert_eq!(result_values.instances[0][2], -Goldilocks::from(1u64)); - } -} diff --git a/singer-utils/src/uint/arithmetic.rs b/singer-utils/src/uint/arithmetic.rs new file mode 100644 index 000000000..497b5521a --- /dev/null +++ b/singer-utils/src/uint/arithmetic.rs @@ -0,0 +1,609 @@ +use crate::{chip_handler::RangeChipOperations, error::UtilError, uint::uint::UInt}; +use ff::Field; +use ff_ext::ExtensionField; +use simple_frontend::structs::{Cell, CellId, CircuitBuilder}; + +impl UInt { + /// Little-endian addition. + /// Assumes users will check the correct range of the result themselves. + // Addition of A + B with limbs [a, b, c] and [d, e, f] respectively + // + // cell_modulo = 2^C + // addend_0 - a b c + // addend_1 - d e f + // -------------------------------------------------- + // result - (a + d) % 2^C (b + e) % 2^C (c + f) % 2^C + // carry - (a + d) // 2^C (b + e) // 2^C (c + f) % 2^C + // + // every limb in addend_0 and addend_1 exists in the range [0, ..., 2^C - 1] + // after summing two limb values, the result exists in [0, ..., 2^(C+1) - 2] + // the carry value is either 0 or 1, + // it cannot be >= 2 as that will require result value >= 2^(C+1) + // + // assuming result range check, there is a unique carry vector that makes all + // constraint pass. + // if a + b > max_cell_value then carry must be set to 1 (if not range check fails) + // if a + b <= max_cell_value then carry must be set to 0 (if not range check fails) + // + // NOTE: this function doesn't perform the required range check! + pub fn add_unsafe( + circuit_builder: &mut CircuitBuilder, + addend_0: &UInt, + addend_1: &UInt, + carry: &[CellId], + ) -> Result, UtilError> { + let result: UInt = circuit_builder + .create_cells(Self::N_OPERAND_CELLS) + .try_into()?; + + for i in 0..Self::N_OPERAND_CELLS { + let (a, b, result) = (addend_0.values[i], addend_1.values[i], result.values[i]); + + // result = a + b - overflow_carry + last_carry + circuit_builder.add(result, a, E::BaseField::ONE); + circuit_builder.add(result, b, E::BaseField::ONE); + Self::handle_carry(result, circuit_builder, i, carry); + } + + Ok(result) + } + + /// Little-endian addition. + pub fn add>( + circuit_builder: &mut CircuitBuilder, + range_chip_handler: &mut H, + addend_0: &UInt, + addend_1: &UInt, + witness: &[CellId], + ) -> Result, UtilError> { + let carry = Self::extract_carry_add(witness); + let range_values = Self::extract_range_values(witness); + let computed_result = Self::add_unsafe(circuit_builder, addend_0, addend_1, carry)?; + range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) + } + + /// Add a constant value to a `UInt` instance + /// Assumes users will check the correct range of the result themselves. + pub fn add_const_unsafe( + circuit_builder: &mut CircuitBuilder, + addend_0: &UInt, + constant: E::BaseField, + carry: &[CellId], + ) -> Result, UtilError> { + let result: UInt = circuit_builder + .create_cells(Self::N_OPERAND_CELLS) + .try_into()?; + + // add constant to the first limb + circuit_builder.add_const(result.values[0], constant); + + // cascade carry + for i in 0..Self::N_OPERAND_CELLS { + let (a, result) = (addend_0.values[i], result.values[i]); + + circuit_builder.add(result, a, E::BaseField::ONE); + Self::handle_carry(result, circuit_builder, i, carry); + } + + Ok(result) + } + + /// Add a constant value to a `UInt` instance + pub fn add_const>( + circuit_builder: &mut CircuitBuilder, + range_chip_handler: &mut H, + addend_0: &UInt, + constant: E::BaseField, + witness: &[CellId], + ) -> Result, UtilError> { + let carry = Self::extract_carry_add(witness); + let range_values = Self::extract_range_values(witness); + let computed_result = Self::add_const_unsafe(circuit_builder, addend_0, constant, carry)?; + range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) + } + + /// Add a constant value to a `UInt` instance + /// Assumes that addition leads to no overflow. + pub fn add_const_no_overflow>( + circuit_builder: &mut CircuitBuilder, + range_chip_handler: &mut H, + addend_0: &UInt, + constant: E::BaseField, + witness: &[CellId], + ) -> Result, UtilError> { + let carry = Self::extract_carry_no_overflow_add(witness); + let range_values = Self::extract_range_values_no_overflow(witness); + let computed_result = Self::add_const_unsafe(circuit_builder, addend_0, constant, carry)?; + range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) + } + + /// Adds a single cell value to a `UInt` instance + /// Assumes users will check the correct range of the result and + pub fn add_cell_unsafe( + circuit_builder: &mut CircuitBuilder, + addend_0: &UInt, + addend_1: CellId, + carry: &[CellId], + ) -> Result, UtilError> { + let result: UInt = circuit_builder + .create_cells(Self::N_OPERAND_CELLS) + .try_into()?; + + // add small_value to the first limb + circuit_builder.add(result.values[0], addend_1, E::BaseField::ONE); + + // cascade carry + for i in 0..Self::N_OPERAND_CELLS { + let (a, result) = (addend_0.values[i], result.values[i]); + + circuit_builder.add(result, a, E::BaseField::ONE); + Self::handle_carry(result, circuit_builder, i, carry); + } + + Ok(result) + } + + /// Adds a single cell value to a `UInt` instance + pub fn add_cell>( + circuit_builder: &mut CircuitBuilder, + range_chip_handler: &mut H, + addend_0: &UInt, + addend_1: CellId, + witness: &[CellId], + ) -> Result, UtilError> { + let carry = Self::extract_carry_add(witness); + let range_values = Self::extract_range_values(witness); + let computed_result = Self::add_cell_unsafe(circuit_builder, addend_0, addend_1, carry)?; + range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) + } + + /// Adds a single cell value to a `UInt` instance + /// Assumes that addition lead to no overflow. + pub fn add_cell_no_overflow>( + circuit_builder: &mut CircuitBuilder, + range_chip_handler: &mut H, + addend_0: &UInt, + addend_1: CellId, + witness: &[CellId], + ) -> Result, UtilError> { + let carry = Self::extract_carry_no_overflow_add(witness); + let range_values = Self::extract_range_values_no_overflow(witness); + let computed_result = Self::add_cell_unsafe(circuit_builder, addend_0, addend_1, carry)?; + range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) + } + + /// Little endian subtraction + /// Assumes users will check the correct range of the result themselves. + pub fn sub_unsafe( + circuit_builder: &mut CircuitBuilder, + minuend: &UInt, + subtrahend: &UInt, + borrow: &[CellId], + ) -> Result, UtilError> { + let result: UInt = circuit_builder + .create_cells(Self::N_OPERAND_CELLS) + .try_into()?; + + for i in 0..Self::N_OPERAND_CELLS { + let (minuend, subtrahend, result) = + (minuend.values[i], subtrahend.values[i], result.values[i]); + + circuit_builder.add(result, minuend, E::BaseField::ONE); + circuit_builder.add(result, subtrahend, -E::BaseField::ONE); + + Self::handle_borrow(result, circuit_builder, i, borrow); + } + + Ok(result) + } + + /// Little endian subtraction + pub fn sub>( + circuit_builder: &mut CircuitBuilder, + range_chip_handler: &mut H, + minuend: &UInt, + subtrahend: &UInt, + witness: &[CellId], + ) -> Result, UtilError> { + let borrow = Self::extract_borrow_sub(witness); + let range_values = Self::extract_range_values(witness); + let computed_result = Self::sub_unsafe(circuit_builder, minuend, subtrahend, borrow)?; + range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) + } + + /// Modify addition result based on carry instructions + fn handle_carry( + result_cell_id: CellId, + circuit_builder: &mut CircuitBuilder, + limb_index: usize, + carry: &[CellId], + ) { + // overflow carry + // represents the portion of the result that should move to the next operation + // inorder to keep the value <= C bits + // carry[i] = (addend_0[i] + addend_1[i]) % 2^C + + // last carry + // represents the carry that was passed from the previous operation + // this carry should be added to the current result + // carry[i - 1] = (addend_0[i - 1] + addend_1[i - 1]) % 2^C + + if limb_index > carry.len() { + return; + } + + // handle overflow carry + // we need to subtract the carry value from the current result + if limb_index < carry.len() { + circuit_builder.add( + result_cell_id, + carry[limb_index], + -E::BaseField::from(1 << C), + ); + } + + // handle last operation carry + // we need to add this to the current result + if limb_index > 0 { + circuit_builder.add(result_cell_id, carry[limb_index - 1], E::BaseField::ONE); + } + } + + /// Modify subtraction result based on borrow instructions + fn handle_borrow( + result_cell_id: CellId, + circuit_builder: &mut CircuitBuilder, + limb_index: usize, + borrow: &[CellId], + ) { + // borrow + // represents the portion of the result that should move from the + // next operation to the current operation i.e. reduce the result + // of the operation to come + // this should be added to the current result + // = borrow[i] + + // last borrow + // represents the portion of the current result that was moved during + // the previous computation + // this should be removed from the current result + + if limb_index > borrow.len() { + return; + } + + // handle borrow + // we need to add borrow units of C to the result + if limb_index < borrow.len() { + circuit_builder.add( + result_cell_id, + borrow[limb_index], + E::BaseField::from(1 << C), + ); + } + + // handle last borrow + // we need to remove this from the current result + if limb_index > 0 { + circuit_builder.add(result_cell_id, borrow[limb_index - 1], -E::BaseField::ONE); + } + } +} + +#[cfg(test)] +mod tests { + use crate::uint::{constants::AddSubConstants, UInt}; + use gkr::structs::{Circuit, CircuitWitness}; + use goldilocks::{Goldilocks, GoldilocksExt2}; + use itertools::Itertools; + use simple_frontend::structs::CircuitBuilder; + + #[test] + fn test_add_unsafe() { + // UInt<20, 5> (4 limbs) + + // A (big-endian representation) + // 01001 | 10100 | 11010 | 11110 + + // B (big-endian representation) + // 00101 | 01010 | 10110 | 10000 + + // A + B + // big endian and represented as field elements + // 9 | 20 | 26 | 30 + // 5 | 10 | 22 | 16 + // result 14 | 31 | 17 | 14 + // carry 0 | 0 | 1 | 1 + + // build the circuit + type UInt20 = UInt<20, 5>; + let mut circuit_builder = CircuitBuilder::::new(); + + // input wires + // addend_0, addend_1, carry + let (addend_0_id, addend_0_cells) = + circuit_builder.create_witness_in(UInt20::N_OPERAND_CELLS); + let (addend_1_id, addend_1_cells) = + circuit_builder.create_witness_in(UInt20::N_OPERAND_CELLS); + let (carry_id, carry_cells) = + circuit_builder.create_witness_in(AddSubConstants::::N_CARRY_CELLS); + + let addend_0 = UInt20::try_from(addend_0_cells).expect("should build uint"); + let addend_1 = UInt20::try_from(addend_1_cells).expect("should build uint"); + + // update circuit builder with circuit instructions + let result = + UInt20::add_unsafe(&mut circuit_builder, &addend_0, &addend_1, &carry_cells).unwrap(); + circuit_builder.configure(); + let circuit = Circuit::new(&circuit_builder); + + // generate witness + // calling rev() to make things little endian representation + let addend_0_witness = vec![9, 20, 26, 30] + .into_iter() + .rev() + .map(|v| Goldilocks::from(v)) + .collect_vec(); + let addend_1_witness = vec![5, 10, 22, 16] + .into_iter() + .rev() + .map(|v| Goldilocks::from(v)) + .collect_vec(); + let carry_witness = vec![0, 0, 1, 1] + .into_iter() + .rev() + .map(|v| Goldilocks::from(v)) + .collect_vec(); + + let mut wires_in = vec![vec![]; circuit.n_witness_in]; + wires_in[addend_0_id as usize] = addend_0_witness; + wires_in[addend_1_id as usize] = addend_1_witness; + wires_in[carry_id as usize] = carry_witness; + + let circuit_witness = { + let challenges = vec![GoldilocksExt2::from(2)]; + let mut circuit_witness = CircuitWitness::new(&circuit, challenges); + circuit_witness.add_instance(&circuit, wires_in); + circuit_witness + }; + + circuit_witness.check_correctness(&circuit); + + // check the result correctness + let result_values = circuit_witness.output_layer_witness_ref().instances[0].to_vec(); + assert_eq!( + result_values, + [14, 17, 31, 14] + .into_iter() + .map(|v| Goldilocks::from(v)) + .collect_vec() + ); + } + + #[test] + fn test_add_constant_unsafe() { + // UInt<20, 5> (4 limbs) + + // A + constant + // A = 14 | 31 | 28 | 14 + // constant = 200 + // big endian and represented as field elements + // 14 | 31 | 28 | 14 + // | | | 200 + // result 15 | 0 | 2 | 22 + // carry 0 | 1 | 1 | 6 + + type UInt20 = UInt<20, 5>; + let mut circuit_builder = CircuitBuilder::::new(); + + // input wires + // addend_0, carry, constant + let (addend_0_id, addend_0_cells) = + circuit_builder.create_witness_in(UInt20::N_OPERAND_CELLS); + let (carry_id, carry_cells) = + circuit_builder.create_witness_in(AddSubConstants::::N_CARRY_CELLS); + + let addend_0 = UInt20::try_from(addend_0_cells).expect("should build uint"); + + // update circuit builder + let result = UInt20::add_const_unsafe( + &mut circuit_builder, + &addend_0, + Goldilocks::from(200), + &carry_cells, + ) + .unwrap(); + circuit_builder.configure(); + let circuit = Circuit::new(&circuit_builder); + + // generate witness + // calling rev() to make things little endian representation + let addend_0_witness = vec![14, 31, 28, 14] + .into_iter() + .rev() + .map(|v| Goldilocks::from(v)) + .collect_vec(); + let carry_witness = vec![0, 1, 1, 6] + .into_iter() + .rev() + .map(|v| Goldilocks::from(v)) + .collect_vec(); + + let mut wires_in = vec![vec![]; circuit.n_witness_in]; + wires_in[addend_0_id as usize] = addend_0_witness; + wires_in[carry_id as usize] = carry_witness; + + let circuit_witness = { + let challenges = vec![GoldilocksExt2::from(2)]; + let mut circuit_witness = CircuitWitness::new(&circuit, challenges); + circuit_witness.add_instance(&circuit, wires_in); + circuit_witness + }; + + circuit_witness.check_correctness(&circuit); + + // check the result correctness + let result_values = circuit_witness.output_layer_witness_ref().instances[0].to_vec(); + assert_eq!( + result_values, + [22, 2, 0, 15] + .into_iter() + .map(|v| Goldilocks::from(v)) + .collect_vec() + ); + } + + #[test] + fn test_add_small_unsafe() { + // UInt<20, 5> (4 limbs) + + // A + constant + // A = 14 | 31 | 28 | 14 + // small = 200 // TODO: fix this should be < 32 + // big endian and represented as field elements + // 14 | 31 | 28 | 14 + // | | | 200 + // result 15 | 0 | 2 | 22 + // carry 0 | 1 | 1 | 6 + + type UInt20 = UInt<20, 5>; + let mut circuit_builder = CircuitBuilder::::new(); + + // input wires + // addend_0, carry, constant + let (addend_0_id, addend_0_cells) = + circuit_builder.create_witness_in(UInt20::N_OPERAND_CELLS); + let (small_value_id, small_value_cell) = circuit_builder.create_witness_in(1); + let (carry_id, carry_cells) = + circuit_builder.create_witness_in(AddSubConstants::::N_CARRY_CELLS); + + let addend_0 = UInt20::try_from(addend_0_cells).expect("should build uint"); + + // update circuit builder + let result = UInt20::add_cell_unsafe( + &mut circuit_builder, + &addend_0, + small_value_cell[0], + &carry_cells, + ) + .unwrap(); + circuit_builder.configure(); + let circuit = Circuit::new(&circuit_builder); + + // generate witness + // calling rev() to make things little endian representation + let addend_0_witness = vec![14, 31, 28, 14] + .into_iter() + .rev() + .map(|v| Goldilocks::from(v)) + .collect_vec(); + let small_value_witness = vec![200] + .into_iter() + .map(|v| Goldilocks::from(v)) + .collect_vec(); + let carry_witness = vec![0, 1, 1, 6] + .into_iter() + .rev() + .map(|v| Goldilocks::from(v)) + .collect_vec(); + + let mut wires_in = vec![vec![]; circuit.n_witness_in]; + wires_in[addend_0_id as usize] = addend_0_witness; + wires_in[small_value_id as usize] = small_value_witness; + wires_in[carry_id as usize] = carry_witness; + + let circuit_witness = { + let challenges = vec![GoldilocksExt2::from(2)]; + let mut circuit_witness = CircuitWitness::new(&circuit, challenges); + circuit_witness.add_instance(&circuit, wires_in); + circuit_witness + }; + + circuit_witness.check_correctness(&circuit); + + // check the result correctness + let result_values = circuit_witness.output_layer_witness_ref().instances[0].to_vec(); + assert_eq!( + result_values, + [22, 2, 0, 15] + .into_iter() + .map(|v| Goldilocks::from(v)) + .collect_vec() + ); + } + + #[test] + fn test_sub_unsafe() { + // A - B + // big endian and represented as field elements + // 9 | 20 | 26 | 30 + // 5 | 30 | 28 | 10 + // result 3 | 21 | 30 | 20 + // borrow 0 | 1 | 1 | 0 + + // build the circuit + type UInt20 = UInt<20, 5>; + let mut circuit_builder = CircuitBuilder::::new(); + + // input wires + // minuend, subtrahend, borrow + let (minuend_id, minuend_cells) = + circuit_builder.create_witness_in(UInt20::N_OPERAND_CELLS); + let (subtrahend_id, subtrahend_cells) = + circuit_builder.create_witness_in(UInt20::N_OPERAND_CELLS); + // |Carry| == |Borrow| + let (borrow_id, borrow_cells) = + circuit_builder.create_witness_in(AddSubConstants::::N_CARRY_CELLS); + + let minuend = UInt20::try_from(minuend_cells).expect("should build uint"); + let subtrahend = UInt20::try_from(subtrahend_cells).expect("should build uint"); + + // update the circuit builder + let result = + UInt20::sub_unsafe(&mut circuit_builder, &minuend, &subtrahend, &borrow_cells).unwrap(); + circuit_builder.configure(); + let circuit = Circuit::new(&circuit_builder); + + // generate witness + // calling rev() to make things little endian representation + let minuend_witness = vec![9, 20, 26, 30] + .into_iter() + .rev() + .map(|v| Goldilocks::from(v)) + .collect_vec(); + let subtrahend_witness = vec![5, 30, 28, 10] + .into_iter() + .rev() + .map(|v| Goldilocks::from(v)) + .collect(); + let borrow_witness = vec![0, 1, 1, 0] + .into_iter() + .rev() + .map(|v| Goldilocks::from(v)) + .collect_vec(); + + let mut wires_in = vec![vec![]; circuit.n_witness_in]; + wires_in[minuend_id as usize] = minuend_witness; + wires_in[subtrahend_id as usize] = subtrahend_witness; + wires_in[borrow_id as usize] = borrow_witness; + + let circuit_witness = { + let challenges = vec![GoldilocksExt2::from(2)]; + let mut circuit_witness = CircuitWitness::new(&circuit, challenges); + circuit_witness.add_instance(&circuit, wires_in); + circuit_witness + }; + + circuit_witness.check_correctness(&circuit); + + // check the result correctness + let result_values = circuit_witness.output_layer_witness_ref().instances[0].to_vec(); + assert_eq!( + result_values, + [20, 30, 21, 3] + .into_iter() + .map(|v| Goldilocks::from(v)) + .collect_vec() + ); + } +} diff --git a/singer-utils/src/uint/cmp.rs b/singer-utils/src/uint/cmp.rs index 2a2ba0058..48920337b 100644 --- a/singer-utils/src/uint/cmp.rs +++ b/singer-utils/src/uint/cmp.rs @@ -1,185 +1,122 @@ +use crate::{ + chip_handler::RangeChipOperations, + error::UtilError, + uint::{constants::AddSubConstants, uint::UInt}, +}; use ff::Field; use ff_ext::ExtensionField; use simple_frontend::structs::{CellId, CircuitBuilder, MixedCell}; -use crate::{chip_handler::RangeChipOperations, error::UtilError, structs::UInt}; - -use super::{UIntAddSub, UIntCmp}; - -impl UIntCmp> { - pub const N_NO_OVERFLOW_WITNESS_CELLS: usize = - UIntAddSub::>::N_NO_OVERFLOW_WITNESS_CELLS; - - pub const N_WITNESS_CELLS: usize = UIntAddSub::>::N_WITNESS_CELLS; - - pub fn extract_range_values(witness: &[CellId]) -> &[CellId] { - &witness[..UInt::::N_RANGE_CHECK_CELLS] - } - - pub fn extract_borrow(witness: &[CellId]) -> &[CellId] { - &UIntAddSub::>::extract_carry(witness) - } - - pub fn extract_unsafe_borrow(witness: &[CellId]) -> &[CellId] { - &UIntAddSub::>::extract_unsafe_carry(witness) - } - - /// Greater than implemented by little-endian subtraction. - pub fn lt>( - circuit_builder: &mut CircuitBuilder, +impl UInt { + /// Generates the required information for asserting lt and leq + pub fn lt>( + circuit_builder: &mut CircuitBuilder, range_chip_handler: &mut H, - oprand_0: &UInt, - oprand_1: &UInt, + operand_0: &UInt, + operand_1: &UInt, witness: &[CellId], ) -> Result<(CellId, UInt), UtilError> { - let borrow = Self::extract_borrow(witness); + let borrow = Self::extract_borrow_sub(witness); let range_values = Self::extract_range_values(witness); - let computed_diff = - UIntAddSub::>::sub_unsafe(circuit_builder, oprand_0, oprand_1, borrow)?; + let computed_diff = Self::sub_unsafe(circuit_builder, operand_0, operand_1, borrow)?; + let diff = range_chip_handler.range_check_uint( circuit_builder, &computed_diff, Some(&range_values), )?; - if borrow.len() == UInt::::N_CARRY_CELLS { - Ok((borrow[UInt::::N_CARRY_CELLS - 1], diff)) + + // if operand_0 < operand_1, the last borrow should equal 1 + if borrow.len() == AddSubConstants::::N_CARRY_CELLS { + Ok((borrow[AddSubConstants::::N_CARRY_CELLS - 1], diff)) } else { Ok((circuit_builder.create_cell(), diff)) } } - pub fn assert_lt>( - circuit_builder: &mut CircuitBuilder, + /// Asserts that operand_0 < operand_1 + pub fn assert_lt>( + circuit_builder: &mut CircuitBuilder, range_chip_handler: &mut H, - oprand_0: &UInt, - oprand_1: &UInt, + operand_0: &UInt, + operand_1: &UInt, witness: &[CellId], ) -> Result<(), UtilError> { let (borrow, _) = Self::lt( circuit_builder, range_chip_handler, - oprand_0, - oprand_1, + operand_0, + operand_1, witness, )?; circuit_builder.assert_const(borrow, 1); Ok(()) } - /// Greater or equal than implemented by little-endian subtraction. - pub fn assert_leq>( - circuit_builder: &mut CircuitBuilder, + /// Asserts that operand_0 <= operand_1 + pub fn assert_leq>( + circuit_builder: &mut CircuitBuilder, range_chip_handler: &mut H, - oprand_0: &UInt, - oprand_1: &UInt, + operand_0: &UInt, + operand_1: &UInt, witness: &[CellId], ) -> Result<(), UtilError> { let (borrow, diff) = Self::lt( circuit_builder, range_chip_handler, - oprand_0, - oprand_1, + operand_0, + operand_1, witness, )?; + + // we have two scenarios + // 1. eq + // in this case, borrow = 0 and diff = [0, ..., 0] + // 2. lt + // in this case, borrow = 1 and diff = [..field_elements..] + // we check for both cases with the following + // if borrow == 0 return diff else return 0 + // then assert that the returned item = 0 + let diff_values = diff.values(); for d in diff_values.iter() { let s = circuit_builder.create_cell(); - // assert_zero({borrow ? 0 : diff}) circuit_builder.sel_mixed( s, (*d).into(), - MixedCell::Constant(Ext::BaseField::ZERO), + MixedCell::Constant(E::BaseField::ZERO), borrow, ); circuit_builder.assert_const(s, 0); } + Ok(()) } - pub fn assert_eq( - circuit_builder: &mut CircuitBuilder, - oprand_0: &UInt, - oprand_1: &UInt, + /// Asserts that two `UInt` instances represent equal value + pub fn assert_eq( + circuit_builder: &mut CircuitBuilder, + operand_0: &UInt, + operand_1: &UInt, ) -> Result<(), UtilError> { - let diff = circuit_builder.create_cells(oprand_0.values().len()); - let opr_0 = oprand_0.values(); - let opr_1 = oprand_1.values(); - for i in 0..diff.len() { - circuit_builder.add(diff[i], opr_0[i], Ext::BaseField::ONE); - circuit_builder.add(diff[i], opr_1[i], -Ext::BaseField::ONE); + let diff = circuit_builder.create_cells(Self::N_OPERAND_CELLS); + let operand_0_cells = operand_0.values(); + let operand_1_cells = operand_1.values(); + for i in 0..Self::N_OPERAND_CELLS { + circuit_builder.add(diff[i], operand_0_cells[i], E::BaseField::ONE); + circuit_builder.add(diff[i], operand_1_cells[i], -E::BaseField::ONE); circuit_builder.assert_const(diff[i], 0); } Ok(()) } -} - -#[cfg(test)] -mod test { - use crate::structs::{ChipChallenges, ROMHandler}; - - use super::{UInt, UIntCmp}; - use goldilocks::{Goldilocks, GoldilocksExt2}; - use simple_frontend::structs::CircuitBuilder; - use gkr::structs::{Circuit, CircuitWitness}; - - #[test] - fn test_lt() { - type Uint256_8 = UInt<256, 8>; - assert_eq!(Uint256_8::N_OPRAND_CELLS, 32); - // build the circuit for lt - let mut circuit_builder = CircuitBuilder::::new(); - let (operand_0_wire_in_id, operand_0_wire_in_cells) = - circuit_builder.create_witness_in(Uint256_8::N_OPRAND_CELLS); - let (operand_1_wire_in_id, operand_1_wire_in_cells) = - circuit_builder.create_witness_in(Uint256_8::N_OPRAND_CELLS); - let (witness_wire_in_id, witness_wire_in_cells) = circuit_builder - .create_witness_in(Uint256_8::N_RANGE_CHECK_CELLS + Uint256_8::N_CARRY_CELLS); - let operand_0 = Uint256_8::try_from(operand_0_wire_in_cells); - let operand_1 = Uint256_8::try_from(operand_1_wire_in_cells); - let mut range_chip_handler = ROMHandler::::new(&ChipChallenges::default()); - let result = UIntCmp::::lt( - &mut circuit_builder, - &mut range_chip_handler, - &operand_0.unwrap(), - &operand_1.unwrap(), - &witness_wire_in_cells, - ); - assert_eq!( - result.unwrap().0, - 2 * Uint256_8::N_OPRAND_CELLS - + Uint256_8::N_RANGE_CHECK_CELLS - + Uint256_8::N_CARRY_CELLS - - 1 - ); - circuit_builder.configure(); - let circuit = Circuit::new(&circuit_builder); - // fill in witnesses - let n_witness_in = circuit.n_witness_in; - let mut wires_in = vec![vec![]; n_witness_in]; - wires_in[operand_0_wire_in_id as usize] = - vec![Goldilocks::from(1u64), Goldilocks::from(1u64)]; - wires_in[operand_0_wire_in_id as usize] - .extend(vec![Goldilocks::from(0u64); Uint256_8::N_OPRAND_CELLS - 2]); - wires_in[operand_1_wire_in_id as usize] = - vec![Goldilocks::from(255u64), Goldilocks::from(254u64)]; - wires_in[operand_1_wire_in_id as usize] - .extend(vec![Goldilocks::from(0u64); Uint256_8::N_OPRAND_CELLS - 2]); - wires_in[witness_wire_in_id as usize] = - vec![Goldilocks::from(2u64), Goldilocks::from(2u64)]; - wires_in[witness_wire_in_id as usize].extend(vec![ - Goldilocks::from(255u64); - Uint256_8::N_RANGE_CHECK_CELLS - 2 - ]); - wires_in[witness_wire_in_id as usize] - .extend(vec![Goldilocks::from(1u64); Uint256_8::N_CARRY_CELLS]); - let circuit_witness = { - let challenges = vec![GoldilocksExt2::from(2), GoldilocksExt2::from(2)]; - let mut circuit_witness = CircuitWitness::new(&circuit, challenges); - circuit_witness.add_instance(&circuit, wires_in); - circuit_witness - }; - //println!("{:?}", circuit_witness); - circuit_witness.check_correctness(&circuit); + /// Asserts that a `UInt` instance and a set of range cells represent equal value + pub fn assert_eq_range_values( + circuit_builder: &mut CircuitBuilder, + operand_0: &UInt, + operand_1: &[CellId], + ) -> Result<(), UtilError> { + let range_as_uint = UInt::from_range_values(circuit_builder, operand_1)?; + Self::assert_eq(circuit_builder, &operand_0, &range_as_uint) } } diff --git a/singer-utils/src/uint/constants.rs b/singer-utils/src/uint/constants.rs new file mode 100644 index 000000000..6f2345bcd --- /dev/null +++ b/singer-utils/src/uint/constants.rs @@ -0,0 +1,66 @@ +use super::uint::UInt; +use crate::constants::RANGE_CHIP_BIT_WIDTH; +use crate::uint::util::const_min; +use std::marker::PhantomData; + +impl UInt { + /// Determines the maximum number of bits that should be represented in each cell + /// independent of the cell capacity `C`. + /// If M < C i.e. total bit < cell capacity, the maximum_usable_cell_capacity + /// is actually M. + /// but if M >= C then maximum_usable_cell_capacity = C + pub const MAX_CELL_BIT_WIDTH: usize = const_min(M, C); + + /// `N_OPERAND_CELLS` represent the minimum number of cells each of size `C` needed + /// to hold `M` total bits + pub const N_OPERAND_CELLS: usize = (M + C - 1) / C; + + /// The number of `RANGE_CHIP_BIT_WIDTH` cells needed to represent one cell of size `C` + const N_RANGE_CELLS_PER_CELL: usize = (C + RANGE_CHIP_BIT_WIDTH - 1) / RANGE_CHIP_BIT_WIDTH; + + /// The number of `RANGE_CHIP_BIT_WIDTH` cells needed to represent the entire `UInt` + pub const N_RANGE_CELLS: usize = Self::N_OPERAND_CELLS * Self::N_RANGE_CELLS_PER_CELL; +} + +/// Holds addition specific constants +pub struct AddSubConstants { + _marker: PhantomData, +} + +impl AddSubConstants> { + /// Number of cells required to track carry information for the addition operation. + /// operand_0 = a b c + /// operand_1 = e f g + /// ---------- + /// result = h i j + /// carry = k l m - + /// |Carry| = |Cells| + pub const N_CARRY_CELLS: usize = UInt::::N_OPERAND_CELLS; + + /// Number of cells required to track carry information if we assume the addition + /// operation cannot lead to overflow. + /// operand_0 = a b c + /// operand_1 = e f g + /// ---------- + /// result = h i j + /// carry = l m - + /// |Carry| = |Cells - 1| + const N_CARRY_CELLS_NO_OVERFLOW: usize = Self::N_CARRY_CELLS - 1; + + /// The size of the witness + pub const N_WITNESS_CELLS: usize = UInt::::N_RANGE_CELLS + Self::N_CARRY_CELLS; + + /// The size of the witness assuming carry has no overflow + /// |Range_values| + |Carry - 1| + pub const N_WITNESS_CELLS_NO_CARRY_OVERFLOW: usize = + UInt::::N_RANGE_CELLS + Self::N_CARRY_CELLS_NO_OVERFLOW; + + pub const N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS: usize = Self::N_CARRY_CELLS_NO_OVERFLOW; + + /// The number of `RANGE_CHIP_BIT_WIDTH` cells needed to represent the carry cells, assuming + /// no overflow. + // TODO: if guaranteed no overflow, then we don't need to range check the highest limb + // hence this can be (N_OPERANDS - 1) * N_RANGE_CELLS_PER_CELL + // update this once, range check logic doesn't assume all limbs + pub const N_RANGE_CELLS_NO_OVERFLOW: usize = UInt::::N_RANGE_CELLS; +} diff --git a/singer-utils/src/uint/uint.rs b/singer-utils/src/uint/uint.rs new file mode 100644 index 000000000..ee2cd0771 --- /dev/null +++ b/singer-utils/src/uint/uint.rs @@ -0,0 +1,260 @@ +use crate::{ + constants::{BYTE_BIT_WIDTH, RANGE_CHIP_BIT_WIDTH}, + error::UtilError, + uint::util::{add_one_to_big_num, convert_decomp, pad_cells}, +}; +use ff_ext::ExtensionField; +use goldilocks::SmallField; +use itertools::Itertools; +use simple_frontend::structs::{CellId, CircuitBuilder}; +use sumcheck::util::ceil_log2; + +#[derive(Clone)] +/// Unsigned integer with `M` total bits. `C` denotes the cell bit width. +/// Represented in little endian form. +pub struct UInt { + pub values: Vec, +} + +impl UInt { + /// Return the `UInt` underlying cell id's + pub fn values(&self) -> &[CellId] { + &self.values + } + + /// Builds a `UInt` instance from a set of cells that represent `RANGE_VALUES` + /// assumes range_values are represented in little endian form + pub fn from_range_values( + circuit_builder: &mut CircuitBuilder, + range_values: &[CellId], + ) -> Result { + Self::from_different_sized_cell_values( + circuit_builder, + range_values, + RANGE_CHIP_BIT_WIDTH, + true, + ) + } + + /// Builds a `UInt` instance from a set of cells that represent big-endian `BYTE_VALUES` + pub fn from_bytes_big_endian( + circuit_builder: &mut CircuitBuilder, + bytes: &[CellId], + ) -> Result { + Self::from_bytes(circuit_builder, bytes, false) + } + + /// Builds a `UInt` instance from a set of cells that represent little-endian `BYTE_VALUES` + pub fn from_bytes_little_endian( + circuit_builder: &mut CircuitBuilder, + bytes: &[CellId], + ) -> Result { + Self::from_bytes(circuit_builder, bytes, true) + } + + /// Builds a `UInt` instance from a set of cells that represent `BYTE_VALUES` + pub fn from_bytes( + circuit_builder: &mut CircuitBuilder, + bytes: &[CellId], + is_little_endian: bool, + ) -> Result { + Self::from_different_sized_cell_values( + circuit_builder, + bytes, + BYTE_BIT_WIDTH, + is_little_endian, + ) + } + + /// Builds a `UInt` instance from a set of cell values of a certain `CELL_WIDTH` + fn from_different_sized_cell_values( + circuit_builder: &mut CircuitBuilder, + cell_values: &[CellId], + cell_width: usize, + is_little_endian: bool, + ) -> Result { + let mut values = convert_decomp( + circuit_builder, + cell_values, + cell_width, + Self::MAX_CELL_BIT_WIDTH, + is_little_endian, + )?; + debug_assert!(values.len() <= Self::N_OPERAND_CELLS); + pad_cells(circuit_builder, &mut values, Self::N_OPERAND_CELLS); + values.try_into() + } + + /// Generate ((0)_{2^C}, (1)_{2^C}, ..., (size - 1)_{2^C}) + pub fn counter_vector(size: usize) -> Vec> { + let num_vars = ceil_log2(size); + let number_of_limbs = (num_vars + C - 1) / C; + let cell_modulo = F::from(1 << C); + + let mut res = vec![vec![F::ZERO; number_of_limbs]]; + + for i in 1..size { + res.push(add_one_to_big_num(cell_modulo, &res[i - 1])); + } + + res + } +} + +/// Construct `UInt` from `Vec` +impl TryFrom> for UInt { + type Error = UtilError; + + fn try_from(values: Vec) -> Result { + if values.len() != Self::N_OPERAND_CELLS { + return Err(UtilError::UIntError(format!( + "cannot construct UInt<{}, {}> from {} cells, requires {} cells", + M, + C, + values.len(), + Self::N_OPERAND_CELLS + ))); + } + + Ok(Self { values }) + } +} + +/// Construct `UInt` from `$[CellId]` +impl TryFrom<&[CellId]> for UInt { + type Error = UtilError; + + fn try_from(values: &[CellId]) -> Result { + values.to_vec().try_into() + } +} + +#[cfg(test)] +mod tests { + use crate::uint::uint::UInt; + use gkr::structs::{Circuit, CircuitWitness}; + use goldilocks::{Goldilocks, GoldilocksExt2}; + use itertools::Itertools; + use simple_frontend::structs::CircuitBuilder; + + #[test] + fn test_uint_from_cell_ids() { + // 33 total bits and each cells holds just 4 bits + // to hold all 33 bits without truncations, we'd need 9 cells + // 9 * 4 = 36 > 33 + type UInt33 = UInt<33, 4>; + assert!(UInt33::try_from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).is_ok()); + assert!(UInt33::try_from(vec![1, 2, 3]).is_err()); + assert!(UInt33::try_from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).is_err()); + } + + #[test] + fn test_uint_from_different_sized_cell_values() { + // build circuit + let mut circuit_builder = CircuitBuilder::::new(); + let (_, small_values) = circuit_builder.create_witness_in(8); + type UInt30 = UInt<30, 6>; + let uint_instance = + UInt30::from_different_sized_cell_values(&mut circuit_builder, &small_values, 2, true) + .unwrap(); + circuit_builder.configure(); + let circuit = Circuit::new(&circuit_builder); + + // input + // we start with cells of bit width 2 (8 of them) + // 11 00 10 11 01 10 01 01 (bit representation) + // 3 0 2 3 1 2 1 1 (field representation) + // + // repacking into cells of bit width 6 + // 110010 110110 010100 + // since total bit = 30 then expect 5 cells ( 30 / 6) + // since we have 3 cells, we need to pad with 2 more + // hence expected output: + // 100011 100111 000101 000000 000000(bit representation) + // 35 39 5 0 0 + + let witness_values = vec![3, 0, 2, 3, 1, 2, 1, 1] + .into_iter() + .map(|v| Goldilocks::from(v)) + .collect_vec(); + let circuit_witness = { + let challenges = vec![GoldilocksExt2::from(2)]; + let mut circuit_witness = CircuitWitness::new(&circuit, challenges); + circuit_witness.add_instance(&circuit, vec![witness_values]); + circuit_witness + }; + circuit_witness.check_correctness(&circuit); + + let output = circuit_witness.output_layer_witness_ref().instances[0].to_vec(); + assert_eq!( + &output[..5], + vec![35, 39, 5, 0, 0] + .into_iter() + .map(|v| Goldilocks::from(v)) + .collect_vec() + ); + + // padding to power of 2 + assert_eq!( + &output[5..], + vec![0, 0, 0] + .into_iter() + .map(|v| Goldilocks::from(v)) + .collect_vec() + ); + } + + #[test] + fn test_counter_vector() { + // each limb has 5 bits so all number from 0..3 should require only 1 limb + type UInt30 = UInt<30, 5>; + let res = UInt30::counter_vector::(3); + assert_eq!( + res, + vec![ + vec![Goldilocks::from(0)], + vec![Goldilocks::from(1)], + vec![Goldilocks::from(2)] + ] + ); + + // each limb has a single bit, number from 0..5 should require 3 limbs each + type UInt50 = UInt<50, 1>; + let res = UInt50::counter_vector::(5); + assert_eq!( + res, + vec![ + // 0 + vec![ + Goldilocks::from(0), + Goldilocks::from(0), + Goldilocks::from(0) + ], + // 1 + vec![ + Goldilocks::from(1), + Goldilocks::from(0), + Goldilocks::from(0) + ], + // 2 + vec![ + Goldilocks::from(0), + Goldilocks::from(1), + Goldilocks::from(0) + ], + // 3 + vec![ + Goldilocks::from(1), + Goldilocks::from(1), + Goldilocks::from(0) + ], + // 4 + vec![ + Goldilocks::from(0), + Goldilocks::from(0), + Goldilocks::from(1) + ], + ] + ); + } +} diff --git a/singer-utils/src/uint/util.rs b/singer-utils/src/uint/util.rs new file mode 100644 index 000000000..61e756931 --- /dev/null +++ b/singer-utils/src/uint/util.rs @@ -0,0 +1,329 @@ +use ff::PrimeField; +use crate::error::UtilError; +use ff_ext::ExtensionField; +use goldilocks::SmallField; +use itertools::Itertools; +use simple_frontend::structs::{CellId, CircuitBuilder}; + +/// Given some data represented by n small cells of size s +/// this function represents the same data in m big cells of size b +/// where b >= s +/// e.g. +/// information = 1100 +/// represented with 2 small cells of size 2 each +/// small -> 11 | 00 +/// we can pack this into a single big cell of size 4 +/// big -> 1100 +pub fn convert_decomp( + circuit_builder: &mut CircuitBuilder, + small_cells: &[CellId], + small_cell_bit_width: usize, + big_cell_bit_width: usize, + is_little_endian: bool, +) -> Result, UtilError> { + assert!(E::BaseField::NUM_BITS >= big_cell_bit_width as u32); + + if small_cell_bit_width > big_cell_bit_width { + return Err(UtilError::UIntError( + "cannot pack bigger width cells into smaller width cells".to_string(), + )); + } + + if small_cell_bit_width == big_cell_bit_width { + return Ok(small_cells.to_vec()); + } + + // ensure the small cell values are in little endian form + let small_cells = if !is_little_endian { + small_cells.to_vec().into_iter().rev().collect() + } else { + small_cells.to_vec() + }; + + // compute the number of small cells that can fit into each big cell + let small_cell_count_per_big_cell = big_cell_bit_width / small_cell_bit_width; + + let mut new_cell_ids = vec![]; + + // iteratively take and pack n small cells into 1 big cell + for values in small_cells.chunks(small_cell_count_per_big_cell) { + let big_cell = circuit_builder.create_cell(); + for (small_chunk_index, small_bit_cell) in values.iter().enumerate() { + let shift_size = small_chunk_index * small_cell_bit_width; + circuit_builder.add( + big_cell, + *small_bit_cell, + E::BaseField::from(1 << shift_size), + ); + } + new_cell_ids.push(big_cell); + } + + Ok(new_cell_ids) +} + +/// Pads a `Vec` with new cells to reach some given size n +pub fn pad_cells( + circuit_builder: &mut CircuitBuilder, + cells: &mut Vec, + size: usize, +) { + if cells.len() < size { + cells.extend(circuit_builder.create_cells(size - cells.len())) + } +} + +/// Compile time evaluated minimum function +/// returns min(a, b) +pub const fn const_min(a: usize, b: usize) -> usize { + if a <= b { + a + } else { + b + } +} + +/// Assumes each limb < max_value +/// adds 1 to the big value, while preserving the above constraint +pub fn add_one_to_big_num(limb_modulo: F, limbs: &[F]) -> Vec { + let mut should_add_one = true; + let mut result = vec![]; + + for limb in limbs { + let mut new_limb_value = limb.clone(); + if should_add_one { + new_limb_value += F::ONE; + if new_limb_value == limb_modulo { + new_limb_value = F::ZERO; + } else { + should_add_one = false; + } + } + result.push(new_limb_value); + } + + result +} + +#[cfg(test)] +mod tests { + use crate::uint::util::{add_one_to_big_num, const_min, convert_decomp, pad_cells}; + use gkr::structs::{Circuit, CircuitWitness}; + use goldilocks::{Goldilocks, GoldilocksExt2}; + use itertools::Itertools; + use simple_frontend::structs::CircuitBuilder; + + #[test] + #[should_panic] + fn test_pack_big_cells_into_small_cells() { + let mut circuit_builder = CircuitBuilder::::new(); + let (_, big_values) = circuit_builder.create_witness_in(5); + let big_bit_width = 5; + let small_bit_width = 2; + let cell_packing_result = convert_decomp( + &mut circuit_builder, + &big_values, + big_bit_width, + small_bit_width, + true, + ) + .unwrap(); + } + + #[test] + fn test_pack_same_size_cells() { + let mut circuit_builder = CircuitBuilder::::new(); + let (_, initial_values) = circuit_builder.create_witness_in(5); + let small_bit_width = 2; + let big_bit_width = 2; + let new_values = convert_decomp( + &mut circuit_builder, + &initial_values, + small_bit_width, + big_bit_width, + true, + ) + .unwrap(); + assert_eq!(initial_values, new_values); + } + + #[test] + fn test_pack_small_cells_into_big_cells() { + let mut circuit_builder = CircuitBuilder::::new(); + let (_, small_values) = circuit_builder.create_witness_in(9); + let small_bit_width = 2; + let big_bit_width = 6; + let big_values = convert_decomp( + &mut circuit_builder, + &small_values, + small_bit_width, + big_bit_width, + true, + ) + .unwrap(); + assert_eq!(big_values.len(), 3); + circuit_builder.create_witness_out_from_cells(&big_values); + + // verify construction against concrete witness values + circuit_builder.configure(); + let circuit = Circuit::new(&circuit_builder); + + // input + // we start with cells of bit width 2 (9 of them) + // 11 00 10 11 01 10 01 01 11 (bit representation) + // 3 0 2 3 1 2 1 1 3 (field representation) + // + // expected output + // repacking into cells of bit width 6 + // we can only fit three 2-bit cells into a 6 bit cell + // 100011 100111 110101 (bit representation) + // 35 39 53 (field representation) + + let witness_values = vec![3, 0, 2, 3, 1, 2, 1, 1, 3] + .into_iter() + .map(|v| Goldilocks::from(v)) + .collect::>(); + let circuit_witness = { + let mut circuit_witness = CircuitWitness::new(&circuit, vec![]); + circuit_witness.add_instance(&circuit, vec![witness_values]); + circuit_witness + }; + + circuit_witness.check_correctness(&circuit); + + let output = circuit_witness.output_layer_witness_ref().instances[0].to_vec(); + + assert_eq!( + &output[..3], + vec![35, 39, 53] + .into_iter() + .map(|v| Goldilocks::from(v)) + .collect::>() + ); + + // padding to power of 2 + assert_eq!( + &output[3..], + vec![0] + .into_iter() + .map(|v| Goldilocks::from(v)) + .collect_vec() + ); + } + + #[test] + fn test_pad_cells() { + let mut circuit_builder = CircuitBuilder::::new(); + let (_, mut small_values) = circuit_builder.create_witness_in(3); + // assert before padding + assert_eq!(small_values, vec![0, 1, 2]); + // pad + pad_cells(&mut circuit_builder, &mut small_values, 5); + // assert after padding + assert_eq!(small_values, vec![0, 1, 2, 3, 4]); + } + + #[test] + fn test_min_function() { + assert_eq!(const_min(2, 3), 2); + assert_eq!(const_min(3, 3), 3); + assert_eq!(const_min(5, 3), 3); + } + + #[test] + fn test_add_one_big_num() { + let limb_modulo = Goldilocks::from(2); + + // 000 + let initial_limbs = vec![Goldilocks::from(0); 3]; + + // 100 + let updated_limbs = add_one_to_big_num(limb_modulo, &initial_limbs); + assert_eq!( + updated_limbs, + vec![ + Goldilocks::from(1), + Goldilocks::from(0), + Goldilocks::from(0) + ] + ); + + // 010 + let updated_limbs = add_one_to_big_num(limb_modulo, &updated_limbs); + assert_eq!( + updated_limbs, + vec![ + Goldilocks::from(0), + Goldilocks::from(1), + Goldilocks::from(0) + ] + ); + + // 110 + let updated_limbs = add_one_to_big_num(limb_modulo, &updated_limbs); + assert_eq!( + updated_limbs, + vec![ + Goldilocks::from(1), + Goldilocks::from(1), + Goldilocks::from(0) + ] + ); + + // 001 + let updated_limbs = add_one_to_big_num(limb_modulo, &updated_limbs); + assert_eq!( + updated_limbs, + vec![ + Goldilocks::from(0), + Goldilocks::from(0), + Goldilocks::from(1) + ] + ); + + // 101 + let updated_limbs = add_one_to_big_num(limb_modulo, &updated_limbs); + assert_eq!( + updated_limbs, + vec![ + Goldilocks::from(1), + Goldilocks::from(0), + Goldilocks::from(1) + ] + ); + + // 011 + let updated_limbs = add_one_to_big_num(limb_modulo, &updated_limbs); + assert_eq!( + updated_limbs, + vec![ + Goldilocks::from(0), + Goldilocks::from(1), + Goldilocks::from(1) + ] + ); + + // 111 + let updated_limbs = add_one_to_big_num(limb_modulo, &updated_limbs); + assert_eq!( + updated_limbs, + vec![ + Goldilocks::from(1), + Goldilocks::from(1), + Goldilocks::from(1) + ] + ); + + // restart cycle + // 000 + let updated_limbs = add_one_to_big_num(limb_modulo, &updated_limbs); + assert_eq!( + updated_limbs, + vec![ + Goldilocks::from(0), + Goldilocks::from(0), + Goldilocks::from(0) + ] + ); + } +} diff --git a/singer-utils/src/uint/witness_extractors.rs b/singer-utils/src/uint/witness_extractors.rs new file mode 100644 index 000000000..32bf8c409 --- /dev/null +++ b/singer-utils/src/uint/witness_extractors.rs @@ -0,0 +1,38 @@ +use crate::uint::constants::AddSubConstants; +use crate::uint::uint::UInt; +use simple_frontend::structs::CellId; + +// TODO: split this into different impls, constrained by specific contexts +// e.g add_sub, mul, ... +impl UInt { + // witness_structure + // [...range_values..., ...carry_witness...] + + pub fn extract_carry_add(witness: &[CellId]) -> &[CellId] { + &witness[Self::N_RANGE_CELLS..] + } + + pub fn extract_carry_no_overflow_add(witness: &[CellId]) -> &[CellId] { + &witness[AddSubConstants::::N_RANGE_CELLS_NO_OVERFLOW..] + } + + pub fn extract_unsafe_carry_add(witness: &[CellId]) -> &[CellId] { + witness + } + + pub fn extract_borrow_sub(witness: &[CellId]) -> &[CellId] { + &witness[Self::N_RANGE_CELLS..] + } + + pub fn extract_unsafe_borrow_sub(witness: &[CellId]) -> &[CellId] { + witness + } + + pub fn extract_range_values(witness: &[CellId]) -> &[CellId] { + &witness[..Self::N_RANGE_CELLS] + } + + pub fn extract_range_values_no_overflow(witness: &[CellId]) -> &[CellId] { + &witness[..AddSubConstants::::N_RANGE_CELLS_NO_OVERFLOW] + } +} diff --git a/singer/src/instructions/add.rs b/singer/src/instructions/add.rs index f03c9fc85..6bdef4942 100644 --- a/singer/src/instructions/add.rs +++ b/singer/src/instructions/add.rs @@ -11,7 +11,7 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::{UIntAddSub, UIntCmp}, + uint::constants::AddSubConstants, }; use std::sync::Arc; @@ -28,23 +28,23 @@ impl InstructionGraph for AddInstruction { register_witness!( AddInstruction, phase0 { - pc => PCUInt::N_OPRAND_CELLS, - stack_ts => TSUInt::N_OPRAND_CELLS, - memory_ts => TSUInt::N_OPRAND_CELLS, + pc => PCUInt::N_OPERAND_CELLS, + stack_ts => TSUInt::N_OPERAND_CELLS, + memory_ts => TSUInt::N_OPERAND_CELLS, stack_top => 1, clk => 1, - pc_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_CELLS, + pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, - old_stack_ts0 => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt0 => UIntCmp::::N_WITNESS_CELLS, - old_stack_ts1 => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt1 => UIntCmp::::N_WITNESS_CELLS, + old_stack_ts0 => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt0 => AddSubConstants::::N_WITNESS_CELLS, + old_stack_ts1 => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt1 => AddSubConstants::::N_WITNESS_CELLS, - addend_0 => StackUInt::N_OPRAND_CELLS, - addend_1 => StackUInt::N_OPRAND_CELLS, - instruction_add => UIntAddSub::::N_WITNESS_CELLS + addend_0 => StackUInt::N_OPERAND_CELLS, + addend_1 => StackUInt::N_OPERAND_CELLS, + instruction_add => AddSubConstants::::N_WITNESS_CELLS } ); @@ -100,7 +100,7 @@ impl Instruction for AddInstruction { "addInstCircuit::phase0_instruction_add: {:?}", Self::phase0_instruction_add() ); - let result = UIntAddSub::::add( + let result = StackUInt::add( &mut circuit_builder, &mut rom_handler, &addend_0, @@ -116,7 +116,7 @@ impl Instruction for AddInstruction { // Pop two values from stack let old_stack_ts0 = (&phase0[Self::phase0_old_stack_ts0()]).try_into()?; - UIntCmp::::assert_lt( + TSUInt::assert_lt( &mut circuit_builder, &mut rom_handler, &old_stack_ts0, @@ -131,7 +131,7 @@ impl Instruction for AddInstruction { ); let old_stack_ts1 = (&phase0[Self::phase0_old_stack_ts1()]).try_into()?; - UIntCmp::::assert_lt( + TSUInt::assert_lt( &mut circuit_builder, &mut rom_handler, &old_stack_ts1, @@ -187,18 +187,22 @@ mod test { 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 singer_utils::{ + constants::RANGE_CHIP_BIT_WIDTH, + structs::{StackUInt, TSUInt}, + uint::constants::AddSubConstants, + }; + use std::{collections::BTreeMap, time::Instant}; use transcript::Transcript; - use crate::instructions::{ - AddInstruction, ChipChallenges, Instruction, InstructionGraph, SingerCircuitBuilder, + use crate::{ + instructions::{ + AddInstruction, ChipChallenges, Instruction, InstructionGraph, SingerCircuitBuilder, + }, + scheme::GKRGraphProverState, + test::{get_uint_params, test_opcode_circuit, u2vec}, + CircuitWiresIn, SingerGraphBuilder, SingerParams, }; - use crate::scheme::GKRGraphProverState; - use crate::test::{get_uint_params, test_opcode_circuit, u2vec}; - use crate::{CircuitWiresIn, SingerGraphBuilder, SingerParams}; impl AddInstruction { #[inline] @@ -276,8 +280,8 @@ mod test { 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(4u64), /* first TSUInt::N_RANGE_CELLS = 1*(48/16) = 3 cells are + * range values, stack_ts + 1 = 4 */ Goldilocks::from(0u64), Goldilocks::from(0u64), // no place for carry @@ -288,15 +292,14 @@ mod test { 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); + let range_values = u2vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); phase0_values_map.insert( "phase0_old_stack_ts_lt0".to_string(), vec![ Goldilocks::from(range_values[0]), Goldilocks::from(range_values[1]), Goldilocks::from(range_values[2]), - Goldilocks::from(range_values[3]), - Goldilocks::from(1u64), // borrow + Goldilocks::from(1u64), ], ); phase0_values_map.insert( @@ -304,21 +307,20 @@ mod test { 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); + let range_values = u2vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); phase0_values_map.insert( "phase0_old_stack_ts_lt1".to_string(), vec![ Goldilocks::from(range_values[0]), Goldilocks::from(range_values[1]), Goldilocks::from(range_values[2]), - Goldilocks::from(range_values[3]), - Goldilocks::from(1u64), // borrow + Goldilocks::from(1u64), ], ); 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 range_values = u2vec::<{ StackUInt::N_RANGE_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])) diff --git a/singer/src/instructions/calldataload.rs b/singer/src/instructions/calldataload.rs index de1ee19d5..6d65d74cf 100644 --- a/singer/src/instructions/calldataload.rs +++ b/singer/src/instructions/calldataload.rs @@ -3,6 +3,7 @@ use ff_ext::ExtensionField; use gkr::structs::Circuit; use paste::paste; use simple_frontend::structs::{CircuitBuilder, MixedCell}; +use singer_utils::uint::constants::AddSubConstants; use singer_utils::{ chip_handler::{ BytecodeChipOperations, CalldataChipOperations, GlobalStateChipOperations, OAMOperations, @@ -11,7 +12,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt, UInt64}, - uint::{UIntAddSub, UIntCmp}, }; use std::sync::Arc; @@ -28,20 +28,20 @@ pub struct CalldataloadInstruction; register_witness!( CalldataloadInstruction, phase0 { - pc => PCUInt::N_OPRAND_CELLS, - stack_ts => TSUInt::N_OPRAND_CELLS, - memory_ts => TSUInt::N_OPRAND_CELLS, - ts => TSUInt::N_OPRAND_CELLS, + pc => PCUInt::N_OPERAND_CELLS, + stack_ts => TSUInt::N_OPERAND_CELLS, + memory_ts => TSUInt::N_OPERAND_CELLS, + ts => TSUInt::N_OPERAND_CELLS, stack_top => 1, clk => 1, - pc_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_CELLS, + pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, - data => StackUInt::N_OPRAND_CELLS, - offset => UInt64::N_OPRAND_CELLS, - old_stack_ts => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt => UIntCmp::::N_WITNESS_CELLS + data => StackUInt::N_OPERAND_CELLS, + offset => UInt64::N_OPERAND_CELLS, + old_stack_ts => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt => AddSubConstants::::N_WITNESS_CELLS } ); @@ -104,7 +104,7 @@ impl Instruction for CalldataloadInstruction { old_stack_ts.values(), offset, ); - UIntCmp::::assert_lt( + TSUInt::assert_lt( &mut circuit_builder, &mut rom_handler, &old_stack_ts, @@ -239,7 +239,7 @@ mod test { 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(4u64), // first TSUInt::N_RANGE_CELLS = 1*(56/16) = 4 cells are range values, stack_ts + 1 = 4 Goldilocks::from(0u64), Goldilocks::from(0u64), Goldilocks::from(0u64), @@ -251,14 +251,14 @@ mod test { 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); + let range_values = u2vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); phase0_values_map.insert( "phase0_old_stack_ts_lt".to_string(), vec![ Goldilocks::from(range_values[0]), Goldilocks::from(range_values[1]), Goldilocks::from(range_values[2]), - Goldilocks::from(range_values[3]), + // Goldilocks::from(range_values[3]), Goldilocks::from(1u64), // borrow ], ); diff --git a/singer/src/instructions/dup.rs b/singer/src/instructions/dup.rs index 6f4dc7e92..f69637c3c 100644 --- a/singer/src/instructions/dup.rs +++ b/singer/src/instructions/dup.rs @@ -3,6 +3,7 @@ use ff_ext::ExtensionField; use gkr::structs::Circuit; use paste::paste; use simple_frontend::structs::{CircuitBuilder, MixedCell}; +use singer_utils::uint::constants::AddSubConstants; use singer_utils::{ chip_handler::{ BytecodeChipOperations, GlobalStateChipOperations, OAMOperations, ROMOperations, @@ -11,7 +12,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::{UIntAddSub, UIntCmp}, }; use std::sync::Arc; @@ -28,18 +28,18 @@ impl InstructionGraph for DupInstruction, phase0 { - pc => PCUInt::N_OPRAND_CELLS, - stack_ts => TSUInt::N_OPRAND_CELLS, - memory_ts => TSUInt::N_OPRAND_CELLS, + pc => PCUInt::N_OPERAND_CELLS, + stack_ts => TSUInt::N_OPERAND_CELLS, + memory_ts => TSUInt::N_OPERAND_CELLS, stack_top => 1, clk => 1, - pc_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_CELLS, + pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, - stack_values => StackUInt::N_OPRAND_CELLS, - old_stack_ts => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt => UIntCmp::::N_WITNESS_CELLS + stack_values => StackUInt::N_OPERAND_CELLS, + old_stack_ts => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt => AddSubConstants::::N_WITNESS_CELLS } ); @@ -103,7 +103,7 @@ impl Instruction for DupInstruction { // Pop rlc of stack[top - N] from stack let old_stack_ts = (&phase0[Self::phase0_old_stack_ts()]).try_into()?; - UIntCmp::::assert_lt( + TSUInt::assert_lt( &mut circuit_builder, &mut rom_handler, &old_stack_ts, @@ -247,7 +247,7 @@ mod test { 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(3u64), // first TSUInt::N_RANGE_CELLS = 1*(56/16) = 4 cells are range values, stack_ts + 1 = 4 Goldilocks::from(0u64), Goldilocks::from(0u64), Goldilocks::from(0u64), @@ -272,14 +272,14 @@ mod test { 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); + let range_values = u2vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); phase0_values_map.insert( "phase0_old_stack_ts_lt".to_string(), vec![ Goldilocks::from(range_values[0]), Goldilocks::from(range_values[1]), Goldilocks::from(range_values[2]), - Goldilocks::from(range_values[3]), + // Goldilocks::from(range_values[3]), Goldilocks::from(1u64), ], ); diff --git a/singer/src/instructions/gt.rs b/singer/src/instructions/gt.rs index 2b0165531..7e0be45dd 100644 --- a/singer/src/instructions/gt.rs +++ b/singer/src/instructions/gt.rs @@ -3,6 +3,7 @@ use ff_ext::ExtensionField; use gkr::structs::Circuit; use paste::paste; use simple_frontend::structs::{CircuitBuilder, MixedCell}; +use singer_utils::uint::constants::AddSubConstants; use singer_utils::{ chip_handler::{ BytecodeChipOperations, GlobalStateChipOperations, OAMOperations, ROMOperations, @@ -11,7 +12,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::{UIntAddSub, UIntCmp}, }; use std::sync::Arc; @@ -28,23 +28,23 @@ impl InstructionGraph for GtInstruction { register_witness!( GtInstruction, phase0 { - pc => PCUInt::N_OPRAND_CELLS, - stack_ts => TSUInt::N_OPRAND_CELLS, - memory_ts => TSUInt::N_OPRAND_CELLS, + pc => PCUInt::N_OPERAND_CELLS, + stack_ts => TSUInt::N_OPERAND_CELLS, + memory_ts => TSUInt::N_OPERAND_CELLS, stack_top => 1, clk => 1, - pc_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_CELLS, + pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, - old_stack_ts0 => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt0 => UIntCmp::::N_WITNESS_CELLS, - old_stack_ts1 => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt1 => UIntCmp::::N_WITNESS_CELLS, + old_stack_ts0 => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt0 => AddSubConstants::::N_WITNESS_CELLS, + old_stack_ts1 => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt1 => AddSubConstants::::N_WITNESS_CELLS, - oprand_0 => StackUInt::N_OPRAND_CELLS, - oprand_1 => StackUInt::N_OPRAND_CELLS, - instruction_gt => UIntCmp::::N_WITNESS_CELLS + oprand_0 => StackUInt::N_OPERAND_CELLS, + oprand_1 => StackUInt::N_OPERAND_CELLS, + instruction_gt => AddSubConstants::::N_WITNESS_CELLS } ); @@ -95,7 +95,7 @@ impl Instruction for GtInstruction { // Execution result = addend0 + addend1, with carry. let oprand_0 = (&phase0[Self::phase0_oprand_0()]).try_into()?; let oprand_1 = (&phase0[Self::phase0_oprand_1()]).try_into()?; - let (result, _) = UIntCmp::::lt( + let (result, _) = StackUInt::lt( &mut circuit_builder, &mut rom_handler, &oprand_1, @@ -111,7 +111,7 @@ impl Instruction for GtInstruction { // Pop two values from stack let old_stack_ts0 = (&phase0[Self::phase0_old_stack_ts0()]).try_into()?; - UIntCmp::::assert_lt( + TSUInt::assert_lt( &mut circuit_builder, &mut rom_handler, &old_stack_ts0, @@ -126,7 +126,7 @@ impl Instruction for GtInstruction { ); let old_stack_ts1 = (&phase0[Self::phase0_old_stack_ts1()]).try_into()?; - UIntCmp::::assert_lt( + TSUInt::assert_lt( &mut circuit_builder, &mut rom_handler, &old_stack_ts1, @@ -272,7 +272,7 @@ mod test { 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(4u64), // first TSUInt::N_RANGE_CELLS = 1*(56/16) = 4 cells are range values, stack_ts + 1 = 4 Goldilocks::from(0u64), Goldilocks::from(0u64), Goldilocks::from(0u64), @@ -284,14 +284,14 @@ mod test { 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); + let range_values = u2vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); phase0_values_map.insert( "phase0_old_stack_ts_lt0".to_string(), vec![ Goldilocks::from(range_values[0]), Goldilocks::from(range_values[1]), Goldilocks::from(range_values[2]), - Goldilocks::from(range_values[3]), + // Goldilocks::from(range_values[3]), Goldilocks::from(1u64), ], ); @@ -300,14 +300,14 @@ mod test { 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); + let range_values = u2vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); phase0_values_map.insert( "phase0_old_stack_ts_lt1".to_string(), vec![ Goldilocks::from(range_values[0]), Goldilocks::from(range_values[1]), Goldilocks::from(range_values[2]), - Goldilocks::from(range_values[3]), + // Goldilocks::from(range_values[3]), Goldilocks::from(1u64), ], ); diff --git a/singer/src/instructions/jump.rs b/singer/src/instructions/jump.rs index 35c1175a4..7a13838f1 100644 --- a/singer/src/instructions/jump.rs +++ b/singer/src/instructions/jump.rs @@ -5,6 +5,7 @@ use ff_ext::ExtensionField; use gkr::structs::Circuit; use paste::paste; use simple_frontend::structs::{CircuitBuilder, MixedCell}; +use singer_utils::uint::constants::AddSubConstants; use singer_utils::{ chip_handler::{ BytecodeChipOperations, GlobalStateChipOperations, OAMOperations, ROMOperations, @@ -13,7 +14,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, TSUInt}, - uint::UIntCmp, }; use crate::error::ZKVMError; @@ -29,16 +29,16 @@ impl InstructionGraph for JumpInstruction { register_witness!( JumpInstruction, phase0 { - pc => PCUInt::N_OPRAND_CELLS, - stack_ts => TSUInt::N_OPRAND_CELLS, - memory_ts => TSUInt::N_OPRAND_CELLS, + pc => PCUInt::N_OPERAND_CELLS, + stack_ts => TSUInt::N_OPERAND_CELLS, + memory_ts => TSUInt::N_OPERAND_CELLS, stack_top => 1, clk => 1, - next_pc => PCUInt::N_OPRAND_CELLS, + next_pc => PCUInt::N_OPERAND_CELLS, - old_stack_ts => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt => UIntCmp::::N_WITNESS_CELLS + old_stack_ts => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt => AddSubConstants::::N_WITNESS_CELLS } ); @@ -74,7 +74,7 @@ impl Instruction for JumpInstruction { let next_pc = &phase0[Self::phase0_next_pc()]; let old_stack_ts = (&phase0[Self::phase0_old_stack_ts()]).try_into()?; - UIntCmp::::assert_lt( + TSUInt::assert_lt( &mut circuit_builder, &mut rom_handler, &old_stack_ts, @@ -205,14 +205,14 @@ mod test { 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); + let range_values = u2vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); phase0_values_map.insert( "phase0_old_stack_ts_lt".to_string(), vec![ Goldilocks::from(range_values[0]), Goldilocks::from(range_values[1]), Goldilocks::from(range_values[2]), - Goldilocks::from(range_values[3]), + // Goldilocks::from(range_values[3]), Goldilocks::from(1u64), // current length has no cells for borrow ], ); diff --git a/singer/src/instructions/jumpdest.rs b/singer/src/instructions/jumpdest.rs index 99cd73343..f21874575 100644 --- a/singer/src/instructions/jumpdest.rs +++ b/singer/src/instructions/jumpdest.rs @@ -3,6 +3,7 @@ use ff_ext::ExtensionField; use gkr::structs::Circuit; use paste::paste; use simple_frontend::structs::{CircuitBuilder, MixedCell}; +use singer_utils::uint::constants::AddSubConstants; use singer_utils::{ chip_handler::{ BytecodeChipOperations, GlobalStateChipOperations, OAMOperations, ROMOperations, @@ -10,7 +11,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, TSUInt}, - uint::UIntAddSub, }; use std::sync::Arc; @@ -27,13 +27,13 @@ impl InstructionGraph for JumpdestInstruction { register_witness!( JumpdestInstruction, phase0 { - pc => PCUInt::N_OPRAND_CELLS , - stack_ts=> TSUInt::N_OPRAND_CELLS, - memory_ts => TSUInt::N_OPRAND_CELLS, + pc => PCUInt::N_OPERAND_CELLS , + stack_ts=> TSUInt::N_OPERAND_CELLS, + memory_ts => TSUInt::N_OPERAND_CELLS, stack_top => 1, clk => 1, - pc_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS + pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS } ); diff --git a/singer/src/instructions/jumpi.rs b/singer/src/instructions/jumpi.rs index d3d4c4103..b007d4f76 100644 --- a/singer/src/instructions/jumpi.rs +++ b/singer/src/instructions/jumpi.rs @@ -12,7 +12,7 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::{UIntAddSub, UIntCmp}, + uint::constants::AddSubConstants, }; use std::sync::Arc; @@ -29,23 +29,23 @@ impl InstructionGraph for JumpiInstruction { register_witness!( JumpiInstruction, phase0 { - pc => PCUInt::N_OPRAND_CELLS , - stack_ts => TSUInt::N_OPRAND_CELLS, - memory_ts => TSUInt::N_OPRAND_CELLS, + pc => PCUInt::N_OPERAND_CELLS , + stack_ts => TSUInt::N_OPERAND_CELLS, + memory_ts => TSUInt::N_OPERAND_CELLS, stack_top => 1, clk => 1, - old_stack_ts_dest => TSUInt::N_OPRAND_CELLS, - old_stack_ts_dest_lt => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS, - old_stack_ts_cond => TSUInt::N_OPRAND_CELLS, - old_stack_ts_cond_lt => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_dest => TSUInt::N_OPERAND_CELLS, + old_stack_ts_dest_lt => AddSubConstants::::N_WITNESS_CELLS, + old_stack_ts_cond => TSUInt::N_OPERAND_CELLS, + old_stack_ts_cond_lt => AddSubConstants::::N_WITNESS_CELLS, - dest_values => StackUInt::N_OPRAND_CELLS, - cond_values => StackUInt::N_OPRAND_CELLS, - cond_values_inv => StackUInt::N_OPRAND_CELLS, + dest_values => StackUInt::N_OPERAND_CELLS, + cond_values => StackUInt::N_OPERAND_CELLS, + cond_values_inv => StackUInt::N_OPERAND_CELLS, cond_non_zero_or_inv => 1, - pc_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, pc_plus_1_opcode => 1 } ); @@ -87,7 +87,7 @@ impl Instruction for JumpiInstruction { let dest_stack_addr = stack_top_expr.sub(E::BaseField::ONE); let old_stack_ts_dest = (&phase0[Self::phase0_old_stack_ts_dest()]).try_into()?; - UIntCmp::::assert_lt( + TSUInt::assert_lt( &mut circuit_builder, &mut rom_handler, &old_stack_ts_dest, @@ -104,7 +104,7 @@ impl Instruction for JumpiInstruction { // Pop the condition from stack. let cond_values = &phase0[Self::phase0_cond_values()]; let old_stack_ts_cond = (&phase0[Self::phase0_old_stack_ts_cond()]).try_into()?; - UIntCmp::::assert_lt( + TSUInt::assert_lt( &mut circuit_builder, &mut rom_handler, &old_stack_ts_cond, @@ -138,8 +138,8 @@ impl Instruction for JumpiInstruction { let pc_add_1 = &phase0[Self::phase0_pc_add()]; let pc_plus_1 = ROMHandler::add_pc_const(&mut circuit_builder, &pc, 1, pc_add_1)?; let pc_plus_1 = pc_plus_1.values(); - let next_pc = circuit_builder.create_cells(PCUInt::N_OPRAND_CELLS); - for i in 0..PCUInt::N_OPRAND_CELLS { + let next_pc = circuit_builder.create_cells(PCUInt::N_OPERAND_CELLS); + for i in 0..PCUInt::N_OPERAND_CELLS { circuit_builder.select(next_pc[i], pc_plus_1[i], dest_values[i], cond_non_zero); } diff --git a/singer/src/instructions/mstore.rs b/singer/src/instructions/mstore.rs index 7185454d9..14f515933 100644 --- a/singer/src/instructions/mstore.rs +++ b/singer/src/instructions/mstore.rs @@ -13,7 +13,7 @@ use singer_utils::{ constants::{OpcodeType, EVM_STACK_BYTE_WIDTH}, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::{UIntAddSub, UIntCmp}, + uint::constants::AddSubConstants, }; use std::{mem, sync::Arc}; @@ -140,21 +140,21 @@ impl InstructionGraph for MstoreInstruction { register_witness!( MstoreInstruction, phase0 { - pc => PCUInt::N_OPRAND_CELLS, - stack_ts => TSUInt::N_OPRAND_CELLS, - memory_ts => TSUInt::N_OPRAND_CELLS, + pc => PCUInt::N_OPERAND_CELLS, + stack_ts => TSUInt::N_OPERAND_CELLS, + memory_ts => TSUInt::N_OPERAND_CELLS, stack_top => 1, clk => 1, - pc_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - memory_ts_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_CELLS, + pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + memory_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, - offset => StackUInt::N_OPRAND_CELLS, + offset => StackUInt::N_OPERAND_CELLS, mem_bytes => EVM_STACK_BYTE_WIDTH, - old_stack_ts_offset => TSUInt::N_OPRAND_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_WITNESS_CELLS + old_stack_ts_offset => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt_offset => AddSubConstants::::N_WITNESS_CELLS, + old_stack_ts_value => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt_value => AddSubConstants::::N_WITNESS_CELLS } ); @@ -209,7 +209,7 @@ impl Instruction for MstoreInstruction { // Pop offset from stack let offset = StackUInt::try_from(&phase0[Self::phase0_offset()])?; let old_stack_ts_offset = TSUInt::try_from(&phase0[Self::phase0_old_stack_ts_offset()])?; - UIntCmp::::assert_lt( + TSUInt::assert_lt( &mut circuit_builder, &mut rom_handler, &old_stack_ts_offset, @@ -227,9 +227,9 @@ impl Instruction for MstoreInstruction { let mem_bytes = &phase0[Self::phase0_mem_bytes()]; rom_handler.range_check_bytes(&mut circuit_builder, mem_bytes)?; - let mem_value = StackUInt::from_bytes_big_endien(&mut circuit_builder, &mem_bytes)?; + let mem_value = StackUInt::from_bytes_big_endian(&mut circuit_builder, &mem_bytes)?; let old_stack_ts_value = TSUInt::try_from(&phase0[Self::phase0_old_stack_ts_value()])?; - UIntCmp::::assert_lt( + TSUInt::assert_lt( &mut circuit_builder, &mut rom_handler, &old_stack_ts_value, @@ -292,17 +292,17 @@ pub struct MstoreAccessory; register_witness!( MstoreAccessory, pred_dup { - memory_ts => TSUInt::N_OPRAND_CELLS, - offset => StackUInt::N_OPRAND_CELLS + memory_ts => TSUInt::N_OPERAND_CELLS, + offset => StackUInt::N_OPERAND_CELLS }, pred_ooo { mem_bytes => 1 }, phase0 { - old_memory_ts => TSUInt::N_OPRAND_CELLS, - old_memory_ts_lt => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS, + old_memory_ts => TSUInt::N_OPERAND_CELLS, + old_memory_ts_lt => AddSubConstants::::N_WITNESS_CELLS, - offset_add_delta => UIntAddSub::::N_WITNESS_CELLS, + offset_add_delta => AddSubConstants::::N_WITNESS_CELLS, prev_mem_bytes => 1 } ); @@ -331,14 +331,14 @@ impl MstoreAccessory { let offset = StackUInt::try_from(&pred_dup[Self::pred_dup_offset()])?; let offset_add_delta = &phase0[Self::phase0_offset_add_delta()]; let delta = circuit_builder.create_counter_in(0)[0]; - let offset_plus_delta = UIntAddSub::::add_small( + let offset_plus_delta = StackUInt::add_cell( &mut circuit_builder, &mut rom_handler, &offset, delta, offset_add_delta, )?; - UIntCmp::::assert_lt( + TSUInt::assert_lt( &mut circuit_builder, &mut rom_handler, &old_memory_ts, @@ -401,8 +401,7 @@ mod test { 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 singer_utils::{constants::RANGE_CHIP_BIT_WIDTH, structs::TSUInt}; use std::collections::BTreeMap; impl MstoreInstruction { @@ -478,7 +477,8 @@ mod test { 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(4u64), /* first TSUInt::N_RANGE_CELLS = 1*(56/16) = 4 cells are + * range values, memory_ts + 1 = 4 */ Goldilocks::from(0u64), Goldilocks::from(0u64), Goldilocks::from(0u64), @@ -491,14 +491,14 @@ mod test { 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); + let range_values = u2vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); phase0_values_map.insert( "phase0_old_stack_ts_lt_offset".to_string(), vec![ Goldilocks::from(range_values[0]), Goldilocks::from(range_values[1]), Goldilocks::from(range_values[2]), - Goldilocks::from(range_values[3]), + // Goldilocks::from(range_values[3]), Goldilocks::from(1u64), // borrow ], ); @@ -511,14 +511,14 @@ mod test { 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); + let range_values = u2vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); phase0_values_map.insert( "phase0_old_stack_ts_lt_value".to_string(), vec![ Goldilocks::from(range_values[0]), Goldilocks::from(range_values[1]), Goldilocks::from(range_values[2]), - Goldilocks::from(range_values[3]), + // Goldilocks::from(range_values[3]), Goldilocks::from(1u64), // borrow ], ); diff --git a/singer/src/instructions/pop.rs b/singer/src/instructions/pop.rs index 32f083671..c82bd6bc6 100644 --- a/singer/src/instructions/pop.rs +++ b/singer/src/instructions/pop.rs @@ -3,6 +3,7 @@ use ff_ext::ExtensionField; use gkr::structs::Circuit; use paste::paste; use simple_frontend::structs::{CircuitBuilder, MixedCell}; +use singer_utils::uint::constants::AddSubConstants; use singer_utils::{ chip_handler::{ BytecodeChipOperations, GlobalStateChipOperations, OAMOperations, ROMOperations, @@ -11,7 +12,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::{UIntAddSub, UIntCmp}, }; use std::sync::Arc; @@ -27,17 +27,17 @@ impl InstructionGraph for PopInstruction { register_witness!( PopInstruction, phase0 { - pc => PCUInt::N_OPRAND_CELLS, - stack_ts => TSUInt::N_OPRAND_CELLS, - memory_ts => TSUInt::N_OPRAND_CELLS, + pc => PCUInt::N_OPERAND_CELLS, + stack_ts => TSUInt::N_OPERAND_CELLS, + memory_ts => TSUInt::N_OPERAND_CELLS, stack_top => 1, clk => 1, - pc_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - old_stack_ts => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt => UIntCmp::::N_WITNESS_CELLS, - stack_values => StackUInt::N_OPRAND_CELLS + old_stack_ts => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt => AddSubConstants::::N_WITNESS_CELLS, + stack_values => StackUInt::N_OPERAND_CELLS } ); @@ -84,7 +84,7 @@ impl Instruction for PopInstruction { // Pop rlc from stack let old_stack_ts = (&phase0[Self::phase0_old_stack_ts()]).try_into()?; - UIntCmp::::assert_lt( + TSUInt::assert_lt( &mut circuit_builder, &mut rom_handler, &old_stack_ts, @@ -210,14 +210,14 @@ mod test { 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); + let range_values = u2vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); phase0_values_map.insert( "phase0_old_stack_ts_lt".to_string(), vec![ Goldilocks::from(range_values[0]), Goldilocks::from(range_values[1]), Goldilocks::from(range_values[2]), - Goldilocks::from(range_values[3]), + // Goldilocks::from(range_values[3]), Goldilocks::from(1u64), // current length has no cells for borrow ], ); diff --git a/singer/src/instructions/push.rs b/singer/src/instructions/push.rs index 6dfe7084f..e42661b5a 100644 --- a/singer/src/instructions/push.rs +++ b/singer/src/instructions/push.rs @@ -3,6 +3,7 @@ use ff_ext::ExtensionField; use gkr::structs::Circuit; use paste::paste; use simple_frontend::structs::{CircuitBuilder, MixedCell}; +use singer_utils::uint::constants::AddSubConstants; use singer_utils::{ chip_handler::{ BytecodeChipOperations, GlobalStateChipOperations, OAMOperations, ROMOperations, @@ -11,7 +12,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::UIntAddSub, }; use std::sync::Arc; @@ -28,14 +28,14 @@ impl InstructionGraph for PushInstruction< register_witness!( PushInstruction, phase0 { - pc => PCUInt::N_OPRAND_CELLS, - stack_ts => TSUInt::N_OPRAND_CELLS, - memory_ts => TSUInt::N_OPRAND_CELLS, + pc => PCUInt::N_OPERAND_CELLS, + stack_ts => TSUInt::N_OPERAND_CELLS, + memory_ts => TSUInt::N_OPERAND_CELLS, stack_top => 1, clk => 1, - pc_add_i_plus_1 => N * UIntAddSub::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_CELLS, + pc_add_i_plus_1 => N * AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, stack_bytes => N } @@ -98,7 +98,7 @@ impl Instruction for PushInstruction { rom_handler.range_check_stack_top(&mut circuit_builder, stack_top_expr)?; let stack_bytes = &phase0[Self::phase0_stack_bytes()]; - let stack_values = StackUInt::from_bytes_big_endien(&mut circuit_builder, stack_bytes)?; + let stack_values = StackUInt::from_bytes_big_endian(&mut circuit_builder, stack_bytes)?; // Push value to stack ram_handler.stack_push( &mut circuit_builder, @@ -114,7 +114,7 @@ impl Instruction for PushInstruction { >::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) + .chunks(AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS) .enumerate() { let next_pc = @@ -221,7 +221,7 @@ mod test { 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(2u64), // first TSUInt::N_RANGE_CELLS = 1*(56/16) = 4 cells are range values, stack_ts + 1 = 4 Goldilocks::from(0u64), Goldilocks::from(0u64), Goldilocks::from(0u64), diff --git a/singer/src/instructions/ret.rs b/singer/src/instructions/ret.rs index ac24c2efa..4a160031f 100644 --- a/singer/src/instructions/ret.rs +++ b/singer/src/instructions/ret.rs @@ -4,6 +4,7 @@ use gkr::structs::Circuit; use gkr_graph::structs::{CircuitGraphBuilder, NodeOutputType, PredType}; use paste::paste; use simple_frontend::structs::{CircuitBuilder, MixedCell}; +use singer_utils::uint::constants::AddSubConstants; use singer_utils::{ chip_handler::{ BytecodeChipOperations, GlobalStateChipOperations, OAMOperations, ROMOperations, @@ -13,7 +14,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::UIntAddSub, }; use std::{mem, sync::Arc}; @@ -262,17 +262,17 @@ impl InstructionGraph for ReturnInstruction { register_witness!( ReturnInstruction, phase0 { - pc => PCUInt::N_OPRAND_CELLS, - stack_ts => TSUInt::N_OPRAND_CELLS, - memory_ts => TSUInt::N_OPRAND_CELLS, + pc => PCUInt::N_OPERAND_CELLS, + stack_ts => TSUInt::N_OPERAND_CELLS, + memory_ts => TSUInt::N_OPERAND_CELLS, stack_top => 1, clk => 1, - old_stack_ts0 => TSUInt::N_OPRAND_CELLS, - old_stack_ts1 => TSUInt::N_OPRAND_CELLS, + old_stack_ts0 => TSUInt::N_OPERAND_CELLS, + old_stack_ts1 => TSUInt::N_OPERAND_CELLS, - offset => StackUInt::N_OPRAND_CELLS, - mem_length => StackUInt::N_OPRAND_CELLS + offset => StackUInt::N_OPERAND_CELLS, + mem_length => StackUInt::N_OPERAND_CELLS } ); @@ -341,7 +341,7 @@ impl Instruction for ReturnInstruction { // Copy length to the target wire. let (target_wire_id, target) = - circuit_builder.create_witness_out(StackUInt::N_OPRAND_CELLS); + circuit_builder.create_witness_out(StackUInt::N_OPERAND_CELLS); let length = length.values(); for i in 1..length.len() { circuit_builder.assert_const(length[i], 0); @@ -377,15 +377,15 @@ impl Instruction for ReturnInstruction { register_witness!( ReturnPublicOutLoad, pred { - offset => StackUInt::N_OPRAND_CELLS + offset => StackUInt::N_OPERAND_CELLS }, public_io { byte => 1 }, phase0 { - old_memory_ts => TSUInt::N_OPRAND_CELLS, + old_memory_ts => TSUInt::N_OPERAND_CELLS, - offset_add => UIntAddSub::::N_WITNESS_CELLS + offset_add => AddSubConstants::::N_WITNESS_CELLS } ); @@ -403,7 +403,7 @@ impl ReturnPublicOutLoad { let delta = circuit_builder.create_counter_in(0); let offset = StackUInt::try_from(&pred[Self::pred_offset()])?; let offset_add_delta_witness = &phase0[Self::phase0_offset_add()]; - let new_offset = UIntAddSub::::add_small( + let new_offset = StackUInt::add_cell( &mut circuit_builder, &mut rom_handler, &offset, @@ -447,8 +447,8 @@ register_witness!( ReturnRestMemLoad, phase0 { mem_byte => 1, - offset => StackUInt::N_OPRAND_CELLS, - old_memory_ts => TSUInt::N_OPRAND_CELLS + offset => StackUInt::N_OPERAND_CELLS, + old_memory_ts => TSUInt::N_OPERAND_CELLS } ); @@ -495,7 +495,7 @@ register_witness!( ReturnRestMemStore, phase0 { mem_byte => 1, - offset => StackUInt::N_OPRAND_CELLS + offset => StackUInt::N_OPERAND_CELLS } ); @@ -510,7 +510,7 @@ impl ReturnRestMemStore { // Load from memory let offset = &phase0[Self::phase0_offset()]; let mem_byte = phase0[Self::phase0_mem_byte().start]; - let memory_ts = circuit_builder.create_cells(StackUInt::N_OPRAND_CELLS); + let memory_ts = circuit_builder.create_cells(StackUInt::N_OPERAND_CELLS); ram_handler.oam_store(&mut circuit_builder, offset, &memory_ts, &[mem_byte]); let (ram_load_id, ram_store_id) = ram_handler.finalize(&mut circuit_builder); @@ -538,8 +538,8 @@ pub struct ReturnRestStackPop; register_witness!( ReturnRestStackPop, phase0 { - old_stack_ts => TSUInt::N_OPRAND_CELLS, - stack_values => StackUInt::N_OPRAND_CELLS + old_stack_ts => TSUInt::N_OPERAND_CELLS, + stack_values => StackUInt::N_OPERAND_CELLS } ); diff --git a/singer/src/instructions/swap.rs b/singer/src/instructions/swap.rs index 5c944652a..fdf52aa17 100644 --- a/singer/src/instructions/swap.rs +++ b/singer/src/instructions/swap.rs @@ -3,6 +3,7 @@ use ff_ext::ExtensionField; use gkr::structs::Circuit; use paste::paste; use simple_frontend::structs::{CircuitBuilder, MixedCell}; +use singer_utils::uint::constants::AddSubConstants; use singer_utils::{ chip_handler::{ BytecodeChipOperations, GlobalStateChipOperations, OAMOperations, ROMOperations, @@ -11,7 +12,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::{UIntAddSub, UIntCmp}, }; use std::sync::Arc; @@ -27,21 +27,21 @@ impl InstructionGraph for SwapInstruction< register_witness!( SwapInstruction, phase0 { - pc => PCUInt::N_OPRAND_CELLS, - stack_ts => TSUInt::N_OPRAND_CELLS, - memory_ts => TSUInt::N_OPRAND_CELLS, + pc => PCUInt::N_OPERAND_CELLS, + stack_ts => TSUInt::N_OPERAND_CELLS, + memory_ts => TSUInt::N_OPERAND_CELLS, stack_top => 1, clk => 1, - pc_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_CELLS, + pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, - old_stack_ts_1 => TSUInt::N_OPRAND_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_WITNESS_CELLS, - stack_values_1 => StackUInt::N_OPRAND_CELLS, - stack_values_n_plus_1 => StackUInt::N_OPRAND_CELLS + old_stack_ts_1 => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt_1 => AddSubConstants::::N_WITNESS_CELLS, + old_stack_ts_n_plus_1 => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt_n_plus_1 => AddSubConstants::::N_WITNESS_CELLS, + stack_values_1 => StackUInt::N_OPERAND_CELLS, + stack_values_n_plus_1 => StackUInt::N_OPERAND_CELLS } ); @@ -107,7 +107,7 @@ impl Instruction for SwapInstruction { // Pop rlc of stack[top - (N + 1)] from stack let old_stack_ts_n_plus_1 = (&phase0[Self::phase0_old_stack_ts_n_plus_1()]).try_into()?; - UIntCmp::::assert_lt( + TSUInt::assert_lt( &mut circuit_builder, &mut rom_handler, &old_stack_ts_n_plus_1, @@ -124,7 +124,7 @@ impl Instruction for SwapInstruction { // Pop rlc of stack[top - 1] from stack let old_stack_ts_1 = (&phase0[Self::phase0_old_stack_ts_1()]).try_into()?; - UIntCmp::::assert_lt( + TSUInt::assert_lt( &mut circuit_builder, &mut rom_handler, &old_stack_ts_1, @@ -279,7 +279,7 @@ mod test { 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(5u64), // first TSUInt::N_RANGE_CELLS = 1*(56/16) = 4 cells are range values, stack_ts + 1 = 4 Goldilocks::from(0u64), Goldilocks::from(0u64), Goldilocks::from(0u64), @@ -291,14 +291,13 @@ mod test { 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); + let range_values = u2vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); phase0_values_map.insert( "phase0_old_stack_ts_lt_1".to_string(), vec![ Goldilocks::from(range_values[0]), Goldilocks::from(range_values[1]), Goldilocks::from(range_values[2]), - Goldilocks::from(range_values[3]), Goldilocks::from(1u64), // current length has no cells for borrow ], ); @@ -307,14 +306,13 @@ mod test { 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); + let range_values = u2vec::<{ TSUInt::N_RANGE_CELLS }, RANGE_CHIP_BIT_WIDTH>(m); phase0_values_map.insert( "phase0_old_stack_ts_lt_n_plus_1".to_string(), vec![ Goldilocks::from(range_values[0]), Goldilocks::from(range_values[1]), Goldilocks::from(range_values[2]), - Goldilocks::from(range_values[3]), Goldilocks::from(1u64), // current length has no cells for borrow ], ); diff --git a/singer/src/test.rs b/singer/src/test.rs index 849189379..30c775fb7 100644 --- a/singer/src/test.rs +++ b/singer/src/test.rs @@ -7,7 +7,7 @@ use gkr::utils::MultilinearExtensionFromVectors; use goldilocks::SmallField; use itertools::Itertools; use simple_frontend::structs::CellId; -use singer_utils::structs::UInt; +use singer_utils::uint::UInt; use std::collections::BTreeMap; use crate::instructions::InstCircuit; @@ -84,6 +84,7 @@ pub(crate) fn test_opcode_circuit( #[cfg(feature = "test-dbg")] println!("{:?}", circuit_witness); + // dbg!(&circuit_witness); circuit_witness.check_correctness(&circuit);