From cc03f522aec6dbd30c39a15096b21ebddce268ee Mon Sep 17 00:00:00 2001 From: Uma Roy Date: Mon, 11 Sep 2023 17:25:14 -0700 Subject: [PATCH 01/18] Added initial starter template --- plonky2x/src/frontend/eth/mpt/rlc.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/plonky2x/src/frontend/eth/mpt/rlc.rs b/plonky2x/src/frontend/eth/mpt/rlc.rs index 4e3f650c4..4eefffe8a 100644 --- a/plonky2x/src/frontend/eth/mpt/rlc.rs +++ b/plonky2x/src/frontend/eth/mpt/rlc.rs @@ -1,5 +1,8 @@ use std::marker::PhantomData; +use plonky2::hash::poseidon::PoseidonHash; +use plonky2::iop::challenger::RecursiveChallenger; + use super::generators::SubarrayEqualGenerator; use crate::prelude::{BoolVariable, ByteVariable, CircuitBuilder, PlonkParameters, Variable}; @@ -35,7 +38,7 @@ impl, const D: usize> CircuitBuilder { b_offset: Variable, len: Variable, ) { - // TODO: implement + // TODO: instead of using the SubarrayEqualGenerator below that doesn't actually check anything, implement an RLC check here let generator: SubarrayEqualGenerator = SubarrayEqualGenerator { a: a.to_vec(), a_offset, @@ -45,6 +48,19 @@ impl, const D: usize> CircuitBuilder { _phantom: PhantomData::, }; self.add_simple_generator(generator); + + // The following methods might be helpful + let mut challenger = RecursiveChallenger::::new(&mut self.api); + let challenger_seed = Vec::new(); // TODO: have to "seed" the challenger with some random inputs from the circuit + challenger.observe_elements(&challenger_seed); + + let random_variables = challenger.get_n_challenges(&mut self.api, 1); + let random_variable = random_variables[0]; + + // To convert from a Target to a Variable, just use Variable(my_target) to get a Variable + + // TODO: now compute a commitment to a[a_offset:a_offset+len] + // TODO: now compute a commitment to b[b_offset:b_offset+len] } } From 22089cb1cdfa666a46b654e19fefae4f9818a454 Mon Sep 17 00:00:00 2001 From: Nikolay Kostadinov Date: Tue, 19 Sep 2023 18:07:43 +0300 Subject: [PATCH 02/18] feat: Implement subarray equality check using rlc --- plonky2x/src/frontend/builder/mod.rs | 9 ++ plonky2x/src/frontend/eth/mpt/rlc.rs | 158 +++++++++++++++++++++------ 2 files changed, 131 insertions(+), 36 deletions(-) diff --git a/plonky2x/src/frontend/builder/mod.rs b/plonky2x/src/frontend/builder/mod.rs index fbb7fd0c0..41e869a77 100644 --- a/plonky2x/src/frontend/builder/mod.rs +++ b/plonky2x/src/frontend/builder/mod.rs @@ -342,6 +342,15 @@ impl, const D: usize> CircuitBuilder { pub fn to_be_bits(&mut self, variable: V) -> Vec { variable.to_be_bits(self) } + + /// Takes a slice of bits and returns the number with little-endian bit representation as a Variable + pub fn le_sum(&mut self, bits: &[BoolVariable]) -> Variable { + let bits_in_booltarget = bits + .iter() + .map(|x| BoolTarget::new_unsafe(x.0.0)) + .collect_vec(); + Variable(self.api.le_sum(bits_in_booltarget.into_iter())) + } } impl, const D: usize> Default for CircuitBuilder { diff --git a/plonky2x/src/frontend/eth/mpt/rlc.rs b/plonky2x/src/frontend/eth/mpt/rlc.rs index 4eefffe8a..0b339f676 100644 --- a/plonky2x/src/frontend/eth/mpt/rlc.rs +++ b/plonky2x/src/frontend/eth/mpt/rlc.rs @@ -1,23 +1,45 @@ -use std::marker::PhantomData; - +use plonky2::field::types::Field; use plonky2::hash::poseidon::PoseidonHash; use plonky2::iop::challenger::RecursiveChallenger; -use super::generators::SubarrayEqualGenerator; use crate::prelude::{BoolVariable, ByteVariable, CircuitBuilder, PlonkParameters, Variable}; -// Checks that a[a_offset:a_offset+len] = b[b_offset:b_offset+len] -pub fn subarray_equal(a: &[u8], a_offset: usize, b: &[u8], b_offset: usize, len: usize) -> u8 { - for i in 0..len { - if a[a_offset + i] != b[b_offset + i] { - return 0; +impl, const D: usize> CircuitBuilder { + /// Generates a commitment for a subarray using RLC + fn commit_subarray( + &mut self, + arr: &[ByteVariable], + offset: Variable, + len: Variable, + random_value: Variable, + ) -> Variable { + let end_idx = self.add(offset, len); + let mut is_within_subarray: Variable = self.zero(); + let mut commitment = self.zero(); + + let _one: Variable = self.one(); + let mut current_multiplier = _one; + for idx in 0..arr.len() { + let idx_target = self.constant(L::Field::from_canonical_usize(idx)); + // is_within_subarray is one if idx is in the range [offset..offset+len] + let is_at_start_idx = self.is_equal(idx_target, offset); + is_within_subarray = self.add(is_within_subarray, is_at_start_idx.0); + let is_at_end_idx = self.is_equal(idx_target, end_idx); + is_within_subarray = self.sub(is_within_subarray, is_at_end_idx.0); + + let to_be_multiplied = self.select(BoolVariable(is_within_subarray), random_value, _one); + current_multiplier = self.mul(current_multiplier, to_be_multiplied); + + let le_value = arr[idx].to_variable(self); + let multiplied_value = self.mul(le_value, current_multiplier); + let random_value_if_in_range = self.mul(is_within_subarray, multiplied_value); + commitment = self.add(commitment, random_value_if_in_range); } + + commitment } - 1 -} -impl, const D: usize> CircuitBuilder { - #[allow(unused_variables, dead_code)] + /// Checks subarrays for equality using a random linear combination pub fn subarray_equal( &mut self, a: &[ByteVariable], @@ -26,7 +48,14 @@ impl, const D: usize> CircuitBuilder { b_offset: Variable, len: Variable, ) -> BoolVariable { - todo!(); + let mut challenger = RecursiveChallenger::::new(&mut self.api); + let challenger_seed = Vec::new(); + challenger.observe_elements(&challenger_seed); + let challenge = Variable(challenger.get_challenge(&mut self.api)); + + let commitment_for_a = self.commit_subarray(a, a_offset, len, challenge); + let commitment_for_b = self.commit_subarray(b, b_offset, len, challenge); + self.is_equal(commitment_for_a, commitment_for_b) } #[allow(unused_variables, dead_code)] @@ -38,32 +67,89 @@ impl, const D: usize> CircuitBuilder { b_offset: Variable, len: Variable, ) { - // TODO: instead of using the SubarrayEqualGenerator below that doesn't actually check anything, implement an RLC check here - let generator: SubarrayEqualGenerator = SubarrayEqualGenerator { - a: a.to_vec(), - a_offset, - b: b.to_vec(), - b_offset, - len, - _phantom: PhantomData::, - }; - self.add_simple_generator(generator); - - // The following methods might be helpful - let mut challenger = RecursiveChallenger::::new(&mut self.api); - let challenger_seed = Vec::new(); // TODO: have to "seed" the challenger with some random inputs from the circuit - challenger.observe_elements(&challenger_seed); + let subarrays_are_equal = self.subarray_equal(a, a_offset, b, b_offset, len); + let _true = self._true(); + self.assert_is_equal(subarrays_are_equal, _true); + } +} + +#[cfg(test)] +pub(crate) mod tests { + use anyhow::Result; + use plonky2::field::types::Field; + use plonky2::iop::witness::PartialWitness; + use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - let random_variables = challenger.get_n_challenges(&mut self.api, 1); - let random_variable = random_variables[0]; + use crate::frontend::builder::DefaultBuilder; + use crate::prelude::{ByteVariable, CircuitVariable, Variable}; - // To convert from a Target to a Variable, just use Variable(my_target) to get a Variable + impl Default for ByteVariable { + fn default() -> ByteVariable { + unsafe { std::mem::zeroed() } + } + } + + #[test] + pub fn test_subarray_equal_should_succeed() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + let mut builder = DefaultBuilder::new(); + + const MAX_LEN: usize = 15; + let mut a: [ByteVariable; MAX_LEN] = Default::default(); + let mut b: [ByteVariable; MAX_LEN] = Default::default(); + + for i in 0..MAX_LEN { + a[i] = ByteVariable::constant(&mut builder, (i + 5) as u8); + } + + for i in 0..MAX_LEN { + b[i] = ByteVariable::constant(&mut builder, i as u8); + } - // TODO: now compute a commitment to a[a_offset:a_offset+len] - // TODO: now compute a commitment to b[b_offset:b_offset+len] + let a_offset = builder.constant(F::ZERO); + let b_offset = builder.constant(F::from_canonical_usize(5)); + let len: Variable = builder.constant(F::from_canonical_usize(5)); + builder.assert_subarray_equal(&a, a_offset, &b, b_offset, len); + + let pw = PartialWitness::new(); + let circuit = builder.build(); + let proof = circuit.data.prove(pw).unwrap(); + circuit.data.verify(proof) } -} -pub(crate) mod tests { - // TODO add a test for subarray_equal + #[test] + #[should_panic] + pub fn test_subarray_equal_should_fail() { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + let mut builder = DefaultBuilder::new(); + + const MAX_LEN: usize = 15; + let mut a: [ByteVariable; MAX_LEN] = Default::default(); + let mut b: [ByteVariable; MAX_LEN] = Default::default(); + + for i in 0..MAX_LEN { + a[i] = ByteVariable::constant(&mut builder, (i + 5) as u8); + } + + for i in 0..MAX_LEN { + b[i] = ByteVariable::constant(&mut builder, i as u8); + } + + // Modify 1 byte here + b[6] = ByteVariable::constant(&mut builder, 0); + + let a_offset = builder.constant(F::ZERO); + let b_offset = builder.constant(F::from_canonical_usize(5)); + let len: Variable = builder.constant(F::from_canonical_usize(5)); + builder.assert_subarray_equal(&a, a_offset, &b, b_offset, len); + + let pw = PartialWitness::new(); + let circuit = builder.build(); + let proof = circuit.data.prove(pw).unwrap(); + circuit.data.verify(proof).unwrap(); // panics + } } From 68cba40f51d68833cd8e3fde074c5b703172fcd0 Mon Sep 17 00:00:00 2001 From: Nikolay Kostadinov Date: Mon, 25 Sep 2023 15:21:28 +0300 Subject: [PATCH 03/18] refactor: Address the comments after opening the PR --- plonky2x/src/frontend/builder/mod.rs | 6 ++-- plonky2x/src/frontend/eth/mpt/rlc.rs | 46 ++++++++++++++++++---------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/plonky2x/src/frontend/builder/mod.rs b/plonky2x/src/frontend/builder/mod.rs index 41e869a77..3a6d02c3d 100644 --- a/plonky2x/src/frontend/builder/mod.rs +++ b/plonky2x/src/frontend/builder/mod.rs @@ -343,13 +343,13 @@ impl, const D: usize> CircuitBuilder { variable.to_be_bits(self) } - /// Takes a slice of bits and returns the number with little-endian bit representation as a Variable + /// Takes a slice of bits and returns the number with little-endian bit representation as a Variable. pub fn le_sum(&mut self, bits: &[BoolVariable]) -> Variable { - let bits_in_booltarget = bits + let bits = bits .iter() .map(|x| BoolTarget::new_unsafe(x.0.0)) .collect_vec(); - Variable(self.api.le_sum(bits_in_booltarget.into_iter())) + Variable(self.api.le_sum(bits.into_iter())) } } diff --git a/plonky2x/src/frontend/eth/mpt/rlc.rs b/plonky2x/src/frontend/eth/mpt/rlc.rs index 0b339f676..46dcbecfa 100644 --- a/plonky2x/src/frontend/eth/mpt/rlc.rs +++ b/plonky2x/src/frontend/eth/mpt/rlc.rs @@ -5,7 +5,7 @@ use plonky2::iop::challenger::RecursiveChallenger; use crate::prelude::{BoolVariable, ByteVariable, CircuitBuilder, PlonkParameters, Variable}; impl, const D: usize> CircuitBuilder { - /// Generates a commitment for a subarray using RLC + /// Generates a commitment for a subarray using RLC. fn commit_subarray( &mut self, arr: &[ByteVariable], @@ -17,17 +17,17 @@ impl, const D: usize> CircuitBuilder { let mut is_within_subarray: Variable = self.zero(); let mut commitment = self.zero(); - let _one: Variable = self.one(); - let mut current_multiplier = _one; + let one: Variable = self.one(); + let mut current_multiplier = one; for idx in 0..arr.len() { let idx_target = self.constant(L::Field::from_canonical_usize(idx)); - // is_within_subarray is one if idx is in the range [offset..offset+len] + // is_within_subarray is one if idx is in the range [offset..offset+len]. let is_at_start_idx = self.is_equal(idx_target, offset); is_within_subarray = self.add(is_within_subarray, is_at_start_idx.0); let is_at_end_idx = self.is_equal(idx_target, end_idx); is_within_subarray = self.sub(is_within_subarray, is_at_end_idx.0); - let to_be_multiplied = self.select(BoolVariable(is_within_subarray), random_value, _one); + let to_be_multiplied = self.select(BoolVariable(is_within_subarray), random_value, one); current_multiplier = self.mul(current_multiplier, to_be_multiplied); let le_value = arr[idx].to_variable(self); @@ -39,7 +39,7 @@ impl, const D: usize> CircuitBuilder { commitment } - /// Checks subarrays for equality using a random linear combination + /// Checks subarrays for equality using a random linear combination. pub fn subarray_equal( &mut self, a: &[ByteVariable], @@ -75,9 +75,7 @@ impl, const D: usize> CircuitBuilder { #[cfg(test)] pub(crate) mod tests { - use anyhow::Result; use plonky2::field::types::Field; - use plonky2::iop::witness::PartialWitness; use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; use crate::frontend::builder::DefaultBuilder; @@ -90,7 +88,7 @@ pub(crate) mod tests { } #[test] - pub fn test_subarray_equal_should_succeed() -> Result<()> { + pub fn test_subarray_equal_should_succeed() { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; @@ -108,15 +106,22 @@ pub(crate) mod tests { b[i] = ByteVariable::constant(&mut builder, i as u8); } - let a_offset = builder.constant(F::ZERO); + let a_offset: Variable = builder.constant(F::ZERO); let b_offset = builder.constant(F::from_canonical_usize(5)); let len: Variable = builder.constant(F::from_canonical_usize(5)); builder.assert_subarray_equal(&a, a_offset, &b, b_offset, len); - let pw = PartialWitness::new(); + // Build your circuit. let circuit = builder.build(); - let proof = circuit.data.prove(pw).unwrap(); - circuit.data.verify(proof) + + // Write to the circuit input. + let input = circuit.input(); + + // Generate a proof. + let (proof, output) = circuit.prove(&input); + + // Verify proof. + circuit.verify(&proof, &input, &output) } #[test] @@ -139,7 +144,7 @@ pub(crate) mod tests { b[i] = ByteVariable::constant(&mut builder, i as u8); } - // Modify 1 byte here + // Modify 1 byte here. b[6] = ByteVariable::constant(&mut builder, 0); let a_offset = builder.constant(F::ZERO); @@ -147,9 +152,16 @@ pub(crate) mod tests { let len: Variable = builder.constant(F::from_canonical_usize(5)); builder.assert_subarray_equal(&a, a_offset, &b, b_offset, len); - let pw = PartialWitness::new(); + // Build your circuit. let circuit = builder.build(); - let proof = circuit.data.prove(pw).unwrap(); - circuit.data.verify(proof).unwrap(); // panics + + // Write to the circuit input. + let input = circuit.input(); + + // Generate a proof. + let (proof, output) = circuit.prove(&input); + + // Verify proof. + circuit.verify(&proof, &input, &output) } } From e543dd1572965e76ef7eafdb6ba9b1b22c1eea3e Mon Sep 17 00:00:00 2001 From: Xearty Date: Mon, 2 Oct 2023 19:05:54 +0300 Subject: [PATCH 04/18] refactor: Fix formatting and add comments --- plonky2x/src/frontend/builder/mod.rs | 6 +++--- plonky2x/src/frontend/eth/mpt/rlc.rs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plonky2x/src/frontend/builder/mod.rs b/plonky2x/src/frontend/builder/mod.rs index 3a6d02c3d..7e63cf4b3 100644 --- a/plonky2x/src/frontend/builder/mod.rs +++ b/plonky2x/src/frontend/builder/mod.rs @@ -346,9 +346,9 @@ impl, const D: usize> CircuitBuilder { /// Takes a slice of bits and returns the number with little-endian bit representation as a Variable. pub fn le_sum(&mut self, bits: &[BoolVariable]) -> Variable { let bits = bits - .iter() - .map(|x| BoolTarget::new_unsafe(x.0.0)) - .collect_vec(); + .iter() + .map(|x| BoolTarget::new_unsafe(x.0.0)) + .collect_vec(); Variable(self.api.le_sum(bits.into_iter())) } } diff --git a/plonky2x/src/frontend/eth/mpt/rlc.rs b/plonky2x/src/frontend/eth/mpt/rlc.rs index 46dcbecfa..b87370e58 100644 --- a/plonky2x/src/frontend/eth/mpt/rlc.rs +++ b/plonky2x/src/frontend/eth/mpt/rlc.rs @@ -14,8 +14,8 @@ impl, const D: usize> CircuitBuilder { random_value: Variable, ) -> Variable { let end_idx = self.add(offset, len); - let mut is_within_subarray: Variable = self.zero(); - let mut commitment = self.zero(); + let mut commitment: Variable = self.zero(); + let mut is_within_subarray = self.zero(); let one: Variable = self.one(); let mut current_multiplier = one; @@ -26,7 +26,7 @@ impl, const D: usize> CircuitBuilder { is_within_subarray = self.add(is_within_subarray, is_at_start_idx.0); let is_at_end_idx = self.is_equal(idx_target, end_idx); is_within_subarray = self.sub(is_within_subarray, is_at_end_idx.0); - + let to_be_multiplied = self.select(BoolVariable(is_within_subarray), random_value, one); current_multiplier = self.mul(current_multiplier, to_be_multiplied); @@ -58,7 +58,7 @@ impl, const D: usize> CircuitBuilder { self.is_equal(commitment_for_a, commitment_for_b) } - #[allow(unused_variables, dead_code)] + /// Asserts that subarrays are equal using a random linear combination. pub fn assert_subarray_equal( &mut self, a: &[ByteVariable], From 687abc7b287d78613bb99aaa92a5961091fb8b90 Mon Sep 17 00:00:00 2001 From: Xearty Date: Mon, 2 Oct 2023 19:18:17 +0300 Subject: [PATCH 05/18] feat: Implement assert_is_true method on the CircuitBuilder --- plonky2x/src/frontend/builder/mod.rs | 8 ++++++++ plonky2x/src/frontend/eth/mpt/rlc.rs | 3 +-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/plonky2x/src/frontend/builder/mod.rs b/plonky2x/src/frontend/builder/mod.rs index 7e63cf4b3..a17c3ebef 100644 --- a/plonky2x/src/frontend/builder/mod.rs +++ b/plonky2x/src/frontend/builder/mod.rs @@ -316,6 +316,14 @@ impl, const D: usize> CircuitBuilder { } } + /// Fails if i1 != true. + pub fn assert_is_true(&mut self, i1: V) { + let one = self.api.one(); + for t1 in i1.targets().iter() { + self.api.connect(*t1, one); + } + } + /// Returns 1 if i1 == i2 and 0 otherwise as a BoolVariable. pub fn is_equal(&mut self, i1: V, i2: V) -> BoolVariable { let mut result = self._true(); diff --git a/plonky2x/src/frontend/eth/mpt/rlc.rs b/plonky2x/src/frontend/eth/mpt/rlc.rs index b87370e58..c03229c9f 100644 --- a/plonky2x/src/frontend/eth/mpt/rlc.rs +++ b/plonky2x/src/frontend/eth/mpt/rlc.rs @@ -68,8 +68,7 @@ impl, const D: usize> CircuitBuilder { len: Variable, ) { let subarrays_are_equal = self.subarray_equal(a, a_offset, b, b_offset, len); - let _true = self._true(); - self.assert_is_equal(subarrays_are_equal, _true); + self.assert_is_true(subarrays_are_equal); } } From 7e373752778858dd9956572b6d954524b99de9bd Mon Sep 17 00:00:00 2001 From: Xearty Date: Mon, 2 Oct 2023 19:20:29 +0300 Subject: [PATCH 06/18] feat: Make challenger seed depend on the input arrays --- plonky2x/src/frontend/eth/mpt/rlc.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plonky2x/src/frontend/eth/mpt/rlc.rs b/plonky2x/src/frontend/eth/mpt/rlc.rs index c03229c9f..7d4804e09 100644 --- a/plonky2x/src/frontend/eth/mpt/rlc.rs +++ b/plonky2x/src/frontend/eth/mpt/rlc.rs @@ -1,3 +1,4 @@ +use itertools::Itertools; use plonky2::field::types::Field; use plonky2::hash::poseidon::PoseidonHash; use plonky2::iop::challenger::RecursiveChallenger; @@ -49,7 +50,11 @@ impl, const D: usize> CircuitBuilder { len: Variable, ) -> BoolVariable { let mut challenger = RecursiveChallenger::::new(&mut self.api); - let challenger_seed = Vec::new(); + let challenger_seed = [a, b] + .concat() + .iter() + .map(|byte| byte.to_variable(self).0) + .collect_vec(); challenger.observe_elements(&challenger_seed); let challenge = Variable(challenger.get_challenge(&mut self.api)); From 95ed36262c05a52df8240d82cdcbdc0108c97968 Mon Sep 17 00:00:00 2001 From: Xearty Date: Mon, 2 Oct 2023 19:21:45 +0300 Subject: [PATCH 07/18] test: Add and mod RLC tests --- plonky2x/src/frontend/eth/mpt/rlc.rs | 90 +++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 7 deletions(-) diff --git a/plonky2x/src/frontend/eth/mpt/rlc.rs b/plonky2x/src/frontend/eth/mpt/rlc.rs index 7d4804e09..8589bc8d2 100644 --- a/plonky2x/src/frontend/eth/mpt/rlc.rs +++ b/plonky2x/src/frontend/eth/mpt/rlc.rs @@ -83,7 +83,7 @@ pub(crate) mod tests { use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; use crate::frontend::builder::DefaultBuilder; - use crate::prelude::{ByteVariable, CircuitVariable, Variable}; + use crate::prelude::{ByteVariable, Variable}; impl Default for ByteVariable { fn default() -> ByteVariable { @@ -103,11 +103,87 @@ pub(crate) mod tests { let mut b: [ByteVariable; MAX_LEN] = Default::default(); for i in 0..MAX_LEN { - a[i] = ByteVariable::constant(&mut builder, (i + 5) as u8); + a[i] = builder.constant::((i + 5) as u8); } for i in 0..MAX_LEN { - b[i] = ByteVariable::constant(&mut builder, i as u8); + b[i] = builder.constant::(i as u8); + } + + let a_offset: Variable = builder.constant(F::ZERO); + let b_offset = builder.constant(F::from_canonical_usize(5)); + let len: Variable = builder.constant(F::from_canonical_usize(5)); + builder.assert_subarray_equal(&a, a_offset, &b, b_offset, len); + + // Build your circuit. + let circuit = builder.build(); + + // Write to the circuit input. + let input = circuit.input(); + + // Generate a proof. + let (proof, output) = circuit.prove(&input); + + // Verify proof. + circuit.verify(&proof, &input, &output) + } + + #[test] + pub fn test_subarray_equal_diff_len_should_succeed() { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + let mut builder = DefaultBuilder::new(); + + const LEN_A: usize = 15; + const LEN_B: usize = 10; + let mut a: [ByteVariable; LEN_A] = Default::default(); + let mut b: [ByteVariable; LEN_B] = Default::default(); + + for i in 0..LEN_A { + a[i] = builder.constant::((i + 5) as u8); + } + + for i in 0..LEN_B { + b[i] = builder.constant::(i as u8); + } + + let a_offset: Variable = builder.constant(F::ZERO); + let b_offset = builder.constant(F::from_canonical_usize(5)); + let len: Variable = builder.constant(F::from_canonical_usize(5)); + builder.assert_subarray_equal(&a, a_offset, &b, b_offset, len); + + // Build your circuit. + let circuit = builder.build(); + + // Write to the circuit input. + let input = circuit.input(); + + // Generate a proof. + let (proof, output) = circuit.prove(&input); + + // Verify proof. + circuit.verify(&proof, &input, &output) + } + + #[test] + pub fn test_subarray_equal_diff_len_inverse_should_succeed() { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + let mut builder = DefaultBuilder::new(); + + const LEN_A: usize = 10; + const LEN_B: usize = 15; + let mut a: [ByteVariable; LEN_A] = Default::default(); + let mut b: [ByteVariable; LEN_B] = Default::default(); + + for i in 0..LEN_A { + a[i] = builder.constant::((i + 5) as u8); + } + + for i in 0..LEN_B { + b[i] = builder.constant::(i as u8); } let a_offset: Variable = builder.constant(F::ZERO); @@ -141,15 +217,15 @@ pub(crate) mod tests { let mut b: [ByteVariable; MAX_LEN] = Default::default(); for i in 0..MAX_LEN { - a[i] = ByteVariable::constant(&mut builder, (i + 5) as u8); + a[i] = builder.constant::((i + 5) as u8); } for i in 0..MAX_LEN { - b[i] = ByteVariable::constant(&mut builder, i as u8); + b[i] = builder.constant::(i as u8); } - + // Modify 1 byte here. - b[6] = ByteVariable::constant(&mut builder, 0); + b[6] = builder.constant::(0); let a_offset = builder.constant(F::ZERO); let b_offset = builder.constant(F::from_canonical_usize(5)); From 7dc11d802e4e580724f2a782b3412e9fdb0fd805 Mon Sep 17 00:00:00 2001 From: Xearty Date: Mon, 2 Oct 2023 21:14:01 +0300 Subject: [PATCH 08/18] refactor: Formatting --- plonky2x/src/frontend/eth/mpt/rlc.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plonky2x/src/frontend/eth/mpt/rlc.rs b/plonky2x/src/frontend/eth/mpt/rlc.rs index 8589bc8d2..e53cf1b8f 100644 --- a/plonky2x/src/frontend/eth/mpt/rlc.rs +++ b/plonky2x/src/frontend/eth/mpt/rlc.rs @@ -55,7 +55,9 @@ impl, const D: usize> CircuitBuilder { .iter() .map(|byte| byte.to_variable(self).0) .collect_vec(); + challenger.observe_elements(&challenger_seed); + let challenge = Variable(challenger.get_challenge(&mut self.api)); let commitment_for_a = self.commit_subarray(a, a_offset, len, challenge); From a59589fa529c55a66417ba9925dd034bce8f1a2b Mon Sep 17 00:00:00 2001 From: Xearty Date: Tue, 3 Oct 2023 14:04:26 +0300 Subject: [PATCH 09/18] refactor: Move RLPDecodeListGenerator to generators.rs --- plonky2x/src/frontend/eth/rlp/builder.rs | 110 +---------------- plonky2x/src/frontend/eth/rlp/generators.rs | 125 ++++++++++++++++++++ plonky2x/src/frontend/eth/rlp/mod.rs | 1 + 3 files changed, 127 insertions(+), 109 deletions(-) create mode 100644 plonky2x/src/frontend/eth/rlp/generators.rs diff --git a/plonky2x/src/frontend/eth/rlp/builder.rs b/plonky2x/src/frontend/eth/rlp/builder.rs index aa3e4bdd4..aef72f810 100644 --- a/plonky2x/src/frontend/eth/rlp/builder.rs +++ b/plonky2x/src/frontend/eth/rlp/builder.rs @@ -12,6 +12,7 @@ use plonky2::iop::witness::PartitionWitness; use plonky2::plonk::circuit_data::CommonCircuitData; use plonky2::util::serialization::{Buffer, IoResult}; +use super::generators::RLPDecodeListGenerator; use crate::prelude::{ ArrayVariable, BoolVariable, ByteVariable, CircuitBuilder, CircuitVariable, PlonkParameters, Variable, @@ -180,115 +181,6 @@ pub fn verify_decoded_list( assert!(claim_poly == encoding_poly); } -#[derive(Debug, Clone)] -pub struct RLPDecodeListGenerator< - L: PlonkParameters, - const D: usize, - const ENCODING_LEN: usize, - const LIST_LEN: usize, - const ELEMENT_LEN: usize, -> { - encoding: ArrayVariable, - length: Variable, - finish: BoolVariable, - pub decoded_list: ArrayVariable, LIST_LEN>, - pub decoded_element_lens: ArrayVariable, - pub len_decoded_list: Variable, - _phantom: PhantomData, -} - -impl< - L: PlonkParameters, - const D: usize, - const ENCODING_LEN: usize, - const LIST_LEN: usize, - const ELEMENT_LEN: usize, - > RLPDecodeListGenerator -{ - pub fn new( - builder: &mut CircuitBuilder, - encoding: ArrayVariable, - length: Variable, - finish: BoolVariable, - ) -> Self { - let decoded_list = - builder.init::, LIST_LEN>>(); - let decoded_element_lens = builder.init::>(); - let len_decoded_list = builder.init::(); - Self { - encoding, - length, - finish, - decoded_list, - decoded_element_lens, - len_decoded_list, - _phantom: PhantomData, - } - } -} - -impl< - L: PlonkParameters, - const D: usize, - const ENCODING_LEN: usize, - const LIST_LEN: usize, - const ELEMENT_LEN: usize, - > SimpleGenerator - for RLPDecodeListGenerator -{ - fn id(&self) -> String { - "RLPDecodeListGenerator".to_string() - } - - fn dependencies(&self) -> Vec { - let mut targets: Vec = Vec::new(); - targets.extend(self.encoding.targets()); - targets.extend(self.length.targets()); - targets.extend(self.finish.targets()); - targets - } - - fn run_once( - &self, - witness: &PartitionWitness, - out_buffer: &mut GeneratedValues, - ) { - let finish = self.finish.get(witness); - let encoding = self.encoding.get(witness); - let length = self.length.get(witness).as_canonical_u64() as usize; - let (decoded_list, decoded_list_lens, len_decoded_list) = - decode_element_as_list::( - &encoding, length, finish, - ); - self.decoded_list.set(out_buffer, decoded_list); - self.decoded_element_lens.set( - out_buffer, - decoded_list_lens - .iter() - .map(|x| L::Field::from_canonical_usize(*x)) - .collect(), - ); - self.len_decoded_list - .set(out_buffer, L::Field::from_canonical_usize(len_decoded_list)); - } - - #[allow(unused_variables)] - fn serialize( - &self, - dst: &mut Vec, - common_data: &CommonCircuitData, - ) -> IoResult<()> { - todo!() - } - - #[allow(unused_variables)] - fn deserialize( - src: &mut Buffer, - common_data: &CommonCircuitData, - ) -> IoResult { - todo!() - } -} impl, const D: usize> CircuitBuilder { pub fn decode_element_as_list< diff --git a/plonky2x/src/frontend/eth/rlp/generators.rs b/plonky2x/src/frontend/eth/rlp/generators.rs new file mode 100644 index 000000000..4d8581d96 --- /dev/null +++ b/plonky2x/src/frontend/eth/rlp/generators.rs @@ -0,0 +1,125 @@ +use core::marker::PhantomData; + +use curta::math::field::PrimeField64; +use plonky2::field::types::Field; +use plonky2::iop::generator::{GeneratedValues, SimpleGenerator}; +use plonky2::iop::target::Target; +use plonky2::iop::witness::PartitionWitness; +use plonky2::plonk::circuit_data::CommonCircuitData; +use plonky2::util::serialization::{Buffer, IoResult}; + +use super::builder::decode_element_as_list; +use crate::prelude::{ + ArrayVariable, BoolVariable, ByteVariable, CircuitBuilder, CircuitVariable, PlonkParameters, + Variable, +}; + +#[derive(Debug, Clone)] +pub struct RLPDecodeListGenerator< + L: PlonkParameters, + const D: usize, + const ENCODING_LEN: usize, + const LIST_LEN: usize, + const ELEMENT_LEN: usize, +> { + encoding: ArrayVariable, + length: Variable, + finish: BoolVariable, + pub decoded_list: ArrayVariable, LIST_LEN>, + pub decoded_element_lens: ArrayVariable, + pub len_decoded_list: Variable, + _phantom: PhantomData, +} + +impl< + L: PlonkParameters, + const D: usize, + const ENCODING_LEN: usize, + const LIST_LEN: usize, + const ELEMENT_LEN: usize, + > RLPDecodeListGenerator +{ + pub fn new( + builder: &mut CircuitBuilder, + encoding: ArrayVariable, + length: Variable, + finish: BoolVariable, + ) -> Self { + let decoded_list = + builder.init::, LIST_LEN>>(); + let decoded_element_lens = builder.init::>(); + let len_decoded_list = builder.init::(); + Self { + encoding, + length, + finish, + decoded_list, + decoded_element_lens, + len_decoded_list, + _phantom: PhantomData, + } + } +} + +impl< + L: PlonkParameters, + const D: usize, + const ENCODING_LEN: usize, + const LIST_LEN: usize, + const ELEMENT_LEN: usize, + > SimpleGenerator + for RLPDecodeListGenerator +{ + fn id(&self) -> String { + "RLPDecodeListGenerator".to_string() + } + + fn dependencies(&self) -> Vec { + let mut targets: Vec = Vec::new(); + targets.extend(self.encoding.targets()); + targets.extend(self.length.targets()); + targets.extend(self.finish.targets()); + targets + } + + fn run_once( + &self, + witness: &PartitionWitness, + out_buffer: &mut GeneratedValues, + ) { + let finish = self.finish.get(witness); + let encoding = self.encoding.get(witness); + let length = self.length.get(witness).as_canonical_u64() as usize; + let (decoded_list, decoded_list_lens, len_decoded_list) = + decode_element_as_list::( + &encoding, length, finish, + ); + self.decoded_list.set(out_buffer, decoded_list); + self.decoded_element_lens.set( + out_buffer, + decoded_list_lens + .iter() + .map(|x| L::Field::from_canonical_usize(*x)) + .collect(), + ); + self.len_decoded_list + .set(out_buffer, L::Field::from_canonical_usize(len_decoded_list)); + } + + #[allow(unused_variables)] + fn serialize( + &self, + dst: &mut Vec, + common_data: &CommonCircuitData, + ) -> IoResult<()> { + todo!() + } + + #[allow(unused_variables)] + fn deserialize( + src: &mut Buffer, + common_data: &CommonCircuitData, + ) -> IoResult { + todo!() + } +} diff --git a/plonky2x/src/frontend/eth/rlp/mod.rs b/plonky2x/src/frontend/eth/rlp/mod.rs index 5575a85eb..e44f78155 100644 --- a/plonky2x/src/frontend/eth/rlp/mod.rs +++ b/plonky2x/src/frontend/eth/rlp/mod.rs @@ -1 +1,2 @@ pub mod builder; +pub mod generators; From 30c6c2dd30bb61d89ca8c00422af1088fbf8978f Mon Sep 17 00:00:00 2001 From: Xearty Date: Tue, 3 Oct 2023 14:06:50 +0300 Subject: [PATCH 10/18] feat: Implement to_bits and to_byte_variable on Variable --- plonky2x/src/frontend/vars/variable.rs | 28 +++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/plonky2x/src/frontend/vars/variable.rs b/plonky2x/src/frontend/vars/variable.rs index 7f1f09867..52b899e81 100644 --- a/plonky2x/src/frontend/vars/variable.rs +++ b/plonky2x/src/frontend/vars/variable.rs @@ -1,11 +1,12 @@ use std::fmt::Debug; +use itertools::Itertools; use plonky2::hash::hash_types::RichField; use plonky2::iop::target::Target; use plonky2::iop::witness::{Witness, WitnessWrite}; use serde::{Deserialize, Serialize}; -use super::CircuitVariable; +use super::{BoolVariable, ByteVariable, CircuitVariable}; use crate::backend::circuit::PlonkParameters; use crate::frontend::builder::CircuitBuilder; use crate::frontend::ops::{Add, Div, Mul, Neg, One, Sub, Zero}; @@ -59,6 +60,31 @@ impl CircuitVariable for Variable { } } +impl Variable { + pub fn to_bits, const D: usize>( + self, + builder: &mut CircuitBuilder, + ) -> [BoolVariable; N] { + builder + .api + .split_le(self.0, N) + .iter() + .rev() + .map(|x| BoolVariable::from(x.target)) + .collect_vec() + .try_into() + .unwrap() + } + + pub fn to_byte_variable, const D: usize>( + self, + builder: &mut CircuitBuilder, + ) -> ByteVariable { + let bits: [BoolVariable; 8] = self.to_bits(builder); + ByteVariable(bits) + } +} + impl From for Variable { fn from(target: Target) -> Self { Self(target) From 81bc3ab62f1518e07b586d8fc15030bdfb31cd20 Mon Sep 17 00:00:00 2001 From: Xearty Date: Tue, 3 Oct 2023 21:32:54 +0300 Subject: [PATCH 11/18] feat: Implement a circuit version of verify_decoded_list --- plonky2x/src/frontend/eth/rlp/builder.rs | 96 +++++++++++++++++++++--- 1 file changed, 84 insertions(+), 12 deletions(-) diff --git a/plonky2x/src/frontend/eth/rlp/builder.rs b/plonky2x/src/frontend/eth/rlp/builder.rs index aef72f810..2b8c70efe 100644 --- a/plonky2x/src/frontend/eth/rlp/builder.rs +++ b/plonky2x/src/frontend/eth/rlp/builder.rs @@ -1,20 +1,13 @@ -use std::marker::PhantomData; - use curta::math::field::Field; -use curta::math::prelude::PrimeField64; use ethers::types::Bytes; use log::info; use num::bigint::ToBigInt; use num::BigInt; -use plonky2::iop::generator::{GeneratedValues, SimpleGenerator}; -use plonky2::iop::target::Target; -use plonky2::iop::witness::PartitionWitness; -use plonky2::plonk::circuit_data::CommonCircuitData; -use plonky2::util::serialization::{Buffer, IoResult}; use super::generators::RLPDecodeListGenerator; +use crate::frontend::ops::math::{Add, LessThanOrEqual}; use crate::prelude::{ - ArrayVariable, BoolVariable, ByteVariable, CircuitBuilder, CircuitVariable, PlonkParameters, + ArrayVariable, BoolVariable, ByteVariable, BytesVariable, CircuitBuilder, PlonkParameters, Variable, }; @@ -181,8 +174,82 @@ pub fn verify_decoded_list( assert!(claim_poly == encoding_poly); } - impl, const D: usize> CircuitBuilder { + fn parse_list_element( + &mut self, + element: ArrayVariable, + len: Variable, + ) -> (ByteVariable, Variable) { + let prefix = element[0]; + let prefix_var = prefix.to_variable(self); + + let zero_byte = self.constant::(0); + let zero_var = zero_byte.to_variable(self); + let one_byte = self.constant::(1); + let one_var = one_byte.to_variable(self); + let max_len_55 = self.constant::(L::Field::from_canonical_u8(55)); + let _0x7f = self.constant::(0x7F); + let _0x7f_var = _0x7f.to_variable(self); + let _0x80 = self.constant::(0x80); + let _0x80_var = _0x80.to_variable(self); + + let len_eq_0 = self.is_equal(len, zero_var); + let len_eq_1 = self.is_equal(len, one_var); + let prx_leq_n = prefix_var.lte(_0x7f_var, self); + + let _0x80_0 = BytesVariable::<2>([_0x80, zero_byte]); + let prx_0 = BytesVariable::<2>([prefix, zero_byte]); + let _len_0x80 = self.add(len, _0x80_var); + let len_0x80 = + BytesVariable::<2>([_len_0x80.to_byte_variable(self), len.to_byte_variable(self)]); + + let len_is_leq_55_pred = len.lte(max_len_55, self); + self.assert_is_true(len_is_leq_55_pred); + + let len_eq_1_prx = self.and(len_eq_1, prx_leq_n); + let greater_than_0x80 = self.select(len_eq_1_prx, prx_0, len_0x80); + let res = self.select(len_eq_0, _0x80_0, greater_than_0x80); + + (res[0], res[1].to_variable(self)) + } + + fn verify_decoded_list< + const ENCODING_LEN: usize, + const LIST_LEN: usize, + const ELEMENT_LEN: usize, + >( + &mut self, + list: ArrayVariable, LIST_LEN>, + lens: ArrayVariable, + encoding: ArrayVariable, + ) { + let zero = self.constant::(L::Field::from_canonical_u8(0)); + let one = self.one(); + let mut start_idx = self.constant::(L::Field::from_canonical_u8(3)); + + let mut encoded_decoding: Vec = Vec::new(); + + for i in 0..LIST_LEN { + let (start_byte, list_len) = self.parse_list_element(list[i].clone(), lens[i]); + encoded_decoding.push(start_byte); + for j in 0..ELEMENT_LEN { + encoded_decoding.push(list[i][j]); + } + + let encoded_decoding_len = list_len.add(one, self); + self.assert_subarray_equal( + &encoded_decoding[..], + zero, + encoding.as_slice(), + start_idx, + encoded_decoding_len, + ); + start_idx = self.add(start_idx, encoded_decoding_len); + + encoded_decoding.clear(); + } + } + pub fn decode_element_as_list< const ENCODING_LEN: usize, const LIST_LEN: usize, @@ -197,9 +264,13 @@ impl, const D: usize> CircuitBuilder { ArrayVariable, Variable, ) { - let generator = RLPDecodeListGenerator::new(self, encoded, len, finish); + let generator = RLPDecodeListGenerator::new(self, encoded.clone(), len, finish); self.add_simple_generator(generator.clone()); - // TODO: here add verification logic constraints using `builder` to check that the decoded list is correct + self.verify_decoded_list::( + generator.decoded_list.clone(), + generator.decoded_element_lens.clone(), + encoded.clone(), + ); ( generator.decoded_list, generator.decoded_element_lens, @@ -213,6 +284,7 @@ mod tests { use super::*; use crate::backend::circuit::DefaultParameters; + use crate::frontend::vars::CircuitVariable; use crate::prelude::{DefaultBuilder, GoldilocksField}; use crate::utils::bytes; From 7e002a9e8eb2a5983c5dbf724b15ee2a7c8dc9e1 Mon Sep 17 00:00:00 2001 From: Martin Ivanov Date: Wed, 4 Oct 2023 09:53:19 +0300 Subject: [PATCH 12/18] broken commit --- plonky2x/src/debug.rs | 76 +++++++++++++++ plonky2x/src/debug.rs:21:16 | 0 plonky2x/src/frontend/eth/rlp/builder.rs | 112 ++++++++++++++++++----- plonky2x/src/frontend/ops/math.rs | 8 +- plonky2x/src/lib.rs | 2 + 5 files changed, 171 insertions(+), 27 deletions(-) create mode 100644 plonky2x/src/debug.rs create mode 100644 plonky2x/src/debug.rs:21:16 diff --git a/plonky2x/src/debug.rs b/plonky2x/src/debug.rs new file mode 100644 index 000000000..55db3083f --- /dev/null +++ b/plonky2x/src/debug.rs @@ -0,0 +1,76 @@ +use std::marker::PhantomData; + +use plonky2::field::types::PrimeField64; +use plonky2::iop::generator::{GeneratedValues, SimpleGenerator}; +use plonky2::iop::target::Target; +use plonky2::iop::witness::PartitionWitness; +use plonky2::plonk::circuit_data::CommonCircuitData; +use plonky2::util::serialization::{Buffer, IoResult}; + +use crate::frontend::vars::{CircuitVariable, Variable}; +use crate::prelude::{CircuitBuilder, PlonkParameters}; + +#[derive(Debug, Clone)] +pub struct DebugGenerator, const D: usize> { + format: String, + variable: Variable, + _phantom: PhantomData, +} + +impl, const D: usize> DebugGenerator { + pub fn new(builder: &mut CircuitBuilder, format: String, variable: Variable) -> Self { + Self { + format, + variable, + _phantom: PhantomData, + } + } +} + +impl, const D: usize> SimpleGenerator for DebugGenerator { + fn id(&self) -> String { + "DebugGenerator".to_string() + } + + fn dependencies(&self) -> Vec { + let mut targets: Vec = Vec::new(); + targets.extend(self.variable.targets()); + targets + } + + #[allow(unused_variables)] + fn run_once( + &self, + witness: &PartitionWitness, + out_buffer: &mut GeneratedValues, + ) { + let value = self.variable.get(witness).to_canonical_u64(); + println!("{} = {}", self.format, value); + } + + #[allow(unused_variables)] + fn serialize( + &self, + dst: &mut Vec, + common_data: &CommonCircuitData, + ) -> IoResult<()> { + todo!() + } + + #[allow(unused_variables)] + fn deserialize( + src: &mut Buffer, + common_data: &CommonCircuitData, + ) -> IoResult { + todo!() + } +} + +pub fn debug, const D: usize>( + builder: &mut CircuitBuilder, + format: String, + variable: Variable, +) { + let generator = DebugGenerator::new(builder, format, variable); + builder.add_simple_generator(generator.clone()); +} diff --git a/plonky2x/src/debug.rs:21:16 b/plonky2x/src/debug.rs:21:16 new file mode 100644 index 000000000..e69de29bb diff --git a/plonky2x/src/frontend/eth/rlp/builder.rs b/plonky2x/src/frontend/eth/rlp/builder.rs index 2b8c70efe..4d7fea782 100644 --- a/plonky2x/src/frontend/eth/rlp/builder.rs +++ b/plonky2x/src/frontend/eth/rlp/builder.rs @@ -5,7 +5,9 @@ use num::bigint::ToBigInt; use num::BigInt; use super::generators::RLPDecodeListGenerator; +use crate::debug; use crate::frontend::ops::math::{Add, LessThanOrEqual}; +use crate::frontend::vars::CircuitVariable; use crate::prelude::{ ArrayVariable, BoolVariable, ByteVariable, BytesVariable, CircuitBuilder, PlonkParameters, Variable, @@ -174,6 +176,8 @@ pub fn verify_decoded_list( assert!(claim_poly == encoding_poly); } +static mut now_debugging: i32 = 0; + impl, const D: usize> CircuitBuilder { fn parse_list_element( &mut self, @@ -223,30 +227,92 @@ impl, const D: usize> CircuitBuilder { lens: ArrayVariable, encoding: ArrayVariable, ) { - let zero = self.constant::(L::Field::from_canonical_u8(0)); - let one = self.one(); - let mut start_idx = self.constant::(L::Field::from_canonical_u8(3)); - - let mut encoded_decoding: Vec = Vec::new(); - - for i in 0..LIST_LEN { - let (start_byte, list_len) = self.parse_list_element(list[i].clone(), lens[i]); - encoded_decoding.push(start_byte); - for j in 0..ELEMENT_LEN { - encoded_decoding.push(list[i][j]); + unsafe { + now_debugging += 1; + if now_debugging == 0 { + return; + } + // @TODO: Accumulate the length and check the prefix (first 2 or 3 bytes of the encoding). + let zero = self.zero(); + let one = self.one(); + let two = self.constant::(L::Field::from_canonical_u8(2)); + + let const_0xf7 = self.constant::(L::Field::from_canonical_u8(0xf7)); + let first_byte = encoding[0].to_variable(self); + let second_byte = encoding[1].to_variable(self); + + debug::debug(self, "first_byte".to_string(), first_byte); + debug::debug(self, "second_byte".to_string(), second_byte); + + let is_long_list = self.gte(first_byte, const_0xf7); + let len_if_long_list = self.sub(first_byte, const_0xf7); + let len_if_long_list = self.mul(is_long_list.0, len_if_long_list); + + debug::debug(self, "len_if_long_list".to_string(), len_if_long_list); + + // Constrain len_if_long_list to be 0, 1 or 2. + let is_zero = self.is_equal(len_if_long_list, zero); + let is_one = self.is_equal(len_if_long_list, one); + let is_two = self.is_equal(len_if_long_list, two); + let is_zero_one = self.or(is_zero, is_one); + let is_zero_one_two = self.or(is_zero_one, is_two); + let _true = self._true(); + self.assert_is_equal(is_zero_one_two, _true); + + let mut start_idx = self.add(len_if_long_list, one); + + let mut encoded_decoding: Vec = Vec::new(); + + for i in 0..LIST_LEN { + let (mut start_byte, list_len) = self.parse_list_element(list[i].clone(), lens[i]); + let start_byte_var = start_byte.to_variable(self); + encoded_decoding.push(start_byte); + for j in 0..ELEMENT_LEN { + encoded_decoding.push(list[i][j]); + } + + debug::debug( + self, + format!("start_byte[{}]", i).to_string(), + start_byte_var, + ); + debug::debug(self, format!("lens[{}]", i).to_string(), lens[i]); + debug::debug(self, format!("list_len[{}]", i).to_string(), list_len); + + // let const_0x80 = self.constant::(L::Field::from_canonical_u8(8)); + //let has_prefix_byte = self.lt(first_byte, const_0x80); + //let byte_is_not_its_own_encoding = self.not(has_prefix_byte); + // let len_is_zero = self.is_zero(lens[i]); + // let len_is_not_zero = self.not(len_is_zero); + // let encoded_decoding_len = self.add(list_len, len_is_not_zero.0); + // let encoded_decoding_len = + // self.mul(encoded_decoding_len, byte_is_not_its_own_encoding.0); + + let first_element = list[i][0].to_variable(self); + let first_element_is_zero = self.is_zero(first_element); + let len_is_zero = self.is_zero(list_len); + let should_skip = self.and(first_element_is_zero, len_is_zero); + let should_skip_multiplier = self.not(should_skip); + + let encoded_decoding_len = self.add(list_len, one); + let encoded_decoding_len = self.mul(encoded_decoding_len, should_skip_multiplier.0); + + debug::debug( + self, + format!("encoded_decoding_len[{}]", i).to_string(), + encoded_decoding_len, + ); + + self.assert_subarray_equal( + &encoded_decoding[..], + zero, + encoding.as_slice(), + start_idx, + encoded_decoding_len, + ); + start_idx = self.add(start_idx, encoded_decoding_len); + encoded_decoding.clear(); } - - let encoded_decoding_len = list_len.add(one, self); - self.assert_subarray_equal( - &encoded_decoding[..], - zero, - encoding.as_slice(), - start_idx, - encoded_decoding_len, - ); - start_idx = self.add(start_idx, encoded_decoding_len); - - encoded_decoding.clear(); } } diff --git a/plonky2x/src/frontend/ops/math.rs b/plonky2x/src/frontend/ops/math.rs index bb3e442de..e141ddec5 100644 --- a/plonky2x/src/frontend/ops/math.rs +++ b/plonky2x/src/frontend/ops/math.rs @@ -183,18 +183,18 @@ impl, const D: usize> CircuitBuilder { pub fn gt(&mut self, lhs: Lhs, rhs: Rhs) -> BoolVariable where Lhs: Sub + One, - Rhs: LessThanOrEqual, + Rhs: LessThanOrEqual, { - self.lte(rhs, lhs) + self.lt(rhs, lhs) } /// The greater than or equal to operation (>=). pub fn gte(&mut self, lhs: Lhs, rhs: Rhs) -> BoolVariable where Lhs: Sub + One, - Rhs: LessThanOrEqual, + Rhs: LessThanOrEqual, { - self.lt(rhs, lhs) + self.lte(rhs, lhs) } /// The within range operation (lhs <= variable < rhs). diff --git a/plonky2x/src/lib.rs b/plonky2x/src/lib.rs index c8d9b722d..6801f5b89 100644 --- a/plonky2x/src/lib.rs +++ b/plonky2x/src/lib.rs @@ -9,6 +9,8 @@ extern crate alloc; extern crate clap; +pub mod debug; + pub mod backend; pub mod frontend; pub mod utils; From c1e71c622129a13bf177376c3d07b2b4e67b6660 Mon Sep 17 00:00:00 2001 From: Aneta Tsvetkova Date: Thu, 5 Oct 2023 16:19:04 +0300 Subject: [PATCH 13/18] Fixed mpt verify logic --- plonky2x/src/debug.rs | 34 +++-- plonky2x/src/debug.rs:21:16 | 0 plonky2x/src/frontend/eth/rlp/builder.rs | 165 +++++++++-------------- 3 files changed, 88 insertions(+), 111 deletions(-) delete mode 100644 plonky2x/src/debug.rs:21:16 diff --git a/plonky2x/src/debug.rs b/plonky2x/src/debug.rs index 55db3083f..ee1dc59b5 100644 --- a/plonky2x/src/debug.rs +++ b/plonky2x/src/debug.rs @@ -12,16 +12,20 @@ use crate::prelude::{CircuitBuilder, PlonkParameters}; #[derive(Debug, Clone)] pub struct DebugGenerator, const D: usize> { - format: String, - variable: Variable, + pub string: Vec, + pub value: Vec, _phantom: PhantomData, } impl, const D: usize> DebugGenerator { - pub fn new(builder: &mut CircuitBuilder, format: String, variable: Variable) -> Self { + pub fn new( + _builder: &mut CircuitBuilder, + string: Vec, + value: Vec, + ) -> Self { Self { - format, - variable, + string, + value, _phantom: PhantomData, } } @@ -34,7 +38,9 @@ impl, const D: usize> SimpleGenerator for Deb fn dependencies(&self) -> Vec { let mut targets: Vec = Vec::new(); - targets.extend(self.variable.targets()); + for v in &self.value { + targets.extend(v.targets()); + } targets } @@ -44,8 +50,14 @@ impl, const D: usize> SimpleGenerator for Deb witness: &PartitionWitness, out_buffer: &mut GeneratedValues, ) { - let value = self.variable.get(witness).to_canonical_u64(); - println!("{} = {}", self.format, value); + for i in 0..self.value.len() { + println!( + "{}: {}", + self.string[i], + self.value[i].get(witness).to_canonical_u64() + ); + } + println!(); } #[allow(unused_variables)] @@ -68,9 +80,9 @@ impl, const D: usize> SimpleGenerator for Deb pub fn debug, const D: usize>( builder: &mut CircuitBuilder, - format: String, - variable: Variable, + format: Vec, + variable: Vec, ) { - let generator = DebugGenerator::new(builder, format, variable); + let generator: DebugGenerator = DebugGenerator::new(builder, format, variable); builder.add_simple_generator(generator.clone()); } diff --git a/plonky2x/src/debug.rs:21:16 b/plonky2x/src/debug.rs:21:16 deleted file mode 100644 index e69de29bb..000000000 diff --git a/plonky2x/src/frontend/eth/rlp/builder.rs b/plonky2x/src/frontend/eth/rlp/builder.rs index 4d7fea782..2a702d6d8 100644 --- a/plonky2x/src/frontend/eth/rlp/builder.rs +++ b/plonky2x/src/frontend/eth/rlp/builder.rs @@ -6,8 +6,7 @@ use num::BigInt; use super::generators::RLPDecodeListGenerator; use crate::debug; -use crate::frontend::ops::math::{Add, LessThanOrEqual}; -use crate::frontend::vars::CircuitVariable; +use crate::frontend::ops::math::LessThanOrEqual; use crate::prelude::{ ArrayVariable, BoolVariable, ByteVariable, BytesVariable, CircuitBuilder, PlonkParameters, Variable, @@ -23,10 +22,8 @@ pub fn bool_to_u32(b: bool) -> u32 { // Note this only decodes bytes and doesn't support long strings pub fn rlp_decode_bytes(input: &[u8]) -> (Vec, usize) { let prefix = input[0]; - if prefix <= 0x7F { + if prefix <= 0x80 { (vec![prefix], 1) - } else if prefix == 0x80 { - (vec![], 1) // null value } else if prefix <= 0xB7 { // Short string (0-55 bytes length) let length = (prefix - 0x80) as usize; @@ -158,7 +155,7 @@ pub fn verify_decoded_list( for j in 0..32 { poly += list[i][j] as u32 * (random.pow(1 + size_accumulator + j as u32)) - * bool_to_u32(j as u32 <= list_len); + * bool_to_u32((j as u32) < list_len); } size_accumulator += 1 + list_len; claim_poly += poly; @@ -176,15 +173,12 @@ pub fn verify_decoded_list( assert!(claim_poly == encoding_poly); } -static mut now_debugging: i32 = 0; - impl, const D: usize> CircuitBuilder { - fn parse_list_element( + fn parse_list_element( &mut self, - element: ArrayVariable, + prefix: ByteVariable, len: Variable, ) -> (ByteVariable, Variable) { - let prefix = element[0]; let prefix_var = prefix.to_variable(self); let zero_byte = self.constant::(0); @@ -201,18 +195,32 @@ impl, const D: usize> CircuitBuilder { let len_eq_1 = self.is_equal(len, one_var); let prx_leq_n = prefix_var.lte(_0x7f_var, self); - let _0x80_0 = BytesVariable::<2>([_0x80, zero_byte]); + let prefix_is_128 = self.is_equal(prefix, _0x80); + let prefix_is_0 = self.is_equal(prefix, zero_byte); + + let _to_be_0x80 = self.or(prefix_is_0, prefix_is_128); + let _base = self.select(prefix_is_128, _0x80, zero_byte); + let prx_0 = BytesVariable::<2>([prefix, zero_byte]); let _len_0x80 = self.add(len, _0x80_var); - let len_0x80 = - BytesVariable::<2>([_len_0x80.to_byte_variable(self), len.to_byte_variable(self)]); + let _len_0x80_var = _len_0x80.to_byte_variable(self); + let len_byte = len.to_byte_variable(self); + let len_0x80 = self.select( + prefix_is_128, + BytesVariable::<2>([prefix, zero_byte]), + BytesVariable::<2>([_len_0x80_var, len_byte]), + ); let len_is_leq_55_pred = len.lte(max_len_55, self); self.assert_is_true(len_is_leq_55_pred); let len_eq_1_prx = self.and(len_eq_1, prx_leq_n); let greater_than_0x80 = self.select(len_eq_1_prx, prx_0, len_0x80); - let res = self.select(len_eq_0, _0x80_0, greater_than_0x80); + let res = self.select( + len_eq_0, + BytesVariable::<2>([_base, zero_byte]), + greater_than_0x80, + ); (res[0], res[1].to_variable(self)) } @@ -227,92 +235,49 @@ impl, const D: usize> CircuitBuilder { lens: ArrayVariable, encoding: ArrayVariable, ) { - unsafe { - now_debugging += 1; - if now_debugging == 0 { - return; - } - // @TODO: Accumulate the length and check the prefix (first 2 or 3 bytes of the encoding). - let zero = self.zero(); - let one = self.one(); - let two = self.constant::(L::Field::from_canonical_u8(2)); - - let const_0xf7 = self.constant::(L::Field::from_canonical_u8(0xf7)); - let first_byte = encoding[0].to_variable(self); - let second_byte = encoding[1].to_variable(self); - - debug::debug(self, "first_byte".to_string(), first_byte); - debug::debug(self, "second_byte".to_string(), second_byte); - - let is_long_list = self.gte(first_byte, const_0xf7); - let len_if_long_list = self.sub(first_byte, const_0xf7); - let len_if_long_list = self.mul(is_long_list.0, len_if_long_list); - - debug::debug(self, "len_if_long_list".to_string(), len_if_long_list); - - // Constrain len_if_long_list to be 0, 1 or 2. - let is_zero = self.is_equal(len_if_long_list, zero); - let is_one = self.is_equal(len_if_long_list, one); - let is_two = self.is_equal(len_if_long_list, two); - let is_zero_one = self.or(is_zero, is_one); - let is_zero_one_two = self.or(is_zero_one, is_two); - let _true = self._true(); - self.assert_is_equal(is_zero_one_two, _true); - - let mut start_idx = self.add(len_if_long_list, one); - - let mut encoded_decoding: Vec = Vec::new(); - - for i in 0..LIST_LEN { - let (mut start_byte, list_len) = self.parse_list_element(list[i].clone(), lens[i]); - let start_byte_var = start_byte.to_variable(self); - encoded_decoding.push(start_byte); - for j in 0..ELEMENT_LEN { - encoded_decoding.push(list[i][j]); - } - - debug::debug( - self, - format!("start_byte[{}]", i).to_string(), - start_byte_var, - ); - debug::debug(self, format!("lens[{}]", i).to_string(), lens[i]); - debug::debug(self, format!("list_len[{}]", i).to_string(), list_len); - - // let const_0x80 = self.constant::(L::Field::from_canonical_u8(8)); - //let has_prefix_byte = self.lt(first_byte, const_0x80); - //let byte_is_not_its_own_encoding = self.not(has_prefix_byte); - // let len_is_zero = self.is_zero(lens[i]); - // let len_is_not_zero = self.not(len_is_zero); - // let encoded_decoding_len = self.add(list_len, len_is_not_zero.0); - // let encoded_decoding_len = - // self.mul(encoded_decoding_len, byte_is_not_its_own_encoding.0); - - let first_element = list[i][0].to_variable(self); - let first_element_is_zero = self.is_zero(first_element); - let len_is_zero = self.is_zero(list_len); - let should_skip = self.and(first_element_is_zero, len_is_zero); - let should_skip_multiplier = self.not(should_skip); - - let encoded_decoding_len = self.add(list_len, one); - let encoded_decoding_len = self.mul(encoded_decoding_len, should_skip_multiplier.0); - - debug::debug( - self, - format!("encoded_decoding_len[{}]", i).to_string(), - encoded_decoding_len, - ); - - self.assert_subarray_equal( - &encoded_decoding[..], - zero, - encoding.as_slice(), - start_idx, - encoded_decoding_len, - ); - start_idx = self.add(start_idx, encoded_decoding_len); - encoded_decoding.clear(); + // @TODO: Accumulate the length and check the prefix (first 2 or 3 bytes of the encoding). + let zero = self.zero(); + let one = self.one(); + let two = self.constant::(L::Field::from_canonical_u8(2)); + + let const_0xf7 = self.constant::(L::Field::from_canonical_u8(0xf7)); + let const_0xf8 = self.constant::(L::Field::from_canonical_u8(0xf8)); + let first_byte = encoding[0].to_variable(self); + + let is_long_list = self.gte(first_byte, const_0xf8); + let len_if_long_list = self.sub(first_byte, const_0xf7); + let len_if_long_list = self.mul(is_long_list.0, len_if_long_list); + + // Constrain len_if_long_list to be 0, 1 or 2. + let is_zero = self.is_equal(len_if_long_list, zero); + let is_one = self.is_equal(len_if_long_list, one); + let is_two = self.is_equal(len_if_long_list, two); + let is_zero_one = self.or(is_zero, is_one); + let is_zero_one_two = self.or(is_zero_one, is_two); + self.assert_is_true(is_zero_one_two); + + let mut start_idx = self.add(len_if_long_list, one); + + let mut encoded_decoding: Vec = Vec::new(); + + for i in 0..LIST_LEN { + let (start_byte, list_len) = self.parse_list_element(list[i][0], lens[i]); + encoded_decoding.push(start_byte); + for j in 0..ELEMENT_LEN { + encoded_decoding.push(list[i][j]); } + + let encoded_decoding_len = self.add(list_len, one); + + self.assert_subarray_equal( + &encoded_decoding[..], + zero, + encoding.as_slice(), + start_idx, + encoded_decoding_len, + ); + start_idx = self.add(start_idx, encoded_decoding_len); + encoded_decoding.clear(); } } From 878a205025b5f3509834e139d4858c13fec26fc4 Mon Sep 17 00:00:00 2001 From: Aneta Tsvetkova Date: Thu, 5 Oct 2023 20:49:23 +0300 Subject: [PATCH 14/18] Renamed variables and added assert for list len --- plonky2x/src/frontend/eth/rlp/builder.rs | 129 ++++++++++++++--------- 1 file changed, 81 insertions(+), 48 deletions(-) diff --git a/plonky2x/src/frontend/eth/rlp/builder.rs b/plonky2x/src/frontend/eth/rlp/builder.rs index 2a702d6d8..8911eb69b 100644 --- a/plonky2x/src/frontend/eth/rlp/builder.rs +++ b/plonky2x/src/frontend/eth/rlp/builder.rs @@ -5,7 +5,6 @@ use num::bigint::ToBigInt; use num::BigInt; use super::generators::RLPDecodeListGenerator; -use crate::debug; use crate::frontend::ops::math::LessThanOrEqual; use crate::prelude::{ ArrayVariable, BoolVariable, ByteVariable, BytesVariable, CircuitBuilder, PlonkParameters, @@ -179,47 +178,32 @@ impl, const D: usize> CircuitBuilder { prefix: ByteVariable, len: Variable, ) -> (ByteVariable, Variable) { - let prefix_var = prefix.to_variable(self); + // We don't handle strings longer than 55 bytes + let const_55 = self.constant::(L::Field::from_canonical_u8(55)); + let len_is_leq_55_pred = len.lte(const_55, self); + self.assert_is_true(len_is_leq_55_pred); - let zero_byte = self.constant::(0); - let zero_var = zero_byte.to_variable(self); - let one_byte = self.constant::(1); - let one_var = one_byte.to_variable(self); - let max_len_55 = self.constant::(L::Field::from_canonical_u8(55)); - let _0x7f = self.constant::(0x7F); - let _0x7f_var = _0x7f.to_variable(self); - let _0x80 = self.constant::(0x80); - let _0x80_var = _0x80.to_variable(self); - - let len_eq_0 = self.is_equal(len, zero_var); - let len_eq_1 = self.is_equal(len, one_var); - let prx_leq_n = prefix_var.lte(_0x7f_var, self); - - let prefix_is_128 = self.is_equal(prefix, _0x80); - let prefix_is_0 = self.is_equal(prefix, zero_byte); - - let _to_be_0x80 = self.or(prefix_is_0, prefix_is_128); - let _base = self.select(prefix_is_128, _0x80, zero_byte); - - let prx_0 = BytesVariable::<2>([prefix, zero_byte]); - let _len_0x80 = self.add(len, _0x80_var); - let _len_0x80_var = _len_0x80.to_byte_variable(self); + let prefix_var = prefix.to_variable(self); let len_byte = len.to_byte_variable(self); - let len_0x80 = self.select( - prefix_is_128, - BytesVariable::<2>([prefix, zero_byte]), - BytesVariable::<2>([_len_0x80_var, len_byte]), - ); - let len_is_leq_55_pred = len.lte(max_len_55, self); - self.assert_is_true(len_is_leq_55_pred); + let const_0 = self.constant::(0); + let const_1 = self.constant::(1); + let const_0x80 = self.constant::(0x80); + let const_0x80_var = const_0x80.to_variable(self); - let len_eq_1_prx = self.and(len_eq_1, prx_leq_n); - let greater_than_0x80 = self.select(len_eq_1_prx, prx_0, len_0x80); + let len_eq_0 = self.is_equal(len_byte, const_0); + let len_eq_1 = self.is_equal(len_byte, const_1); + let prefix_lte_0x80 = prefix_var.lte(const_0x80_var, self); + + let len_add_0x80 = self.add(len, const_0x80_var); + let len_add_0x80_var = len_add_0x80.to_byte_variable(self); + + let len_eq_1_prefix = self.and(len_eq_1, prefix_lte_0x80); + let len_eq_0_or_1_and_prefix = self.or(len_eq_0, len_eq_1_prefix); let res = self.select( - len_eq_0, - BytesVariable::<2>([_base, zero_byte]), - greater_than_0x80, + len_eq_0_or_1_and_prefix, + BytesVariable::<2>([prefix, const_0]), + BytesVariable::<2>([len_add_0x80_var, len_byte]), ); (res[0], res[1].to_variable(self)) @@ -235,31 +219,73 @@ impl, const D: usize> CircuitBuilder { lens: ArrayVariable, encoding: ArrayVariable, ) { - // @TODO: Accumulate the length and check the prefix (first 2 or 3 bytes of the encoding). let zero = self.zero(); let one = self.one(); let two = self.constant::(L::Field::from_canonical_u8(2)); - let const_0xf7 = self.constant::(L::Field::from_canonical_u8(0xf7)); - let const_0xf8 = self.constant::(L::Field::from_canonical_u8(0xf8)); + let const_247 = self.constant::(L::Field::from_canonical_u8(0xf7)); + let const_248 = self.constant::(L::Field::from_canonical_u8(0xf8)); let first_byte = encoding[0].to_variable(self); - let is_long_list = self.gte(first_byte, const_0xf8); - let len_if_long_list = self.sub(first_byte, const_0xf7); - let len_if_long_list = self.mul(is_long_list.0, len_if_long_list); + // Extract the number of bytes the length of the list is encoded in + // len_if_long_list will be 0, 1 or 2 + let is_long_list = self.gte(first_byte, const_248); + let len_of_len = self.sub(first_byte, const_247); + let len_of_len = self.mul(is_long_list.0, len_of_len); - // Constrain len_if_long_list to be 0, 1 or 2. - let is_zero = self.is_equal(len_if_long_list, zero); - let is_one = self.is_equal(len_if_long_list, one); - let is_two = self.is_equal(len_if_long_list, two); + // Constrain len_of_len to be 0, 1 or 2. + let is_zero = self.is_equal(len_of_len, zero); + let is_one = self.is_equal(len_of_len, one); + let is_two = self.is_equal(len_of_len, two); let is_zero_one = self.or(is_zero, is_one); let is_zero_one_two = self.or(is_zero_one, is_two); self.assert_is_true(is_zero_one_two); - let mut start_idx = self.add(len_if_long_list, one); + let list_prefix_byte = self.select_array(encoding.as_slice(), zero); + let list_prefix_byte_var = list_prefix_byte.to_variable(self); + let list_prefix_byte_is_zero = self.is_zero(list_prefix_byte_var); + let list_prefix_byte_is_not_zero = self.not(list_prefix_byte_is_zero); + + let first_len_byte = self.select_array(encoding.as_slice(), one); + let first_len_var = first_len_byte.to_variable(self); + let second_len_byte = self.select_array(encoding.as_slice(), two); + + let len_len_is_zero = self.is_equal(len_of_len, zero); + let len_len_is_one = self.is_equal(len_of_len, one); + let len_len_is_two = self.is_equal(len_of_len, two); + + let const_192 = self.constant::(L::Field::from_canonical_u8(0xc0)); + let extracted_length_if_len_len_0 = self.sub(list_prefix_byte_var, const_192); + let extracted_length_if_len_len_0 = self.mul( + list_prefix_byte_is_not_zero.0, + extracted_length_if_len_len_0, + ); + let extracted_length_if_len_len_0 = + self.mul(len_len_is_zero.0, extracted_length_if_len_len_0); + + let const_256 = self.constant::(L::Field::from_canonical_u64(256)); + let extracted_length_if_len_len_1 = self.mul(len_len_is_one.0, first_len_var); + + let first_len_byte_as_var = first_len_byte.to_variable(self); + let second_len_byte_as_var = second_len_byte.to_variable(self); + let extracted_length_if_len_len_2 = self.mul(first_len_byte_as_var, const_256); + let extracted_length_if_len_len_2 = + self.add(extracted_length_if_len_len_2, second_len_byte_as_var); + let extracted_length_if_len_len_2 = + self.mul(len_len_is_two.0, extracted_length_if_len_len_2); + + let extracted_length = self.add_many(&[ + extracted_length_if_len_len_0, + extracted_length_if_len_len_1, + extracted_length_if_len_len_2, + ]); + + let mut start_idx = self.add(len_of_len, one); let mut encoded_decoding: Vec = Vec::new(); + let mut accumulated_list_len = self.zero(); + for i in 0..LIST_LEN { let (start_byte, list_len) = self.parse_list_element(list[i][0], lens[i]); encoded_decoding.push(start_byte); @@ -277,8 +303,15 @@ impl, const D: usize> CircuitBuilder { encoded_decoding_len, ); start_idx = self.add(start_idx, encoded_decoding_len); + + let start_byte_var = start_byte.to_variable(self); + let start_byte_is_zero = self.is_zero(start_byte_var); + let start_byte_not_zero = self.not(start_byte_is_zero); + let list_len_zero = self.mul(start_byte_not_zero.0, encoded_decoding_len); + accumulated_list_len = self.add(accumulated_list_len, list_len_zero); encoded_decoding.clear(); } + self.assert_is_equal(extracted_length, accumulated_list_len); } pub fn decode_element_as_list< From 8f68d84d04b62f44504b246fb1e1ca8bd16fb768 Mon Sep 17 00:00:00 2001 From: Aneta Tsvetkova Date: Fri, 6 Oct 2023 11:44:11 +0300 Subject: [PATCH 15/18] Added utils for or many and is not zero --- plonky2x/src/frontend/builder/mod.rs | 8 +++++++- plonky2x/src/frontend/eth/mpt/builder.rs | 12 +++++++----- plonky2x/src/frontend/eth/rlp/builder.rs | 9 +++------ plonky2x/src/frontend/ops/bitwise.rs | 13 ++++++++++++- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/plonky2x/src/frontend/builder/mod.rs b/plonky2x/src/frontend/builder/mod.rs index a17c3ebef..f865f79d4 100644 --- a/plonky2x/src/frontend/builder/mod.rs +++ b/plonky2x/src/frontend/builder/mod.rs @@ -309,6 +309,12 @@ impl, const D: usize> CircuitBuilder { self.api.is_equal(i1.0, zero).target.into() } + /// Returns 1 if i1 is not zero, 0 otherwise as a boolean. + pub fn is_not_zero(&mut self, i1: Variable) -> BoolVariable { + let is_zero = self.is_zero(i1); + self.not(is_zero) + } + /// Fails if i1 != i2. pub fn assert_is_equal(&mut self, i1: V, i2: V) { for (t1, t2) in i1.targets().iter().zip(i2.targets().iter()) { @@ -355,7 +361,7 @@ impl, const D: usize> CircuitBuilder { pub fn le_sum(&mut self, bits: &[BoolVariable]) -> Variable { let bits = bits .iter() - .map(|x| BoolTarget::new_unsafe(x.0.0)) + .map(|x| BoolTarget::new_unsafe(x.0 .0)) .collect_vec(); Variable(self.api.le_sum(bits.into_iter())) } diff --git a/plonky2x/src/frontend/eth/mpt/builder.rs b/plonky2x/src/frontend/eth/mpt/builder.rs index a8c72c5a1..f2f57b6f2 100644 --- a/plonky2x/src/frontend/eth/mpt/builder.rs +++ b/plonky2x/src/frontend/eth/mpt/builder.rs @@ -129,8 +129,7 @@ impl, const D: usize> CircuitBuilder { let case_len_le_32 = self.and(node_len_le_32, first_32_bytes_eq); let inter = self.not(node_len_le_32); let case_len_gt_32 = self.and(inter, hash_eq); - let equality_fulfilled = self.or(case_len_le_32, case_len_gt_32); - let checked_equality = self.or(equality_fulfilled, finished); + let checked_equality = self.or_many(&[case_len_le_32, case_len_gt_32, finished]); let t = self._true(); self.assert_is_equal(checked_equality, t); } @@ -199,9 +198,12 @@ impl, const D: usize> CircuitBuilder { let prefix_leaf_even_and_leaf = self.and(prefix_leaf_even, is_leaf); let prefix_leaf_odd_and_leaf = self.and(prefix_leaf_odd, is_leaf); - let l = self.or(is_branch_and_key_terminated, prefix_leaf_even_and_leaf); - let m = self.or(l, prefix_leaf_odd_and_leaf); - finished = self.or(finished, m); + finished = self.or_many(&[ + finished, + is_branch_and_key_terminated, + prefix_leaf_even_and_leaf, + prefix_leaf_odd_and_leaf, + ]); } let current_node_len = self.sub_byte(current_node_id[0], const_128); diff --git a/plonky2x/src/frontend/eth/rlp/builder.rs b/plonky2x/src/frontend/eth/rlp/builder.rs index 8911eb69b..82be2f028 100644 --- a/plonky2x/src/frontend/eth/rlp/builder.rs +++ b/plonky2x/src/frontend/eth/rlp/builder.rs @@ -237,14 +237,12 @@ impl, const D: usize> CircuitBuilder { let is_zero = self.is_equal(len_of_len, zero); let is_one = self.is_equal(len_of_len, one); let is_two = self.is_equal(len_of_len, two); - let is_zero_one = self.or(is_zero, is_one); - let is_zero_one_two = self.or(is_zero_one, is_two); + let is_zero_one_two = self.or_many(&[is_zero, is_one, is_two]); self.assert_is_true(is_zero_one_two); let list_prefix_byte = self.select_array(encoding.as_slice(), zero); let list_prefix_byte_var = list_prefix_byte.to_variable(self); - let list_prefix_byte_is_zero = self.is_zero(list_prefix_byte_var); - let list_prefix_byte_is_not_zero = self.not(list_prefix_byte_is_zero); + let list_prefix_byte_is_not_zero = self.is_not_zero(list_prefix_byte_var); let first_len_byte = self.select_array(encoding.as_slice(), one); let first_len_var = first_len_byte.to_variable(self); @@ -305,8 +303,7 @@ impl, const D: usize> CircuitBuilder { start_idx = self.add(start_idx, encoded_decoding_len); let start_byte_var = start_byte.to_variable(self); - let start_byte_is_zero = self.is_zero(start_byte_var); - let start_byte_not_zero = self.not(start_byte_is_zero); + let start_byte_not_zero = self.is_not_zero(start_byte_var); let list_len_zero = self.mul(start_byte_not_zero.0, encoded_decoding_len); accumulated_list_len = self.add(accumulated_list_len, list_len_zero); encoded_decoding.clear(); diff --git a/plonky2x/src/frontend/ops/bitwise.rs b/plonky2x/src/frontend/ops/bitwise.rs index e3dafa3f2..ad152cd6d 100644 --- a/plonky2x/src/frontend/ops/bitwise.rs +++ b/plonky2x/src/frontend/ops/bitwise.rs @@ -23,7 +23,7 @@ impl, const D: usize> CircuitBuilder { /// The bitwise OR operation. /// -/// Types implementing this trait can be used within the `builder.or(lhs, rhs)` method. +/// Types implementing this trait can be used within the `builder.or(lhs, rhs)` method or the `builder.or_many(values)` method. pub trait BitOr, const D: usize, Rhs = Self> { type Output; @@ -37,6 +37,17 @@ impl, const D: usize> CircuitBuilder { { lhs.bitor(rhs, self) } + + pub fn or_many(&mut self, values: &[T]) -> >::Output + where + T: BitOr + Clone, + { + let mut or_res = values[0].clone(); + for i in 1..values.len() { + or_res = or_res.bitor(values[i].clone(), self); + } + or_res + } } /// The bitwise XOR operation. From 785722cd5df099b4753f244b3a68c5bc5b55a782 Mon Sep 17 00:00:00 2001 From: Aneta Tsvetkova Date: Fri, 6 Oct 2023 13:11:39 +0300 Subject: [PATCH 16/18] Fixed rlp failing test --- plonky2x/src/frontend/eth/rlp/builder.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plonky2x/src/frontend/eth/rlp/builder.rs b/plonky2x/src/frontend/eth/rlp/builder.rs index 82be2f028..8c9c5885b 100644 --- a/plonky2x/src/frontend/eth/rlp/builder.rs +++ b/plonky2x/src/frontend/eth/rlp/builder.rs @@ -124,9 +124,7 @@ pub fn decode_element_as_list< fn parse_list_element(element: [u8; 32], len: u8) -> (u32, u32) { let prefix = element[0]; - if len == 0 { - (0x80, 0) - } else if len == 1 && prefix <= 0x7F { + if len == 1 && prefix <= 0x80 { (prefix as u32, 0) } else if len == 1 && prefix > 0x7F { // TODO: maybe this is the same as the below case @@ -421,7 +419,7 @@ mod tests { assert!(len == F::from_canonical_usize(17)); for i in 0..17 { if i == 16 { - assert!(decoded_element_lens[i] == F::from_canonical_usize(0)); + assert!(decoded_element_lens[i] == F::from_canonical_usize(1)); } else { assert!(decoded_element_lens[i] == F::from_canonical_usize(32)); } From e8beaafad4f896285f858ede817d4311d047f2dd Mon Sep 17 00:00:00 2001 From: Aneta Tsvetkova Date: Fri, 6 Oct 2023 13:51:09 +0300 Subject: [PATCH 17/18] Extracted logic for fetching encoding bytes len to a separate function --- plonky2x/src/frontend/eth/rlp/builder.rs | 120 ++++++++++++----------- 1 file changed, 62 insertions(+), 58 deletions(-) diff --git a/plonky2x/src/frontend/eth/rlp/builder.rs b/plonky2x/src/frontend/eth/rlp/builder.rs index 8c9c5885b..6a6bceebc 100644 --- a/plonky2x/src/frontend/eth/rlp/builder.rs +++ b/plonky2x/src/frontend/eth/rlp/builder.rs @@ -171,6 +171,66 @@ pub fn verify_decoded_list( } impl, const D: usize> CircuitBuilder { + fn extract_list_len(&mut self, prefix_bytes: &[ByteVariable]) -> (Variable, Variable) { + let zero = self.zero(); + let one = self.one(); + let two = self.constant::(L::Field::from_canonical_u8(2)); + + let const_247 = self.constant::(L::Field::from_canonical_u8(0xf7)); + let const_248 = self.constant::(L::Field::from_canonical_u8(0xf8)); + + let prefix_byte_var = prefix_bytes[0].to_variable(self); + let first_len_byte_var = prefix_bytes[1].to_variable(self); + let second_len_byte_var = prefix_bytes[2].to_variable(self); + + // Extract the number of bytes the length of the list is encoded in + // len_if_long_list will be 0, 1 or 2 + let is_long_list = self.gte(prefix_byte_var, const_248); + let len_of_len = self.sub(prefix_byte_var, const_247); + let len_of_len = self.mul(is_long_list.0, len_of_len); + + // Constrain len_of_len to be 0, 1 or 2. + let is_zero = self.is_equal(len_of_len, zero); + let is_one = self.is_equal(len_of_len, one); + let is_two = self.is_equal(len_of_len, two); + let is_zero_one_two = self.or_many(&[is_zero, is_one, is_two]); + self.assert_is_true(is_zero_one_two); + + let list_prefix_byte = prefix_bytes[0]; + let list_prefix_byte_var = list_prefix_byte.to_variable(self); + let list_prefix_byte_is_not_zero = self.is_not_zero(list_prefix_byte_var); + + let len_len_is_zero = self.is_equal(len_of_len, zero); + let len_len_is_one = self.is_equal(len_of_len, one); + let len_len_is_two = self.is_equal(len_of_len, two); + + let const_192 = self.constant::(L::Field::from_canonical_u8(0xc0)); + let extracted_length_if_len_len_0 = self.sub(list_prefix_byte_var, const_192); + let extracted_length_if_len_len_0 = self.mul( + list_prefix_byte_is_not_zero.0, + extracted_length_if_len_len_0, + ); + let extracted_length_if_len_len_0 = + self.mul(len_len_is_zero.0, extracted_length_if_len_len_0); + + let const_256 = self.constant::(L::Field::from_canonical_u64(256)); + let extracted_length_if_len_len_1 = self.mul(len_len_is_one.0, first_len_byte_var); + + let extracted_length_if_len_len_2 = self.mul(first_len_byte_var, const_256); + let extracted_length_if_len_len_2 = + self.add(extracted_length_if_len_len_2, second_len_byte_var); + let extracted_length_if_len_len_2 = + self.mul(len_len_is_two.0, extracted_length_if_len_len_2); + + let extracted_length = self.add_many(&[ + extracted_length_if_len_len_0, + extracted_length_if_len_len_1, + extracted_length_if_len_len_2, + ]); + + (extracted_length, len_of_len) + } + fn parse_list_element( &mut self, prefix: ByteVariable, @@ -219,69 +279,13 @@ impl, const D: usize> CircuitBuilder { ) { let zero = self.zero(); let one = self.one(); - let two = self.constant::(L::Field::from_canonical_u8(2)); - - let const_247 = self.constant::(L::Field::from_canonical_u8(0xf7)); - let const_248 = self.constant::(L::Field::from_canonical_u8(0xf8)); - let first_byte = encoding[0].to_variable(self); - - // Extract the number of bytes the length of the list is encoded in - // len_if_long_list will be 0, 1 or 2 - let is_long_list = self.gte(first_byte, const_248); - let len_of_len = self.sub(first_byte, const_247); - let len_of_len = self.mul(is_long_list.0, len_of_len); - - // Constrain len_of_len to be 0, 1 or 2. - let is_zero = self.is_equal(len_of_len, zero); - let is_one = self.is_equal(len_of_len, one); - let is_two = self.is_equal(len_of_len, two); - let is_zero_one_two = self.or_many(&[is_zero, is_one, is_two]); - self.assert_is_true(is_zero_one_two); - - let list_prefix_byte = self.select_array(encoding.as_slice(), zero); - let list_prefix_byte_var = list_prefix_byte.to_variable(self); - let list_prefix_byte_is_not_zero = self.is_not_zero(list_prefix_byte_var); - - let first_len_byte = self.select_array(encoding.as_slice(), one); - let first_len_var = first_len_byte.to_variable(self); - let second_len_byte = self.select_array(encoding.as_slice(), two); - - let len_len_is_zero = self.is_equal(len_of_len, zero); - let len_len_is_one = self.is_equal(len_of_len, one); - let len_len_is_two = self.is_equal(len_of_len, two); - - let const_192 = self.constant::(L::Field::from_canonical_u8(0xc0)); - let extracted_length_if_len_len_0 = self.sub(list_prefix_byte_var, const_192); - let extracted_length_if_len_len_0 = self.mul( - list_prefix_byte_is_not_zero.0, - extracted_length_if_len_len_0, - ); - let extracted_length_if_len_len_0 = - self.mul(len_len_is_zero.0, extracted_length_if_len_len_0); - - let const_256 = self.constant::(L::Field::from_canonical_u64(256)); - let extracted_length_if_len_len_1 = self.mul(len_len_is_one.0, first_len_var); - - let first_len_byte_as_var = first_len_byte.to_variable(self); - let second_len_byte_as_var = second_len_byte.to_variable(self); - let extracted_length_if_len_len_2 = self.mul(first_len_byte_as_var, const_256); - let extracted_length_if_len_len_2 = - self.add(extracted_length_if_len_len_2, second_len_byte_as_var); - let extracted_length_if_len_len_2 = - self.mul(len_len_is_two.0, extracted_length_if_len_len_2); - - let extracted_length = self.add_many(&[ - extracted_length_if_len_len_0, - extracted_length_if_len_len_1, - extracted_length_if_len_len_2, - ]); + let (extracted_length, len_of_len) = self.extract_list_len(&encoding.as_slice()[..3]); let mut start_idx = self.add(len_of_len, one); + let mut accumulated_list_len = zero; let mut encoded_decoding: Vec = Vec::new(); - let mut accumulated_list_len = self.zero(); - for i in 0..LIST_LEN { let (start_byte, list_len) = self.parse_list_element(list[i][0], lens[i]); encoded_decoding.push(start_byte); From 276344e4f1a6beb0216b2fafe8f73847b96b9246 Mon Sep 17 00:00:00 2001 From: Aneta Tsvetkova Date: Fri, 6 Oct 2023 19:06:46 +0300 Subject: [PATCH 18/18] Fixed zero element length acknowledgement and lte logic --- plonky2x/src/frontend/eth/rlp/builder.rs | 20 ++++++++++++++------ plonky2x/src/frontend/ops/math.rs | 14 +++++++------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/plonky2x/src/frontend/eth/rlp/builder.rs b/plonky2x/src/frontend/eth/rlp/builder.rs index 6a6bceebc..2b5c14e87 100644 --- a/plonky2x/src/frontend/eth/rlp/builder.rs +++ b/plonky2x/src/frontend/eth/rlp/builder.rs @@ -286,6 +286,11 @@ impl, const D: usize> CircuitBuilder { let mut accumulated_list_len = zero; let mut encoded_decoding: Vec = Vec::new(); + let encoding_start_byte = encoding[0].to_variable(self); + let encoding_start_byte_not_zero = self.is_not_zero(encoding_start_byte); + let end_index = + self.add_many(&[encoding_start_byte_not_zero.0, len_of_len, extracted_length]); + for i in 0..LIST_LEN { let (start_byte, list_len) = self.parse_list_element(list[i][0], lens[i]); encoded_decoding.push(start_byte); @@ -296,20 +301,23 @@ impl, const D: usize> CircuitBuilder { let encoded_decoding_len = self.add(list_len, one); self.assert_subarray_equal( - &encoded_decoding[..], + encoded_decoding.as_slice(), zero, encoding.as_slice(), start_idx, encoded_decoding_len, ); - start_idx = self.add(start_idx, encoded_decoding_len); - let start_byte_var = start_byte.to_variable(self); - let start_byte_not_zero = self.is_not_zero(start_byte_var); - let list_len_zero = self.mul(start_byte_not_zero.0, encoded_decoding_len); - accumulated_list_len = self.add(accumulated_list_len, list_len_zero); + let is_within_encoding = self.lt(start_idx, end_index); + let increment = self.mul(is_within_encoding.0, encoded_decoding_len); + accumulated_list_len = self.add(accumulated_list_len, increment); + + start_idx = self.add(start_idx, encoded_decoding_len); encoded_decoding.clear(); } + + // TODO assert that when the encoding finishes the rest is all zero padding + self.assert_is_equal(extracted_length, accumulated_list_len); } diff --git a/plonky2x/src/frontend/ops/math.rs b/plonky2x/src/frontend/ops/math.rs index e141ddec5..41b6548ef 100644 --- a/plonky2x/src/frontend/ops/math.rs +++ b/plonky2x/src/frontend/ops/math.rs @@ -171,19 +171,19 @@ impl, const D: usize> CircuitBuilder { /// The less than operation (<). pub fn lt(&mut self, lhs: Lhs, rhs: Rhs) -> BoolVariable where - Lhs: LessThanOrEqual, - Rhs: Sub + One, + Lhs: Add + LessThanOrEqual, + Rhs: One, { let one = self.one::(); - let upper_bound = rhs.sub(one, self); - self.lte(lhs, upper_bound) + let lower_bound = lhs.add(one, self); + self.lte(lower_bound, rhs) } /// The greater than operation (>). pub fn gt(&mut self, lhs: Lhs, rhs: Rhs) -> BoolVariable where - Lhs: Sub + One, - Rhs: LessThanOrEqual, + Lhs: One, + Rhs: Add + LessThanOrEqual, { self.lt(rhs, lhs) } @@ -200,7 +200,7 @@ impl, const D: usize> CircuitBuilder { /// The within range operation (lhs <= variable < rhs). pub fn within_range(&mut self, variable: V, lhs: V, rhs: V) -> BoolVariable where - V: LessThanOrEqual + Sub + One + Clone, + V: LessThanOrEqual + Add + One + Clone, { let lower_bound_satisfied = self.lte(lhs, variable.clone()); let upper_bound_satisfied = self.lt(variable, rhs);