From cba09be46e76a29ed7a4a955e119d1aa718a2680 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Wed, 29 May 2024 08:17:17 +0100 Subject: [PATCH 01/50] confirm const across file impl --- singer-utils/src/lib.rs | 1 + singer-utils/src/uint.rs | 4 +++- singer-utils/src/uint_new/constants.rs | 12 ++++++++++++ singer-utils/src/uint_new/mod.rs | 14 ++++++++++++++ singer-utils/src/uint_new/uint.rs | 13 +++++++++++++ 5 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 singer-utils/src/uint_new/constants.rs create mode 100644 singer-utils/src/uint_new/mod.rs create mode 100644 singer-utils/src/uint_new/uint.rs diff --git a/singer-utils/src/lib.rs b/singer-utils/src/lib.rs index 5e98a75b6..84d53882f 100644 --- a/singer-utils/src/lib.rs +++ b/singer-utils/src/lib.rs @@ -6,6 +6,7 @@ pub mod constants; pub mod error; pub mod structs; pub mod uint; +mod uint_new; #[macro_use] pub mod macros; diff --git a/singer-utils/src/uint.rs b/singer-utils/src/uint.rs index 6b3d524f0..edad0527c 100644 --- a/singer-utils/src/uint.rs +++ b/singer-utils/src/uint.rs @@ -1,3 +1,5 @@ +// TODO: delete this + use ff::Field; use gkr::utils::ceil_log2; use goldilocks::SmallField; @@ -160,7 +162,7 @@ fn convert_decomp( circuit_builder.add( tmp, small_values[k], - F::BaseField::from((1 as u64) << k-j * small_bit_width), + F::BaseField::from((1 as u64) << k - j * small_bit_width), ); } tmp diff --git a/singer-utils/src/uint_new/constants.rs b/singer-utils/src/uint_new/constants.rs new file mode 100644 index 000000000..aaac9a952 --- /dev/null +++ b/singer-utils/src/uint_new/constants.rs @@ -0,0 +1,12 @@ +use super::uint::UInt; + +// TODO: determine constant access controls + +impl UInt { + pub(crate) const HMM: usize = M + C; + + pub fn hello() { + dbg!("hello"); + dbg!(Self::HMM); + } +} diff --git a/singer-utils/src/uint_new/mod.rs b/singer-utils/src/uint_new/mod.rs new file mode 100644 index 000000000..bab9fc195 --- /dev/null +++ b/singer-utils/src/uint_new/mod.rs @@ -0,0 +1,14 @@ +mod constants; +mod uint; + +#[cfg(test)] +mod tests { + use super::*; + use crate::uint_new::uint::UInt; + + #[test] + fn hello_bye() { + UInt::<5, 6>::hello(); + UInt::<5, 6>::bye(); + } +} diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs new file mode 100644 index 000000000..1000ef3a8 --- /dev/null +++ b/singer-utils/src/uint_new/uint.rs @@ -0,0 +1,13 @@ +use simple_frontend::structs::CellId; + +pub struct UInt { + // TODO: handle access control + pub values: Vec, +} + +impl UInt { + pub fn bye() { + dbg!("bye"); + dbg!(Self::HMM); + } +} From 7b6c70b5e2a9dc5a5fc439ea77d78f1687c14ad4 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Wed, 29 May 2024 10:34:40 +0100 Subject: [PATCH 02/50] implement try from cell_id's to uint --- singer-utils/src/uint_new/constants.rs | 8 ++----- singer-utils/src/uint_new/mod.rs | 12 ---------- singer-utils/src/uint_new/uint.rs | 33 ++++++++++++++++++++++---- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/singer-utils/src/uint_new/constants.rs b/singer-utils/src/uint_new/constants.rs index aaac9a952..8f6d43d9c 100644 --- a/singer-utils/src/uint_new/constants.rs +++ b/singer-utils/src/uint_new/constants.rs @@ -3,10 +3,6 @@ use super::uint::UInt; // TODO: determine constant access controls impl UInt { - pub(crate) const HMM: usize = M + C; - - pub fn hello() { - dbg!("hello"); - dbg!(Self::HMM); - } + // TODO: explain why this and how this + pub(crate) const N_OPERAND_CELLS: usize = (M + C - 1) / C; } diff --git a/singer-utils/src/uint_new/mod.rs b/singer-utils/src/uint_new/mod.rs index bab9fc195..7a8c35015 100644 --- a/singer-utils/src/uint_new/mod.rs +++ b/singer-utils/src/uint_new/mod.rs @@ -1,14 +1,2 @@ mod constants; mod uint; - -#[cfg(test)] -mod tests { - use super::*; - use crate::uint_new::uint::UInt; - - #[test] - fn hello_bye() { - UInt::<5, 6>::hello(); - UInt::<5, 6>::bye(); - } -} diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs index 1000ef3a8..d4a16a0eb 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint_new/uint.rs @@ -1,13 +1,38 @@ +use crate::error::UtilError; use simple_frontend::structs::CellId; +/// Unsigned integer with `M` total bits. `C` denotes the cell bit width. pub struct UInt { // TODO: handle access control pub values: Vec, } -impl UInt { - pub fn bye() { - dbg!("bye"); - dbg!(Self::HMM); +// do we need another try from for the Vec?? +// we convert the slice to a vec +// won't make sense to do that for a Vec also +// input vec expected Self(vec) +// input slice expected slice -> vec -> Self(vec) +// hence the vec can come first + +// TODO: add documentation + test +impl TryFrom> for UInt { + type Error = UtilError; + + fn try_from(values: Vec) -> Result { + if values.len() != Self::N_OPERAND_CELLS { + // TODO: think about error handling, might need something more specific + return Err(UtilError::UIntError); + } + + Ok(Self { values }) + } +} + +// TODO: add documentation + test +impl TryFrom<&[CellId]> for UInt { + type Error = UtilError; + + fn try_from(values: &[CellId]) -> Result { + values.to_vec().try_into() } } From 18cfcac580900100b8f8d6fde87265bce0d5cd74 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Wed, 29 May 2024 15:41:58 +0100 Subject: [PATCH 03/50] add test for uint from cell ids --- singer-utils/src/error.rs | 3 ++- singer-utils/src/uint.rs | 3 ++- singer-utils/src/uint_new/uint.rs | 24 ++++++++++++++++++++++-- 3 files changed, 26 insertions(+), 4 deletions(-) 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/uint.rs b/singer-utils/src/uint.rs index 18f22882e..28574cd06 100644 --- a/singer-utils/src/uint.rs +++ b/singer-utils/src/uint.rs @@ -17,7 +17,8 @@ impl TryFrom<&[usize]> for UInt { type Error = UtilError; fn try_from(values: &[usize]) -> Result { if values.len() != Self::N_OPRAND_CELLS { - return Err(UtilError::UIntError); + // TODO: this will be replaced soon + return Err(UtilError::UIntError("placeholder".to_string())); } Ok(Self { values: values.to_vec(), diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs index d4a16a0eb..b306702a8 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint_new/uint.rs @@ -20,8 +20,13 @@ impl TryFrom> for UInt { fn try_from(values: Vec) -> Result { if values.len() != Self::N_OPERAND_CELLS { - // TODO: think about error handling, might need something more specific - return Err(UtilError::UIntError); + return Err(UtilError::UIntError(format!( + "cannot construct UInt<{}, {}> from {} cells, requires {} cells", + M, + C, + values.len(), + Self::N_OPERAND_CELLS + ))); } Ok(Self { values }) @@ -36,3 +41,18 @@ impl TryFrom<&[CellId]> for UInt { values.to_vec().try_into() } } + +#[cfg(test)] +mod tests { + use crate::uint_new::uint::UInt; + + #[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 UInt64 = UInt<33, 4>; + assert!(UInt64::try_from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).is_ok()); + assert!(UInt64::try_from(vec![1, 2, 3]).is_err()); + } +} From a2b39d640f2ffda387b0e36d3a98f90d66a3d9fc Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Wed, 29 May 2024 15:45:38 +0100 Subject: [PATCH 04/50] add documentation --- singer-utils/src/uint_new/uint.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs index b306702a8..34edbd6a1 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint_new/uint.rs @@ -3,18 +3,10 @@ use simple_frontend::structs::CellId; /// Unsigned integer with `M` total bits. `C` denotes the cell bit width. pub struct UInt { - // TODO: handle access control - pub values: Vec, + values: Vec, } -// do we need another try from for the Vec?? -// we convert the slice to a vec -// won't make sense to do that for a Vec also -// input vec expected Self(vec) -// input slice expected slice -> vec -> Self(vec) -// hence the vec can come first - -// TODO: add documentation + test +/// Construct `UInt` from `Vec` impl TryFrom> for UInt { type Error = UtilError; @@ -33,7 +25,7 @@ impl TryFrom> for UInt { } } -// TODO: add documentation + test +/// Construct `UInt` from `$[CellId]` impl TryFrom<&[CellId]> for UInt { type Error = UtilError; From 9bd42ed74e5e8635fcf10e53b49b20bdb6138a6e Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Wed, 29 May 2024 16:04:40 +0100 Subject: [PATCH 05/50] document N_OPERAND_CELLS constant --- singer-utils/src/uint_new/constants.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/singer-utils/src/uint_new/constants.rs b/singer-utils/src/uint_new/constants.rs index 8f6d43d9c..13f1f6a47 100644 --- a/singer-utils/src/uint_new/constants.rs +++ b/singer-utils/src/uint_new/constants.rs @@ -1,8 +1,7 @@ use super::uint::UInt; -// TODO: determine constant access controls - impl UInt { - // TODO: explain why this and how this + /// `N_OPERAND_CELLS` represent the minimum number of cells each of size `C` needed + /// to hold `M` total bits pub(crate) const N_OPERAND_CELLS: usize = (M + C - 1) / C; } From 38181033fcbc359abeb50035584c9eee2fd683f4 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 30 May 2024 08:39:06 +0100 Subject: [PATCH 06/50] wip --- singer-utils/src/uint_new/uint.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs index 34edbd6a1..d5d768783 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint_new/uint.rs @@ -6,6 +6,25 @@ pub struct UInt { values: Vec, } +impl UInt { + // TODO: add documentation + fn values(&self) -> &[CellId] { + &self.values + } + + // TODO: add documentation + fn from_range_values() { + // need to implement bit fitting (into new cell types) + todo!() + } + + // TODO: add documentation + fn from_bytes_bit_endian() { + // need to implement bit fitting (into new cell types first) + todo!() + } +} + /// Construct `UInt` from `Vec` impl TryFrom> for UInt { type Error = UtilError; From 74516f38ff1bbf3a006fb8e195f03ef0ad4e56ee Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Fri, 31 May 2024 13:42:10 +0100 Subject: [PATCH 07/50] test cell repacking --- singer-utils/src/uint_new/mod.rs | 1 + singer-utils/src/uint_new/uint.rs | 2 + singer-utils/src/uint_new/util.rs | 172 ++++++++++++++++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 singer-utils/src/uint_new/util.rs diff --git a/singer-utils/src/uint_new/mod.rs b/singer-utils/src/uint_new/mod.rs index 7a8c35015..43b55e7f1 100644 --- a/singer-utils/src/uint_new/mod.rs +++ b/singer-utils/src/uint_new/mod.rs @@ -1,2 +1,3 @@ mod constants; mod uint; +mod util; diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs index d5d768783..f5bf98f77 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint_new/uint.rs @@ -3,6 +3,7 @@ use simple_frontend::structs::CellId; /// Unsigned integer with `M` total bits. `C` denotes the cell bit width. pub struct UInt { + // TODO: we need to ensure C is not bigger than the system word length values: Vec, } @@ -20,6 +21,7 @@ impl UInt { // TODO: add documentation fn from_bytes_bit_endian() { + // TODO: what about little endian // need to implement bit fitting (into new cell types first) todo!() } diff --git a/singer-utils/src/uint_new/util.rs b/singer-utils/src/uint_new/util.rs new file mode 100644 index 000000000..2e687651b --- /dev/null +++ b/singer-utils/src/uint_new/util.rs @@ -0,0 +1,172 @@ +// what do we call this convert_decomp +// what is it actually doing? +// basically, we need a way to convert the bit width to something else +// and the something else can be bigger or smaller +// what is the circuit builder used for here? +// it creates new cells that represent the new values +// seems you might not be able to split something big into something smaller + +// v1 assume the bit change is strictly increasing + no packing maximization +// v2 assume packing maximization +// v3 assume can increase or decrease +// there might be tradeoff so v3 is not necessarily better than previous versions + +// TODO: explain tradeoffs with other potential approaches + +use crate::error::UtilError; +use ff_ext::ExtensionField; +use simple_frontend::structs::{CellId, CircuitBuilder}; + +// TODO: add documentation +fn pack_small_width_cells_into_bigger_width_cells( + circuit_builder: &mut CircuitBuilder, + small_cells: &[CellId], + small_cell_bit_width: usize, + big_cell_bit_width: usize, + is_little_endian: bool, +) -> Result, UtilError> { + // TODO: technically there is a limit on the bit width (based on the field size), + // we should handle this edge case + + 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()); + } + + // TODO: think about the effect of endianness + // 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; + + // TODO: specify example that outlines what's happening here + + let mut new_cell_ids = vec![]; + + 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_cell_count_per_big_cell - small_chunk_index - 1) * 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) +} + +#[cfg(test)] +mod tests { + use crate::uint_new::util::pack_small_width_cells_into_bigger_width_cells; + use gkr::structs::{Circuit, CircuitWitness}; + use goldilocks::{Goldilocks, GoldilocksExt2}; + 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 = pack_small_width_cells_into_bigger_width_cells( + &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 = pack_small_width_cells_into_bigger_width_cells( + &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 (witness_id, small_values) = circuit_builder.create_witness_in(9); + let small_bit_width = 2; + let big_bit_width = 5; + let big_values = pack_small_width_cells_into_bigger_width_cells( + &mut circuit_builder, + &small_values, + small_bit_width, + big_bit_width, + true, + ) + .unwrap(); + assert_eq!(big_values.len(), 5); + + // 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 5 + // we can only fit two 2-bit cells into a 5 bit cell + // 1100 1011 0110 0101 1100 (bit representation) + // 12 11 6 5 12 (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 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(); + + // TODO: why is the full output length 8 instead of 5? + // what are the extra zeros for + // assert_eq!(output.len(), 5); + + assert_eq!( + &output[..5], + vec![12, 11, 6, 5, 12] + .into_iter() + .map(|v| Goldilocks::from(v)) + .collect::>() + ); + } +} From 31a51c1380f54877aa18a334dfd02dc174a7eb8f Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Fri, 31 May 2024 14:48:50 +0100 Subject: [PATCH 08/50] add documentation --- singer-utils/src/uint_new/uint.rs | 6 +++--- singer-utils/src/uint_new/util.rs | 31 +++++++++++-------------------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs index f5bf98f77..c8f545c58 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint_new/uint.rs @@ -3,7 +3,7 @@ use simple_frontend::structs::CellId; /// Unsigned integer with `M` total bits. `C` denotes the cell bit width. pub struct UInt { - // TODO: we need to ensure C is not bigger than the system word length + // TODO: the size of C should not be more than the size of the underlying field values: Vec, } @@ -15,14 +15,14 @@ impl UInt { // TODO: add documentation fn from_range_values() { - // need to implement bit fitting (into new cell types) + // need to implement bit fitting (into new cell width) todo!() } // TODO: add documentation fn from_bytes_bit_endian() { // TODO: what about little endian - // need to implement bit fitting (into new cell types first) + // need to implement bit fitting (into new cell width first) todo!() } } diff --git a/singer-utils/src/uint_new/util.rs b/singer-utils/src/uint_new/util.rs index 2e687651b..360e7b3a6 100644 --- a/singer-utils/src/uint_new/util.rs +++ b/singer-utils/src/uint_new/util.rs @@ -1,23 +1,16 @@ -// what do we call this convert_decomp -// what is it actually doing? -// basically, we need a way to convert the bit width to something else -// and the something else can be bigger or smaller -// what is the circuit builder used for here? -// it creates new cells that represent the new values -// seems you might not be able to split something big into something smaller - -// v1 assume the bit change is strictly increasing + no packing maximization -// v2 assume packing maximization -// v3 assume can increase or decrease -// there might be tradeoff so v3 is not necessarily better than previous versions - -// TODO: explain tradeoffs with other potential approaches - use crate::error::UtilError; use ff_ext::ExtensionField; use simple_frontend::structs::{CellId, CircuitBuilder}; -// TODO: add documentation +/// 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 fn pack_small_width_cells_into_bigger_width_cells( circuit_builder: &mut CircuitBuilder, small_cells: &[CellId], @@ -27,7 +20,7 @@ fn pack_small_width_cells_into_bigger_width_cells( ) -> Result, UtilError> { // TODO: technically there is a limit on the bit width (based on the field size), // we should handle this edge case - + // not sure this should (or can) be handled here tho if small_cell_bit_width > big_cell_bit_width { return Err(UtilError::UIntError( "cannot pack bigger width cells into smaller width cells".to_string(), @@ -38,7 +31,6 @@ fn pack_small_width_cells_into_bigger_width_cells( return Ok(small_cells.to_vec()); } - // TODO: think about the effect of endianness // 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() @@ -49,10 +41,9 @@ fn pack_small_width_cells_into_bigger_width_cells( // 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; - // TODO: specify example that outlines what's happening here - 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() { From c3e24e9a1fea32eabd2912dfd62cd89631c7849a Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Fri, 31 May 2024 15:30:13 +0100 Subject: [PATCH 09/50] update packing test, ensure remaining values are 0s --- singer-utils/src/uint_new/util.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/singer-utils/src/uint_new/util.rs b/singer-utils/src/uint_new/util.rs index 360e7b3a6..1d686d047 100644 --- a/singer-utils/src/uint_new/util.rs +++ b/singer-utils/src/uint_new/util.rs @@ -11,7 +11,7 @@ use simple_frontend::structs::{CellId, CircuitBuilder}; /// small -> 11 | 00 /// we can pack this into a single big cell of size 4 /// big -> 1100 -fn pack_small_width_cells_into_bigger_width_cells( +fn convert_decomp( circuit_builder: &mut CircuitBuilder, small_cells: &[CellId], small_cell_bit_width: usize, @@ -63,9 +63,10 @@ fn pack_small_width_cells_into_bigger_width_cells( #[cfg(test)] mod tests { - use crate::uint_new::util::pack_small_width_cells_into_bigger_width_cells; + use crate::uint_new::util::convert_decomp; use gkr::structs::{Circuit, CircuitWitness}; use goldilocks::{Goldilocks, GoldilocksExt2}; + use itertools::Itertools; use simple_frontend::structs::CircuitBuilder; #[test] @@ -75,7 +76,7 @@ mod tests { let (_, big_values) = circuit_builder.create_witness_in(5); let big_bit_width = 5; let small_bit_width = 2; - let cell_packing_result = pack_small_width_cells_into_bigger_width_cells( + let cell_packing_result = convert_decomp( &mut circuit_builder, &big_values, big_bit_width, @@ -91,7 +92,7 @@ mod tests { let (_, initial_values) = circuit_builder.create_witness_in(5); let small_bit_width = 2; let big_bit_width = 2; - let new_values = pack_small_width_cells_into_bigger_width_cells( + let new_values = convert_decomp( &mut circuit_builder, &initial_values, small_bit_width, @@ -108,7 +109,7 @@ mod tests { let (witness_id, small_values) = circuit_builder.create_witness_in(9); let small_bit_width = 2; let big_bit_width = 5; - let big_values = pack_small_width_cells_into_bigger_width_cells( + let big_values = convert_decomp( &mut circuit_builder, &small_values, small_bit_width, @@ -148,10 +149,6 @@ mod tests { let output = circuit_witness.output_layer_witness_ref().instances[0].to_vec(); - // TODO: why is the full output length 8 instead of 5? - // what are the extra zeros for - // assert_eq!(output.len(), 5); - assert_eq!( &output[..5], vec![12, 11, 6, 5, 12] @@ -159,5 +156,13 @@ mod tests { .map(|v| Goldilocks::from(v)) .collect::>() ); + + assert_eq!( + &output[5..], + vec![0, 0, 0] + .into_iter() + .map(|v| Goldilocks::from(v)) + .collect_vec() + ); } } From e94e1e595bc8282369cb885cc369e6e3dba6e067 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 3 Jun 2024 08:56:01 +0100 Subject: [PATCH 10/50] add more uint instantiation methods --- singer-utils/src/constants.rs | 1 + singer-utils/src/uint_new/mod.rs | 2 +- singer-utils/src/uint_new/uint.rs | 64 +++++++++++++++++++++++++------ singer-utils/src/uint_new/util.rs | 2 +- 4 files changed, 56 insertions(+), 13 deletions(-) diff --git a/singer-utils/src/constants.rs b/singer-utils/src/constants.rs index 9a40eece5..54a0f164a 100644 --- a/singer-utils/src/constants.rs +++ b/singer-utils/src/constants.rs @@ -1,5 +1,6 @@ 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/uint_new/mod.rs b/singer-utils/src/uint_new/mod.rs index 43b55e7f1..1faba8819 100644 --- a/singer-utils/src/uint_new/mod.rs +++ b/singer-utils/src/uint_new/mod.rs @@ -1,3 +1,3 @@ mod constants; mod uint; -mod util; +pub mod util; diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs index c8f545c58..5f0332c51 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint_new/uint.rs @@ -1,5 +1,8 @@ +use crate::constants::{BYTE_BIT_WIDTH, RANGE_CHIP_BIT_WIDTH}; use crate::error::UtilError; -use simple_frontend::structs::CellId; +use crate::uint_new::util::convert_decomp; +use ff_ext::ExtensionField; +use simple_frontend::structs::{Cell, CellId, CircuitBuilder}; /// Unsigned integer with `M` total bits. `C` denotes the cell bit width. pub struct UInt { @@ -8,22 +11,61 @@ pub struct UInt { } impl UInt { - // TODO: add documentation + /// Return the `UInt` underlying cell id's fn values(&self) -> &[CellId] { &self.values } - // TODO: add documentation - fn from_range_values() { - // need to implement bit fitting (into new cell width) - todo!() + /// Builds a `UInt` instance from a set of cells that represent `RANGE_VALUES` + fn from_range_values( + circuit_builder: &mut CircuitBuilder, + range_values: &[CellId], + ) -> Result { + let max_cell_width = M.min(C); + let mut values = convert_decomp( + circuit_builder, + range_values, + RANGE_CHIP_BIT_WIDTH, + max_cell_width, + true, + )?; + if values.len() < Self::N_OPERAND_CELLS { + values.extend(circuit_builder.create_cells(Self::N_OPERAND_CELLS - values.len())); + } + values.try_into() + } + + /// Builds a `UInt` instance from a set of cells that represent big-endian `BYTE_VALUES` + 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` + fn from_bytes_little_endian( + circuit_builder: &mut CircuitBuilder, + bytes: &[CellId], + ) -> Result { + Self::from_bytes(circuit_builder, bytes, true) } - // TODO: add documentation - fn from_bytes_bit_endian() { - // TODO: what about little endian - // need to implement bit fitting (into new cell width first) - todo!() + /// Builds a `UInt` instance from a set of cells that represent `BYTE_VALUES` + fn from_bytes( + circuit_builder: &mut CircuitBuilder, + bytes: &[CellId], + is_little_endian: bool, + ) -> Result { + let max_cell_width = M.min(C); + convert_decomp( + circuit_builder, + bytes, + BYTE_BIT_WIDTH, + max_cell_width, + is_little_endian, + ) + .try_into() } } diff --git a/singer-utils/src/uint_new/util.rs b/singer-utils/src/uint_new/util.rs index 1d686d047..77bad8753 100644 --- a/singer-utils/src/uint_new/util.rs +++ b/singer-utils/src/uint_new/util.rs @@ -11,7 +11,7 @@ use simple_frontend::structs::{CellId, CircuitBuilder}; /// small -> 11 | 00 /// we can pack this into a single big cell of size 4 /// big -> 1100 -fn convert_decomp( +pub fn convert_decomp( circuit_builder: &mut CircuitBuilder, small_cells: &[CellId], small_cell_bit_width: usize, From 97676c81ea34f90aca5ad6dc8ac5273de468369e Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 3 Jun 2024 09:48:41 +0100 Subject: [PATCH 11/50] add test placeholders --- singer-utils/src/uint_new/uint.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs index 5f0332c51..f71265a21 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint_new/uint.rs @@ -110,4 +110,14 @@ mod tests { assert!(UInt64::try_from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).is_ok()); assert!(UInt64::try_from(vec![1, 2, 3]).is_err()); } + + #[test] + fn test_uint_from_range_values() { + // TODO: implement test + } + + #[test] + fn test_uint_from_bytes() { + // TODO: implement test + } } From a283f776685e8e1102182fdb6a5f25d735bc4a40 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 3 Jun 2024 10:05:08 +0100 Subject: [PATCH 12/50] sketch base plan for cmp --- singer-utils/src/uint_new/cmp.rs | 22 ++++++++++++++++++++++ singer-utils/src/uint_new/mod.rs | 1 + singer-utils/src/uint_new/uint.rs | 2 ++ 3 files changed, 25 insertions(+) create mode 100644 singer-utils/src/uint_new/cmp.rs diff --git a/singer-utils/src/uint_new/cmp.rs b/singer-utils/src/uint_new/cmp.rs new file mode 100644 index 000000000..57923fe39 --- /dev/null +++ b/singer-utils/src/uint_new/cmp.rs @@ -0,0 +1,22 @@ +// TODO: document module +// mostly holds comparison methods on the uint type + +// what methods were implemented previously +// lt +// assert_lt +// assert_leq +// assert_eq +// assert_eq_range_values + +// seems only 1 direction is enough, no need for lt and gt +// why lt then assert_lt but no leq only assert_leq +// will assume no need for it for now and wait until things break + +// going to focus on assert methods +// assert_lt +// assert_leq +// assert_eq +// assert_eq_range_values (this feels weird) +// feels like we should be able to convert the range values to uint +// then implement assert_eq on them +// what is the problem with this diff --git a/singer-utils/src/uint_new/mod.rs b/singer-utils/src/uint_new/mod.rs index 1faba8819..372830334 100644 --- a/singer-utils/src/uint_new/mod.rs +++ b/singer-utils/src/uint_new/mod.rs @@ -1,3 +1,4 @@ +mod cmp; mod constants; mod uint; pub mod util; diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs index f71265a21..78eb29a40 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint_new/uint.rs @@ -29,6 +29,8 @@ impl UInt { max_cell_width, true, )?; + // TODO: move this logic to convert_decomp + // also think about whether it is safe to do so if values.len() < Self::N_OPERAND_CELLS { values.extend(circuit_builder.create_cells(Self::N_OPERAND_CELLS - values.len())); } From 65af7b5ae2cfd44f693ab9ddec5d02dc85b9ba7e Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 3 Jun 2024 10:37:51 +0100 Subject: [PATCH 13/50] implement and test pad cells --- singer-utils/src/uint_new/uint.rs | 7 ++++--- singer-utils/src/uint_new/util.rs | 27 +++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs index 78eb29a40..b6b07b4d4 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint_new/uint.rs @@ -5,6 +5,7 @@ use ff_ext::ExtensionField; use simple_frontend::structs::{Cell, CellId, CircuitBuilder}; /// Unsigned integer with `M` total bits. `C` denotes the cell bit width. +/// Represented in little endian form. pub struct UInt { // TODO: the size of C should not be more than the size of the underlying field values: Vec, @@ -17,6 +18,7 @@ impl UInt { } /// Builds a `UInt` instance from a set of cells that represent `RANGE_VALUES` + /// assumes range_values are represented in little endian form fn from_range_values( circuit_builder: &mut CircuitBuilder, range_values: &[CellId], @@ -29,8 +31,7 @@ impl UInt { max_cell_width, true, )?; - // TODO: move this logic to convert_decomp - // also think about whether it is safe to do so + // TODO: replace this line with pad cell function if values.len() < Self::N_OPERAND_CELLS { values.extend(circuit_builder.create_cells(Self::N_OPERAND_CELLS - values.len())); } @@ -66,7 +67,7 @@ impl UInt { BYTE_BIT_WIDTH, max_cell_width, is_little_endian, - ) + )? .try_into() } } diff --git a/singer-utils/src/uint_new/util.rs b/singer-utils/src/uint_new/util.rs index 77bad8753..5b7de1380 100644 --- a/singer-utils/src/uint_new/util.rs +++ b/singer-utils/src/uint_new/util.rs @@ -61,9 +61,20 @@ pub fn convert_decomp( 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())) + } +} + #[cfg(test)] mod tests { - use crate::uint_new::util::convert_decomp; + use crate::uint_new::util::{convert_decomp, pad_cells}; use gkr::structs::{Circuit, CircuitWitness}; use goldilocks::{Goldilocks, GoldilocksExt2}; use itertools::Itertools; @@ -106,7 +117,7 @@ mod tests { #[test] fn test_pack_small_cells_into_big_cells() { let mut circuit_builder = CircuitBuilder::::new(); - let (witness_id, small_values) = circuit_builder.create_witness_in(9); + let (_, small_values) = circuit_builder.create_witness_in(9); let small_bit_width = 2; let big_bit_width = 5; let big_values = convert_decomp( @@ -165,4 +176,16 @@ mod tests { .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]); + } } From 3a89440ddf90fe519e1a07ea0064d7dab637c0ee Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 3 Jun 2024 10:43:32 +0100 Subject: [PATCH 14/50] modify from_range and from_bytes to use cell padding --- singer-utils/src/uint_new/uint.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs index b6b07b4d4..243dd8b92 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint_new/uint.rs @@ -1,6 +1,6 @@ use crate::constants::{BYTE_BIT_WIDTH, RANGE_CHIP_BIT_WIDTH}; use crate::error::UtilError; -use crate::uint_new::util::convert_decomp; +use crate::uint_new::util::{convert_decomp, pad_cells}; use ff_ext::ExtensionField; use simple_frontend::structs::{Cell, CellId, CircuitBuilder}; @@ -31,10 +31,8 @@ impl UInt { max_cell_width, true, )?; - // TODO: replace this line with pad cell function - if values.len() < Self::N_OPERAND_CELLS { - values.extend(circuit_builder.create_cells(Self::N_OPERAND_CELLS - values.len())); - } + // TODO: is this safe, do we need to ensure that the padded cells are always 0? + pad_cells(circuit_builder, &mut values, Self::N_OPERAND_CELLS); values.try_into() } @@ -61,14 +59,15 @@ impl UInt { is_little_endian: bool, ) -> Result { let max_cell_width = M.min(C); - convert_decomp( + let mut values = convert_decomp( circuit_builder, bytes, BYTE_BIT_WIDTH, max_cell_width, is_little_endian, - )? - .try_into() + )?; + pad_cells(circuit_builder, &mut values, Self::N_OPERAND_CELLS); + values.try_into() } } @@ -117,6 +116,7 @@ mod tests { #[test] fn test_uint_from_range_values() { // TODO: implement test + // first test without padding then test with padding, same for from_bytes } #[test] From eff16cf0c3b5c837feb7dc422a44f12d3bf565aa Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 3 Jun 2024 12:47:46 +0100 Subject: [PATCH 15/50] extract logic for uint from arbitrary cell_width to make from_range and from_bytes easier --- singer-utils/src/uint_new/cmp.rs | 4 ++-- singer-utils/src/uint_new/uint.rs | 31 +++++++++++++++++++++---------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/singer-utils/src/uint_new/cmp.rs b/singer-utils/src/uint_new/cmp.rs index 57923fe39..40f438797 100644 --- a/singer-utils/src/uint_new/cmp.rs +++ b/singer-utils/src/uint_new/cmp.rs @@ -18,5 +18,5 @@ // assert_eq // assert_eq_range_values (this feels weird) // feels like we should be able to convert the range values to uint -// then implement assert_eq on them -// what is the problem with this +// then run assert_eq on them +// what is the problem with this? diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs index 243dd8b92..9731b6fa4 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint_new/uint.rs @@ -2,7 +2,7 @@ use crate::constants::{BYTE_BIT_WIDTH, RANGE_CHIP_BIT_WIDTH}; use crate::error::UtilError; use crate::uint_new::util::{convert_decomp, pad_cells}; use ff_ext::ExtensionField; -use simple_frontend::structs::{Cell, CellId, CircuitBuilder}; +use simple_frontend::structs::{CellId, CircuitBuilder}; /// Unsigned integer with `M` total bits. `C` denotes the cell bit width. /// Represented in little endian form. @@ -23,17 +23,12 @@ impl UInt { circuit_builder: &mut CircuitBuilder, range_values: &[CellId], ) -> Result { - let max_cell_width = M.min(C); - let mut values = convert_decomp( + Self::from_different_sized_cell_values( circuit_builder, range_values, RANGE_CHIP_BIT_WIDTH, - max_cell_width, true, - )?; - // TODO: is this safe, do we need to ensure that the padded cells are always 0? - pad_cells(circuit_builder, &mut values, Self::N_OPERAND_CELLS); - values.try_into() + ) } /// Builds a `UInt` instance from a set of cells that represent big-endian `BYTE_VALUES` @@ -58,14 +53,30 @@ impl UInt { bytes: &[CellId], is_little_endian: bool, ) -> Result { - let max_cell_width = M.min(C); - let mut values = convert_decomp( + 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 max_cell_width = M.min(C); + let mut values = convert_decomp( + circuit_builder, + cell_values, + cell_width, max_cell_width, is_little_endian, )?; + // TODO: is this safe, do we need to ensure that the padded cells are always 0? pad_cells(circuit_builder, &mut values, Self::N_OPERAND_CELLS); values.try_into() } From e9d2a41cf7eb10c34a3305ef1840fb81b312163c Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 3 Jun 2024 13:15:47 +0100 Subject: [PATCH 16/50] add compile time evaluated min function --- singer-utils/src/uint_new/constants.rs | 6 ++++++ singer-utils/src/uint_new/util.rs | 19 ++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/singer-utils/src/uint_new/constants.rs b/singer-utils/src/uint_new/constants.rs index 13f1f6a47..87b4c3dc1 100644 --- a/singer-utils/src/uint_new/constants.rs +++ b/singer-utils/src/uint_new/constants.rs @@ -1,7 +1,13 @@ use super::uint::UInt; +use crate::uint_new::util::const_min; impl UInt { /// `N_OPERAND_CELLS` represent the minimum number of cells each of size `C` needed /// to hold `M` total bits pub(crate) const N_OPERAND_CELLS: usize = (M + C - 1) / C; + + // TODO: consider renaming M and C to communicate what they actually mean + pub(crate) const MAX_CELL_BIT_WIDTH: usize = const_min(M, C); } + +// TODO: test generated usize constants diff --git a/singer-utils/src/uint_new/util.rs b/singer-utils/src/uint_new/util.rs index 5b7de1380..27fa7e4d0 100644 --- a/singer-utils/src/uint_new/util.rs +++ b/singer-utils/src/uint_new/util.rs @@ -72,9 +72,19 @@ pub fn pad_cells( } } +/// 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 + } +} + #[cfg(test)] mod tests { - use crate::uint_new::util::{convert_decomp, pad_cells}; + use crate::uint_new::util::{const_min, convert_decomp, pad_cells}; use gkr::structs::{Circuit, CircuitWitness}; use goldilocks::{Goldilocks, GoldilocksExt2}; use itertools::Itertools; @@ -188,4 +198,11 @@ mod tests { // 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); + } } From ffe140ee68747c65e27ed7ffae55a9bd7e850116 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 3 Jun 2024 13:27:05 +0100 Subject: [PATCH 17/50] introduce new constant cleans up uint_from* --- singer-utils/src/uint_new/constants.rs | 8 ++++++-- singer-utils/src/uint_new/uint.rs | 3 +-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/singer-utils/src/uint_new/constants.rs b/singer-utils/src/uint_new/constants.rs index 87b4c3dc1..c0cd70a8a 100644 --- a/singer-utils/src/uint_new/constants.rs +++ b/singer-utils/src/uint_new/constants.rs @@ -6,8 +6,12 @@ impl UInt { /// to hold `M` total bits pub(crate) const N_OPERAND_CELLS: usize = (M + C - 1) / C; - // TODO: consider renaming M and C to communicate what they actually mean + /// 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(crate) const MAX_CELL_BIT_WIDTH: usize = const_min(M, C); } -// TODO: test generated usize constants +// TODO: test generated usize constants (test once we get all constants) diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs index 9731b6fa4..1d46bff46 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint_new/uint.rs @@ -68,12 +68,11 @@ impl UInt { cell_width: usize, is_little_endian: bool, ) -> Result { - let max_cell_width = M.min(C); let mut values = convert_decomp( circuit_builder, cell_values, cell_width, - max_cell_width, + Self::MAX_CELL_BIT_WIDTH, is_little_endian, )?; // TODO: is this safe, do we need to ensure that the padded cells are always 0? From 3bf521a58dd1fd963d9e1ff3cff88bfddec78316 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 3 Jun 2024 13:32:36 +0100 Subject: [PATCH 18/50] update uint from cell id test to test greater than N_OPERANDS --- singer-utils/src/uint_new/uint.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs index 1d46bff46..98666f872 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint_new/uint.rs @@ -121,6 +121,7 @@ mod tests { type UInt64 = UInt<33, 4>; assert!(UInt64::try_from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).is_ok()); assert!(UInt64::try_from(vec![1, 2, 3]).is_err()); + assert!(UInt64::try_from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).is_err()); } #[test] From 2c4a0ab3411ff6527b3e45ffbcc1ae1337c634db Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 3 Jun 2024 14:15:01 +0100 Subject: [PATCH 19/50] add test for uint from different sized cells --- singer-utils/src/uint_new/uint.rs | 73 ++++++++++++++++++++++++++----- singer-utils/src/uint_new/util.rs | 1 + 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs index 98666f872..bfa914c87 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint_new/uint.rs @@ -112,26 +112,75 @@ impl TryFrom<&[CellId]> for UInt { #[cfg(test)] mod tests { use crate::uint_new::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 UInt64 = UInt<33, 4>; - assert!(UInt64::try_from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).is_ok()); - assert!(UInt64::try_from(vec![1, 2, 3]).is_err()); - assert!(UInt64::try_from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).is_err()); + 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_range_values() { - // TODO: implement test - // first test without padding then test with padding, same for from_bytes - } - - #[test] - fn test_uint_from_bytes() { - // TODO: implement 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: + // 110010 110110 010100 000000 000000(bit representation) + // 50 54 20 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![50, 54, 20, 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() + ); } } diff --git a/singer-utils/src/uint_new/util.rs b/singer-utils/src/uint_new/util.rs index 27fa7e4d0..c8d2d24ef 100644 --- a/singer-utils/src/uint_new/util.rs +++ b/singer-utils/src/uint_new/util.rs @@ -178,6 +178,7 @@ mod tests { .collect::>() ); + // padding to power of 2 assert_eq!( &output[5..], vec![0, 0, 0] From 957ba246ebf112a76ee3ec4dc6d067a3dadb3a32 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Tue, 11 Jun 2024 00:34:09 +0100 Subject: [PATCH 20/50] implement add_unsafe --- singer-utils/src/uint/add_sub.rs | 21 +++++++++++ singer-utils/src/uint_new/add.rs | 58 +++++++++++++++++++++++++++++++ singer-utils/src/uint_new/mod.rs | 1 + singer-utils/src/uint_new/uint.rs | 3 +- 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 singer-utils/src/uint_new/add.rs diff --git a/singer-utils/src/uint/add_sub.rs b/singer-utils/src/uint/add_sub.rs index 48e02ce3f..e94c3ed46 100644 --- a/singer-utils/src/uint/add_sub.rs +++ b/singer-utils/src/uint/add_sub.rs @@ -63,6 +63,27 @@ impl UIntAddSub> { } /// Little-endian addition. + /// want to check = [a, b] + /// lookuptable = [a, b, c, d, e, f, g, h] + /// is 16bits + /// should_be_16 = [a, b] + /// table = [all possible 16 bit values] + /// range_values = contains all possible values of that range? + /// + /// a + b = c + /// c = computed_result + /// function(a, b, carry) = c + /// carry can be manipulated to give different c's + /// how do we ensure that the c we compute is the actual c + /// + /// [1, 0, 0] - carry_0 + /// [0, 0, 0] - carry_1 + /// + /// Double check: false carry should violate range check + /// + /// a b c + /// d e f + /// ----- pub fn add>( circuit_builder: &mut CircuitBuilder, range_chip_handler: &mut H, diff --git a/singer-utils/src/uint_new/add.rs b/singer-utils/src/uint_new/add.rs new file mode 100644 index 000000000..79003a9e0 --- /dev/null +++ b/singer-utils/src/uint_new/add.rs @@ -0,0 +1,58 @@ +use crate::error::UtilError; +use crate::uint_new::uint::UInt; +use ff::Field; +use ff_ext::ExtensionField; +use simple_frontend::structs::{CellId, CircuitBuilder}; + +// TODO: test +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]); + + circuit_builder.add(result, a, E::BaseField::ONE); + circuit_builder.add(result, b, E::BaseField::ONE); + + 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) + } +} diff --git a/singer-utils/src/uint_new/mod.rs b/singer-utils/src/uint_new/mod.rs index 372830334..56268d93c 100644 --- a/singer-utils/src/uint_new/mod.rs +++ b/singer-utils/src/uint_new/mod.rs @@ -1,3 +1,4 @@ +mod add; mod cmp; mod constants; mod uint; diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs index bfa914c87..6300f4b7d 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint_new/uint.rs @@ -8,7 +8,8 @@ use simple_frontend::structs::{CellId, CircuitBuilder}; /// Represented in little endian form. pub struct UInt { // TODO: the size of C should not be more than the size of the underlying field - values: Vec, + // TODO: should this be private? + pub values: Vec, } impl UInt { From d49f31a691908540bc7d8e21079be90344be7eb1 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Tue, 11 Jun 2024 20:02:59 +0100 Subject: [PATCH 21/50] add functions sketch --- singer-utils/src/uint_new/add.rs | 143 +++++++++++++++++- singer-utils/src/uint_new/constants.rs | 18 +++ singer-utils/src/uint_new/mod.rs | 1 + .../src/uint_new/witness_extractors.rs | 28 ++++ 4 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 singer-utils/src/uint_new/witness_extractors.rs diff --git a/singer-utils/src/uint_new/add.rs b/singer-utils/src/uint_new/add.rs index 79003a9e0..11515eaa1 100644 --- a/singer-utils/src/uint_new/add.rs +++ b/singer-utils/src/uint_new/add.rs @@ -1,10 +1,12 @@ +use crate::chip_handler::RangeChipOperations; use crate::error::UtilError; use crate::uint_new::uint::UInt; use ff::Field; use ff_ext::ExtensionField; -use simple_frontend::structs::{CellId, CircuitBuilder}; +use simple_frontend::structs::{Cell, CellId, CircuitBuilder}; // TODO: test +// TODO: reconfirm logic impl UInt { /// Little-endian addition. /// Assumes users will check the correct range of the result themselves. @@ -55,4 +57,143 @@ impl UInt { 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)?; + // TODO: uncomment this once you change to new uint + // range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) + todo!() + } + + // TODO: add documentation + // TODO: figure out how to remove duplication + 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()?; + + 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); + circuit_builder.add_const(result, constant); + + 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) + } + + // TODO: add documentation + 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(witness); + let range_values = Self::extract_range_values(witness); + let computed_result = Self::add_const_unsafe(circuit_builder, addend_0, constant, carry)?; + // TODO: uncomment this once you change to new uint + // range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) + todo!() + } + + // TODO: add documentation + 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> { + // TODO: confirm that carry without overflow does not affect add_const_unsafe logic + 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)) + todo!() + } + + // TODO: add documentation and explanation + warning (do same for other unsafe) + // TODO: remove duplication + 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(Self::N_OPERAND_CELLS) + .try_into()?; + + 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); + circuit_builder.add(result, addend_1, E::BaseField::ONE); + + 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) + } + + // TODO: add documentation + 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)) + todo!() + } + + // TODO: add documentation + 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> { + // TODO: confirm no overflow size doesn't affect add_small logic + 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)) + todo!() + } } diff --git a/singer-utils/src/uint_new/constants.rs b/singer-utils/src/uint_new/constants.rs index c0cd70a8a..2ff09d26f 100644 --- a/singer-utils/src/uint_new/constants.rs +++ b/singer-utils/src/uint_new/constants.rs @@ -1,11 +1,29 @@ use super::uint::UInt; +use crate::constants::RANGE_CHIP_BIT_WIDTH; use crate::uint_new::util::const_min; +// TODO: arrange this into sensible units impl UInt { /// `N_OPERAND_CELLS` represent the minimum number of cells each of size `C` needed /// to hold `M` total bits pub(crate) const N_OPERAND_CELLS: usize = (M + C - 1) / C; + // TODO: add documentation + pub(crate) const N_CARRY_CELLS: usize = Self::N_OPERAND_CELLS; + + // TODO: add documentation + const N_CARRY_CELLS_NO_OVERFLOW: usize = Self::N_CARRY_CELLS - 1; + + /// 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(crate) const N_RANGE_CELLS: usize = Self::N_OPERAND_CELLS * Self::N_RANGE_CELLS_PER_CELL; + + // TODO: add documentation + pub(crate) const N_RANGE_CELLS_NO_OVERFLOW: usize = + Self::N_CARRY_CELLS_NO_OVERFLOW * Self::N_RANGE_CELLS_PER_CELL; + /// 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 diff --git a/singer-utils/src/uint_new/mod.rs b/singer-utils/src/uint_new/mod.rs index 56268d93c..308d1887a 100644 --- a/singer-utils/src/uint_new/mod.rs +++ b/singer-utils/src/uint_new/mod.rs @@ -3,3 +3,4 @@ mod cmp; mod constants; mod uint; pub mod util; +mod witness_extractors; diff --git a/singer-utils/src/uint_new/witness_extractors.rs b/singer-utils/src/uint_new/witness_extractors.rs new file mode 100644 index 000000000..7606ed327 --- /dev/null +++ b/singer-utils/src/uint_new/witness_extractors.rs @@ -0,0 +1,28 @@ +use crate::uint_new::uint::UInt; +use simple_frontend::structs::CellId; + +// TODO: test +impl UInt { + // witness_structure + // [...range_values..., ...carry_witness...] + + // TODO: add documentation + pub fn extract_carry(witness: &[CellId]) -> &[CellId] { + &witness[Self::N_RANGE_CELLS..] + } + + // TODO: add documentation + pub fn extract_range_values(witness: &[CellId]) -> &[CellId] { + &witness[..Self::N_RANGE_CELLS] + } + + // TODO: add documentation + pub fn extract_carry_no_overflow(witness: &[CellId]) -> &[CellId] { + &witness[Self::N_RANGE_CELLS_NO_OVERFLOW..] + } + + // TODO: add documentation + pub fn extract_range_values_no_overflow(witness: &[CellId]) -> &[CellId] { + &witness[..Self::N_RANGE_CELLS_NO_OVERFLOW] + } +} From 632820f565b29f9090ebbb72ce42aafa3f91b409 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 13 Jun 2024 12:52:56 +0100 Subject: [PATCH 22/50] wip --- .../src/uint_new/{add.rs => arithmetic.rs} | 50 ++++++ singer-utils/src/uint_new/cmp.rs | 155 +++++++++++++++--- singer-utils/src/uint_new/mod.rs | 2 +- singer-utils/src/uint_new/uint.rs | 10 +- .../src/uint_new/witness_extractors.rs | 13 +- 5 files changed, 201 insertions(+), 29 deletions(-) rename singer-utils/src/uint_new/{add.rs => arithmetic.rs} (80%) diff --git a/singer-utils/src/uint_new/add.rs b/singer-utils/src/uint_new/arithmetic.rs similarity index 80% rename from singer-utils/src/uint_new/add.rs rename to singer-utils/src/uint_new/arithmetic.rs index 11515eaa1..97dfd53b3 100644 --- a/singer-utils/src/uint_new/add.rs +++ b/singer-utils/src/uint_new/arithmetic.rs @@ -196,4 +196,54 @@ impl UInt { // range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) todo!() } + + // TODO: add documentation + // minuend - subtrahend + 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()?; + + // we do limb by limb subtraction but we have to make use of the borrow + // we need to add the borrow + 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); + + if i < borrow.len() { + circuit_builder.add(result, borrow[i], E::BaseField::from(1 << C)); + } + + // TODO: confirm this logic + if i > 0 && i - 1 < borrow.len() { + circuit_builder.add(result, borrow[i - 1], -E::BaseField::ONE); + } + } + + Ok(result) + } + + // TODO: add documentation + 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(witness); + let range_values = Self::extract_range_values(witness); + let computed_result = Self::sub_unsafe(circuit_builder, minuend, subtrahend, borrow)?; + // TODO: uncomment this once you change to new uint + // range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) + todo!() + } } diff --git a/singer-utils/src/uint_new/cmp.rs b/singer-utils/src/uint_new/cmp.rs index 40f438797..e983621d4 100644 --- a/singer-utils/src/uint_new/cmp.rs +++ b/singer-utils/src/uint_new/cmp.rs @@ -1,22 +1,139 @@ // TODO: document module // mostly holds comparison methods on the uint type -// what methods were implemented previously -// lt -// assert_lt -// assert_leq -// assert_eq -// assert_eq_range_values - -// seems only 1 direction is enough, no need for lt and gt -// why lt then assert_lt but no leq only assert_leq -// will assume no need for it for now and wait until things break - -// going to focus on assert methods -// assert_lt -// assert_leq -// assert_eq -// assert_eq_range_values (this feels weird) -// feels like we should be able to convert the range values to uint -// then run assert_eq on them -// what is the problem with this? +use crate::chip_handler::RangeChipOperations; +use crate::error::UtilError; +use crate::uint_new::uint::UInt; +use ff::Field; +use ff_ext::ExtensionField; +use simple_frontend::structs::{CellId, CircuitBuilder, MixedCell}; + +impl UInt { + // TODO: what should this do? + // operand_0 < operand_1 + // this isn't really checking for less than, more like creating the necessary data needed for less than check + // TODO: change name + pub fn lt>( + circuit_builder: &mut CircuitBuilder, + range_chip_handler: &mut H, + operand_0: &UInt, + operand_1: &UInt, + witness: &[CellId], + ) -> Result<(CellId, UInt), UtilError> { + // achieves less than, by subtracting and then verifying that the result is in + // some range, so it's technically not correct, depends on the range values that are passed + // in + // if operand_0 is less then the borrow will be 1 for the MSB + let borrow = Self::extract_borrow(witness); + let range_values = Self::extract_range_values(witness); + let computed_diff = Self::sub_unsafe(circuit_builder, operand_0, operand_1, borrow)?; + + // TODO: uncomment once you change range_check_uint + // let diff = range_chip_handler.range_check_uint( + // circuit_builder, + // &computed_diff, + // Some(&range_values) + // )?; + // + // if borrow.len() == Self::N_CARRY_CELLS { + // Ok((borrow[Self::N_CARRY_CELLS - 1], diff)) + // } else { + // // TODO: if we reach here then definitiely not lt + // Ok((circuit_builder.create_cell(), diff)) + // } + // + todo!() + } + + // TODO: add documentation + // describe logic + pub fn assert_lt>( + circuit_builder: &mut CircuitBuilder, + range_chip_handler: &mut H, + operand_0: &UInt, + operand_1: &UInt, + witness: &[CellId], + ) -> Result<(), UtilError> { + let (borrow, _) = Self::lt( + circuit_builder, + range_chip_handler, + operand_0, + operand_1, + witness, + )?; + circuit_builder.assert_const(borrow, 1); + Ok(()) + } + + // TODO: add documentation + pub fn assert_leq>( + circuit_builder: &mut CircuitBuilder, + range_chip_handler: &mut H, + operand_0: &UInt, + operand_1: &UInt, + witness: &[CellId], + ) -> Result<(), UtilError> { + let (borrow, diff) = Self::lt( + circuit_builder, + range_chip_handler, + operand_0, + operand_1, + witness, + )?; + + // what will be the content of borrow and diif is less than? + // borrow will be 1 and diff will be diff + // what will be the content if equal + // borrow will be 0 and diff should be 0 + // how do we ensure that it's only that case that works? + + // if borrow = 0 return diff + // if borrow = 1 return 0 + // does this hold across all values? + + let diff_values = diff.values(); + for d in diff_values.iter() { + let s = circuit_builder.create_cell(); + // if borrow == 0 return diff else return 0 + // TODO: explain this + circuit_builder.sel_mixed( + s, + (*d).into(), + MixedCell::Constant(E::BaseField::ZERO), + borrow, + ); + circuit_builder.assert_const(s, 0); + } + + Ok(()) + } + + // TODO: add documentation (shuffle) + // TODO: document the steps + pub fn assert_eq( + circuit_builder: &mut CircuitBuilder, + operand_0: &UInt, + operand_1: &UInt, + ) -> Result<(), UtilError> { + let diff = circuit_builder.create_cells(Self::N_OPERAND_CELLS); + let operand_0_cells = operand_0.values(); + let operand_1_cells = operand_1.values(); + for i in 0..Self::N_OPERAND_CELLS { + circuit_builder.add(diff[i], operand_0_cells[i], E::BaseField::ONE); + circuit_builder.add(diff[i], operand_1_cells[i], -E::BaseField::ONE); + circuit_builder.assert_const(diff[i], 0); + } + Ok(()) + } + + // TODO: add documentation + pub fn assert_eq_range_values( + circuit_builder: &mut CircuitBuilder, + operand_0: &UInt, + operand_1: &[CellId], + ) -> Result<(), UtilError> { + // TODO: really need to test this, different from reference implementation + 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_new/mod.rs b/singer-utils/src/uint_new/mod.rs index 308d1887a..ee003a00a 100644 --- a/singer-utils/src/uint_new/mod.rs +++ b/singer-utils/src/uint_new/mod.rs @@ -1,4 +1,4 @@ -mod add; +mod arithmetic; mod cmp; mod constants; mod uint; diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs index 6300f4b7d..a4db9de10 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint_new/uint.rs @@ -14,13 +14,13 @@ pub struct UInt { impl UInt { /// Return the `UInt` underlying cell id's - fn values(&self) -> &[CellId] { + 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 - fn from_range_values( + pub fn from_range_values( circuit_builder: &mut CircuitBuilder, range_values: &[CellId], ) -> Result { @@ -33,7 +33,7 @@ impl UInt { } /// Builds a `UInt` instance from a set of cells that represent big-endian `BYTE_VALUES` - fn from_bytes_big_endian( + pub fn from_bytes_big_endian( circuit_builder: &mut CircuitBuilder, bytes: &[CellId], ) -> Result { @@ -41,7 +41,7 @@ impl UInt { } /// Builds a `UInt` instance from a set of cells that represent little-endian `BYTE_VALUES` - fn from_bytes_little_endian( + pub fn from_bytes_little_endian( circuit_builder: &mut CircuitBuilder, bytes: &[CellId], ) -> Result { @@ -49,7 +49,7 @@ impl UInt { } /// Builds a `UInt` instance from a set of cells that represent `BYTE_VALUES` - fn from_bytes( + pub fn from_bytes( circuit_builder: &mut CircuitBuilder, bytes: &[CellId], is_little_endian: bool, diff --git a/singer-utils/src/uint_new/witness_extractors.rs b/singer-utils/src/uint_new/witness_extractors.rs index 7606ed327..d0529f9d6 100644 --- a/singer-utils/src/uint_new/witness_extractors.rs +++ b/singer-utils/src/uint_new/witness_extractors.rs @@ -12,13 +12,18 @@ impl UInt { } // TODO: add documentation - pub fn extract_range_values(witness: &[CellId]) -> &[CellId] { - &witness[..Self::N_RANGE_CELLS] + pub fn extract_carry_no_overflow(witness: &[CellId]) -> &[CellId] { + &witness[Self::N_RANGE_CELLS_NO_OVERFLOW..] } // TODO: add documentation - pub fn extract_carry_no_overflow(witness: &[CellId]) -> &[CellId] { - &witness[Self::N_RANGE_CELLS_NO_OVERFLOW..] + pub fn extract_borrow(witness: &[CellId]) -> &[CellId] { + &witness[Self::N_RANGE_CELLS..] + } + + // TODO: add documentation + pub fn extract_range_values(witness: &[CellId]) -> &[CellId] { + &witness[..Self::N_RANGE_CELLS] } // TODO: add documentation From 5b298c489903c8d9ba4649b3c130c496603ee3c3 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 13 Jun 2024 12:55:55 +0100 Subject: [PATCH 23/50] all func --- singer-utils/src/uint_new/uint.rs | 25 +++++++++++++++++++++++++ singer-utils/src/uint_new/util.rs | 3 +++ 2 files changed, 28 insertions(+) diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint_new/uint.rs index a4db9de10..390042ea7 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint_new/uint.rs @@ -2,6 +2,9 @@ use crate::constants::{BYTE_BIT_WIDTH, RANGE_CHIP_BIT_WIDTH}; use crate::error::UtilError; use crate::uint_new::util::{convert_decomp, pad_cells}; use ff_ext::ExtensionField; +use gkr::utils::ceil_log2; +use goldilocks::SmallField; +use itertools::Itertools; use simple_frontend::structs::{CellId, CircuitBuilder}; /// Unsigned integer with `M` total bits. `C` denotes the cell bit width. @@ -80,6 +83,28 @@ impl UInt { pad_cells(circuit_builder, &mut values, Self::N_OPERAND_CELLS); values.try_into() } + + /// Generate (0, 1, ..., size) + // TODO: refactor, move and test + 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 + } } /// Construct `UInt` from `Vec` diff --git a/singer-utils/src/uint_new/util.rs b/singer-utils/src/uint_new/util.rs index c8d2d24ef..bc57365ba 100644 --- a/singer-utils/src/uint_new/util.rs +++ b/singer-utils/src/uint_new/util.rs @@ -1,5 +1,8 @@ use crate::error::UtilError; use ff_ext::ExtensionField; +use gkr::utils::ceil_log2; +use goldilocks::SmallField; +use itertools::Itertools; use simple_frontend::structs::{CellId, CircuitBuilder}; /// Given some data represented by n small cells of size s From e5403e45976ec550f98e654a73b252484e4ee1cd Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 13 Jun 2024 13:40:56 +0100 Subject: [PATCH 24/50] building --- singer-pro/src/basic_block/bb_final.rs | 9 +- singer-pro/src/basic_block/bb_ret.rs | 16 +- singer-pro/src/basic_block/bb_start.rs | 21 +- singer-pro/src/instructions/add.rs | 13 +- singer-pro/src/instructions/calldataload.rs | 8 +- singer-pro/src/instructions/gt.rs | 15 +- singer-pro/src/instructions/jump.rs | 6 +- singer-pro/src/instructions/jumpi.rs | 16 +- singer-pro/src/instructions/mstore.rs | 27 ++- singer-pro/src/instructions/ret.rs | 13 +- singer-utils/src/chip_handler.rs | 6 +- singer-utils/src/chip_handler/range.rs | 18 +- singer-utils/src/chips/bytecode.rs | 4 +- singer-utils/src/chips/calldata.rs | 8 +- singer-utils/src/lib.rs | 2 +- singer-utils/src/structs.rs | 12 +- .../src/{uint_new => uint}/arithmetic.rs | 10 +- singer-utils/src/uint/cmp.rs | 220 +++++++----------- .../src/{uint_new => uint}/constants.rs | 24 +- singer-utils/src/{uint_new => uint}/mod.rs | 1 + singer-utils/src/{uint_new => uint}/uint.rs | 5 +- singer-utils/src/{uint_new => uint}/util.rs | 2 +- .../{uint_new => uint}/witness_extractors.rs | 14 +- singer-utils/src/uint_new/cmp.rs | 139 ----------- singer-utils/src/{uint.rs => uint_old.rs} | 14 +- .../src/{uint => uint_old}/add_sub.rs | 38 +-- singer-utils/src/uint_old/cmp.rs | 185 +++++++++++++++ singer/src/instructions/add.rs | 31 ++- singer/src/instructions/calldataload.rs | 23 +- singer/src/instructions/dup.rs | 19 +- singer/src/instructions/gt.rs | 31 ++- singer/src/instructions/jump.rs | 15 +- singer/src/instructions/jumpdest.rs | 9 +- singer/src/instructions/jumpi.rs | 31 ++- singer/src/instructions/mstore.rs | 41 ++-- singer/src/instructions/pop.rs | 17 +- singer/src/instructions/push.rs | 15 +- singer/src/instructions/ret.rs | 37 ++- singer/src/instructions/swap.rs | 27 ++- singer/src/test.rs | 2 +- 40 files changed, 578 insertions(+), 566 deletions(-) rename singer-utils/src/{uint_new => uint}/arithmetic.rs (97%) rename singer-utils/src/{uint_new => uint}/constants.rs (58%) rename singer-utils/src/{uint_new => uint}/mod.rs (81%) rename singer-utils/src/{uint_new => uint}/uint.rs (98%) rename singer-utils/src/{uint_new => uint}/util.rs (98%) rename singer-utils/src/{uint_new => uint}/witness_extractors.rs (73%) delete mode 100644 singer-utils/src/uint_new/cmp.rs rename singer-utils/src/{uint.rs => uint_old.rs} (94%) rename singer-utils/src/{uint => uint_old}/add_sub.rs (94%) create mode 100644 singer-utils/src/uint_old/cmp.rs diff --git a/singer-pro/src/basic_block/bb_final.rs b/singer-pro/src/basic_block/bb_final.rs index bf97b69d6..fc109c80c 100644 --- a/singer-pro/src/basic_block/bb_final.rs +++ b/singer-pro/src/basic_block/bb_final.rs @@ -12,7 +12,6 @@ use singer_utils::{ chips::IntoEnumIterator, register_witness, structs::{ChipChallenges, InstOutChipType, PCUInt, RAMHandler, ROMHandler, TSUInt}, - uint::UIntAddSub, }; use std::sync::Arc; @@ -28,7 +27,7 @@ pub struct BasicBlockFinal; register_witness!(BasicBlockFinal, phase0 { // State in related - stack_ts_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_CELLS + stack_ts_add => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS }); impl BasicBlockFinal { @@ -49,12 +48,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 +67,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( 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..de806a2da 100644 --- a/singer-pro/src/basic_block/bb_start.rs +++ b/singer-pro/src/basic_block/bb_start.rs @@ -11,7 +11,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 +26,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) => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS }); impl BasicBlockStart { @@ -83,7 +82,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 +101,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 +113,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 a950d23c7..ae296f116 100644 --- a/singer-pro/src/instructions/add.rs +++ b/singer-pro/src/instructions/add.rs @@ -7,7 +7,6 @@ use singer_utils::{ chips::IntoEnumIterator, register_witness, structs::{ChipChallenges, InstOutChipType, ROMHandler, StackUInt, TSUInt}, - uint::UIntAddSub, }; use std::sync::Arc; @@ -29,7 +28,7 @@ register_witness!( AddInstruction, phase0 { // Witness for addend_0 + addend_1 - instruction_add => UIntAddSub::::N_WITNESS_CELLS + instruction_add => StackUInt::N_WITNESS_CELLS } ); @@ -41,16 +40,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, @@ -60,7 +59,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 8db5e284d..37c5b307b 100644 --- a/singer-pro/src/instructions/calldataload.rs +++ b/singer-pro/src/instructions/calldataload.rs @@ -27,7 +27,7 @@ pub struct CalldataloadInstruction; register_witness!( CalldataloadInstruction, phase0 { - data => StackUInt::N_OPRAND_CELLS + data => StackUInt::N_OPERAND_CELLS } ); @@ -38,8 +38,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); @@ -51,7 +51,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 a3b1f0e43..69567a741 100644 --- a/singer-pro/src/instructions/gt.rs +++ b/singer-pro/src/instructions/gt.rs @@ -7,7 +7,6 @@ use singer_utils::{ chips::IntoEnumIterator, register_witness, structs::{ChipChallenges, InstOutChipType, ROMHandler, StackUInt, TSUInt}, - uint::UIntCmp, }; use std::sync::Arc; @@ -27,7 +26,7 @@ register_witness!( GtInstruction, phase0 { // Witness for operand_0 > operand_1 - instruction_gt => UIntCmp::::N_WITNESS_CELLS + instruction_gt => StackUInt::N_WITNESS_CELLS } ); @@ -39,18 +38,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, @@ -59,13 +58,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 4fd99361e..8dc49714b 100644 --- a/singer-pro/src/instructions/jump.rs +++ b/singer-pro/src/instructions/jump.rs @@ -24,8 +24,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()); @@ -33,7 +33,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 fe0f76854..87974048d 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 } ); @@ -45,10 +45,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); @@ -69,8 +69,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); } @@ -88,7 +88,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 c45bb114d..3edffdefe 100644 --- a/singer-pro/src/instructions/mstore.rs +++ b/singer-pro/src/instructions/mstore.rs @@ -10,7 +10,6 @@ use singer_utils::{ constants::EVM_STACK_BYTE_WIDTH, register_witness, structs::{ChipChallenges, InstOutChipType, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::{UIntAddSub, UIntCmp}, }; use std::{mem, sync::Arc}; @@ -111,7 +110,7 @@ impl InstructionGraph for MstoreInstruction { register_witness!( MstoreInstruction, phase0 { - memory_ts_add => UIntAddSub::::N_NO_OVERFLOW_WITNESS_CELLS, + memory_ts_add => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, mem_bytes => EVM_STACK_BYTE_WIDTH } ); @@ -123,10 +122,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); @@ -148,8 +147,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); @@ -209,17 +208,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 => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, - offset_add_delta => UIntAddSub::::N_WITNESS_CELLS, + offset_add_delta => StackUInt::N_WITNESS_CELLS, prev_mem_byte => 1 } ); @@ -248,14 +247,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_small( &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 899c74a69..ef713b965 100644 --- a/singer-pro/src/instructions/ret.rs +++ b/singer-pro/src/instructions/ret.rs @@ -8,7 +8,6 @@ use singer_utils::{ chips::{IntoEnumIterator, SingerChipBuilder}, register_witness, structs::{ChipChallenges, InstOutChipType, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::UIntAddSub, }; use std::{mem, sync::Arc}; @@ -76,8 +75,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 => StackUInt::N_WITNESS_CELLS } ); @@ -92,8 +91,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); @@ -102,7 +101,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_small( &mut circuit_builder, &mut rom_handler, &offset, @@ -124,7 +123,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 fac94f232..3e2748f80 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 98fe08456..2d579af75 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::RangeChipOperations; @@ -40,7 +40,8 @@ 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); + // TODO: use the self paradigm here + 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) { @@ -96,8 +97,9 @@ impl ROMHandler { constant: i64, witness: &[CellId], ) -> Result { - let carry = UIntAddSub::::extract_unsafe_carry(witness); - UIntAddSub::::add_const_unsafe( + // TODO: why unsafe here? + let carry = PCUInt::extract_unsafe_carry(witness); + PCUInt::add_const_unsafe( circuit_builder, &pc, i64_to_base_field::(constant), @@ -112,8 +114,10 @@ impl ROMHandler { constant: i64, witness: &[CellId], ) -> Result { - let carry = UIntAddSub::::extract_unsafe_carry(witness); - UIntAddSub::::add_const( + // TODO: why safe but with unsafe carry + // TODO: there must be a bug here + let carry = TSUInt::extract_unsafe_carry(witness); + TSUInt::add_const( circuit_builder, self, &ts, diff --git a/singer-utils/src/chips/bytecode.rs b/singer-utils/src/chips/bytecode.rs index 865e9a521..d0a48411d 100644 --- a/singer-utils/src/chips/bytecode.rs +++ b/singer-utils/src/chips/bytecode.rs @@ -25,7 +25,7 @@ pub(crate) fn construct_bytecode_table_and_witness( real_challenges: &[E], ) -> Result<(PredType, PredType, usize), UtilError> { let mut circuit_builder = CircuitBuilder::::new(); - let (_, pc_cells) = circuit_builder.create_witness_in(PCUInt::N_OPRAND_CELLS); + let (_, pc_cells) = circuit_builder.create_witness_in(PCUInt::N_OPERAND_CELLS); let (_, bytecode_cells) = circuit_builder.create_witness_in(1); let rlc = circuit_builder.create_ext_cell(); @@ -93,7 +93,7 @@ pub(crate) fn construct_bytecode_table( challenges: &ChipChallenges, ) -> Result<(PredType, PredType, usize), UtilError> { let mut circuit_builder = CircuitBuilder::::new(); - let (_, pc_cells) = circuit_builder.create_witness_in(PCUInt::N_OPRAND_CELLS); + let (_, pc_cells) = circuit_builder.create_witness_in(PCUInt::N_OPERAND_CELLS); let (_, bytecode_cells) = circuit_builder.create_witness_in(1); let rlc = circuit_builder.create_ext_cell(); diff --git a/singer-utils/src/chips/calldata.rs b/singer-utils/src/chips/calldata.rs index 95410e7fd..a7b9c419e 100644 --- a/singer-utils/src/chips/calldata.rs +++ b/singer-utils/src/chips/calldata.rs @@ -25,7 +25,7 @@ pub(crate) fn construct_calldata_table_and_witness( ) -> Result<(PredType, PredType, usize), UtilError> { let mut circuit_builder = CircuitBuilder::::new(); let (_, id_cells) = circuit_builder.create_witness_in(1); - let (_, calldata_cells) = circuit_builder.create_witness_in(StackUInt::N_OPRAND_CELLS); + let (_, calldata_cells) = circuit_builder.create_witness_in(StackUInt::N_OPERAND_CELLS); let rlc = circuit_builder.create_ext_cell(); let mut items = vec![MixedCell::Constant(E::BaseField::from( @@ -65,9 +65,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() @@ -102,7 +102,7 @@ pub(crate) fn construct_calldata_table( ) -> Result<(PredType, PredType, usize), UtilError> { let mut circuit_builder = CircuitBuilder::::new(); let (_, id_cells) = circuit_builder.create_witness_in(1); - let (_, calldata_cells) = circuit_builder.create_witness_in(StackUInt::N_OPRAND_CELLS); + let (_, calldata_cells) = circuit_builder.create_witness_in(StackUInt::N_OPERAND_CELLS); let rlc = circuit_builder.create_ext_cell(); let mut items = vec![MixedCell::Constant(E::BaseField::from( diff --git a/singer-utils/src/lib.rs b/singer-utils/src/lib.rs index 84d53882f..fda1ad872 100644 --- a/singer-utils/src/lib.rs +++ b/singer-utils/src/lib.rs @@ -5,8 +5,8 @@ pub mod chips; pub mod constants; pub mod error; pub mod structs; +// pub mod uint_old; pub mod uint; -mod uint_new; #[macro_use] pub mod macros; diff --git a/singer-utils/src/structs.rs b/singer-utils/src/structs.rs index 20367d985..1ed5187b3 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,11 +48,11 @@ 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, -} +// /// 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; diff --git a/singer-utils/src/uint_new/arithmetic.rs b/singer-utils/src/uint/arithmetic.rs similarity index 97% rename from singer-utils/src/uint_new/arithmetic.rs rename to singer-utils/src/uint/arithmetic.rs index 97dfd53b3..aaba72ee8 100644 --- a/singer-utils/src/uint_new/arithmetic.rs +++ b/singer-utils/src/uint/arithmetic.rs @@ -1,10 +1,12 @@ use crate::chip_handler::RangeChipOperations; use crate::error::UtilError; -use crate::uint_new::uint::UInt; +use crate::uint::uint::UInt; use ff::Field; use ff_ext::ExtensionField; use simple_frontend::structs::{Cell, CellId, CircuitBuilder}; +// TODO: this functions should use self, better UI (refactor for this) + // TODO: test // TODO: reconfirm logic impl UInt { @@ -69,7 +71,7 @@ impl UInt { 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)?; - // TODO: uncomment this once you change to new uint + // TODO: uncomment this once you change to new uint_old // range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) todo!() } @@ -115,7 +117,7 @@ impl UInt { 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)?; - // TODO: uncomment this once you change to new uint + // TODO: uncomment this once you change to new uint_old // range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) todo!() } @@ -242,7 +244,7 @@ impl UInt { let borrow = Self::extract_borrow(witness); let range_values = Self::extract_range_values(witness); let computed_result = Self::sub_unsafe(circuit_builder, minuend, subtrahend, borrow)?; - // TODO: uncomment this once you change to new uint + // TODO: uncomment this once you change to new uint_old // range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) todo!() } diff --git a/singer-utils/src/uint/cmp.rs b/singer-utils/src/uint/cmp.rs index 2a2ba0058..aee37b691 100644 --- a/singer-utils/src/uint/cmp.rs +++ b/singer-utils/src/uint/cmp.rs @@ -1,185 +1,139 @@ +// TODO: document module +// mostly holds comparison methods on the uint_old type + +use crate::chip_handler::RangeChipOperations; +use crate::error::UtilError; +use crate::uint::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 { + // TODO: what should this do? + // operand_0 < operand_1 + // this isn't really checking for less than, more like creating the necessary data needed for less than check + // TODO: change name + 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> { + // achieves less than, by subtracting and then verifying that the result is in + // some range, so it's technically not correct, depends on the range values that are passed + // in + // if operand_0 is less then the borrow will be 1 for the MSB let borrow = Self::extract_borrow(witness); let range_values = Self::extract_range_values(witness); - let computed_diff = - UIntAddSub::>::sub_unsafe(circuit_builder, oprand_0, oprand_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)) - } else { - Ok((circuit_builder.create_cell(), diff)) - } + let computed_diff = Self::sub_unsafe(circuit_builder, operand_0, operand_1, borrow)?; + + // TODO: uncomment once you change range_check_uint + // let diff = range_chip_handler.range_check_uint( + // circuit_builder, + // &computed_diff, + // Some(&range_values) + // )?; + // + // if borrow.len() == Self::N_CARRY_CELLS { + // Ok((borrow[Self::N_CARRY_CELLS - 1], diff)) + // } else { + // // TODO: if we reach here then definitiely not lt + // Ok((circuit_builder.create_cell(), diff)) + // } + // + todo!() } - pub fn assert_lt>( - circuit_builder: &mut CircuitBuilder, + // TODO: add documentation + // describe logic + 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, + // TODO: add documentation + 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, )?; + + // what will be the content of borrow and diif is less than? + // borrow will be 1 and diff will be diff + // what will be the content if equal + // borrow will be 0 and diff should be 0 + // how do we ensure that it's only that case that works? + + // if borrow = 0 return diff + // if borrow = 1 return 0 + // does this hold across all values? + let diff_values = diff.values(); for d in diff_values.iter() { let s = circuit_builder.create_cell(); - // assert_zero({borrow ? 0 : diff}) + // if borrow == 0 return diff else return 0 + // TODO: explain this 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, + // TODO: add documentation (shuffle) + // TODO: document the steps + 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); + // TODO: add documentation + pub fn assert_eq_range_values( + circuit_builder: &mut CircuitBuilder, + operand_0: &UInt, + operand_1: &[CellId], + ) -> Result<(), UtilError> { + // TODO: really need to test this, different from reference implementation + 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_new/constants.rs b/singer-utils/src/uint/constants.rs similarity index 58% rename from singer-utils/src/uint_new/constants.rs rename to singer-utils/src/uint/constants.rs index 2ff09d26f..b5c064000 100644 --- a/singer-utils/src/uint_new/constants.rs +++ b/singer-utils/src/uint/constants.rs @@ -1,15 +1,15 @@ use super::uint::UInt; use crate::constants::RANGE_CHIP_BIT_WIDTH; -use crate::uint_new::util::const_min; +use crate::uint::util::const_min; // TODO: arrange this into sensible units impl UInt { /// `N_OPERAND_CELLS` represent the minimum number of cells each of size `C` needed /// to hold `M` total bits - pub(crate) const N_OPERAND_CELLS: usize = (M + C - 1) / C; + pub const N_OPERAND_CELLS: usize = (M + C - 1) / C; // TODO: add documentation - pub(crate) const N_CARRY_CELLS: usize = Self::N_OPERAND_CELLS; + pub const N_CARRY_CELLS: usize = Self::N_OPERAND_CELLS; // TODO: add documentation const N_CARRY_CELLS_NO_OVERFLOW: usize = Self::N_CARRY_CELLS - 1; @@ -18,10 +18,10 @@ impl UInt { 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(crate) const N_RANGE_CELLS: usize = Self::N_OPERAND_CELLS * Self::N_RANGE_CELLS_PER_CELL; + pub const N_RANGE_CELLS: usize = Self::N_OPERAND_CELLS * Self::N_RANGE_CELLS_PER_CELL; // TODO: add documentation - pub(crate) const N_RANGE_CELLS_NO_OVERFLOW: usize = + pub const N_RANGE_CELLS_NO_OVERFLOW: usize = Self::N_CARRY_CELLS_NO_OVERFLOW * Self::N_RANGE_CELLS_PER_CELL; /// Determines the maximum number of bits that should be represented in each cell @@ -29,7 +29,19 @@ impl UInt { /// 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(crate) const MAX_CELL_BIT_WIDTH: usize = const_min(M, C); + pub const MAX_CELL_BIT_WIDTH: usize = const_min(M, C); + + // TODO: add documentation + // TODO: shouldn't the range cells have no overflow also? + pub const N_NO_OVERFLOW_WITNESS_CELLS: usize = + Self::N_RANGE_CELLS + Self::N_CARRY_CELLS_NO_OVERFLOW; + + // TODO: add documentation + // TODO: potential rename with all the cell thing + pub const N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS: usize = Self::N_CARRY_CELLS_NO_OVERFLOW; + + // TODO: add documentation + pub const N_WITNESS_CELLS: usize = Self::N_RANGE_CELLS + Self::N_CARRY_CELLS; } // TODO: test generated usize constants (test once we get all constants) diff --git a/singer-utils/src/uint_new/mod.rs b/singer-utils/src/uint/mod.rs similarity index 81% rename from singer-utils/src/uint_new/mod.rs rename to singer-utils/src/uint/mod.rs index ee003a00a..e3f42ffc2 100644 --- a/singer-utils/src/uint_new/mod.rs +++ b/singer-utils/src/uint/mod.rs @@ -2,5 +2,6 @@ mod arithmetic; mod cmp; mod constants; mod uint; +pub use uint::UInt; pub mod util; mod witness_extractors; diff --git a/singer-utils/src/uint_new/uint.rs b/singer-utils/src/uint/uint.rs similarity index 98% rename from singer-utils/src/uint_new/uint.rs rename to singer-utils/src/uint/uint.rs index 390042ea7..98c04cae2 100644 --- a/singer-utils/src/uint_new/uint.rs +++ b/singer-utils/src/uint/uint.rs @@ -1,12 +1,13 @@ use crate::constants::{BYTE_BIT_WIDTH, RANGE_CHIP_BIT_WIDTH}; use crate::error::UtilError; -use crate::uint_new::util::{convert_decomp, pad_cells}; +use crate::uint::util::{convert_decomp, pad_cells}; use ff_ext::ExtensionField; use gkr::utils::ceil_log2; use goldilocks::SmallField; use itertools::Itertools; use simple_frontend::structs::{CellId, CircuitBuilder}; +#[derive(Clone)] /// Unsigned integer with `M` total bits. `C` denotes the cell bit width. /// Represented in little endian form. pub struct UInt { @@ -137,7 +138,7 @@ impl TryFrom<&[CellId]> for UInt { #[cfg(test)] mod tests { - use crate::uint_new::uint::UInt; + use crate::uint::uint::UInt; use gkr::structs::{Circuit, CircuitWitness}; use goldilocks::{Goldilocks, GoldilocksExt2}; use itertools::Itertools; diff --git a/singer-utils/src/uint_new/util.rs b/singer-utils/src/uint/util.rs similarity index 98% rename from singer-utils/src/uint_new/util.rs rename to singer-utils/src/uint/util.rs index bc57365ba..0685a15c2 100644 --- a/singer-utils/src/uint_new/util.rs +++ b/singer-utils/src/uint/util.rs @@ -87,7 +87,7 @@ pub const fn const_min(a: usize, b: usize) -> usize { #[cfg(test)] mod tests { - use crate::uint_new::util::{const_min, convert_decomp, pad_cells}; + use crate::uint::util::{const_min, convert_decomp, pad_cells}; use gkr::structs::{Circuit, CircuitWitness}; use goldilocks::{Goldilocks, GoldilocksExt2}; use itertools::Itertools; diff --git a/singer-utils/src/uint_new/witness_extractors.rs b/singer-utils/src/uint/witness_extractors.rs similarity index 73% rename from singer-utils/src/uint_new/witness_extractors.rs rename to singer-utils/src/uint/witness_extractors.rs index d0529f9d6..fe0318f05 100644 --- a/singer-utils/src/uint_new/witness_extractors.rs +++ b/singer-utils/src/uint/witness_extractors.rs @@ -1,4 +1,4 @@ -use crate::uint_new::uint::UInt; +use crate::uint::uint::UInt; use simple_frontend::structs::CellId; // TODO: test @@ -16,11 +16,23 @@ impl UInt { &witness[Self::N_RANGE_CELLS_NO_OVERFLOW..] } + // TODO: add documentation + // TODO: why do we need this + pub fn extract_unsafe_carry(witness: &[CellId]) -> &[CellId] { + witness + } + // TODO: add documentation pub fn extract_borrow(witness: &[CellId]) -> &[CellId] { &witness[Self::N_RANGE_CELLS..] } + // TODO: add documentation + // TODO: why do we need this + pub fn extract_unsafe_borrow(witness: &[CellId]) -> &[CellId] { + witness + } + // TODO: add documentation pub fn extract_range_values(witness: &[CellId]) -> &[CellId] { &witness[..Self::N_RANGE_CELLS] diff --git a/singer-utils/src/uint_new/cmp.rs b/singer-utils/src/uint_new/cmp.rs deleted file mode 100644 index e983621d4..000000000 --- a/singer-utils/src/uint_new/cmp.rs +++ /dev/null @@ -1,139 +0,0 @@ -// TODO: document module -// mostly holds comparison methods on the uint type - -use crate::chip_handler::RangeChipOperations; -use crate::error::UtilError; -use crate::uint_new::uint::UInt; -use ff::Field; -use ff_ext::ExtensionField; -use simple_frontend::structs::{CellId, CircuitBuilder, MixedCell}; - -impl UInt { - // TODO: what should this do? - // operand_0 < operand_1 - // this isn't really checking for less than, more like creating the necessary data needed for less than check - // TODO: change name - pub fn lt>( - circuit_builder: &mut CircuitBuilder, - range_chip_handler: &mut H, - operand_0: &UInt, - operand_1: &UInt, - witness: &[CellId], - ) -> Result<(CellId, UInt), UtilError> { - // achieves less than, by subtracting and then verifying that the result is in - // some range, so it's technically not correct, depends on the range values that are passed - // in - // if operand_0 is less then the borrow will be 1 for the MSB - let borrow = Self::extract_borrow(witness); - let range_values = Self::extract_range_values(witness); - let computed_diff = Self::sub_unsafe(circuit_builder, operand_0, operand_1, borrow)?; - - // TODO: uncomment once you change range_check_uint - // let diff = range_chip_handler.range_check_uint( - // circuit_builder, - // &computed_diff, - // Some(&range_values) - // )?; - // - // if borrow.len() == Self::N_CARRY_CELLS { - // Ok((borrow[Self::N_CARRY_CELLS - 1], diff)) - // } else { - // // TODO: if we reach here then definitiely not lt - // Ok((circuit_builder.create_cell(), diff)) - // } - // - todo!() - } - - // TODO: add documentation - // describe logic - pub fn assert_lt>( - circuit_builder: &mut CircuitBuilder, - range_chip_handler: &mut H, - operand_0: &UInt, - operand_1: &UInt, - witness: &[CellId], - ) -> Result<(), UtilError> { - let (borrow, _) = Self::lt( - circuit_builder, - range_chip_handler, - operand_0, - operand_1, - witness, - )?; - circuit_builder.assert_const(borrow, 1); - Ok(()) - } - - // TODO: add documentation - pub fn assert_leq>( - circuit_builder: &mut CircuitBuilder, - range_chip_handler: &mut H, - operand_0: &UInt, - operand_1: &UInt, - witness: &[CellId], - ) -> Result<(), UtilError> { - let (borrow, diff) = Self::lt( - circuit_builder, - range_chip_handler, - operand_0, - operand_1, - witness, - )?; - - // what will be the content of borrow and diif is less than? - // borrow will be 1 and diff will be diff - // what will be the content if equal - // borrow will be 0 and diff should be 0 - // how do we ensure that it's only that case that works? - - // if borrow = 0 return diff - // if borrow = 1 return 0 - // does this hold across all values? - - let diff_values = diff.values(); - for d in diff_values.iter() { - let s = circuit_builder.create_cell(); - // if borrow == 0 return diff else return 0 - // TODO: explain this - circuit_builder.sel_mixed( - s, - (*d).into(), - MixedCell::Constant(E::BaseField::ZERO), - borrow, - ); - circuit_builder.assert_const(s, 0); - } - - Ok(()) - } - - // TODO: add documentation (shuffle) - // TODO: document the steps - pub fn assert_eq( - circuit_builder: &mut CircuitBuilder, - operand_0: &UInt, - operand_1: &UInt, - ) -> Result<(), UtilError> { - let diff = circuit_builder.create_cells(Self::N_OPERAND_CELLS); - let operand_0_cells = operand_0.values(); - let operand_1_cells = operand_1.values(); - for i in 0..Self::N_OPERAND_CELLS { - circuit_builder.add(diff[i], operand_0_cells[i], E::BaseField::ONE); - circuit_builder.add(diff[i], operand_1_cells[i], -E::BaseField::ONE); - circuit_builder.assert_const(diff[i], 0); - } - Ok(()) - } - - // TODO: add documentation - pub fn assert_eq_range_values( - circuit_builder: &mut CircuitBuilder, - operand_0: &UInt, - operand_1: &[CellId], - ) -> Result<(), UtilError> { - // TODO: really need to test this, different from reference implementation - 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.rs b/singer-utils/src/uint_old.rs similarity index 94% rename from singer-utils/src/uint.rs rename to singer-utils/src/uint_old.rs index 28574cd06..c03ebdc7a 100644 --- a/singer-utils/src/uint.rs +++ b/singer-utils/src/uint_old.rs @@ -16,7 +16,7 @@ pub mod cmp; impl TryFrom<&[usize]> for UInt { type Error = UtilError; fn try_from(values: &[usize]) -> Result { - if values.len() != Self::N_OPRAND_CELLS { + if values.len() != Self::N_OPERAND_CELLS { // TODO: this will be replaced soon return Err(UtilError::UIntError("placeholder".to_string())); } @@ -35,14 +35,14 @@ impl TryFrom> for UInt { } impl UInt { - pub const N_OPRAND_CELLS: usize = (M + C - 1) / C; + pub const N_OPERAND_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; + const N_CARRY_CELLS: usize = Self::N_OPERAND_CELLS; + const N_CARRY_NO_OVERFLOW_CELLS: usize = Self::N_OPERAND_CELLS - 1; pub const N_RANGE_CHECK_CELLS: usize = - Self::N_OPRAND_CELLS * (C + RANGE_CHIP_BIT_WIDTH - 1) / RANGE_CHIP_BIT_WIDTH; + Self::N_OPERAND_CELLS * (C + RANGE_CHIP_BIT_WIDTH - 1) / RANGE_CHIP_BIT_WIDTH; pub const N_RANGE_CHECK_NO_OVERFLOW_CELLS: usize = - (Self::N_OPRAND_CELLS - 1) * (C + RANGE_CHIP_BIT_WIDTH - 1) / RANGE_CHIP_BIT_WIDTH; + (Self::N_OPERAND_CELLS - 1) * (C + RANGE_CHIP_BIT_WIDTH - 1) / RANGE_CHIP_BIT_WIDTH; pub fn values(&self) -> &[CellId] { &self.values @@ -57,7 +57,7 @@ impl UInt { } else { convert_decomp(circuit_builder, range_values, RANGE_CHIP_BIT_WIDTH, M, true) }; - while values.len() < Self::N_OPRAND_CELLS { + while values.len() < Self::N_OPERAND_CELLS { values.push(circuit_builder.create_cell()); } Self::try_from(values) diff --git a/singer-utils/src/uint/add_sub.rs b/singer-utils/src/uint_old/add_sub.rs similarity index 94% rename from singer-utils/src/uint/add_sub.rs rename to singer-utils/src/uint_old/add_sub.rs index e94c3ed46..2788f110a 100644 --- a/singer-utils/src/uint/add_sub.rs +++ b/singer-utils/src/uint_old/add_sub.rs @@ -44,9 +44,9 @@ impl UIntAddSub> { carry: &[CellId], ) -> Result, UtilError> { let result: UInt = circuit_builder - .create_cells(UInt::::N_OPRAND_CELLS) + .create_cells(UInt::::N_OPERAND_CELLS) .try_into()?; - for i in 0..UInt::::N_OPRAND_CELLS { + for i in 0..UInt::::N_OPERAND_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); @@ -106,7 +106,7 @@ impl UIntAddSub> { carry: &[CellId], ) -> Result, UtilError> { let result: UInt = circuit_builder - .create_cells(UInt::::N_OPRAND_CELLS) + .create_cells(UInt::::N_OPERAND_CELLS) .try_into()?; for i in 0..result.values.len() { let (a, result) = (addend_0.values[i], result.values[i]); @@ -161,7 +161,7 @@ impl UIntAddSub> { carry: &[CellId], ) -> Result, UtilError> { let result: UInt = circuit_builder - .create_cells(UInt::::N_OPRAND_CELLS) + .create_cells(UInt::::N_OPERAND_CELLS) .try_into()?; for i in 0..result.values.len() { let (a, result) = (addend_0.values[i], result.values[i]); @@ -218,7 +218,7 @@ impl UIntAddSub> { borrow: &[CellId], ) -> Result, UtilError> { let result: UInt = circuit_builder - .create_cells(UInt::::N_OPRAND_CELLS) + .create_cells(UInt::::N_OPERAND_CELLS) .try_into()?; // result = minuend - subtrahend + borrow * (1 << BIT_WIDTH) - last_borrow for i in 0..result.values.len() { @@ -247,16 +247,16 @@ mod test { #[test] fn test_add_unsafe() { type Uint256_8 = UInt<256, 8>; - assert_eq!(Uint256_8::N_OPRAND_CELLS, 32); + assert_eq!(Uint256_8::N_OPERAND_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); + circuit_builder.create_witness_in(Uint256_8::N_OPERAND_CELLS); let (addend_1_wire_in_id, addend_1_wire_in_cells) = - circuit_builder.create_witness_in(Uint256_8::N_OPRAND_CELLS); + circuit_builder.create_witness_in(Uint256_8::N_OPERAND_CELLS); let (carry_wire_in_id, carry_wire_in_cells) = - circuit_builder.create_witness_in(Uint256_8::N_OPRAND_CELLS); + circuit_builder.create_witness_in(Uint256_8::N_OPERAND_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( @@ -277,14 +277,14 @@ mod test { 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]); + .extend(vec![Goldilocks::from(0u64); Uint256_8::N_OPERAND_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]); + .extend(vec![Goldilocks::from(0u64); Uint256_8::N_OPERAND_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]); + .extend(vec![Goldilocks::from(0u64); Uint256_8::N_OPERAND_CELLS - 2]); let circuit_witness = { let challenges = vec![GoldilocksExt2::from(2)]; let mut circuit_witness = CircuitWitness::new(&circuit, challenges); @@ -305,16 +305,16 @@ mod test { #[test] fn test_sub_unsafe() { type Uint256_8 = UInt<256, 8>; - assert_eq!(Uint256_8::N_OPRAND_CELLS, 32); + assert_eq!(Uint256_8::N_OPERAND_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); + circuit_builder.create_witness_in(Uint256_8::N_OPERAND_CELLS); let (subtrend_wire_in_id, subtrend_wire_in_cells) = - circuit_builder.create_witness_in(Uint256_8::N_OPRAND_CELLS); + circuit_builder.create_witness_in(Uint256_8::N_OPERAND_CELLS); let (borrow_wire_in_id, borrow_wire_in_cells) = - circuit_builder.create_witness_in(Uint256_8::N_OPRAND_CELLS); + circuit_builder.create_witness_in(Uint256_8::N_OPERAND_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( @@ -335,14 +335,14 @@ mod test { 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]); + .extend(vec![Goldilocks::from(0u64); Uint256_8::N_OPERAND_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]); + .extend(vec![Goldilocks::from(0u64); Uint256_8::N_OPERAND_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]); + .extend(vec![Goldilocks::from(0u64); Uint256_8::N_OPERAND_CELLS - 2]); let circuit_witness = { let challenges = vec![GoldilocksExt2::from(2)]; let mut circuit_witness = CircuitWitness::new(&circuit, challenges); diff --git a/singer-utils/src/uint_old/cmp.rs b/singer-utils/src/uint_old/cmp.rs new file mode 100644 index 000000000..81fd18053 --- /dev/null +++ b/singer-utils/src/uint_old/cmp.rs @@ -0,0 +1,185 @@ +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, + range_chip_handler: &mut H, + oprand_0: &UInt, + oprand_1: &UInt, + witness: &[CellId], + ) -> Result<(CellId, UInt), UtilError> { + let borrow = Self::extract_borrow(witness); + let range_values = Self::extract_range_values(witness); + let computed_diff = + UIntAddSub::>::sub_unsafe(circuit_builder, oprand_0, oprand_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)) + } else { + Ok((circuit_builder.create_cell(), diff)) + } + } + + pub fn assert_lt>( + circuit_builder: &mut CircuitBuilder, + range_chip_handler: &mut H, + oprand_0: &UInt, + oprand_1: &UInt, + witness: &[CellId], + ) -> Result<(), UtilError> { + let (borrow, _) = Self::lt( + circuit_builder, + range_chip_handler, + oprand_0, + oprand_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, + range_chip_handler: &mut H, + oprand_0: &UInt, + oprand_1: &UInt, + witness: &[CellId], + ) -> Result<(), UtilError> { + let (borrow, diff) = Self::lt( + circuit_builder, + range_chip_handler, + oprand_0, + oprand_1, + witness, + )?; + 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), + borrow, + ); + circuit_builder.assert_const(s, 0); + } + Ok(()) + } + + pub fn assert_eq( + circuit_builder: &mut CircuitBuilder, + oprand_0: &UInt, + oprand_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); + 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_OPERAND_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_OPERAND_CELLS); + let (operand_1_wire_in_id, operand_1_wire_in_cells) = + circuit_builder.create_witness_in(Uint256_8::N_OPERAND_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_OPERAND_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_OPERAND_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_OPERAND_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); + } +} diff --git a/singer/src/instructions/add.rs b/singer/src/instructions/add.rs index 8843964b2..0f5ade721 100644 --- a/singer/src/instructions/add.rs +++ b/singer/src/instructions/add.rs @@ -11,7 +11,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::{UIntAddSub, UIntCmp}, }; use std::sync::Arc; @@ -28,23 +27,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 => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + stack_ts_add => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, - old_stack_ts0 => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt0 => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS, - old_stack_ts1 => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt1 => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts0 => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt0 => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts1 => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt1 => TSUInt::N_NO_OVERFLOW_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 => StackUInt::N_WITNESS_CELLS } ); @@ -97,7 +96,7 @@ impl Instruction for AddInstruction { // Execution result = addend0 + addend1, with carry. let addend_0 = (&phase0[Self::phase0_addend_0()]).try_into()?; let addend_1 = (&phase0[Self::phase0_addend_1()]).try_into()?; - let result = UIntAddSub::::add( + let result = StackUInt::add( &mut circuit_builder, &mut rom_handler, &addend_0, @@ -113,7 +112,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, @@ -128,7 +127,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, diff --git a/singer/src/instructions/calldataload.rs b/singer/src/instructions/calldataload.rs index 5935eeefa..949ec6921 100644 --- a/singer/src/instructions/calldataload.rs +++ b/singer/src/instructions/calldataload.rs @@ -11,7 +11,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt, UInt64}, - uint::{UIntAddSub, UIntCmp}, }; use std::sync::Arc; @@ -28,20 +27,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 => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + stack_ts_add => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, - data => StackUInt::N_OPRAND_CELLS, - offset => UInt64::N_OPRAND_CELLS, - old_stack_ts => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS + data => StackUInt::N_OPERAND_CELLS, + offset => UInt64::N_OPERAND_CELLS, + old_stack_ts => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS } ); @@ -106,7 +105,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, diff --git a/singer/src/instructions/dup.rs b/singer/src/instructions/dup.rs index 5de39a5b6..906b7acb1 100644 --- a/singer/src/instructions/dup.rs +++ b/singer/src/instructions/dup.rs @@ -11,7 +11,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::{UIntAddSub, UIntCmp}, }; use std::sync::Arc; @@ -28,18 +27,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 => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + stack_ts_add => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, - stack_values => StackUInt::N_OPRAND_CELLS, - old_stack_ts => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS + stack_values => StackUInt::N_OPERAND_CELLS, + old_stack_ts => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS } ); @@ -101,7 +100,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, diff --git a/singer/src/instructions/gt.rs b/singer/src/instructions/gt.rs index 252a05239..c46d3c86b 100644 --- a/singer/src/instructions/gt.rs +++ b/singer/src/instructions/gt.rs @@ -11,7 +11,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::{UIntAddSub, UIntCmp}, }; use std::sync::Arc; @@ -28,23 +27,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 => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + stack_ts_add => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, - old_stack_ts0 => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt0 => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS, - old_stack_ts1 => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt1 => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts0 => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt0 => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts1 => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt1 => TSUInt::N_NO_OVERFLOW_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 => StackUInt::N_WITNESS_CELLS } ); @@ -97,7 +96,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, @@ -113,7 +112,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, @@ -128,7 +127,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, diff --git a/singer/src/instructions/jump.rs b/singer/src/instructions/jump.rs index 57baac5aa..3297de9ec 100644 --- a/singer/src/instructions/jump.rs +++ b/singer/src/instructions/jump.rs @@ -13,7 +13,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, TSUInt}, - uint::UIntCmp, }; use crate::error::ZKVMError; @@ -29,16 +28,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 => TSUInt::N_WITNESS_CELLS } ); @@ -76,7 +75,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, diff --git a/singer/src/instructions/jumpdest.rs b/singer/src/instructions/jumpdest.rs index f5a2a74de..e6a18d287 100644 --- a/singer/src/instructions/jumpdest.rs +++ b/singer/src/instructions/jumpdest.rs @@ -10,7 +10,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, TSUInt}, - uint::UIntAddSub, }; use std::sync::Arc; @@ -27,13 +26,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 => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS } ); diff --git a/singer/src/instructions/jumpi.rs b/singer/src/instructions/jumpi.rs index 3c2a99866..178129389 100644 --- a/singer/src/instructions/jumpi.rs +++ b/singer/src/instructions/jumpi.rs @@ -12,7 +12,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::{UIntAddSub, UIntCmp}, }; use std::sync::Arc; @@ -29,23 +28,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 => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_cond => TSUInt::N_OPERAND_CELLS, + old_stack_ts_cond_lt => TSUInt::N_NO_OVERFLOW_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 => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, pc_plus_1_opcode => 1 } ); @@ -89,7 +88,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, @@ -106,7 +105,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, @@ -140,8 +139,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 8cebb0c4c..8765c0f92 100644 --- a/singer/src/instructions/mstore.rs +++ b/singer/src/instructions/mstore.rs @@ -13,7 +13,6 @@ use singer_utils::{ constants::{OpcodeType, EVM_STACK_BYTE_WIDTH}, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::{UIntAddSub, UIntCmp}, }; use std::{mem, sync::Arc}; @@ -140,21 +139,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 => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + memory_ts_add => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, - 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_NO_OVERFLOW_WITNESS_CELLS, - old_stack_ts_value => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt_value => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS + old_stack_ts_offset => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt_offset => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_value => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt_value => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS } ); @@ -211,7 +210,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, @@ -229,9 +228,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, @@ -290,17 +289,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 => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, - offset_add_delta => UIntAddSub::::N_WITNESS_CELLS, + offset_add_delta => StackUInt::N_WITNESS_CELLS, prev_mem_bytes => 1 } ); @@ -327,14 +326,14 @@ impl Instruction for 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_small( &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/src/instructions/pop.rs b/singer/src/instructions/pop.rs index a83710525..e79ba8d13 100644 --- a/singer/src/instructions/pop.rs +++ b/singer/src/instructions/pop.rs @@ -11,7 +11,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::{UIntAddSub, UIntCmp}, }; use std::sync::Arc; @@ -27,17 +26,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 => PCUInt::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 => TSUInt::N_WITNESS_CELLS, + stack_values => StackUInt::N_OPERAND_CELLS } ); @@ -86,7 +85,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, diff --git a/singer/src/instructions/push.rs b/singer/src/instructions/push.rs index 02ba265c5..fea39022a 100644 --- a/singer/src/instructions/push.rs +++ b/singer/src/instructions/push.rs @@ -11,7 +11,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::UIntAddSub, }; use std::sync::Arc; @@ -28,14 +27,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 * PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + stack_ts_add => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, stack_bytes => N } @@ -97,7 +96,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, @@ -109,7 +108,7 @@ impl Instruction for PushInstruction { // Bytecode check for (pc, PUSH{N}), (pc + 1, byte[0]), ..., (pc + N, byte[N - 1]) rom_handler.bytecode_with_pc_opcode(&mut circuit_builder, pc.values(), Self::OPCODE); 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(PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS) .enumerate() { let next_pc = diff --git a/singer/src/instructions/ret.rs b/singer/src/instructions/ret.rs index 9d75d39d6..50330aa40 100644 --- a/singer/src/instructions/ret.rs +++ b/singer/src/instructions/ret.rs @@ -13,7 +13,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::UIntAddSub, }; use std::{mem, sync::Arc}; @@ -262,17 +261,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 } ); @@ -339,7 +338,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); @@ -369,15 +368,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 => StackUInt::N_WITNESS_CELLS } ); @@ -393,7 +392,7 @@ impl Instruction for 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_small( &mut circuit_builder, &mut rom_handler, &offset, @@ -433,8 +432,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 } ); @@ -475,7 +474,7 @@ register_witness!( ReturnRestMemStore, phase0 { mem_byte => 1, - offset => StackUInt::N_OPRAND_CELLS + offset => StackUInt::N_OPERAND_CELLS } ); @@ -488,7 +487,7 @@ impl Instruction for 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); @@ -512,8 +511,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 efd282d38..32a090589 100644 --- a/singer/src/instructions/swap.rs +++ b/singer/src/instructions/swap.rs @@ -11,7 +11,6 @@ use singer_utils::{ constants::OpcodeType, register_witness, structs::{PCUInt, RAMHandler, ROMHandler, StackUInt, TSUInt}, - uint::{UIntAddSub, UIntCmp}, }; use std::sync::Arc; @@ -27,21 +26,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 => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + stack_ts_add => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, - old_stack_ts_1 => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt_1 => UIntCmp::::N_NO_OVERFLOW_WITNESS_CELLS, - old_stack_ts_n_plus_1 => TSUInt::N_OPRAND_CELLS, - old_stack_ts_lt_n_plus_1 => UIntCmp::::N_NO_OVERFLOW_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 => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_n_plus_1 => TSUInt::N_OPERAND_CELLS, + old_stack_ts_lt_n_plus_1 => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + stack_values_1 => StackUInt::N_OPERAND_CELLS, + stack_values_n_plus_1 => StackUInt::N_OPERAND_CELLS } ); @@ -104,7 +103,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, @@ -121,7 +120,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, diff --git a/singer/src/test.rs b/singer/src/test.rs index 02f9ad7d9..1c71d5a82 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; From 5dce9db2bc20be48fa3ca3bba17b6290fe8bb9d0 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 13 Jun 2024 13:44:13 +0100 Subject: [PATCH 25/50] test compiling --- singer/src/instructions/jump.rs | 2 +- singer/src/instructions/pop.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/singer/src/instructions/jump.rs b/singer/src/instructions/jump.rs index 3297de9ec..c9c86bf33 100644 --- a/singer/src/instructions/jump.rs +++ b/singer/src/instructions/jump.rs @@ -193,7 +193,7 @@ 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![ diff --git a/singer/src/instructions/pop.rs b/singer/src/instructions/pop.rs index e79ba8d13..8ef270a37 100644 --- a/singer/src/instructions/pop.rs +++ b/singer/src/instructions/pop.rs @@ -196,7 +196,7 @@ 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![ From e810356add67916af748c596b706e66d1d5a08bb Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 13 Jun 2024 13:48:56 +0100 Subject: [PATCH 26/50] use range checker --- singer-utils/src/uint/arithmetic.rs | 21 ++++++--------------- singer-utils/src/uint/cmp.rs | 27 ++++++++++++--------------- 2 files changed, 18 insertions(+), 30 deletions(-) diff --git a/singer-utils/src/uint/arithmetic.rs b/singer-utils/src/uint/arithmetic.rs index aaba72ee8..c3a79c8a2 100644 --- a/singer-utils/src/uint/arithmetic.rs +++ b/singer-utils/src/uint/arithmetic.rs @@ -71,9 +71,7 @@ impl UInt { 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)?; - // TODO: uncomment this once you change to new uint_old - // range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) - todo!() + range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) } // TODO: add documentation @@ -117,9 +115,7 @@ impl UInt { 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)?; - // TODO: uncomment this once you change to new uint_old - // range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) - todo!() + range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) } // TODO: add documentation @@ -134,8 +130,7 @@ impl UInt { 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)) - todo!() + range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) } // TODO: add documentation and explanation + warning (do same for other unsafe) @@ -179,8 +174,7 @@ impl UInt { 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)) - todo!() + range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) } // TODO: add documentation @@ -195,8 +189,7 @@ impl UInt { 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)) - todo!() + range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) } // TODO: add documentation @@ -244,8 +237,6 @@ impl UInt { let borrow = Self::extract_borrow(witness); let range_values = Self::extract_range_values(witness); let computed_result = Self::sub_unsafe(circuit_builder, minuend, subtrahend, borrow)?; - // TODO: uncomment this once you change to new uint_old - // range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) - todo!() + range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) } } diff --git a/singer-utils/src/uint/cmp.rs b/singer-utils/src/uint/cmp.rs index aee37b691..3474a020a 100644 --- a/singer-utils/src/uint/cmp.rs +++ b/singer-utils/src/uint/cmp.rs @@ -28,21 +28,18 @@ impl UInt { let range_values = Self::extract_range_values(witness); let computed_diff = Self::sub_unsafe(circuit_builder, operand_0, operand_1, borrow)?; - // TODO: uncomment once you change range_check_uint - // let diff = range_chip_handler.range_check_uint( - // circuit_builder, - // &computed_diff, - // Some(&range_values) - // )?; - // - // if borrow.len() == Self::N_CARRY_CELLS { - // Ok((borrow[Self::N_CARRY_CELLS - 1], diff)) - // } else { - // // TODO: if we reach here then definitiely not lt - // Ok((circuit_builder.create_cell(), diff)) - // } - // - todo!() + let diff = range_chip_handler.range_check_uint( + circuit_builder, + &computed_diff, + Some(&range_values), + )?; + + if borrow.len() == Self::N_CARRY_CELLS { + Ok((borrow[Self::N_CARRY_CELLS - 1], diff)) + } else { + // TODO: if we reach here then definitiely not lt + Ok((circuit_builder.create_cell(), diff)) + } } // TODO: add documentation From 17a63aaa77f42445ae63a310e44910e51c40cbeb Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 13 Jun 2024 19:01:50 +0100 Subject: [PATCH 27/50] refactor constants second pass --- singer-pro/src/basic_block/bb_final.rs | 2 +- singer-pro/src/basic_block/bb_start.rs | 2 +- singer-pro/src/instructions/mstore.rs | 4 +- singer-utils/src/uint/constants.rs | 59 +++++++++++++-------- singer-utils/src/uint/witness_extractors.rs | 12 +---- singer/src/instructions/add.rs | 6 +-- singer/src/instructions/calldataload.rs | 4 +- singer/src/instructions/dup.rs | 4 +- singer/src/instructions/gt.rs | 6 +-- singer/src/instructions/jumpi.rs | 4 +- singer/src/instructions/mstore.rs | 8 +-- singer/src/instructions/push.rs | 2 +- singer/src/instructions/swap.rs | 6 +-- 13 files changed, 62 insertions(+), 57 deletions(-) diff --git a/singer-pro/src/basic_block/bb_final.rs b/singer-pro/src/basic_block/bb_final.rs index fc109c80c..f57a45394 100644 --- a/singer-pro/src/basic_block/bb_final.rs +++ b/singer-pro/src/basic_block/bb_final.rs @@ -27,7 +27,7 @@ pub struct BasicBlockFinal; register_witness!(BasicBlockFinal, phase0 { // State in related - stack_ts_add => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS + stack_ts_add => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW }); impl BasicBlockFinal { diff --git a/singer-pro/src/basic_block/bb_start.rs b/singer-pro/src/basic_block/bb_start.rs index de806a2da..dbb57004e 100644 --- a/singer-pro/src/basic_block/bb_start.rs +++ b/singer-pro/src/basic_block/bb_start.rs @@ -35,7 +35,7 @@ register_multi_witness!(BasicBlockStart, phase0(n_stack_items) { // Stack values 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) => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS + old_stack_ts_lt(n_stack_items) => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW }); impl BasicBlockStart { diff --git a/singer-pro/src/instructions/mstore.rs b/singer-pro/src/instructions/mstore.rs index 3edffdefe..cb8180780 100644 --- a/singer-pro/src/instructions/mstore.rs +++ b/singer-pro/src/instructions/mstore.rs @@ -110,7 +110,7 @@ impl InstructionGraph for MstoreInstruction { register_witness!( MstoreInstruction, phase0 { - memory_ts_add => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + memory_ts_add => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, mem_bytes => EVM_STACK_BYTE_WIDTH } ); @@ -216,7 +216,7 @@ register_witness!( }, phase0 { old_memory_ts => TSUInt::N_OPERAND_CELLS, - old_memory_ts_lt => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + old_memory_ts_lt => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, offset_add_delta => StackUInt::N_WITNESS_CELLS, prev_mem_byte => 1 diff --git a/singer-utils/src/uint/constants.rs b/singer-utils/src/uint/constants.rs index b5c064000..b7c9ba840 100644 --- a/singer-utils/src/uint/constants.rs +++ b/singer-utils/src/uint/constants.rs @@ -2,46 +2,59 @@ use super::uint::UInt; use crate::constants::RANGE_CHIP_BIT_WIDTH; use crate::uint::util::const_min; -// TODO: arrange this into sensible units 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; - // TODO: add documentation - pub const N_CARRY_CELLS: usize = Self::N_OPERAND_CELLS; - - // TODO: add documentation - const N_CARRY_CELLS_NO_OVERFLOW: usize = Self::N_CARRY_CELLS - 1; - /// 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; - // TODO: add documentation - pub const N_RANGE_CELLS_NO_OVERFLOW: usize = + /// The number of `RANGE_CHIP_BIT_WIDTH` cells needed to represent the carry cells, assuming + /// no overflow. + pub const N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW: usize = Self::N_CARRY_CELLS_NO_OVERFLOW * Self::N_RANGE_CELLS_PER_CELL; - /// 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); + /// The size of the witness + pub const N_WITNESS_CELLS: usize = Self::N_RANGE_CELLS + Self::N_CARRY_CELLS; - // TODO: add documentation - // TODO: shouldn't the range cells have no overflow also? - pub const N_NO_OVERFLOW_WITNESS_CELLS: usize = + /// The size of the witness assuming carry has no overflow + /// |Range_values| + |Carry - 1| + pub const N_WITNESS_CELLS_NO_CARRY_OVERFLOW: usize = Self::N_RANGE_CELLS + Self::N_CARRY_CELLS_NO_OVERFLOW; // TODO: add documentation - // TODO: potential rename with all the cell thing + // TODO: why this? pub const N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS: usize = Self::N_CARRY_CELLS_NO_OVERFLOW; - // TODO: add documentation - pub const N_WITNESS_CELLS: usize = Self::N_RANGE_CELLS + Self::N_CARRY_CELLS; -} + // Arithmetic Constants -// TODO: test generated usize constants (test once we get all constants) + /// 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 = Self::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; +} diff --git a/singer-utils/src/uint/witness_extractors.rs b/singer-utils/src/uint/witness_extractors.rs index fe0318f05..82bdc65eb 100644 --- a/singer-utils/src/uint/witness_extractors.rs +++ b/singer-utils/src/uint/witness_extractors.rs @@ -1,45 +1,37 @@ use crate::uint::uint::UInt; use simple_frontend::structs::CellId; -// TODO: test impl UInt { // witness_structure // [...range_values..., ...carry_witness...] - // TODO: add documentation pub fn extract_carry(witness: &[CellId]) -> &[CellId] { &witness[Self::N_RANGE_CELLS..] } - // TODO: add documentation pub fn extract_carry_no_overflow(witness: &[CellId]) -> &[CellId] { - &witness[Self::N_RANGE_CELLS_NO_OVERFLOW..] + &witness[Self::N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW..] } - // TODO: add documentation // TODO: why do we need this pub fn extract_unsafe_carry(witness: &[CellId]) -> &[CellId] { witness } - // TODO: add documentation pub fn extract_borrow(witness: &[CellId]) -> &[CellId] { &witness[Self::N_RANGE_CELLS..] } - // TODO: add documentation // TODO: why do we need this pub fn extract_unsafe_borrow(witness: &[CellId]) -> &[CellId] { witness } - // TODO: add documentation pub fn extract_range_values(witness: &[CellId]) -> &[CellId] { &witness[..Self::N_RANGE_CELLS] } - // TODO: add documentation pub fn extract_range_values_no_overflow(witness: &[CellId]) -> &[CellId] { - &witness[..Self::N_RANGE_CELLS_NO_OVERFLOW] + &witness[..Self::N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW] } } diff --git a/singer/src/instructions/add.rs b/singer/src/instructions/add.rs index 0f5ade721..7b899923f 100644 --- a/singer/src/instructions/add.rs +++ b/singer/src/instructions/add.rs @@ -34,12 +34,12 @@ register_witness!( clk => 1, pc_add => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + stack_ts_add => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, old_stack_ts0 => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt0 => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_lt0 => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, old_stack_ts1 => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt1 => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_lt1 => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, addend_0 => StackUInt::N_OPERAND_CELLS, addend_1 => StackUInt::N_OPERAND_CELLS, diff --git a/singer/src/instructions/calldataload.rs b/singer/src/instructions/calldataload.rs index 949ec6921..4f6ac6459 100644 --- a/singer/src/instructions/calldataload.rs +++ b/singer/src/instructions/calldataload.rs @@ -35,12 +35,12 @@ register_witness!( clk => 1, pc_add => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + stack_ts_add => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, data => StackUInt::N_OPERAND_CELLS, offset => UInt64::N_OPERAND_CELLS, old_stack_ts => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS + old_stack_ts_lt => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW } ); diff --git a/singer/src/instructions/dup.rs b/singer/src/instructions/dup.rs index 906b7acb1..5d3423a50 100644 --- a/singer/src/instructions/dup.rs +++ b/singer/src/instructions/dup.rs @@ -34,11 +34,11 @@ register_witness!( clk => 1, pc_add => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + stack_ts_add => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, stack_values => StackUInt::N_OPERAND_CELLS, old_stack_ts => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS + old_stack_ts_lt => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW } ); diff --git a/singer/src/instructions/gt.rs b/singer/src/instructions/gt.rs index c46d3c86b..cc47ed571 100644 --- a/singer/src/instructions/gt.rs +++ b/singer/src/instructions/gt.rs @@ -34,12 +34,12 @@ register_witness!( clk => 1, pc_add => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + stack_ts_add => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, old_stack_ts0 => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt0 => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_lt0 => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, old_stack_ts1 => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt1 => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_lt1 => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, oprand_0 => StackUInt::N_OPERAND_CELLS, oprand_1 => StackUInt::N_OPERAND_CELLS, diff --git a/singer/src/instructions/jumpi.rs b/singer/src/instructions/jumpi.rs index 178129389..fbacd2ad2 100644 --- a/singer/src/instructions/jumpi.rs +++ b/singer/src/instructions/jumpi.rs @@ -35,9 +35,9 @@ register_witness!( clk => 1, old_stack_ts_dest => TSUInt::N_OPERAND_CELLS, - old_stack_ts_dest_lt => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_dest_lt => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, old_stack_ts_cond => TSUInt::N_OPERAND_CELLS, - old_stack_ts_cond_lt => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_cond_lt => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, dest_values => StackUInt::N_OPERAND_CELLS, cond_values => StackUInt::N_OPERAND_CELLS, diff --git a/singer/src/instructions/mstore.rs b/singer/src/instructions/mstore.rs index 8765c0f92..812c2c13f 100644 --- a/singer/src/instructions/mstore.rs +++ b/singer/src/instructions/mstore.rs @@ -146,14 +146,14 @@ register_witness!( clk => 1, pc_add => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - memory_ts_add => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + memory_ts_add => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, offset => StackUInt::N_OPERAND_CELLS, mem_bytes => EVM_STACK_BYTE_WIDTH, old_stack_ts_offset => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt_offset => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_lt_offset => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, old_stack_ts_value => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt_value => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS + old_stack_ts_lt_value => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW } ); @@ -297,7 +297,7 @@ register_witness!( }, phase0 { old_memory_ts => TSUInt::N_OPERAND_CELLS, - old_memory_ts_lt => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + old_memory_ts_lt => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, offset_add_delta => StackUInt::N_WITNESS_CELLS, prev_mem_bytes => 1 diff --git a/singer/src/instructions/push.rs b/singer/src/instructions/push.rs index fea39022a..194d89ccd 100644 --- a/singer/src/instructions/push.rs +++ b/singer/src/instructions/push.rs @@ -34,7 +34,7 @@ register_witness!( clk => 1, pc_add_i_plus_1 => N * PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + stack_ts_add => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, stack_bytes => N } diff --git a/singer/src/instructions/swap.rs b/singer/src/instructions/swap.rs index 32a090589..6c348de08 100644 --- a/singer/src/instructions/swap.rs +++ b/singer/src/instructions/swap.rs @@ -33,12 +33,12 @@ register_witness!( clk => 1, pc_add => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + stack_ts_add => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, old_stack_ts_1 => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt_1 => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_lt_1 => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, old_stack_ts_n_plus_1 => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt_n_plus_1 => TSUInt::N_NO_OVERFLOW_WITNESS_CELLS, + old_stack_ts_lt_n_plus_1 => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, stack_values_1 => StackUInt::N_OPERAND_CELLS, stack_values_n_plus_1 => StackUInt::N_OPERAND_CELLS } From 2033535cc4df3d6af67410e7f6e1d48206747b2d Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 13 Jun 2024 19:23:31 +0100 Subject: [PATCH 28/50] document cmp --- singer-utils/src/uint/cmp.rs | 45 +++++++++++++----------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/singer-utils/src/uint/cmp.rs b/singer-utils/src/uint/cmp.rs index 3474a020a..a45d91a21 100644 --- a/singer-utils/src/uint/cmp.rs +++ b/singer-utils/src/uint/cmp.rs @@ -1,6 +1,3 @@ -// TODO: document module -// mostly holds comparison methods on the uint_old type - use crate::chip_handler::RangeChipOperations; use crate::error::UtilError; use crate::uint::uint::UInt; @@ -8,11 +5,10 @@ use ff::Field; use ff_ext::ExtensionField; use simple_frontend::structs::{CellId, CircuitBuilder, MixedCell}; +// TODO: make this take in self + impl UInt { - // TODO: what should this do? - // operand_0 < operand_1 - // this isn't really checking for less than, more like creating the necessary data needed for less than check - // TODO: change name + /// Generates the required information for asserting lt and leq pub fn lt>( circuit_builder: &mut CircuitBuilder, range_chip_handler: &mut H, @@ -20,10 +16,6 @@ impl UInt { operand_1: &UInt, witness: &[CellId], ) -> Result<(CellId, UInt), UtilError> { - // achieves less than, by subtracting and then verifying that the result is in - // some range, so it's technically not correct, depends on the range values that are passed - // in - // if operand_0 is less then the borrow will be 1 for the MSB let borrow = Self::extract_borrow(witness); let range_values = Self::extract_range_values(witness); let computed_diff = Self::sub_unsafe(circuit_builder, operand_0, operand_1, borrow)?; @@ -34,16 +26,15 @@ impl UInt { Some(&range_values), )?; + // if operand_0 < operand_1, the last borrow should equal 1 if borrow.len() == Self::N_CARRY_CELLS { Ok((borrow[Self::N_CARRY_CELLS - 1], diff)) } else { - // TODO: if we reach here then definitiely not lt Ok((circuit_builder.create_cell(), diff)) } } - // TODO: add documentation - // describe logic + /// Asserts that operand_0 < operand_1 pub fn assert_lt>( circuit_builder: &mut CircuitBuilder, range_chip_handler: &mut H, @@ -62,7 +53,7 @@ impl UInt { Ok(()) } - // TODO: add documentation + /// Asserts that operand_0 <= operand_1 pub fn assert_leq>( circuit_builder: &mut CircuitBuilder, range_chip_handler: &mut H, @@ -78,21 +69,18 @@ impl UInt { witness, )?; - // what will be the content of borrow and diif is less than? - // borrow will be 1 and diff will be diff - // what will be the content if equal - // borrow will be 0 and diff should be 0 - // how do we ensure that it's only that case that works? - - // if borrow = 0 return diff - // if borrow = 1 return 0 - // does this hold across all values? + // 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(); - // if borrow == 0 return diff else return 0 - // TODO: explain this circuit_builder.sel_mixed( s, (*d).into(), @@ -105,8 +93,7 @@ impl UInt { Ok(()) } - // TODO: add documentation (shuffle) - // TODO: document the steps + /// Asserts that two `UInt` instances represent equal value pub fn assert_eq( circuit_builder: &mut CircuitBuilder, operand_0: &UInt, @@ -123,7 +110,7 @@ impl UInt { Ok(()) } - // TODO: add documentation + /// 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, From 9ca2e4245939b1d4f272fcd2543e9cc40e564e9e Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 13 Jun 2024 19:30:06 +0100 Subject: [PATCH 29/50] wip --- singer-utils/src/uint/cmp.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/singer-utils/src/uint/cmp.rs b/singer-utils/src/uint/cmp.rs index a45d91a21..f9c9256d5 100644 --- a/singer-utils/src/uint/cmp.rs +++ b/singer-utils/src/uint/cmp.rs @@ -5,8 +5,6 @@ use ff::Field; use ff_ext::ExtensionField; use simple_frontend::structs::{CellId, CircuitBuilder, MixedCell}; -// TODO: make this take in self - impl UInt { /// Generates the required information for asserting lt and leq pub fn lt>( From 14d0254e686536a42502e2cbe3d1ae81db2b2a70 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 13 Jun 2024 20:08:45 +0100 Subject: [PATCH 30/50] arithmetic second pass --- singer-utils/src/uint/arithmetic.rs | 38 +++++++++++++---------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/singer-utils/src/uint/arithmetic.rs b/singer-utils/src/uint/arithmetic.rs index c3a79c8a2..df1cb9edb 100644 --- a/singer-utils/src/uint/arithmetic.rs +++ b/singer-utils/src/uint/arithmetic.rs @@ -3,12 +3,8 @@ use crate::error::UtilError; use crate::uint::uint::UInt; use ff::Field; use ff_ext::ExtensionField; -use simple_frontend::structs::{Cell, CellId, CircuitBuilder}; +use simple_frontend::structs::{CellId, CircuitBuilder}; -// TODO: this functions should use self, better UI (refactor for this) - -// TODO: test -// TODO: reconfirm logic impl UInt { /// Little-endian addition. /// Assumes users will check the correct range of the result themselves. @@ -74,8 +70,8 @@ impl UInt { range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) } - // TODO: add documentation - // TODO: figure out how to remove duplication + /// Add a constant value to every cell in the `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, @@ -104,7 +100,7 @@ impl UInt { Ok(result) } - // TODO: add documentation + /// Add a constant value to every cell in the `UInt` instance pub fn add_const>( circuit_builder: &mut CircuitBuilder, range_chip_handler: &mut H, @@ -118,7 +114,8 @@ impl UInt { range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) } - // TODO: add documentation + /// Add a constant value to every cell in the `UInt` instance + /// Assumes that addition leads to no overflow. pub fn add_const_no_overflow>( circuit_builder: &mut CircuitBuilder, range_chip_handler: &mut H, @@ -126,15 +123,15 @@ impl UInt { constant: E::BaseField, witness: &[CellId], ) -> Result, UtilError> { - // TODO: confirm that carry without overflow does not affect add_const_unsafe logic 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)) } - // TODO: add documentation and explanation + warning (do same for other unsafe) - // TODO: remove duplication + /// Adds a single cell value to every operand in the `UInt` instance + /// Assumes users will check the correct range of the result and + /// guarantee addend_1 < 1 << C. pub fn add_small_unsafe( circuit_builder: &mut CircuitBuilder, addend_0: &UInt, @@ -163,7 +160,8 @@ impl UInt { Ok(result) } - // TODO: add documentation + /// Adds a single cell value to every operand in the `UInt` instance + /// Assumes users will guarantee addend_1 < 1 << C. pub fn add_small>( circuit_builder: &mut CircuitBuilder, range_chip_handler: &mut H, @@ -177,7 +175,9 @@ impl UInt { range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) } - // TODO: add documentation + /// Adds a single cell value to every operand in the `UInt` instance + /// Assumes users will guarantee addend_1 < 1 << C. + /// Assumes that addition lead to no overflow. pub fn add_small_no_overflow>( circuit_builder: &mut CircuitBuilder, range_chip_handler: &mut H, @@ -185,15 +185,14 @@ impl UInt { addend_1: CellId, witness: &[CellId], ) -> Result, UtilError> { - // TODO: confirm no overflow size doesn't affect add_small logic 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)) } - // TODO: add documentation - // minuend - subtrahend + /// Little endian subtraction + /// Assumes users will check the correct range of the result themselves. pub fn sub_unsafe( circuit_builder: &mut CircuitBuilder, minuend: &UInt, @@ -204,8 +203,6 @@ impl UInt { .create_cells(Self::N_OPERAND_CELLS) .try_into()?; - // we do limb by limb subtraction but we have to make use of the borrow - // we need to add the borrow for i in 0..Self::N_OPERAND_CELLS { let (minuend, subtrahend, result) = (minuend.values[i], subtrahend.values[i], result.values[i]); @@ -217,7 +214,6 @@ impl UInt { circuit_builder.add(result, borrow[i], E::BaseField::from(1 << C)); } - // TODO: confirm this logic if i > 0 && i - 1 < borrow.len() { circuit_builder.add(result, borrow[i - 1], -E::BaseField::ONE); } @@ -226,7 +222,7 @@ impl UInt { Ok(result) } - // TODO: add documentation + /// Little endian subtraction pub fn sub>( circuit_builder: &mut CircuitBuilder, range_chip_handler: &mut H, From c2ccb5eeb9fbc4a77d0c4d949341e3e0786d7af6 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 13 Jun 2024 20:09:34 +0100 Subject: [PATCH 31/50] delete old uint --- singer-utils/src/uint_old/add_sub.rs | 362 --------------------------- singer-utils/src/uint_old/cmp.rs | 185 -------------- 2 files changed, 547 deletions(-) delete mode 100644 singer-utils/src/uint_old/add_sub.rs delete mode 100644 singer-utils/src/uint_old/cmp.rs diff --git a/singer-utils/src/uint_old/add_sub.rs b/singer-utils/src/uint_old/add_sub.rs deleted file mode 100644 index 2788f110a..000000000 --- a/singer-utils/src/uint_old/add_sub.rs +++ /dev/null @@ -1,362 +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_OPERAND_CELLS) - .try_into()?; - for i in 0..UInt::::N_OPERAND_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. - /// want to check = [a, b] - /// lookuptable = [a, b, c, d, e, f, g, h] - /// is 16bits - /// should_be_16 = [a, b] - /// table = [all possible 16 bit values] - /// range_values = contains all possible values of that range? - /// - /// a + b = c - /// c = computed_result - /// function(a, b, carry) = c - /// carry can be manipulated to give different c's - /// how do we ensure that the c we compute is the actual c - /// - /// [1, 0, 0] - carry_0 - /// [0, 0, 0] - carry_1 - /// - /// Double check: false carry should violate range check - /// - /// a b c - /// d e f - /// ----- - 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_OPERAND_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_OPERAND_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_OPERAND_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_OPERAND_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_OPERAND_CELLS); - let (addend_1_wire_in_id, addend_1_wire_in_cells) = - circuit_builder.create_witness_in(Uint256_8::N_OPERAND_CELLS); - let (carry_wire_in_id, carry_wire_in_cells) = - circuit_builder.create_witness_in(Uint256_8::N_OPERAND_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_OPERAND_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_OPERAND_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_OPERAND_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_OPERAND_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_OPERAND_CELLS); - let (subtrend_wire_in_id, subtrend_wire_in_cells) = - circuit_builder.create_witness_in(Uint256_8::N_OPERAND_CELLS); - let (borrow_wire_in_id, borrow_wire_in_cells) = - circuit_builder.create_witness_in(Uint256_8::N_OPERAND_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_OPERAND_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_OPERAND_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_OPERAND_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_old/cmp.rs b/singer-utils/src/uint_old/cmp.rs deleted file mode 100644 index 81fd18053..000000000 --- a/singer-utils/src/uint_old/cmp.rs +++ /dev/null @@ -1,185 +0,0 @@ -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, - range_chip_handler: &mut H, - oprand_0: &UInt, - oprand_1: &UInt, - witness: &[CellId], - ) -> Result<(CellId, UInt), UtilError> { - let borrow = Self::extract_borrow(witness); - let range_values = Self::extract_range_values(witness); - let computed_diff = - UIntAddSub::>::sub_unsafe(circuit_builder, oprand_0, oprand_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)) - } else { - Ok((circuit_builder.create_cell(), diff)) - } - } - - pub fn assert_lt>( - circuit_builder: &mut CircuitBuilder, - range_chip_handler: &mut H, - oprand_0: &UInt, - oprand_1: &UInt, - witness: &[CellId], - ) -> Result<(), UtilError> { - let (borrow, _) = Self::lt( - circuit_builder, - range_chip_handler, - oprand_0, - oprand_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, - range_chip_handler: &mut H, - oprand_0: &UInt, - oprand_1: &UInt, - witness: &[CellId], - ) -> Result<(), UtilError> { - let (borrow, diff) = Self::lt( - circuit_builder, - range_chip_handler, - oprand_0, - oprand_1, - witness, - )?; - 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), - borrow, - ); - circuit_builder.assert_const(s, 0); - } - Ok(()) - } - - pub fn assert_eq( - circuit_builder: &mut CircuitBuilder, - oprand_0: &UInt, - oprand_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); - 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_OPERAND_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_OPERAND_CELLS); - let (operand_1_wire_in_id, operand_1_wire_in_cells) = - circuit_builder.create_witness_in(Uint256_8::N_OPERAND_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_OPERAND_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_OPERAND_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_OPERAND_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); - } -} From 8977106297d0539868862d4dc43d54fb05d18786 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Fri, 14 Jun 2024 09:54:09 +0100 Subject: [PATCH 32/50] refactor arithmetic --- singer-utils/src/lib.rs | 1 - singer-utils/src/uint/arithmetic.rs | 80 +++++++++++++++++++---------- singer-utils/src/uint/uint.rs | 2 +- singer-utils/src/uint/util.rs | 1 - 4 files changed, 53 insertions(+), 31 deletions(-) diff --git a/singer-utils/src/lib.rs b/singer-utils/src/lib.rs index fda1ad872..5e98a75b6 100644 --- a/singer-utils/src/lib.rs +++ b/singer-utils/src/lib.rs @@ -5,7 +5,6 @@ pub mod chips; pub mod constants; pub mod error; pub mod structs; -// pub mod uint_old; pub mod uint; #[macro_use] diff --git a/singer-utils/src/uint/arithmetic.rs b/singer-utils/src/uint/arithmetic.rs index df1cb9edb..ebb35f97b 100644 --- a/singer-utils/src/uint/arithmetic.rs +++ b/singer-utils/src/uint/arithmetic.rs @@ -3,7 +3,7 @@ use crate::error::UtilError; use crate::uint::uint::UInt; use ff::Field; use ff_ext::ExtensionField; -use simple_frontend::structs::{CellId, CircuitBuilder}; +use simple_frontend::structs::{Cell, CellId, CircuitBuilder}; impl UInt { /// Little-endian addition. @@ -41,16 +41,10 @@ impl UInt { 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); - - 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); - } + Self::handle_carry(result, circuit_builder, i, carry); } Ok(result) @@ -70,7 +64,7 @@ impl UInt { range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) } - /// Add a constant value to every cell in the `UInt` instance + /// 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, @@ -82,19 +76,15 @@ impl UInt { .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); - circuit_builder.add_const(result, constant); - - 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); - } + Self::handle_carry(result, circuit_builder, i, carry); } Ok(result) @@ -142,19 +132,15 @@ impl UInt { .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); - circuit_builder.add(result, addend_1, E::BaseField::ONE); - - 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); - } + Self::handle_carry(result, circuit_builder, i, carry); } Ok(result) @@ -235,4 +221,42 @@ impl UInt { 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); + } + } } diff --git a/singer-utils/src/uint/uint.rs b/singer-utils/src/uint/uint.rs index 98c04cae2..2c067e48f 100644 --- a/singer-utils/src/uint/uint.rs +++ b/singer-utils/src/uint/uint.rs @@ -2,10 +2,10 @@ use crate::constants::{BYTE_BIT_WIDTH, RANGE_CHIP_BIT_WIDTH}; use crate::error::UtilError; use crate::uint::util::{convert_decomp, pad_cells}; use ff_ext::ExtensionField; -use gkr::utils::ceil_log2; 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. diff --git a/singer-utils/src/uint/util.rs b/singer-utils/src/uint/util.rs index 0685a15c2..8f5d34ebc 100644 --- a/singer-utils/src/uint/util.rs +++ b/singer-utils/src/uint/util.rs @@ -1,6 +1,5 @@ use crate::error::UtilError; use ff_ext::ExtensionField; -use gkr::utils::ceil_log2; use goldilocks::SmallField; use itertools::Itertools; use simple_frontend::structs::{CellId, CircuitBuilder}; From 6dfaf7103f97cbb4de4222e17bdcb2953fa3293e Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Fri, 14 Jun 2024 09:55:57 +0100 Subject: [PATCH 33/50] fix documentation --- singer-utils/src/uint/arithmetic.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/singer-utils/src/uint/arithmetic.rs b/singer-utils/src/uint/arithmetic.rs index ebb35f97b..ce3ecd32c 100644 --- a/singer-utils/src/uint/arithmetic.rs +++ b/singer-utils/src/uint/arithmetic.rs @@ -90,7 +90,7 @@ impl UInt { Ok(result) } - /// Add a constant value to every cell in the `UInt` instance + /// Add a constant value to a `UInt` instance pub fn add_const>( circuit_builder: &mut CircuitBuilder, range_chip_handler: &mut H, @@ -104,7 +104,7 @@ impl UInt { range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) } - /// Add a constant value to every cell in the `UInt` instance + /// 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, @@ -119,7 +119,7 @@ impl UInt { range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) } - /// Adds a single cell value to every operand in the `UInt` instance + /// Adds a single cell value to a `UInt` instance /// Assumes users will check the correct range of the result and /// guarantee addend_1 < 1 << C. pub fn add_small_unsafe( @@ -146,7 +146,7 @@ impl UInt { Ok(result) } - /// Adds a single cell value to every operand in the `UInt` instance + /// Adds a single cell value to a `UInt` instance /// Assumes users will guarantee addend_1 < 1 << C. pub fn add_small>( circuit_builder: &mut CircuitBuilder, @@ -161,7 +161,7 @@ impl UInt { range_chip_handler.range_check_uint(circuit_builder, &computed_result, Some(range_values)) } - /// Adds a single cell value to every operand in the `UInt` instance + /// Adds a single cell value to a `UInt` instance /// Assumes users will guarantee addend_1 < 1 << C. /// Assumes that addition lead to no overflow. pub fn add_small_no_overflow>( From 08813e225850a52652d22207f58d28e4357aecca Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Fri, 14 Jun 2024 18:04:27 +0100 Subject: [PATCH 34/50] add test for add --- singer-utils/src/structs.rs | 6 - singer-utils/src/uint/arithmetic.rs | 109 +++++++++++++ singer-utils/src/uint_old.rs | 228 ---------------------------- 3 files changed, 109 insertions(+), 234 deletions(-) delete mode 100644 singer-utils/src/uint_old.rs diff --git a/singer-utils/src/structs.rs b/singer-utils/src/structs.rs index 1ed5187b3..7c0208756 100644 --- a/singer-utils/src/structs.rs +++ b/singer-utils/src/structs.rs @@ -48,12 +48,6 @@ 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>; diff --git a/singer-utils/src/uint/arithmetic.rs b/singer-utils/src/uint/arithmetic.rs index ce3ecd32c..5b99a029a 100644 --- a/singer-utils/src/uint/arithmetic.rs +++ b/singer-utils/src/uint/arithmetic.rs @@ -260,3 +260,112 @@ impl UInt { } } } + +#[cfg(test)] +mod tests { + use crate::uint::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(UInt20::N_OPERAND_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() { + // // 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 + // + // } +} diff --git a/singer-utils/src/uint_old.rs b/singer-utils/src/uint_old.rs deleted file mode 100644 index 0827fb840..000000000 --- a/singer-utils/src/uint_old.rs +++ /dev/null @@ -1,228 +0,0 @@ -// TODO: delete this - -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_OPERAND_CELLS { - // TODO: this will be replaced soon - return Err(UtilError::UIntError("placeholder".to_string())); - } - 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_OPERAND_CELLS: usize = (M + C - 1) / C; - - const N_CARRY_CELLS: usize = Self::N_OPERAND_CELLS; - const N_CARRY_NO_OVERFLOW_CELLS: usize = Self::N_OPERAND_CELLS - 1; - pub const N_RANGE_CHECK_CELLS: usize = - Self::N_OPERAND_CELLS * (C + RANGE_CHIP_BIT_WIDTH - 1) / RANGE_CHIP_BIT_WIDTH; - pub const N_RANGE_CHECK_NO_OVERFLOW_CELLS: usize = - (Self::N_OPERAND_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_OPERAND_CELLS { - values.push(circuit_builder.create_cell()); - } - Self::try_from(values) - } - - pub fn from_bytes_big_endien( - circuit_builder: &mut CircuitBuilder, - bytes: &[CellId], - ) -> Result { - if C <= M { - convert_decomp(circuit_builder, bytes, 8, C, true).try_into() - } else { - convert_decomp(circuit_builder, bytes, 8, M, true).try_into() - } - } - - 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 gkr::structs::{Circuit, CircuitWitness}; - use goldilocks::{Goldilocks, GoldilocksExt2}; - use simple_frontend::structs::CircuitBuilder; - - #[test] - fn test_convert_decomp() { - // use of convert_decomp must ensure that - // small_len * small_bit_width does not exceed - // the field's max bit size (64 for Goldlilocks) - 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)); - } - } -} From d031363006b6eb4667eae886a92e087900e567e7 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Fri, 14 Jun 2024 18:30:38 +0100 Subject: [PATCH 35/50] test add constant unsafe --- singer-utils/src/uint/arithmetic.rs | 91 +++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 19 deletions(-) diff --git a/singer-utils/src/uint/arithmetic.rs b/singer-utils/src/uint/arithmetic.rs index 5b99a029a..d55d39385 100644 --- a/singer-utils/src/uint/arithmetic.rs +++ b/singer-utils/src/uint/arithmetic.rs @@ -296,7 +296,7 @@ mod tests { 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(UInt20::N_OPERAND_CELLS); + let (carry_id, carry_cells) = circuit_builder.create_witness_in(UInt20::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"); @@ -350,22 +350,75 @@ mod tests { ); } - // #[test] - // fn test_add_constant() { - // // 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 - // - // } + #[test] + fn test_add_constant() { + // 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(UInt20::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() + ); + } } From 97f3945dbac49e0e1dfac19f70ec6e5aae0aed1b Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Fri, 14 Jun 2024 18:37:45 +0100 Subject: [PATCH 36/50] add test for add_small_unsafe --- singer-utils/src/uint/arithmetic.rs | 80 ++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/singer-utils/src/uint/arithmetic.rs b/singer-utils/src/uint/arithmetic.rs index d55d39385..823158f1d 100644 --- a/singer-utils/src/uint/arithmetic.rs +++ b/singer-utils/src/uint/arithmetic.rs @@ -351,7 +351,7 @@ mod tests { } #[test] - fn test_add_constant() { + fn test_add_constant_unsafe() { // UInt<20, 5> (4 limbs) // A + constant @@ -421,4 +421,82 @@ mod tests { .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(UInt20::N_CARRY_CELLS); + + let addend_0 = UInt20::try_from(addend_0_cells).expect("should build uint"); + + // update circuit builder + let result = UInt20::add_small_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() + ); + } } From 87879126824606a7689966ff7bac2860996c8aac Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Fri, 14 Jun 2024 19:04:29 +0100 Subject: [PATCH 37/50] implement handle borrow --- singer-utils/src/uint/arithmetic.rs | 48 ++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/singer-utils/src/uint/arithmetic.rs b/singer-utils/src/uint/arithmetic.rs index 823158f1d..df351fceb 100644 --- a/singer-utils/src/uint/arithmetic.rs +++ b/singer-utils/src/uint/arithmetic.rs @@ -196,13 +196,7 @@ impl UInt { 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); - } + Self::handle_borrow(result, circuit_builder, i, borrow); } Ok(result) @@ -259,6 +253,46 @@ impl UInt { 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)] From 8937ab06707694a36f760ec64a93eab2834b4e9b Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Fri, 14 Jun 2024 19:20:18 +0100 Subject: [PATCH 38/50] test sub_unsafe --- singer-utils/src/uint/arithmetic.rs | 74 +++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/singer-utils/src/uint/arithmetic.rs b/singer-utils/src/uint/arithmetic.rs index df351fceb..1413a3ea0 100644 --- a/singer-utils/src/uint/arithmetic.rs +++ b/singer-utils/src/uint/arithmetic.rs @@ -533,4 +533,78 @@ mod tests { .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(UInt20::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() + ); + } } From 4d956157844c3f2e3b7c4d43c738d0a015f79cd7 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 17 Jun 2024 20:37:35 +0100 Subject: [PATCH 39/50] rename add_small to add_cell --- singer-pro/src/instructions/mstore.rs | 2 +- singer-pro/src/instructions/ret.rs | 2 +- singer-utils/src/uint/arithmetic.rs | 15 ++++++--------- singer/src/instructions/mstore.rs | 2 +- singer/src/instructions/ret.rs | 2 +- 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/singer-pro/src/instructions/mstore.rs b/singer-pro/src/instructions/mstore.rs index 894d7a974..1bdd0e499 100644 --- a/singer-pro/src/instructions/mstore.rs +++ b/singer-pro/src/instructions/mstore.rs @@ -249,7 +249,7 @@ 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 = StackUInt::add_small( + let offset_plus_delta = StackUInt::add_cell( &mut circuit_builder, &mut rom_handler, &offset, diff --git a/singer-pro/src/instructions/ret.rs b/singer-pro/src/instructions/ret.rs index f1c8e52f7..2181d11b7 100644 --- a/singer-pro/src/instructions/ret.rs +++ b/singer-pro/src/instructions/ret.rs @@ -137,7 +137,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 = StackUInt::add_small( + let offset_plus_delta = StackUInt::add_cell( &mut circuit_builder, &mut rom_handler, &offset, diff --git a/singer-utils/src/uint/arithmetic.rs b/singer-utils/src/uint/arithmetic.rs index 1413a3ea0..9a3ed6959 100644 --- a/singer-utils/src/uint/arithmetic.rs +++ b/singer-utils/src/uint/arithmetic.rs @@ -121,8 +121,7 @@ impl UInt { /// Adds a single cell value to a `UInt` instance /// Assumes users will check the correct range of the result and - /// guarantee addend_1 < 1 << C. - pub fn add_small_unsafe( + pub fn add_cell_unsafe( circuit_builder: &mut CircuitBuilder, addend_0: &UInt, addend_1: CellId, @@ -147,8 +146,7 @@ impl UInt { } /// Adds a single cell value to a `UInt` instance - /// Assumes users will guarantee addend_1 < 1 << C. - pub fn add_small>( + pub fn add_cell>( circuit_builder: &mut CircuitBuilder, range_chip_handler: &mut H, addend_0: &UInt, @@ -157,14 +155,13 @@ impl UInt { ) -> 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)?; + 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 users will guarantee addend_1 < 1 << C. /// Assumes that addition lead to no overflow. - pub fn add_small_no_overflow>( + pub fn add_cell_no_overflow>( circuit_builder: &mut CircuitBuilder, range_chip_handler: &mut H, addend_0: &UInt, @@ -173,7 +170,7 @@ impl UInt { ) -> 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)?; + 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)) } @@ -482,7 +479,7 @@ mod tests { let addend_0 = UInt20::try_from(addend_0_cells).expect("should build uint"); // update circuit builder - let result = UInt20::add_small_unsafe( + let result = UInt20::add_cell_unsafe( &mut circuit_builder, &addend_0, small_value_cell[0], diff --git a/singer/src/instructions/mstore.rs b/singer/src/instructions/mstore.rs index e19d11d19..4b06a804d 100644 --- a/singer/src/instructions/mstore.rs +++ b/singer/src/instructions/mstore.rs @@ -330,7 +330,7 @@ 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 = StackUInt::add_small( + let offset_plus_delta = StackUInt::add_cell( &mut circuit_builder, &mut rom_handler, &offset, diff --git a/singer/src/instructions/ret.rs b/singer/src/instructions/ret.rs index d2b6c80d0..214e359c1 100644 --- a/singer/src/instructions/ret.rs +++ b/singer/src/instructions/ret.rs @@ -402,7 +402,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 = StackUInt::add_small( + let new_offset = StackUInt::add_cell( &mut circuit_builder, &mut rom_handler, &offset, From 640541ec1c94ecbacb29cee674337d6735250145 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 17 Jun 2024 20:39:55 +0100 Subject: [PATCH 40/50] wip --- singer-utils/src/chips/bytecode.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/singer-utils/src/chips/bytecode.rs b/singer-utils/src/chips/bytecode.rs index 979ecde89..1a428d61e 100644 --- a/singer-utils/src/chips/bytecode.rs +++ b/singer-utils/src/chips/bytecode.rs @@ -54,13 +54,7 @@ pub(crate) fn construct_bytecode_table_and_witness( .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( From aec94b22665d26d6a261c3574747bc05379dac67 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 17 Jun 2024 20:42:06 +0100 Subject: [PATCH 41/50] change uint mod management --- singer-utils/src/chips/bytecode.rs | 3 ++- singer-utils/src/{uint/mod.rs => uint.rs} | 0 2 files changed, 2 insertions(+), 1 deletion(-) rename singer-utils/src/{uint/mod.rs => uint.rs} (100%) diff --git a/singer-utils/src/chips/bytecode.rs b/singer-utils/src/chips/bytecode.rs index 1a428d61e..03897ddd1 100644 --- a/singer-utils/src/chips/bytecode.rs +++ b/singer-utils/src/chips/bytecode.rs @@ -54,7 +54,8 @@ pub(crate) fn construct_bytecode_table_and_witness( .into_iter() .map(|x| vec![x]) .collect_vec(), - }; 2 + }; + 2 ]; let table_node_id = builder.add_node_with_witness( diff --git a/singer-utils/src/uint/mod.rs b/singer-utils/src/uint.rs similarity index 100% rename from singer-utils/src/uint/mod.rs rename to singer-utils/src/uint.rs From 3c610786200ae9306d8cc15e2e3d901d997eb245 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Mon, 17 Jun 2024 21:11:16 +0100 Subject: [PATCH 42/50] setup structure for namespacing constants --- singer-utils/src/uint/constants.rs | 10 ++++++++++ singer-utils/src/uint/uint.rs | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/singer-utils/src/uint/constants.rs b/singer-utils/src/uint/constants.rs index b7c9ba840..d69d68d03 100644 --- a/singer-utils/src/uint/constants.rs +++ b/singer-utils/src/uint/constants.rs @@ -1,6 +1,7 @@ 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 @@ -58,3 +59,12 @@ impl UInt { /// |Carry| = |Cells - 1| const N_CARRY_CELLS_NO_OVERFLOW: usize = Self::N_CARRY_CELLS - 1; } + +/// Holds addition specific constants +struct AddSubConstants { + _marker: PhantomData, +} + +impl AddSubConstants> { + // TODO: fill this as specific constants become clearer +} diff --git a/singer-utils/src/uint/uint.rs b/singer-utils/src/uint/uint.rs index 2c067e48f..2762efa3a 100644 --- a/singer-utils/src/uint/uint.rs +++ b/singer-utils/src/uint/uint.rs @@ -85,7 +85,7 @@ impl UInt { values.try_into() } - /// Generate (0, 1, ..., size) + /// Generate ((0)_{2^C}, (1)_{2^C}, ..., (size - 1)_{2^C}) // TODO: refactor, move and test pub fn counter_vector(size: usize) -> Vec { let num_vars = ceil_log2(size); From 02bbaeef07ca01abff6327909eda5f2c9fb1f62a Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Tue, 18 Jun 2024 11:05:17 +0100 Subject: [PATCH 43/50] refactor counter vector implementation --- singer-utils/src/chips/bytecode.rs | 3 - singer-utils/src/uint/uint.rs | 81 +++++++++++++++---- singer-utils/src/uint/util.rs | 121 ++++++++++++++++++++++++++++- 3 files changed, 184 insertions(+), 21 deletions(-) diff --git a/singer-utils/src/chips/bytecode.rs b/singer-utils/src/chips/bytecode.rs index 03897ddd1..314b3afa1 100644 --- a/singer-utils/src/chips/bytecode.rs +++ b/singer-utils/src/chips/bytecode.rs @@ -51,9 +51,6 @@ 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(), }; 2 ]; diff --git a/singer-utils/src/uint/uint.rs b/singer-utils/src/uint/uint.rs index 2762efa3a..3872359c4 100644 --- a/singer-utils/src/uint/uint.rs +++ b/singer-utils/src/uint/uint.rs @@ -1,6 +1,6 @@ use crate::constants::{BYTE_BIT_WIDTH, RANGE_CHIP_BIT_WIDTH}; use crate::error::UtilError; -use crate::uint::util::{convert_decomp, pad_cells}; +use crate::uint::util::{add_one_to_big_num, convert_decomp, pad_cells}; use ff_ext::ExtensionField; use goldilocks::SmallField; use itertools::Itertools; @@ -86,24 +86,17 @@ impl UInt { } /// Generate ((0)_{2^C}, (1)_{2^C}, ..., (size - 1)_{2^C}) - // TODO: refactor, move and test - pub fn counter_vector(size: usize) -> Vec { + 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); + 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 } } @@ -210,4 +203,58 @@ mod tests { .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 index 8f5d34ebc..6e39396cd 100644 --- a/singer-utils/src/uint/util.rs +++ b/singer-utils/src/uint/util.rs @@ -84,9 +84,31 @@ pub const fn const_min(a: usize, b: usize) -> usize { } } +/// 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::{const_min, convert_decomp, pad_cells}; + 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; @@ -208,4 +230,101 @@ mod tests { 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) + ] + ); + } } From 55519da367ee2fbb5665c11609840348a61cc87e Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Tue, 25 Jun 2024 09:06:53 +0100 Subject: [PATCH 44/50] move add specific constants to AddSub block --- singer-pro/src/basic_block/bb_final.rs | 3 +- singer-pro/src/basic_block/bb_start.rs | 3 +- singer-pro/src/instructions/add.rs | 3 +- singer-pro/src/instructions/gt.rs | 3 +- singer-pro/src/instructions/mstore.rs | 7 ++-- singer-pro/src/instructions/ret.rs | 3 +- singer-utils/src/uint.rs | 2 +- singer-utils/src/uint/arithmetic.rs | 13 ++++--- singer-utils/src/uint/cmp.rs | 5 +-- singer-utils/src/uint/constants.rs | 38 +++++++++------------ singer-utils/src/uint/witness_extractors.rs | 5 +-- singer/src/instructions/add.rs | 12 ++++--- singer/src/instructions/calldataload.rs | 7 ++-- singer/src/instructions/dup.rs | 7 ++-- singer/src/instructions/gt.rs | 11 +++--- singer/src/instructions/jump.rs | 3 +- singer/src/instructions/jumpdest.rs | 3 +- singer/src/instructions/jumpi.rs | 7 ++-- singer/src/instructions/mstore.rs | 13 +++---- singer/src/instructions/pop.rs | 5 +-- singer/src/instructions/push.rs | 7 ++-- singer/src/instructions/ret.rs | 3 +- singer/src/instructions/swap.rs | 9 ++--- 23 files changed, 96 insertions(+), 76 deletions(-) diff --git a/singer-pro/src/basic_block/bb_final.rs b/singer-pro/src/basic_block/bb_final.rs index 31c2eb7f8..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, @@ -27,7 +28,7 @@ pub struct BasicBlockFinal; register_witness!(BasicBlockFinal, phase0 { // State in related - stack_ts_add => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW + stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW }); impl BasicBlockFinal { diff --git a/singer-pro/src/basic_block/bb_start.rs b/singer-pro/src/basic_block/bb_start.rs index dbb57004e..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, @@ -35,7 +36,7 @@ register_multi_witness!(BasicBlockStart, phase0(n_stack_items) { // Stack values 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) => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW + old_stack_ts_lt(n_stack_items) => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW }); impl BasicBlockStart { diff --git a/singer-pro/src/instructions/add.rs b/singer-pro/src/instructions/add.rs index 44e63188d..b9fa98d25 100644 --- a/singer-pro/src/instructions/add.rs +++ b/singer-pro/src/instructions/add.rs @@ -2,6 +2,7 @@ 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, @@ -29,7 +30,7 @@ register_witness!( AddInstruction, phase0 { // Witness for addend_0 + addend_1 - instruction_add => StackUInt::N_WITNESS_CELLS + instruction_add => AddSubConstants::::N_WITNESS_CELLS } ); diff --git a/singer-pro/src/instructions/gt.rs b/singer-pro/src/instructions/gt.rs index c42336030..ea55a3dc6 100644 --- a/singer-pro/src/instructions/gt.rs +++ b/singer-pro/src/instructions/gt.rs @@ -2,6 +2,7 @@ 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, @@ -27,7 +28,7 @@ register_witness!( GtInstruction, phase0 { // Witness for operand_0 > operand_1 - instruction_gt => StackUInt::N_WITNESS_CELLS + instruction_gt => AddSubConstants::::N_WITNESS_CELLS } ); diff --git a/singer-pro/src/instructions/mstore.rs b/singer-pro/src/instructions/mstore.rs index 1bdd0e499..be309a1ce 100644 --- a/singer-pro/src/instructions/mstore.rs +++ b/singer-pro/src/instructions/mstore.rs @@ -4,6 +4,7 @@ use gkr_graph::structs::{CircuitGraphBuilder, NodeOutputType, PredType}; use itertools::Itertools; use paste::paste; use simple_frontend::structs::CircuitBuilder; +use singer_utils::uint::constants::AddSubConstants; use singer_utils::{ chip_handler::{MemoryChipOperations, ROMOperations, RangeChipOperations}, chips::{IntoEnumIterator, SingerChipBuilder}, @@ -110,7 +111,7 @@ impl InstructionGraph for MstoreInstruction { register_witness!( MstoreInstruction, phase0 { - memory_ts_add => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, + memory_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, mem_bytes => EVM_STACK_BYTE_WIDTH } ); @@ -218,9 +219,9 @@ register_witness!( }, phase0 { old_memory_ts => TSUInt::N_OPERAND_CELLS, - old_memory_ts_lt => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, + old_memory_ts_lt => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, - offset_add_delta => StackUInt::N_WITNESS_CELLS, + offset_add_delta => AddSubConstants::::N_WITNESS_CELLS, prev_mem_byte => 1 } ); diff --git a/singer-pro/src/instructions/ret.rs b/singer-pro/src/instructions/ret.rs index 2181d11b7..84ea49685 100644 --- a/singer-pro/src/instructions/ret.rs +++ b/singer-pro/src/instructions/ret.rs @@ -3,6 +3,7 @@ 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}, @@ -110,7 +111,7 @@ register_witness!( }, phase0 { old_memory_ts => TSUInt::N_OPERAND_CELLS, - offset_add => StackUInt::N_WITNESS_CELLS + offset_add => AddSubConstants::::N_WITNESS_CELLS } ); diff --git a/singer-utils/src/uint.rs b/singer-utils/src/uint.rs index e3f42ffc2..1bf0190f0 100644 --- a/singer-utils/src/uint.rs +++ b/singer-utils/src/uint.rs @@ -1,6 +1,6 @@ mod arithmetic; mod cmp; -mod constants; +pub mod constants; mod uint; pub use uint::UInt; pub mod util; diff --git a/singer-utils/src/uint/arithmetic.rs b/singer-utils/src/uint/arithmetic.rs index 9a3ed6959..88b8e389d 100644 --- a/singer-utils/src/uint/arithmetic.rs +++ b/singer-utils/src/uint/arithmetic.rs @@ -294,6 +294,7 @@ impl UInt { #[cfg(test)] mod tests { + use crate::uint::constants::AddSubConstants; use crate::uint::UInt; use gkr::structs::{Circuit, CircuitWitness}; use goldilocks::{Goldilocks, GoldilocksExt2}; @@ -327,7 +328,8 @@ mod tests { 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(UInt20::N_CARRY_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"); @@ -401,7 +403,8 @@ mod tests { // 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(UInt20::N_CARRY_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"); @@ -474,7 +477,8 @@ mod tests { 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(UInt20::N_CARRY_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"); @@ -551,7 +555,8 @@ mod tests { 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(UInt20::N_CARRY_CELLS); + 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"); diff --git a/singer-utils/src/uint/cmp.rs b/singer-utils/src/uint/cmp.rs index f9c9256d5..dfc212ea7 100644 --- a/singer-utils/src/uint/cmp.rs +++ b/singer-utils/src/uint/cmp.rs @@ -1,5 +1,6 @@ use crate::chip_handler::RangeChipOperations; use crate::error::UtilError; +use crate::uint::constants::AddSubConstants; use crate::uint::uint::UInt; use ff::Field; use ff_ext::ExtensionField; @@ -25,8 +26,8 @@ impl UInt { )?; // if operand_0 < operand_1, the last borrow should equal 1 - if borrow.len() == Self::N_CARRY_CELLS { - Ok((borrow[Self::N_CARRY_CELLS - 1], diff)) + if borrow.len() == AddSubConstants::::N_CARRY_CELLS { + Ok((borrow[AddSubConstants::::N_CARRY_CELLS - 1], diff)) } else { Ok((circuit_builder.create_cell(), diff)) } diff --git a/singer-utils/src/uint/constants.rs b/singer-utils/src/uint/constants.rs index d69d68d03..5d1ec1da5 100644 --- a/singer-utils/src/uint/constants.rs +++ b/singer-utils/src/uint/constants.rs @@ -20,26 +20,19 @@ impl UInt { /// 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; +} - /// The number of `RANGE_CHIP_BIT_WIDTH` cells needed to represent the carry cells, assuming - /// no overflow. - pub const N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW: usize = - Self::N_CARRY_CELLS_NO_OVERFLOW * Self::N_RANGE_CELLS_PER_CELL; +/// Holds addition specific constants +pub struct AddSubConstants { + _marker: PhantomData, +} +impl AddSubConstants> { /// The size of the witness - pub const N_WITNESS_CELLS: usize = Self::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 = - Self::N_RANGE_CELLS + Self::N_CARRY_CELLS_NO_OVERFLOW; + pub const N_WITNESS_CELLS: usize = UInt::::N_RANGE_CELLS + Self::N_CARRY_CELLS; - // TODO: add documentation - // TODO: why this? pub const N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS: usize = Self::N_CARRY_CELLS_NO_OVERFLOW; - // Arithmetic Constants - /// Number of cells required to track carry information for the addition operation. /// operand_0 = a b c /// operand_1 = e f g @@ -47,7 +40,7 @@ impl UInt { /// result = h i j /// carry = k l m - /// |Carry| = |Cells| - pub const N_CARRY_CELLS: usize = Self::N_OPERAND_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. @@ -58,13 +51,14 @@ impl UInt { /// carry = l m - /// |Carry| = |Cells - 1| const N_CARRY_CELLS_NO_OVERFLOW: usize = Self::N_CARRY_CELLS - 1; -} -/// Holds addition specific constants -struct AddSubConstants { - _marker: PhantomData, -} + /// The number of `RANGE_CHIP_BIT_WIDTH` cells needed to represent the carry cells, assuming + /// no overflow. + pub const N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW: usize = + Self::N_CARRY_CELLS_NO_OVERFLOW * UInt::::N_RANGE_CELLS_PER_CELL; -impl AddSubConstants> { - // TODO: fill this as specific constants become clearer + /// 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; } diff --git a/singer-utils/src/uint/witness_extractors.rs b/singer-utils/src/uint/witness_extractors.rs index 82bdc65eb..92a9f1611 100644 --- a/singer-utils/src/uint/witness_extractors.rs +++ b/singer-utils/src/uint/witness_extractors.rs @@ -1,3 +1,4 @@ +use crate::uint::constants::AddSubConstants; use crate::uint::uint::UInt; use simple_frontend::structs::CellId; @@ -10,7 +11,7 @@ impl UInt { } pub fn extract_carry_no_overflow(witness: &[CellId]) -> &[CellId] { - &witness[Self::N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW..] + &witness[AddSubConstants::::N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW..] } // TODO: why do we need this @@ -32,6 +33,6 @@ impl UInt { } pub fn extract_range_values_no_overflow(witness: &[CellId]) -> &[CellId] { - &witness[..Self::N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW] + &witness[..AddSubConstants::::N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW] } } diff --git a/singer/src/instructions/add.rs b/singer/src/instructions/add.rs index 29a389869..97dd6869d 100644 --- a/singer/src/instructions/add.rs +++ b/singer/src/instructions/add.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, @@ -33,17 +34,17 @@ register_witness!( stack_top => 1, clk => 1, - pc_add => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, + pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, old_stack_ts0 => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt0 => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, + old_stack_ts_lt0 => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, old_stack_ts1 => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt1 => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, + old_stack_ts_lt1 => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, addend_0 => StackUInt::N_OPERAND_CELLS, addend_1 => StackUInt::N_OPERAND_CELLS, - instruction_add => StackUInt::N_WITNESS_CELLS + instruction_add => AddSubConstants::::N_WITNESS_CELLS } ); @@ -188,6 +189,7 @@ mod test { use simple_frontend::structs::CellId; use singer_utils::constants::RANGE_CHIP_BIT_WIDTH; use singer_utils::structs::{StackUInt, TSUInt}; + use singer_utils::uint::constants::AddSubConstants; use std::collections::BTreeMap; use std::time::Instant; use transcript::Transcript; diff --git a/singer/src/instructions/calldataload.rs b/singer/src/instructions/calldataload.rs index ff7b52fb5..9bb0e4fa2 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, @@ -34,13 +35,13 @@ register_witness!( stack_top => 1, clk => 1, - pc_add => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, + pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, data => StackUInt::N_OPERAND_CELLS, offset => UInt64::N_OPERAND_CELLS, old_stack_ts => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt => TSUInt::N_WITNESS_CELLS + old_stack_ts_lt => AddSubConstants::::N_WITNESS_CELLS } ); diff --git a/singer/src/instructions/dup.rs b/singer/src/instructions/dup.rs index e8c55ba3e..5835b1f2d 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, @@ -33,12 +34,12 @@ register_witness!( stack_top => 1, clk => 1, - pc_add => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, + pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, stack_values => StackUInt::N_OPERAND_CELLS, old_stack_ts => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt => TSUInt::N_WITNESS_CELLS + old_stack_ts_lt => AddSubConstants::::N_WITNESS_CELLS } ); diff --git a/singer/src/instructions/gt.rs b/singer/src/instructions/gt.rs index 9c1d326fd..b510ceba9 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, @@ -33,17 +34,17 @@ register_witness!( stack_top => 1, clk => 1, - pc_add => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, + pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, old_stack_ts0 => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt0 => TSUInt::N_WITNESS_CELLS, + old_stack_ts_lt0 => AddSubConstants::::N_WITNESS_CELLS, old_stack_ts1 => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt1 => TSUInt::N_WITNESS_CELLS, + old_stack_ts_lt1 => AddSubConstants::::N_WITNESS_CELLS, oprand_0 => StackUInt::N_OPERAND_CELLS, oprand_1 => StackUInt::N_OPERAND_CELLS, - instruction_gt => StackUInt::N_WITNESS_CELLS + instruction_gt => AddSubConstants::::N_WITNESS_CELLS } ); diff --git a/singer/src/instructions/jump.rs b/singer/src/instructions/jump.rs index cf5296cec..fd89d0554 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, @@ -37,7 +38,7 @@ register_witness!( next_pc => PCUInt::N_OPERAND_CELLS, old_stack_ts => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt => TSUInt::N_WITNESS_CELLS + old_stack_ts_lt => AddSubConstants::::N_WITNESS_CELLS } ); diff --git a/singer/src/instructions/jumpdest.rs b/singer/src/instructions/jumpdest.rs index 77f1a2394..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, @@ -32,7 +33,7 @@ register_witness!( stack_top => 1, clk => 1, - pc_add => PCUInt::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 b3f312c06..f1197f81f 100644 --- a/singer/src/instructions/jumpi.rs +++ b/singer/src/instructions/jumpi.rs @@ -4,6 +4,7 @@ use gkr::structs::Circuit; use itertools::izip; use paste::paste; use simple_frontend::structs::{CircuitBuilder, MixedCell}; +use singer_utils::uint::constants::AddSubConstants; use singer_utils::{ chip_handler::{ BytecodeChipOperations, GlobalStateChipOperations, OAMOperations, ROMOperations, @@ -35,16 +36,16 @@ register_witness!( clk => 1, old_stack_ts_dest => TSUInt::N_OPERAND_CELLS, - old_stack_ts_dest_lt => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, + old_stack_ts_dest_lt => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, old_stack_ts_cond => TSUInt::N_OPERAND_CELLS, - old_stack_ts_cond_lt => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, + old_stack_ts_cond_lt => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, 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 => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, pc_plus_1_opcode => 1 } ); diff --git a/singer/src/instructions/mstore.rs b/singer/src/instructions/mstore.rs index 4b06a804d..a96835a89 100644 --- a/singer/src/instructions/mstore.rs +++ b/singer/src/instructions/mstore.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, MemoryChipOperations, OAMOperations, @@ -145,15 +146,15 @@ register_witness!( stack_top => 1, clk => 1, - pc_add => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - memory_ts_add => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, + pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + memory_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, offset => StackUInt::N_OPERAND_CELLS, mem_bytes => EVM_STACK_BYTE_WIDTH, old_stack_ts_offset => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt_offset => TSUInt::N_WITNESS_CELLS, + old_stack_ts_lt_offset => AddSubConstants::::N_WITNESS_CELLS, old_stack_ts_value => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt_value => TSUInt::N_WITNESS_CELLS + old_stack_ts_lt_value => AddSubConstants::::N_WITNESS_CELLS } ); @@ -299,9 +300,9 @@ register_witness!( }, phase0 { old_memory_ts => TSUInt::N_OPERAND_CELLS, - old_memory_ts_lt => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, + old_memory_ts_lt => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, - offset_add_delta => StackUInt::N_WITNESS_CELLS, + offset_add_delta => AddSubConstants::::N_WITNESS_CELLS, prev_mem_bytes => 1 } ); diff --git a/singer/src/instructions/pop.rs b/singer/src/instructions/pop.rs index 2b271eb56..3d07b8abd 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, @@ -32,10 +33,10 @@ register_witness!( stack_top => 1, clk => 1, - pc_add => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, old_stack_ts => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt => TSUInt::N_WITNESS_CELLS, + old_stack_ts_lt => AddSubConstants::::N_WITNESS_CELLS, stack_values => StackUInt::N_OPERAND_CELLS } ); diff --git a/singer/src/instructions/push.rs b/singer/src/instructions/push.rs index 68a84c381..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, @@ -33,8 +34,8 @@ register_witness!( stack_top => 1, clk => 1, - pc_add_i_plus_1 => N * PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, + 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 } @@ -113,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(PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS) + .chunks(AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS) .enumerate() { let next_pc = diff --git a/singer/src/instructions/ret.rs b/singer/src/instructions/ret.rs index 214e359c1..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, @@ -384,7 +385,7 @@ register_witness!( phase0 { old_memory_ts => TSUInt::N_OPERAND_CELLS, - offset_add => StackUInt::N_WITNESS_CELLS + offset_add => AddSubConstants::::N_WITNESS_CELLS } ); diff --git a/singer/src/instructions/swap.rs b/singer/src/instructions/swap.rs index a54ceb1e1..ea8f0f126 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, @@ -32,13 +33,13 @@ register_witness!( stack_top => 1, clk => 1, - pc_add => PCUInt::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, - stack_ts_add => TSUInt::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, + pc_add => AddSubConstants::::N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS, + stack_ts_add => AddSubConstants::::N_WITNESS_CELLS_NO_CARRY_OVERFLOW, old_stack_ts_1 => TSUInt::N_OPERAND_CELLS, - old_stack_ts_lt_1 => TSUInt::N_WITNESS_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 => TSUInt::N_WITNESS_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 } From d8aa67d6682e6ffcc6fa531b347310e371a37141 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Wed, 26 Jun 2024 08:44:18 +0100 Subject: [PATCH 45/50] fix comment arithmetic precedence --- singer-utils/src/uint/arithmetic.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/singer-utils/src/uint/arithmetic.rs b/singer-utils/src/uint/arithmetic.rs index 88b8e389d..ec7de48ca 100644 --- a/singer-utils/src/uint/arithmetic.rs +++ b/singer-utils/src/uint/arithmetic.rs @@ -223,12 +223,12 @@ impl UInt { // 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 + // 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 + // carry[i - 1] = (addend_0[i - 1] + addend_1[i - 1]) % 2^C if limb_index > carry.len() { return; From 834783fbb569b940f41103745890d1f3bae4dbce Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Wed, 26 Jun 2024 09:25:29 +0100 Subject: [PATCH 46/50] wip --- singer-utils/src/uint/constants.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/singer-utils/src/uint/constants.rs b/singer-utils/src/uint/constants.rs index 5d1ec1da5..b25582e77 100644 --- a/singer-utils/src/uint/constants.rs +++ b/singer-utils/src/uint/constants.rs @@ -28,11 +28,6 @@ pub struct AddSubConstants { } impl AddSubConstants> { - /// The size of the witness - pub const N_WITNESS_CELLS: usize = UInt::::N_RANGE_CELLS + Self::N_CARRY_CELLS; - - pub const N_NO_OVERFLOW_WITNESS_UNSAFE_CELLS: usize = Self::N_CARRY_CELLS_NO_OVERFLOW; - /// Number of cells required to track carry information for the addition operation. /// operand_0 = a b c /// operand_1 = e f g @@ -52,13 +47,19 @@ impl AddSubConstants> { /// |Carry| = |Cells - 1| const N_CARRY_CELLS_NO_OVERFLOW: usize = Self::N_CARRY_CELLS - 1; - /// The number of `RANGE_CHIP_BIT_WIDTH` cells needed to represent the carry cells, assuming - /// no overflow. - pub const N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW: usize = - Self::N_CARRY_CELLS_NO_OVERFLOW * UInt::::N_RANGE_CELLS_PER_CELL; + /// 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. + pub const N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW: usize = + Self::N_CARRY_CELLS_NO_OVERFLOW * UInt::::N_RANGE_CELLS_PER_CELL; + } From ca607d28fdc290275026a03a886a0506b9d21853 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 27 Jun 2024 08:50:29 +0100 Subject: [PATCH 47/50] use range cells instead of range cells no overflow --- singer-utils/src/uint/constants.rs | 8 ++++---- singer-utils/src/uint/witness_extractors.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/singer-utils/src/uint/constants.rs b/singer-utils/src/uint/constants.rs index b25582e77..743b077e4 100644 --- a/singer-utils/src/uint/constants.rs +++ b/singer-utils/src/uint/constants.rs @@ -57,9 +57,9 @@ impl AddSubConstants> { 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. - pub const N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW: usize = - Self::N_CARRY_CELLS_NO_OVERFLOW * UInt::::N_RANGE_CELLS_PER_CELL; + // /// The number of `RANGE_CHIP_BIT_WIDTH` cells needed to represent the carry cells, assuming + // /// no overflow. + // pub const N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW: usize = + // Self::N_CARRY_CELLS_NO_OVERFLOW * UInt::::N_RANGE_CELLS_PER_CELL; } diff --git a/singer-utils/src/uint/witness_extractors.rs b/singer-utils/src/uint/witness_extractors.rs index 92a9f1611..d2d3c7ec8 100644 --- a/singer-utils/src/uint/witness_extractors.rs +++ b/singer-utils/src/uint/witness_extractors.rs @@ -11,7 +11,7 @@ impl UInt { } pub fn extract_carry_no_overflow(witness: &[CellId]) -> &[CellId] { - &witness[AddSubConstants::::N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW..] + &witness[Self::N_RANGE_CELLS..] } // TODO: why do we need this @@ -33,6 +33,6 @@ impl UInt { } pub fn extract_range_values_no_overflow(witness: &[CellId]) -> &[CellId] { - &witness[..AddSubConstants::::N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW] + &witness[..Self::N_RANGE_CELLS] } } From d0e1d06ee37877b8abfe27e2d759c1f90b7cb60d Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 27 Jun 2024 09:02:18 +0100 Subject: [PATCH 48/50] add optimization comment --- singer-utils/src/uint/constants.rs | 11 ++++++----- singer-utils/src/uint/witness_extractors.rs | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/singer-utils/src/uint/constants.rs b/singer-utils/src/uint/constants.rs index 743b077e4..cef8e4d44 100644 --- a/singer-utils/src/uint/constants.rs +++ b/singer-utils/src/uint/constants.rs @@ -57,9 +57,10 @@ impl AddSubConstants> { 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. - // pub const N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW: usize = - // Self::N_CARRY_CELLS_NO_OVERFLOW * UInt::::N_RANGE_CELLS_PER_CELL; - + /// 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_IN_CARRY_NO_OVERFLOW: usize = UInt::::N_RANGE_CELLS; } diff --git a/singer-utils/src/uint/witness_extractors.rs b/singer-utils/src/uint/witness_extractors.rs index d2d3c7ec8..92a9f1611 100644 --- a/singer-utils/src/uint/witness_extractors.rs +++ b/singer-utils/src/uint/witness_extractors.rs @@ -11,7 +11,7 @@ impl UInt { } pub fn extract_carry_no_overflow(witness: &[CellId]) -> &[CellId] { - &witness[Self::N_RANGE_CELLS..] + &witness[AddSubConstants::::N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW..] } // TODO: why do we need this @@ -33,6 +33,6 @@ impl UInt { } pub fn extract_range_values_no_overflow(witness: &[CellId]) -> &[CellId] { - &witness[..Self::N_RANGE_CELLS] + &witness[..AddSubConstants::::N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW] } } From fff654961ab68cb8a4ab348043c9eedaf6010d79 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 27 Jun 2024 10:30:55 +0100 Subject: [PATCH 49/50] wip --- singer-utils/src/uint/constants.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/singer-utils/src/uint/constants.rs b/singer-utils/src/uint/constants.rs index cef8e4d44..6f2345bcd 100644 --- a/singer-utils/src/uint/constants.rs +++ b/singer-utils/src/uint/constants.rs @@ -62,5 +62,5 @@ impl AddSubConstants> { // 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_IN_CARRY_NO_OVERFLOW: usize = UInt::::N_RANGE_CELLS; + pub const N_RANGE_CELLS_NO_OVERFLOW: usize = UInt::::N_RANGE_CELLS; } From 3e74ec7a5f053e493410be1980ed92c32d588626 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Thu, 27 Jun 2024 11:08:57 +0100 Subject: [PATCH 50/50] rename witness extractors --- singer-utils/src/chip_handler/range.rs | 2 +- singer-utils/src/uint/arithmetic.rs | 12 ++++++------ singer-utils/src/uint/cmp.rs | 2 +- singer-utils/src/uint/witness_extractors.rs | 12 +++++++----- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/singer-utils/src/chip_handler/range.rs b/singer-utils/src/chip_handler/range.rs index 04512280a..1f6f37896 100644 --- a/singer-utils/src/chip_handler/range.rs +++ b/singer-utils/src/chip_handler/range.rs @@ -100,7 +100,7 @@ impl ROMHandler { witness: &[CellId], ) -> Result { // TODO: why unsafe here? - let carry = PCUInt::extract_unsafe_carry(witness); + let carry = PCUInt::extract_unsafe_carry_add_sub(witness); PCUInt::add_const_unsafe( circuit_builder, &pc, diff --git a/singer-utils/src/uint/arithmetic.rs b/singer-utils/src/uint/arithmetic.rs index ec7de48ca..b1ab1a882 100644 --- a/singer-utils/src/uint/arithmetic.rs +++ b/singer-utils/src/uint/arithmetic.rs @@ -58,7 +58,7 @@ impl UInt { addend_1: &UInt, witness: &[CellId], ) -> Result, UtilError> { - let carry = Self::extract_carry(witness); + let carry = Self::extract_carry_add_sub(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)) @@ -98,7 +98,7 @@ impl UInt { constant: E::BaseField, witness: &[CellId], ) -> Result, UtilError> { - let carry = Self::extract_carry(witness); + let carry = Self::extract_carry_add_sub(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)) @@ -113,7 +113,7 @@ impl UInt { constant: E::BaseField, witness: &[CellId], ) -> Result, UtilError> { - let carry = Self::extract_carry_no_overflow(witness); + let carry = Self::extract_carry_no_overflow_add_sub(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)) @@ -153,7 +153,7 @@ impl UInt { addend_1: CellId, witness: &[CellId], ) -> Result, UtilError> { - let carry = Self::extract_carry(witness); + let carry = Self::extract_carry_add_sub(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)) @@ -168,7 +168,7 @@ impl UInt { addend_1: CellId, witness: &[CellId], ) -> Result, UtilError> { - let carry = Self::extract_carry_no_overflow(witness); + let carry = Self::extract_carry_no_overflow_add_sub(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)) @@ -207,7 +207,7 @@ impl UInt { subtrahend: &UInt, witness: &[CellId], ) -> Result, UtilError> { - let borrow = Self::extract_borrow(witness); + let borrow = Self::extract_borrow_add_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)) diff --git a/singer-utils/src/uint/cmp.rs b/singer-utils/src/uint/cmp.rs index dfc212ea7..ebfb7734f 100644 --- a/singer-utils/src/uint/cmp.rs +++ b/singer-utils/src/uint/cmp.rs @@ -15,7 +15,7 @@ impl UInt { operand_1: &UInt, witness: &[CellId], ) -> Result<(CellId, UInt), UtilError> { - let borrow = Self::extract_borrow(witness); + let borrow = Self::extract_borrow_add_sub(witness); let range_values = Self::extract_range_values(witness); let computed_diff = Self::sub_unsafe(circuit_builder, operand_0, operand_1, borrow)?; diff --git a/singer-utils/src/uint/witness_extractors.rs b/singer-utils/src/uint/witness_extractors.rs index 92a9f1611..258281e1c 100644 --- a/singer-utils/src/uint/witness_extractors.rs +++ b/singer-utils/src/uint/witness_extractors.rs @@ -2,29 +2,31 @@ 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(witness: &[CellId]) -> &[CellId] { + pub fn extract_carry_add_sub(witness: &[CellId]) -> &[CellId] { &witness[Self::N_RANGE_CELLS..] } - pub fn extract_carry_no_overflow(witness: &[CellId]) -> &[CellId] { + pub fn extract_carry_no_overflow_add_sub(witness: &[CellId]) -> &[CellId] { &witness[AddSubConstants::::N_RANGE_CELLS_IN_CARRY_NO_OVERFLOW..] } // TODO: why do we need this - pub fn extract_unsafe_carry(witness: &[CellId]) -> &[CellId] { + pub fn extract_unsafe_carry_add_sub(witness: &[CellId]) -> &[CellId] { witness } - pub fn extract_borrow(witness: &[CellId]) -> &[CellId] { + pub fn extract_borrow_add_sub(witness: &[CellId]) -> &[CellId] { &witness[Self::N_RANGE_CELLS..] } // TODO: why do we need this - pub fn extract_unsafe_borrow(witness: &[CellId]) -> &[CellId] { + pub fn extract_unsafe_borrow_add_sub(witness: &[CellId]) -> &[CellId] { witness }