Skip to content
This repository was archived by the owner on Jul 5, 2024. It is now read-only.

Commit 3d0d97d

Browse files
committed
feat: extract bit columns from BinaryNumberChip
Extract the bit columns from BinaryNumberChip into BinaryNumberBits so that the columns can be used in a table without associated constraints. This is useful for the CopyTable to be used in unit tests outside of the CopyCircuit (without needing the BinaryNumberChip constraints).
1 parent e887b6a commit 3d0d97d

File tree

7 files changed

+142
-81
lines changed

7 files changed

+142
-81
lines changed

gadgets/src/binary_number.rs

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,58 @@ where
3434
}
3535
}
3636

37+
/// Columns of the binary number chip. This can be instantiated without the associated constraints
38+
/// of the BinaryNumberChip in order to be used as part of a shared table for unit tests.
39+
#[derive(Clone, Copy, Debug)]
40+
pub struct BinaryNumberBits<const N: usize>(
41+
/// Must be constrained to be binary for correctness.
42+
pub [Column<Advice>; N],
43+
);
44+
45+
impl<const N: usize> BinaryNumberBits<N> {
46+
/// Construct a new BinaryNumberBits without adding any constraints.
47+
pub fn construct<F: Field>(meta: &mut ConstraintSystem<F>) -> Self {
48+
Self([0; N].map(|_| meta.advice_column()))
49+
}
50+
51+
/// Assign a value to the binary number bits. A generic type that implements
52+
/// the AsBits trait can be provided for assignment.
53+
pub fn assign<F: Field, T: AsBits<N>>(
54+
&self,
55+
region: &mut Region<'_, F>,
56+
offset: usize,
57+
value: &T,
58+
) -> Result<(), Error> {
59+
for (&bit, &column) in value.as_bits().iter().zip(&self.0) {
60+
region.assign_advice(
61+
|| format!("binary number {:?}", column),
62+
column,
63+
offset,
64+
|| Value::known(F::from(bit as u64)),
65+
)?;
66+
}
67+
Ok(())
68+
}
69+
70+
/// Returns the expression value of the bits at the given rotation.
71+
pub fn value<F: Field>(
72+
&self,
73+
rotation: Rotation,
74+
) -> impl FnOnce(&mut VirtualCells<'_, F>) -> Expression<F> {
75+
let bits = self.0;
76+
move |meta: &mut VirtualCells<'_, F>| {
77+
let bits = bits.map(|bit| meta.query_advice(bit, rotation));
78+
bits.iter()
79+
.fold(0.expr(), |result, bit| bit.clone() + result * 2.expr())
80+
}
81+
}
82+
}
83+
3784
/// Config for the binary number chip.
3885
#[derive(Clone, Copy, Debug)]
3986
pub struct BinaryNumberConfig<T, const N: usize> {
4087
/// Must be constrained to be binary for correctness.
41-
pub bits: [Column<Advice>; N],
88+
pub bits: BinaryNumberBits<N>,
4289
_marker: PhantomData<T>,
4390
}
4491

@@ -51,12 +98,7 @@ where
5198
&self,
5299
rotation: Rotation,
53100
) -> impl FnOnce(&mut VirtualCells<'_, F>) -> Expression<F> {
54-
let bits = self.bits;
55-
move |meta: &mut VirtualCells<'_, F>| {
56-
let bits = bits.map(|bit| meta.query_advice(bit, rotation));
57-
bits.iter()
58-
.fold(0.expr(), |result, bit| bit.clone() + result * 2.expr())
59-
}
101+
self.bits.value(rotation)
60102
}
61103

62104
/// Return the constant that represents a given value. To be compared with the value expression.
@@ -77,7 +119,9 @@ where
77119
rotation: Rotation,
78120
) -> impl FnOnce(&mut VirtualCells<'_, F>) -> Expression<F> {
79121
let bits = self.bits;
80-
move |meta| Self::value_equals_expr(value, bits.map(|bit| meta.query_advice(bit, rotation)))
122+
move |meta| {
123+
Self::value_equals_expr(value, bits.0.map(|bit| meta.query_advice(bit, rotation)))
124+
}
81125
}
82126

83127
/// Returns a binary expression that evaluates to 1 if expressions are equal
@@ -104,10 +148,11 @@ where
104148
/// Annotates columns of this gadget embedded within a circuit region.
105149
pub fn annotate_columns_in_region<F: Field>(&self, region: &mut Region<F>, prefix: &str) {
106150
let mut annotations = Vec::new();
107-
for (i, _) in self.bits.iter().enumerate() {
151+
for (i, _) in self.bits.0.iter().enumerate() {
108152
annotations.push(format!("GADGETS_binary_number_{}", i));
109153
}
110154
self.bits
155+
.0
111156
.iter()
112157
.zip(annotations.iter())
113158
.for_each(|(col, ann)| region.name_column(|| format!("{}_{}", prefix, ann), *col));
@@ -140,11 +185,11 @@ where
140185
/// Configure constraints for the binary number chip.
141186
pub fn configure(
142187
meta: &mut ConstraintSystem<F>,
188+
bits: BinaryNumberBits<N>,
143189
selector: Column<Fixed>,
144190
value: Option<Column<Advice>>,
145191
) -> BinaryNumberConfig<T, N> {
146-
let bits = [0; N].map(|_| meta.advice_column());
147-
bits.map(|bit| {
192+
bits.0.map(|bit| {
148193
meta.create_gate("bit column is 0 or 1", |meta| {
149194
let selector = meta.query_fixed(selector, Rotation::cur());
150195
let bit = meta.query_advice(bit, Rotation::cur());
@@ -194,15 +239,7 @@ where
194239
offset: usize,
195240
value: &T,
196241
) -> Result<(), Error> {
197-
for (&bit, &column) in value.as_bits().iter().zip(&self.config.bits) {
198-
region.assign_advice(
199-
|| format!("binary number {:?}", column),
200-
column,
201-
offset,
202-
|| Value::known(F::from(bit as u64)),
203-
)?;
204-
}
205-
Ok(())
242+
self.config.bits.assign(region, offset, value)
206243
}
207244
}
208245

zkevm-circuits/src/copy_circuit.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ pub struct CopyCircuitConfig<F> {
6969
/// The Copy Table contains the columns that are exposed via the lookup
7070
/// expressions
7171
pub copy_table: CopyTable,
72+
/// BinaryNumber config out of the tag bits from `copy_table`
73+
copy_table_tag: BinaryNumberConfig<CopyDataType, 3>,
7274
/// Lt chip to check: src_addr < src_addr_end.
7375
/// Since `src_addr` and `src_addr_end` are u64, 8 bytes are sufficient for
7476
/// the Lt chip.
@@ -128,14 +130,15 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
128130
let rlc_acc = copy_table.rlc_acc;
129131
let rw_counter = copy_table.rw_counter;
130132
let rwc_inc_left = copy_table.rwc_inc_left;
131-
let tag = copy_table.tag;
133+
let tag_bits = copy_table.tag;
132134

133135
// annotate table columns
134136
tx_table.annotate_columns(meta);
135137
rw_table.annotate_columns(meta);
136138
bytecode_table.annotate_columns(meta);
137139
copy_table.annotate_columns(meta);
138140

141+
let tag = BinaryNumberChip::configure(meta, tag_bits, q_enable, None);
139142
let addr_lt_addr_end = LtChip::configure(
140143
meta,
141144
|meta| meta.query_selector(q_step),
@@ -471,6 +474,7 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
471474
q_enable,
472475
addr_lt_addr_end,
473476
copy_table,
477+
copy_table_tag: tag,
474478
tx_table,
475479
rw_table,
476480
bytecode_table,
@@ -526,7 +530,11 @@ impl<F: Field> CopyCircuitConfig<F> {
526530
.zip_eq(table_row)
527531
{
528532
// Leave sr_addr_end and bytes_left unassigned when !is_read
529-
if !is_read && (label == "src_addr_end" || label == "bytes_left") {
533+
// Leave tag_bit columns unassigned, since they are already assigned via
534+
// `tag_chip.assign`
535+
if (!is_read && (label == "src_addr_end" || label == "bytes_left"))
536+
|| label == "tag_bit"
537+
{
530538
} else {
531539
region.assign_advice(
532540
|| format!("{} at row: {}", label, offset),
@@ -605,7 +613,7 @@ impl<F: Field> CopyCircuitConfig<F> {
605613
);
606614
let filler_rows = max_copy_rows - copy_rows_needed - DISABLED_ROWS;
607615

608-
let tag_chip = BinaryNumberChip::construct(self.copy_table.tag);
616+
let tag_chip = BinaryNumberChip::construct(self.copy_table_tag);
609617
let lt_chip = LtChip::construct(self.addr_lt_addr_end);
610618

611619
lt_chip.load(layouter)?;

zkevm-circuits/src/state_circuit.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use constraint_builder::{ConstraintBuilder, Queries};
2626
use eth_types::{Address, Field, Word};
2727
use gadgets::{
2828
batched_is_zero::{BatchedIsZeroChip, BatchedIsZeroConfig},
29-
binary_number::{BinaryNumberChip, BinaryNumberConfig},
29+
binary_number::{BinaryNumberBits, BinaryNumberChip, BinaryNumberConfig},
3030
};
3131
use halo2_proofs::{
3232
circuit::{Layouter, Region, Value},
@@ -107,7 +107,8 @@ impl<F: Field> SubCircuitConfig<F> for StateCircuitConfig<F> {
107107
let lookups = LookupsChip::configure(meta, u8_table, u10_table, u16_table);
108108

109109
let rw_counter = MpiChip::configure(meta, selector, [rw_table.rw_counter], lookups);
110-
let tag = BinaryNumberChip::configure(meta, selector, Some(rw_table.tag));
110+
let bits = BinaryNumberBits::construct(meta);
111+
let tag = BinaryNumberChip::configure(meta, bits, selector, Some(rw_table.tag));
111112
let id = MpiChip::configure(meta, selector, [rw_table.id], lookups);
112113

113114
let address = MpiChip::configure(meta, selector, [rw_table.address], lookups);
@@ -529,8 +530,8 @@ impl<F: Field> SubCircuit<F> for StateCircuit<F> {
529530

530531
fn queries<F: Field>(meta: &mut VirtualCells<'_, F>, c: &StateCircuitConfig<F>) -> Queries<F> {
531532
let first_different_limb = c.lexicographic_ordering.first_different_limb;
532-
let final_bits_sum = meta.query_advice(first_different_limb.bits[3], Rotation::cur())
533-
+ meta.query_advice(first_different_limb.bits[4], Rotation::cur());
533+
let final_bits_sum = meta.query_advice(first_different_limb.bits.0[3], Rotation::cur())
534+
+ meta.query_advice(first_different_limb.bits.0[4], Rotation::cur());
534535
let mpt_update_table_expressions = c.mpt_table.table_exprs(meta);
535536
assert_eq!(mpt_update_table_expressions.len(), 12);
536537

@@ -592,16 +593,17 @@ fn queries<F: Field>(meta: &mut VirtualCells<'_, F>, c: &StateCircuitConfig<F>)
592593
.sort_keys
593594
.tag
594595
.bits
596+
.0
595597
.map(|bit| meta.query_advice(bit, Rotation::cur())),
596598
id: MpiQueries::new(meta, c.sort_keys.id),
597599
// this isn't binary! only 0 if most significant 3 bits are all 0 and at most 1 of the two
598600
// least significant bits is 1.
599601
// TODO: this can mask off just the top 3 bits if you want, since the 4th limb index is
600602
// Address9, which is always 0 for Rw::Stack rows.
601603
is_tag_and_id_unchanged: 4.expr()
602-
* (meta.query_advice(first_different_limb.bits[0], Rotation::cur())
603-
+ meta.query_advice(first_different_limb.bits[1], Rotation::cur())
604-
+ meta.query_advice(first_different_limb.bits[2], Rotation::cur()))
604+
* (meta.query_advice(first_different_limb.bits.0[0], Rotation::cur())
605+
+ meta.query_advice(first_different_limb.bits.0[1], Rotation::cur())
606+
+ meta.query_advice(first_different_limb.bits.0[2], Rotation::cur()))
605607
+ final_bits_sum.clone() * (1.expr() - final_bits_sum),
606608
address: MpiQueries::new(meta, c.sort_keys.address),
607609
storage_key: MpiQueries::new(meta, c.sort_keys.storage_key),
@@ -611,7 +613,7 @@ fn queries<F: Field>(meta: &mut VirtualCells<'_, F>, c: &StateCircuitConfig<F>)
611613
mpt_proof_type: meta.query_advice(c.mpt_proof_type, Rotation::cur()),
612614
lookups: LookupsQueries::new(meta, c.lookups),
613615
first_different_limb: [0, 1, 2, 3]
614-
.map(|idx| meta.query_advice(first_different_limb.bits[idx], Rotation::cur())),
616+
.map(|idx| meta.query_advice(first_different_limb.bits.0[idx], Rotation::cur())),
615617
not_first_access: meta.query_advice(c.not_first_access, Rotation::cur()),
616618
last_access: 1.expr() - meta.query_advice(c.not_first_access, Rotation::next()),
617619
state_root: meta_query_word(meta, c.state_root, Rotation::cur()),

zkevm-circuits/src/state_circuit/dev.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,15 @@ impl AdviceColumn {
119119
Self::RwCounterLimb0 => config.sort_keys.rw_counter.limbs[0],
120120
Self::_RwCounterLimb1 => config.sort_keys.rw_counter.limbs[1],
121121
Self::Tag => config.rw_table.tag,
122-
Self::TagBit0 => config.sort_keys.tag.bits[0],
123-
Self::TagBit1 => config.sort_keys.tag.bits[1],
124-
Self::TagBit2 => config.sort_keys.tag.bits[2],
125-
Self::TagBit3 => config.sort_keys.tag.bits[3],
126-
Self::_LimbIndexBit0 => config.lexicographic_ordering.first_different_limb.bits[0],
127-
Self::LimbIndexBit1 => config.lexicographic_ordering.first_different_limb.bits[1],
128-
Self::LimbIndexBit2 => config.lexicographic_ordering.first_different_limb.bits[2],
129-
Self::_LimbIndexBit3 => config.lexicographic_ordering.first_different_limb.bits[3],
130-
Self::_LimbIndexBit4 => config.lexicographic_ordering.first_different_limb.bits[4],
122+
Self::TagBit0 => config.sort_keys.tag.bits.0[0],
123+
Self::TagBit1 => config.sort_keys.tag.bits.0[1],
124+
Self::TagBit2 => config.sort_keys.tag.bits.0[2],
125+
Self::TagBit3 => config.sort_keys.tag.bits.0[3],
126+
Self::_LimbIndexBit0 => config.lexicographic_ordering.first_different_limb.bits.0[0],
127+
Self::LimbIndexBit1 => config.lexicographic_ordering.first_different_limb.bits.0[1],
128+
Self::LimbIndexBit2 => config.lexicographic_ordering.first_different_limb.bits.0[2],
129+
Self::_LimbIndexBit3 => config.lexicographic_ordering.first_different_limb.bits.0[3],
130+
Self::_LimbIndexBit4 => config.lexicographic_ordering.first_different_limb.bits.0[4],
131131
Self::InitialValueLo => config.initial_value.lo(),
132132
Self::InitialValueHi => config.initial_value.hi(),
133133
Self::IsZero => config.is_non_exist.is_zero,

zkevm-circuits/src/state_circuit/lexicographic_ordering.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::{lookups, param::*, SortKeysConfig};
22
use crate::{evm_circuit::param::N_BYTES_WORD, impl_expr, util::Expr, witness::Rw};
33
use eth_types::{Field, ToBigEndian};
4-
use gadgets::binary_number::{AsBits, BinaryNumberChip, BinaryNumberConfig};
4+
use gadgets::binary_number::{AsBits, BinaryNumberBits, BinaryNumberChip, BinaryNumberConfig};
55
use halo2_proofs::{
66
circuit::{Region, Value},
77
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells},
@@ -110,7 +110,8 @@ impl Config {
110110
powers_of_randomness: [Expression<F>; N_BYTES_WORD - 1],
111111
) -> Self {
112112
let selector = meta.fixed_column();
113-
let first_different_limb = BinaryNumberChip::configure(meta, selector, None);
113+
let bits = BinaryNumberBits::construct(meta);
114+
let first_different_limb = BinaryNumberChip::configure(meta, bits, selector, None);
114115
let limb_difference = meta.advice_column();
115116
let limb_difference_inverse = meta.advice_column();
116117

zkevm-circuits/src/table.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use bus_mapping::circuit_input_builder::{CopyDataType, CopyEvent, CopyStep};
1111
use core::iter::once;
1212
use eth_types::{Field, ToScalar, U256};
1313
use gadgets::{
14-
binary_number::{BinaryNumberChip, BinaryNumberConfig},
14+
binary_number::BinaryNumberBits,
1515
util::{split_u256, split_u256_limb64},
1616
};
1717
use halo2_proofs::{

0 commit comments

Comments
 (0)