Skip to content

Commit

Permalink
opt: add another bytecode circuit to improve super circuit capacity (#…
Browse files Browse the repository at this point in the history
…1368)

* add bytecode_table1 in evm circuit

* update input_exprs

* SameContextGadget add is_first_bytecode_table cell

* update some opcodes

* modify assign_exec_step

* assign_exec_step with block, call

* adjust block, call input position

* update opcodes

* update jump for destination lookup

* update jumpi

* fix jumpi

* super circuit add  bytecode circuit

* copy event add flag is_first_bytecode_circuit

* rename copy event/table new field

* modify table_row addr position

* constrain copy circuit new column

* conditionally lookup second bytecode table in copy circuit

* copy circuit refactor two table lookup into one

* revert CopyEvent change

* add algorithm find_two_closest_subset, remove old

* apply find_two_closest_subset for block bytecodes

* modify copy circuit assign with bytecode_map

* add one more bytecode_table into evm circuit

* supercircuit: get each sub circuit bytecodes and construct

* add feature: dual_bytecode

* updates per feature

* fix feature related

* update param per new feature

* change program_counter_offset to expression

* fix stop gadget byte lookup

* remove comment

* add feature dual_bytecode to scroll

* fix types

* fix copy circuit test

* fix copytable lookup

* add feature control

* update err msg

* fix return_revert opcode lookup

* update call lookup per feature

* update common error gadget

* add BytecodeLengthGadget handle bytelen lookup

* update invalidjumpi

* fix CommonErrorGadget

* update create/return

* misc updates

* temp fix error jump

* mark feature

* refactor & fix extcodecopy regarding bytelength gadget

* fix return_data test

* add missing feature

* update ErrorPrecompileFailed

* fix extcodecopy non existing address

* revise lookup_opcode

* modify copy circuit try to fix

* remove feature on err msg

* clean up debug info

* extract helpers to get bytecode and map info

* fix type

* modify BytecodeCircuit::<Fr>::min_num_rows_block to return max rows

* fix default build

* use BytecodeLengthGadget

* add feature

* use lookup_opcode

* remvoe is_code parameter

* remove outdated

* update comment

* refactor lookup_opcode_with_push_rlc helper

* fix some clippy, remaining some

* fix clippy

* refactor is_first_bytecode_table

* update helper names

* add BytecodeLookupGadget for opcodes that don't use samecontext gadget

* create/return use BytecodeLookupGadget

* error precompile use BytecodeLookupGadget

* BytecodeLengthGadget adds is_first_bytecode_table cell

* stop use BytecodeLookupGadget

* remove feature for bytecode_map

* fix bytecode_map

* samecontext etc remove feature

* remove some feature cfg

* byte length gadget assign etc.

* fix clippy

* update bytecode len gadget constraint

* fix fmt

* remove wrong constraint

* fix failures

* remove program_counter_offset and local rand test pass

* figure out why combination bytecode table not work and remove comment

* add rows_required helper

* try use greedy_simple_partition

* tmp remove dual_bytecode from scroll feature list

* restore dual_bytecode in scroll feature list

* revise comment

* greedy_simple_partition with generic type

* replace log::error

* add comment to explain stable sort helper

* rename feature dual-bytecode

* minor updates

* some minor change per review

* clean up extcodecopy's is_first_bytecode_table

* clean up

---------

Co-authored-by: Rohit Narurkar <rohit.narurkar@proton.me>
  • Loading branch information
DreamWuGit and roynalnaruto authored Sep 6, 2024
1 parent e609b72 commit 6e6ee0e
Show file tree
Hide file tree
Showing 26 changed files with 895 additions and 102 deletions.
4 changes: 3 additions & 1 deletion zkevm-circuits/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ paste = "1.0"
default = ["test", "test-circuits", "debug-annotations", "parallel_syn"]
test = ["mock", "bus-mapping/test"]

scroll = ["bus-mapping/scroll", "eth-types/scroll", "mock?/scroll", "zktrie", "poseidon-codehash"]
scroll = ["bus-mapping/scroll", "eth-types/scroll", "mock?/scroll", "zktrie", "poseidon-codehash", "dual-bytecode"]

strict-ccc = ["bus-mapping/strict-ccc"]
test-circuits = []
Expand All @@ -71,3 +71,5 @@ debug-annotations = []
enable-stack = ["bus-mapping/enable-stack"]
enable-memory = ["bus-mapping/enable-memory"]
enable-storage = ["bus-mapping/enable-storage"]
dual-bytecode = []

50 changes: 41 additions & 9 deletions zkevm-circuits/src/bytecode_circuit/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use halo2_proofs::{
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells},
poly::Rotation,
};
use std::vec;
use std::{cmp::max, vec};

use super::{
bytecode_unroller::{unroll_with_codehash, BytecodeRow, UnrolledBytecode},
Expand Down Expand Up @@ -1014,6 +1014,22 @@ impl<F: Field> BytecodeCircuit<F> {
.collect();
Self::new(bytecodes, bytecode_size)
}

#[cfg(feature = "dual-bytecode")]
/// Creates bytecode sub circuit from block, is_first_bytecode indicates first or second
/// sub bytecode circuit.
pub fn new_from_block_for_dual_circuit(
block: &witness::Block,
is_first_bytecode: bool,
) -> Self {
let bytecodes: Vec<UnrolledBytecode<F>> = block
.bytecodes
.iter()
.filter(|code| block.is_first_bytecode_circuit(code.0) == is_first_bytecode)
.map(|(codehash, b)| unroll_with_codehash(*codehash, b.bytes.clone()))
.collect();
Self::new(bytecodes, block.circuits_params.max_bytecode)
}
}

impl<F: Field> SubCircuit<F> for BytecodeCircuit<F> {
Expand All @@ -1038,14 +1054,30 @@ impl<F: Field> SubCircuit<F> for BytecodeCircuit<F> {

/// Return the minimum number of rows required to prove the block
fn min_num_rows_block(block: &witness::Block) -> (usize, usize) {
(
block
.bytecodes
.values()
.map(|bytecode| bytecode.bytes.len() + 1)
.sum(),
block.circuits_params.max_bytecode,
)
if block.bytecode_map.is_some() {
// when enable feature "dual-bytecode", get two sets of bytecodes here.
let (first_bytecodes, second_bytecodes) = block.get_bytecodes_for_dual_sub_circuits();
let minimum_row: usize = max(
first_bytecodes
.iter()
.map(|bytecode| bytecode.rows_required())
.sum(),
second_bytecodes
.iter()
.map(|bytecode| bytecode.rows_required())
.sum(),
);
(minimum_row, block.circuits_params.max_bytecode)
} else {
(
block
.bytecodes
.values()
.map(|bytecode| bytecode.rows_required())
.sum(),
block.circuits_params.max_bytecode,
)
}
}

/// Make the assignments to the TxCircuit
Expand Down
118 changes: 103 additions & 15 deletions zkevm-circuits/src/copy_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ use crate::{

use self::copy_gadgets::{
constrain_address, constrain_bytes_left, constrain_event_rlc_acc, constrain_first_last,
constrain_forward_parameters, constrain_is_memory_copy, constrain_is_pad, constrain_mask,
constrain_masked_value, constrain_must_terminate, constrain_non_pad_non_mask,
constrain_rw_counter, constrain_rw_word_complete, constrain_tag, constrain_value_rlc,
constrain_word_index, constrain_word_rlc,
constrain_forward_parameters, constrain_is_first_bytecode_table, constrain_is_memory_copy,
constrain_is_pad, constrain_mask, constrain_masked_value, constrain_must_terminate,
constrain_non_pad_non_mask, constrain_rw_counter, constrain_rw_word_complete, constrain_tag,
constrain_value_rlc, constrain_word_index, constrain_word_rlc,
};

/// The current row.
Expand Down Expand Up @@ -122,13 +122,19 @@ pub struct CopyCircuitConfig<F> {
pub is_word_end: IsEqualConfig<F>,
/// non pad and non mask witness to reduce the degree of lookups.
pub non_pad_non_mask: Column<Advice>,
#[cfg(feature = "dual-bytecode")]
/// Whether the bytecode is belong to the first bytecode sub circuit .
pub is_first_bytecode_table: Column<Advice>,
// External tables
/// TxTable
pub tx_table: TxTable,
/// RwTable
pub rw_table: RwTable,
/// BytecodeTable
pub bytecode_table: BytecodeTable,
#[cfg(feature = "dual-bytecode")]
/// BytecodeTable1
pub bytecode_table1: BytecodeTable,
}

/// Circuit configuration arguments
Expand All @@ -139,6 +145,9 @@ pub struct CopyCircuitConfigArgs<F: Field> {
pub rw_table: RwTable,
/// BytecodeTable
pub bytecode_table: BytecodeTable,
#[cfg(feature = "dual-bytecode")]
/// BytecodeTable1
pub bytecode_table1: BytecodeTable,
/// CopyTable
pub copy_table: CopyTable,
/// q_enable
Expand All @@ -158,6 +167,8 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
tx_table,
rw_table,
bytecode_table,
#[cfg(feature = "dual-bytecode")]
bytecode_table1,
copy_table,
q_enable,
challenges,
Expand All @@ -176,6 +187,8 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
let [is_pad, is_tx_calldata, is_bytecode, is_memory, is_memory_copy, is_tx_log, is_access_list_address, is_access_list_storage_key] =
array_init(|_| meta.advice_column());
let is_first = copy_table.is_first;
#[cfg(feature = "dual-bytecode")]
let is_first_bytecode_table = meta.advice_column();
let id = copy_table.id;
let addr = copy_table.addr;
let src_addr_end = copy_table.src_addr_end;
Expand All @@ -193,6 +206,9 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
tx_table.annotate_columns(meta);
rw_table.annotate_columns(meta);
bytecode_table.annotate_columns(meta);
#[cfg(feature = "dual-bytecode")]
bytecode_table1.annotate_columns(meta);

copy_table.annotate_columns(meta);

let is_id_unchange = IsEqualChip::configure_with_value_inv(
Expand Down Expand Up @@ -384,6 +400,9 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
is_memory_copy,
);
constrain_rw_word_complete(cb, is_last_step, is_rw_word_type.expr(), is_word_end);

#[cfg(feature = "dual-bytecode")]
constrain_is_first_bytecode_table(cb, meta, is_first_bytecode_table, is_last_col);
}

cb.gate(meta.query_fixed(q_enable, CURRENT))
Expand Down Expand Up @@ -444,11 +463,22 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
.collect()
});

// lookup first bytecode table
meta.lookup_any("Bytecode lookup", |meta| {
let cond = meta.query_fixed(q_enable, CURRENT)
#[cfg(feature = "dual-bytecode")]
let is_first_bytecode = meta.query_advice(is_first_bytecode_table, CURRENT);

let mut cond = meta.query_fixed(q_enable, CURRENT)
* meta.query_advice(is_bytecode, CURRENT)
* meta.query_advice(non_pad_non_mask, CURRENT);

#[cfg(feature = "dual-bytecode")]
{
cond = cond * is_first_bytecode.expr();
}

let table_expr = bytecode_table.table_exprs_mini(meta);

vec![
1.expr(),
meta.query_advice(id, CURRENT),
Expand All @@ -457,12 +487,37 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
meta.query_advice(value, CURRENT),
]
.into_iter()
.zip_eq(bytecode_table.table_exprs_mini(meta))
.zip_eq(table_expr)
.map(|(arg, table)| (cond.clone() * arg, table))
.collect()
});

meta.lookup_any("rw lookup", |meta| {
// lookup second bytecode table
#[cfg(feature = "dual-bytecode")]
meta.lookup_any("Bytecode1 lookup", |meta| {
let is_first_bytecode = meta.query_advice(is_first_bytecode_table, CURRENT);

let cond = meta.query_fixed(q_enable, CURRENT)
* meta.query_advice(is_bytecode, CURRENT)
* meta.query_advice(non_pad_non_mask, CURRENT)
* (1.expr() - is_first_bytecode);

let table_expr = bytecode_table1.table_exprs_mini(meta);

vec![
1.expr(),
meta.query_advice(id, CURRENT),
BytecodeFieldTag::Byte.expr(),
meta.query_advice(addr, CURRENT),
meta.query_advice(value, CURRENT),
]
.into_iter()
.zip_eq(table_expr)
.map(|(arg, table)| (cond.clone() * arg, table))
.collect()
});

meta.lookup_any("tx lookup for CallData", |meta| {
let cond = meta.query_fixed(q_enable, CURRENT)
* meta.query_advice(is_tx_calldata, CURRENT)
* meta.query_advice(non_pad_non_mask, CURRENT);
Expand Down Expand Up @@ -604,9 +659,13 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
is_src_end,
is_word_end,
non_pad_non_mask,
#[cfg(feature = "dual-bytecode")]
is_first_bytecode_table,
tx_table,
rw_table,
bytecode_table,
#[cfg(feature = "dual-bytecode")]
bytecode_table1,
}
}
}
Expand All @@ -624,12 +683,11 @@ impl<F: Field> CopyCircuitConfig<F> {
lt_word_end_chip: &IsEqualChip<F>,
challenges: Challenges<Value<F>>,
copy_event: &CopyEvent,
bytecode_map: Option<&BTreeMap<Word, bool>>,
) -> Result<(), Error> {
for (step_idx, (tag, table_row, circuit_row)) in
CopyTable::assignments(copy_event, challenges)
.iter()
.enumerate()
{
let copy_rows = CopyTable::assignments(copy_event, challenges, bytecode_map);

for (step_idx, (tag, table_row, circuit_row)) in copy_rows.iter().enumerate() {
let is_read = step_idx % 2 == 0;

// Copy table assignments
Expand Down Expand Up @@ -670,6 +728,8 @@ impl<F: Field> CopyCircuitConfig<F> {
self.mask,
self.front_mask,
self.word_index,
#[cfg(feature = "dual-bytecode")]
self.is_first_bytecode_table,
]
.iter()
.zip_eq(circuit_row)
Expand Down Expand Up @@ -787,6 +847,7 @@ impl<F: Field> CopyCircuitConfig<F> {
copy_events: &[CopyEvent],
max_copy_rows: usize,
challenges: Challenges<Value<F>>,
bytecode_map: Option<&BTreeMap<Word, bool>>,
) -> Result<(), Error> {
let copy_rows_needed = copy_events
.iter()
Expand Down Expand Up @@ -846,6 +907,7 @@ impl<F: Field> CopyCircuitConfig<F> {
&lt_word_end_chip,
challenges,
copy_event,
bytecode_map,
)?;
log::trace!("offset after {}th copy event: {}", ev_idx, offset);
}
Expand Down Expand Up @@ -909,6 +971,14 @@ impl<F: Field> CopyCircuitConfig<F> {
*offset,
|| Value::known(F::zero()),
)?;
#[cfg(feature = "dual-bytecode")]
// is_first_bytecode_table
region.assign_advice(
|| format!("assign is_first_bytecode_table {}", *offset),
self.is_first_bytecode_table,
*offset,
|| Value::known(F::zero()),
)?;
// is_last
region.assign_advice(
|| format!("assign is_last {}", *offset),
Expand Down Expand Up @@ -1105,17 +1175,25 @@ pub struct CopyCircuit<F: Field> {
pub copy_events: Vec<CopyEvent>,
/// Max number of rows in copy circuit
pub max_copy_rows: usize,
/// map for <code_hash, bool> bool value indicates come from first
/// bytecode circuit.
pub bytecode_map: Option<BTreeMap<Word, bool>>,
_marker: PhantomData<F>,
/// Data for external lookup tables
/// Data for external lookup tables, currently this field only used for testing.
pub external_data: ExternalData,
}

impl<F: Field> CopyCircuit<F> {
/// Return a new CopyCircuit
pub fn new(copy_events: Vec<CopyEvent>, max_copy_rows: usize) -> Self {
pub fn new(
copy_events: Vec<CopyEvent>,
max_copy_rows: usize,
bytecode_map: Option<BTreeMap<Word, bool>>,
) -> Self {
Self {
copy_events,
max_copy_rows,
bytecode_map,
_marker: PhantomData,
external_data: ExternalData::default(),
}
Expand All @@ -1125,11 +1203,13 @@ impl<F: Field> CopyCircuit<F> {
pub fn new_with_external_data(
copy_events: Vec<CopyEvent>,
max_copy_rows: usize,
bytecode_map: Option<BTreeMap<Word, bool>>,
external_data: ExternalData,
) -> Self {
Self {
copy_events,
max_copy_rows,
bytecode_map,
_marker: PhantomData,
external_data,
}
Expand All @@ -1143,6 +1223,7 @@ impl<F: Field> CopyCircuit<F> {
Self::new(
block.copy_events.clone(),
block.circuits_params.max_copy_rows,
block.bytecode_map.clone(),
)
}
}
Expand All @@ -1160,6 +1241,7 @@ impl<F: Field> SubCircuit<F> for CopyCircuit<F> {
Self::new_with_external_data(
block.copy_events.clone(),
block.circuits_params.max_copy_rows,
block.bytecode_map.clone(),
ExternalData {
max_txs: block.circuits_params.max_txs,
max_calldata: block.circuits_params.max_calldata,
Expand Down Expand Up @@ -1190,7 +1272,13 @@ impl<F: Field> SubCircuit<F> for CopyCircuit<F> {
challenges: &Challenges<Value<F>>,
layouter: &mut impl Layouter<F>,
) -> Result<(), Error> {
config.assign_copy_events(layouter, &self.copy_events, self.max_copy_rows, *challenges)
config.assign_copy_events(
layouter,
&self.copy_events,
self.max_copy_rows,
*challenges,
self.bytecode_map.as_ref(),
)
}
}

Expand Down
Loading

0 comments on commit 6e6ee0e

Please sign in to comment.