Skip to content

Commit

Permalink
RLP Circuit and RLP Table (scroll-tech#225)
Browse files Browse the repository at this point in the history
* 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
roynalnaruto and kunxian-xia authored Dec 26, 2022
1 parent 3a74d93 commit 047f4d2
Show file tree
Hide file tree
Showing 22 changed files with 3,813 additions and 40 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

108 changes: 108 additions & 0 deletions gadgets/src/comparator.rs
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 {
&()
}
}
248 changes: 248 additions & 0 deletions gadgets/src/is_equal.rs
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
);
}
}
Loading

0 comments on commit 047f4d2

Please sign in to comment.