forked from chaosma/scroll-prover
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
RLP Circuit and RLP Table (scroll-tech#225)
* feat: RLP encoding verification circuit * fix: compilation and tx rlp encode tests * fix: refactor redundant columns, is_first and is_last are advices * fix: assign dummy rows front and back * feat: assign multiple inputs to rlp circuit * feat: add tx table lookup from rlp circuit * fix: lookup for all fields of tx * fix: calldata rlc lookup * hash of rlp encoding * refactor: remove receipt related verification * fix: remove lookups from rlp circuit * refactor: separate out rlp table and embed in circuit * feat: add eip-155 support for unsigned tx * chore: refactor tag_index into RLP table * chore: refactor constraints into TxSign and TxHash * feat: signed tx support * feat: verify sig_r and sig_s fields * fix: add missing check for only one tag * chore: remove unused gadget * fix: randomness | add rlp table to tx circuit * feat: updates to RLP circuit/table * fix: compilation * chore: clippy fix * refactor witness of rlp circuit to use value api * refactor rlp_circuit to use single set of constraints to handle tags * fix clippy errors * fix * add padding constraints * reduce degree to 9 * fix * add rlp_circuit to super_circuit * disable lt/cmp chips for padding rows to reduce witness assign time * fix: get chain_id from block header * fix Co-authored-by: kunxian xia <[email protected]>
- Loading branch information
1 parent
3a74d93
commit 047f4d2
Showing
22 changed files
with
3,813 additions
and
40 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
//! Comparator can be used to compare LT, EQ (and indirectly GT) for two | ||
//! expressions LHS and RHS. | ||
use eth_types::Field; | ||
use halo2_proofs::{ | ||
arithmetic::FieldExt, | ||
circuit::{Chip, Region, Value}, | ||
plonk::{ConstraintSystem, Error, Expression, VirtualCells}, | ||
poly::Rotation, | ||
}; | ||
|
||
use crate::{is_equal::IsEqualInstruction, less_than::LtInstruction}; | ||
|
||
use super::{is_equal::IsEqualChip, less_than::LtChip}; | ||
|
||
/// Instruction that the Comparator chip needs to implement. | ||
pub trait ComparatorInstruction<F: FieldExt> { | ||
/// Assign the lhs and rhs witnesses to the Comparator chip's region. | ||
fn assign( | ||
&self, | ||
region: &mut Region<'_, F>, | ||
offset: usize, | ||
lhs: F, | ||
rhs: F, | ||
) -> Result<(), Error>; | ||
} | ||
|
||
/// Config for the Comparator chip. | ||
#[derive(Clone, Debug)] | ||
pub struct ComparatorConfig<F, const N_BYTES: usize> { | ||
/// Config for the LessThan chip. | ||
pub lt_chip: LtChip<F, N_BYTES>, | ||
/// Config for the IsEqual chip. | ||
pub eq_chip: IsEqualChip<F>, | ||
} | ||
|
||
impl<F: Field, const N_BYTES: usize> ComparatorConfig<F, N_BYTES> { | ||
/// Returns (lt, eq) for a comparison between lhs and rhs. | ||
pub fn expr( | ||
&self, | ||
meta: &mut VirtualCells<F>, | ||
rotation: Option<Rotation>, | ||
) -> (Expression<F>, Expression<F>) { | ||
( | ||
self.lt_chip.config.is_lt(meta, rotation), | ||
self.eq_chip.config.is_equal_expression.clone(), | ||
) | ||
} | ||
} | ||
|
||
/// Chip to compare two expressions. | ||
#[derive(Clone, Debug)] | ||
pub struct ComparatorChip<F, const N_BYTES: usize> { | ||
config: ComparatorConfig<F, N_BYTES>, | ||
} | ||
|
||
impl<F: Field, const N_BYTES: usize> ComparatorChip<F, N_BYTES> { | ||
/// Configure the comparator chip. Returns the config. | ||
pub fn configure( | ||
meta: &mut ConstraintSystem<F>, | ||
q_enable: impl FnOnce(&mut VirtualCells<F>) -> Expression<F> + Clone, | ||
lhs: impl FnOnce(&mut VirtualCells<F>) -> Expression<F> + Clone, | ||
rhs: impl FnOnce(&mut VirtualCells<F>) -> Expression<F> + Clone, | ||
) -> ComparatorConfig<F, N_BYTES> { | ||
let lt_config = LtChip::configure(meta, q_enable.clone(), lhs.clone(), rhs.clone()); | ||
let eq_config = IsEqualChip::configure(meta, q_enable, lhs, rhs); | ||
|
||
ComparatorConfig { | ||
lt_chip: LtChip::construct(lt_config), | ||
eq_chip: IsEqualChip::construct(eq_config), | ||
} | ||
} | ||
|
||
/// Constructs a comparator chip given its config. | ||
pub fn construct(config: ComparatorConfig<F, N_BYTES>) -> ComparatorChip<F, N_BYTES> { | ||
ComparatorChip { config } | ||
} | ||
} | ||
|
||
impl<F: Field, const N_BYTES: usize> ComparatorInstruction<F> for ComparatorChip<F, N_BYTES> { | ||
fn assign( | ||
&self, | ||
region: &mut Region<'_, F>, | ||
offset: usize, | ||
lhs: F, | ||
rhs: F, | ||
) -> Result<(), Error> { | ||
self.config().lt_chip.assign(region, offset, lhs, rhs)?; | ||
self.config() | ||
.eq_chip | ||
.assign(region, offset, Value::known(lhs), Value::known(rhs))?; | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
impl<F: Field, const N_BYTES: usize> Chip<F> for ComparatorChip<F, N_BYTES> { | ||
type Config = ComparatorConfig<F, N_BYTES>; | ||
type Loaded = (); | ||
|
||
fn config(&self) -> &Self::Config { | ||
&self.config | ||
} | ||
|
||
fn loaded(&self) -> &Self::Loaded { | ||
&() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,248 @@ | ||
//! IsEqual chip can be used to check equality of two expressions. | ||
use eth_types::Field; | ||
use halo2_proofs::{ | ||
arithmetic::FieldExt, | ||
circuit::{Chip, Region, Value}, | ||
plonk::{ConstraintSystem, Error, Expression, VirtualCells}, | ||
}; | ||
|
||
use super::is_zero::{IsZeroChip, IsZeroInstruction}; | ||
|
||
/// Instruction that the IsEqual chip needs to implement. | ||
pub trait IsEqualInstruction<F: FieldExt> { | ||
/// Assign lhs and rhs witnesses to the IsEqual chip's region. | ||
fn assign( | ||
&self, | ||
region: &mut Region<'_, F>, | ||
offset: usize, | ||
lhs: Value<F>, | ||
rhs: Value<F>, | ||
) -> Result<(), Error>; | ||
} | ||
|
||
/// Config for the IsEqual chip. | ||
#[derive(Clone, Debug)] | ||
pub struct IsEqualConfig<F> { | ||
/// Stores an IsZero chip. | ||
pub is_zero_chip: IsZeroChip<F>, | ||
/// Expression that denotes whether the chip evaluated to equal or not. | ||
pub is_equal_expression: Expression<F>, | ||
} | ||
|
||
/// Chip that compares equality between two expressions. | ||
#[derive(Clone, Debug)] | ||
pub struct IsEqualChip<F> { | ||
/// Config for the IsEqual chip. | ||
pub(crate) config: IsEqualConfig<F>, | ||
} | ||
|
||
impl<F: Field> IsEqualChip<F> { | ||
/// Configure the IsEqual chip. | ||
pub fn configure( | ||
meta: &mut ConstraintSystem<F>, | ||
q_enable: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression<F>, | ||
lhs: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression<F>, | ||
rhs: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression<F>, | ||
) -> IsEqualConfig<F> { | ||
let value = |meta: &mut VirtualCells<F>| lhs(meta) - rhs(meta); | ||
let value_inv = meta.advice_column(); | ||
|
||
let is_zero_config = IsZeroChip::configure(meta, q_enable, value, value_inv); | ||
let is_equal_expression = is_zero_config.is_zero_expression.clone(); | ||
|
||
IsEqualConfig { | ||
is_zero_chip: IsZeroChip::construct(is_zero_config), | ||
is_equal_expression, | ||
} | ||
} | ||
|
||
/// Construct an IsEqual chip given a config. | ||
pub fn construct(config: IsEqualConfig<F>) -> Self { | ||
Self { config } | ||
} | ||
} | ||
|
||
impl<F: Field> IsEqualInstruction<F> for IsEqualChip<F> { | ||
fn assign( | ||
&self, | ||
region: &mut Region<'_, F>, | ||
offset: usize, | ||
lhs: Value<F>, | ||
rhs: Value<F>, | ||
) -> Result<(), Error> { | ||
self.config.is_zero_chip.assign(region, offset, lhs - rhs)?; | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
impl<F: Field> Chip<F> for IsEqualChip<F> { | ||
type Config = IsEqualConfig<F>; | ||
type Loaded = (); | ||
|
||
fn config(&self) -> &Self::Config { | ||
&self.config | ||
} | ||
|
||
fn loaded(&self) -> &Self::Loaded { | ||
&() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use std::marker::PhantomData; | ||
|
||
use eth_types::Field; | ||
use halo2_proofs::{ | ||
arithmetic::FieldExt, | ||
circuit::{Layouter, SimpleFloorPlanner, Value}, | ||
dev::MockProver, | ||
halo2curves::bn256::Fr as Fp, | ||
plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Selector, VirtualCells}, | ||
poly::Rotation, | ||
}; | ||
use rand::Rng; | ||
|
||
use super::{IsEqualChip, IsEqualConfig, IsEqualInstruction}; | ||
use crate::util::Expr; | ||
|
||
#[derive(Clone, Debug)] | ||
struct TestCircuitConfig<F, const RHS: u64> { | ||
q_enable: Selector, | ||
value: Column<Advice>, | ||
check: Column<Advice>, | ||
is_equal: IsEqualConfig<F>, | ||
} | ||
|
||
#[derive(Default)] | ||
struct TestCircuit<F: FieldExt, const RHS: u64> { | ||
values: Vec<u64>, | ||
checks: Vec<bool>, | ||
_marker: PhantomData<F>, | ||
} | ||
|
||
impl<F: Field, const RHS: u64> Circuit<F> for TestCircuit<F, RHS> { | ||
type Config = TestCircuitConfig<F, RHS>; | ||
type FloorPlanner = SimpleFloorPlanner; | ||
|
||
fn without_witnesses(&self) -> Self { | ||
Self::default() | ||
} | ||
|
||
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config { | ||
let q_enable = meta.complex_selector(); | ||
let value = meta.advice_column(); | ||
let check = meta.advice_column(); | ||
|
||
let lhs = |meta: &mut VirtualCells<F>| meta.query_advice(value, Rotation::cur()); | ||
let rhs = |_meta: &mut VirtualCells<F>| RHS.expr(); | ||
|
||
let is_equal = | ||
IsEqualChip::configure(meta, |meta| meta.query_selector(q_enable), lhs, rhs); | ||
|
||
let config = Self::Config { | ||
q_enable, | ||
value, | ||
check, | ||
is_equal, | ||
}; | ||
|
||
meta.create_gate("check is_equal", |meta| { | ||
let q_enable = meta.query_selector(q_enable); | ||
|
||
let check = meta.query_advice(check, Rotation::cur()); | ||
|
||
vec![q_enable * (config.is_equal.is_equal_expression.clone() - check)] | ||
}); | ||
|
||
config | ||
} | ||
|
||
fn synthesize( | ||
&self, | ||
config: Self::Config, | ||
mut layouter: impl Layouter<F>, | ||
) -> Result<(), Error> { | ||
let chip = IsEqualChip::construct(config.is_equal.clone()); | ||
|
||
layouter.assign_region( | ||
|| "witness", | ||
|mut region| { | ||
let checks = self.checks.clone(); | ||
|
||
for (idx, (value, check)) in self.values.iter().cloned().zip(checks).enumerate() | ||
{ | ||
region.assign_advice( | ||
|| "value", | ||
config.value, | ||
idx + 1, | ||
|| Value::known(F::from(value)), | ||
)?; | ||
region.assign_advice( | ||
|| "check", | ||
config.check, | ||
idx + 1, | ||
|| Value::known(F::from(check as u64)), | ||
)?; | ||
config.q_enable.enable(&mut region, idx + 1)?; | ||
chip.assign( | ||
&mut region, | ||
idx + 1, | ||
Value::known(F::from(value)), | ||
Value::known(F::from(RHS)), | ||
)?; | ||
} | ||
|
||
Ok(()) | ||
}, | ||
) | ||
} | ||
} | ||
|
||
macro_rules! try_test { | ||
($values:expr, $checks:expr, $rhs:expr, $is_ok_or_err:ident) => { | ||
let k = usize::BITS - $values.len().leading_zeros() + 2; | ||
let circuit = TestCircuit::<Fp, $rhs> { | ||
values: $values, | ||
checks: $checks, | ||
_marker: PhantomData, | ||
}; | ||
let prover = MockProver::<Fp>::run(k, &circuit, vec![]).unwrap(); | ||
assert!(prover.verify().$is_ok_or_err()); | ||
}; | ||
} | ||
|
||
fn random() -> u64 { | ||
rand::thread_rng().gen::<u64>() | ||
} | ||
|
||
#[test] | ||
fn is_equal_gadget() { | ||
try_test!( | ||
vec![random(), 123, random(), 123, 123, random()], | ||
vec![false, true, false, true, true, false], | ||
123, | ||
is_ok | ||
); | ||
try_test!( | ||
vec![random(), 321321, 321321, random()], | ||
vec![false, true, true, false], | ||
321321, | ||
is_ok | ||
); | ||
try_test!( | ||
vec![random(), random(), random(), 1846123], | ||
vec![false, false, false, true], | ||
1846123, | ||
is_ok | ||
); | ||
try_test!( | ||
vec![123, 234, 345, 456], | ||
vec![true, true, false, false], | ||
234, | ||
is_err | ||
); | ||
} | ||
} |
Oops, something went wrong.