Skip to content
This repository was archived by the owner on Apr 18, 2025. It is now read-only.

Commit 6e6ee0e

Browse files
opt: add another bytecode circuit to improve super circuit capacity (#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 <[email protected]>
1 parent e609b72 commit 6e6ee0e

26 files changed

+895
-102
lines changed

zkevm-circuits/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ paste = "1.0"
5757
default = ["test", "test-circuits", "debug-annotations", "parallel_syn"]
5858
test = ["mock", "bus-mapping/test"]
5959

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

6262
strict-ccc = ["bus-mapping/strict-ccc"]
6363
test-circuits = []
@@ -71,3 +71,5 @@ debug-annotations = []
7171
enable-stack = ["bus-mapping/enable-stack"]
7272
enable-memory = ["bus-mapping/enable-memory"]
7373
enable-storage = ["bus-mapping/enable-storage"]
74+
dual-bytecode = []
75+

zkevm-circuits/src/bytecode_circuit/circuit.rs

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use halo2_proofs::{
1616
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells},
1717
poly::Rotation,
1818
};
19-
use std::vec;
19+
use std::{cmp::max, vec};
2020

2121
use super::{
2222
bytecode_unroller::{unroll_with_codehash, BytecodeRow, UnrolledBytecode},
@@ -1014,6 +1014,22 @@ impl<F: Field> BytecodeCircuit<F> {
10141014
.collect();
10151015
Self::new(bytecodes, bytecode_size)
10161016
}
1017+
1018+
#[cfg(feature = "dual-bytecode")]
1019+
/// Creates bytecode sub circuit from block, is_first_bytecode indicates first or second
1020+
/// sub bytecode circuit.
1021+
pub fn new_from_block_for_dual_circuit(
1022+
block: &witness::Block,
1023+
is_first_bytecode: bool,
1024+
) -> Self {
1025+
let bytecodes: Vec<UnrolledBytecode<F>> = block
1026+
.bytecodes
1027+
.iter()
1028+
.filter(|code| block.is_first_bytecode_circuit(code.0) == is_first_bytecode)
1029+
.map(|(codehash, b)| unroll_with_codehash(*codehash, b.bytes.clone()))
1030+
.collect();
1031+
Self::new(bytecodes, block.circuits_params.max_bytecode)
1032+
}
10171033
}
10181034

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

10391055
/// Return the minimum number of rows required to prove the block
10401056
fn min_num_rows_block(block: &witness::Block) -> (usize, usize) {
1041-
(
1042-
block
1043-
.bytecodes
1044-
.values()
1045-
.map(|bytecode| bytecode.bytes.len() + 1)
1046-
.sum(),
1047-
block.circuits_params.max_bytecode,
1048-
)
1057+
if block.bytecode_map.is_some() {
1058+
// when enable feature "dual-bytecode", get two sets of bytecodes here.
1059+
let (first_bytecodes, second_bytecodes) = block.get_bytecodes_for_dual_sub_circuits();
1060+
let minimum_row: usize = max(
1061+
first_bytecodes
1062+
.iter()
1063+
.map(|bytecode| bytecode.rows_required())
1064+
.sum(),
1065+
second_bytecodes
1066+
.iter()
1067+
.map(|bytecode| bytecode.rows_required())
1068+
.sum(),
1069+
);
1070+
(minimum_row, block.circuits_params.max_bytecode)
1071+
} else {
1072+
(
1073+
block
1074+
.bytecodes
1075+
.values()
1076+
.map(|bytecode| bytecode.rows_required())
1077+
.sum(),
1078+
block.circuits_params.max_bytecode,
1079+
)
1080+
}
10491081
}
10501082

10511083
/// Make the assignments to the TxCircuit

zkevm-circuits/src/copy_circuit.rs

Lines changed: 103 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ use crate::{
4848

4949
use self::copy_gadgets::{
5050
constrain_address, constrain_bytes_left, constrain_event_rlc_acc, constrain_first_last,
51-
constrain_forward_parameters, constrain_is_memory_copy, constrain_is_pad, constrain_mask,
52-
constrain_masked_value, constrain_must_terminate, constrain_non_pad_non_mask,
53-
constrain_rw_counter, constrain_rw_word_complete, constrain_tag, constrain_value_rlc,
54-
constrain_word_index, constrain_word_rlc,
51+
constrain_forward_parameters, constrain_is_first_bytecode_table, constrain_is_memory_copy,
52+
constrain_is_pad, constrain_mask, constrain_masked_value, constrain_must_terminate,
53+
constrain_non_pad_non_mask, constrain_rw_counter, constrain_rw_word_complete, constrain_tag,
54+
constrain_value_rlc, constrain_word_index, constrain_word_rlc,
5555
};
5656

5757
/// The current row.
@@ -122,13 +122,19 @@ pub struct CopyCircuitConfig<F> {
122122
pub is_word_end: IsEqualConfig<F>,
123123
/// non pad and non mask witness to reduce the degree of lookups.
124124
pub non_pad_non_mask: Column<Advice>,
125+
#[cfg(feature = "dual-bytecode")]
126+
/// Whether the bytecode is belong to the first bytecode sub circuit .
127+
pub is_first_bytecode_table: Column<Advice>,
125128
// External tables
126129
/// TxTable
127130
pub tx_table: TxTable,
128131
/// RwTable
129132
pub rw_table: RwTable,
130133
/// BytecodeTable
131134
pub bytecode_table: BytecodeTable,
135+
#[cfg(feature = "dual-bytecode")]
136+
/// BytecodeTable1
137+
pub bytecode_table1: BytecodeTable,
132138
}
133139

134140
/// Circuit configuration arguments
@@ -139,6 +145,9 @@ pub struct CopyCircuitConfigArgs<F: Field> {
139145
pub rw_table: RwTable,
140146
/// BytecodeTable
141147
pub bytecode_table: BytecodeTable,
148+
#[cfg(feature = "dual-bytecode")]
149+
/// BytecodeTable1
150+
pub bytecode_table1: BytecodeTable,
142151
/// CopyTable
143152
pub copy_table: CopyTable,
144153
/// q_enable
@@ -158,6 +167,8 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
158167
tx_table,
159168
rw_table,
160169
bytecode_table,
170+
#[cfg(feature = "dual-bytecode")]
171+
bytecode_table1,
161172
copy_table,
162173
q_enable,
163174
challenges,
@@ -176,6 +187,8 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
176187
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] =
177188
array_init(|_| meta.advice_column());
178189
let is_first = copy_table.is_first;
190+
#[cfg(feature = "dual-bytecode")]
191+
let is_first_bytecode_table = meta.advice_column();
179192
let id = copy_table.id;
180193
let addr = copy_table.addr;
181194
let src_addr_end = copy_table.src_addr_end;
@@ -193,6 +206,9 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
193206
tx_table.annotate_columns(meta);
194207
rw_table.annotate_columns(meta);
195208
bytecode_table.annotate_columns(meta);
209+
#[cfg(feature = "dual-bytecode")]
210+
bytecode_table1.annotate_columns(meta);
211+
196212
copy_table.annotate_columns(meta);
197213

198214
let is_id_unchange = IsEqualChip::configure_with_value_inv(
@@ -384,6 +400,9 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
384400
is_memory_copy,
385401
);
386402
constrain_rw_word_complete(cb, is_last_step, is_rw_word_type.expr(), is_word_end);
403+
404+
#[cfg(feature = "dual-bytecode")]
405+
constrain_is_first_bytecode_table(cb, meta, is_first_bytecode_table, is_last_col);
387406
}
388407

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

466+
// lookup first bytecode table
447467
meta.lookup_any("Bytecode lookup", |meta| {
448-
let cond = meta.query_fixed(q_enable, CURRENT)
468+
#[cfg(feature = "dual-bytecode")]
469+
let is_first_bytecode = meta.query_advice(is_first_bytecode_table, CURRENT);
470+
471+
let mut cond = meta.query_fixed(q_enable, CURRENT)
449472
* meta.query_advice(is_bytecode, CURRENT)
450473
* meta.query_advice(non_pad_non_mask, CURRENT);
451474

475+
#[cfg(feature = "dual-bytecode")]
476+
{
477+
cond = cond * is_first_bytecode.expr();
478+
}
479+
480+
let table_expr = bytecode_table.table_exprs_mini(meta);
481+
452482
vec![
453483
1.expr(),
454484
meta.query_advice(id, CURRENT),
@@ -457,12 +487,37 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
457487
meta.query_advice(value, CURRENT),
458488
]
459489
.into_iter()
460-
.zip_eq(bytecode_table.table_exprs_mini(meta))
490+
.zip_eq(table_expr)
461491
.map(|(arg, table)| (cond.clone() * arg, table))
462492
.collect()
463493
});
464494

465-
meta.lookup_any("rw lookup", |meta| {
495+
// lookup second bytecode table
496+
#[cfg(feature = "dual-bytecode")]
497+
meta.lookup_any("Bytecode1 lookup", |meta| {
498+
let is_first_bytecode = meta.query_advice(is_first_bytecode_table, CURRENT);
499+
500+
let cond = meta.query_fixed(q_enable, CURRENT)
501+
* meta.query_advice(is_bytecode, CURRENT)
502+
* meta.query_advice(non_pad_non_mask, CURRENT)
503+
* (1.expr() - is_first_bytecode);
504+
505+
let table_expr = bytecode_table1.table_exprs_mini(meta);
506+
507+
vec![
508+
1.expr(),
509+
meta.query_advice(id, CURRENT),
510+
BytecodeFieldTag::Byte.expr(),
511+
meta.query_advice(addr, CURRENT),
512+
meta.query_advice(value, CURRENT),
513+
]
514+
.into_iter()
515+
.zip_eq(table_expr)
516+
.map(|(arg, table)| (cond.clone() * arg, table))
517+
.collect()
518+
});
519+
520+
meta.lookup_any("tx lookup for CallData", |meta| {
466521
let cond = meta.query_fixed(q_enable, CURRENT)
467522
* meta.query_advice(is_tx_calldata, CURRENT)
468523
* meta.query_advice(non_pad_non_mask, CURRENT);
@@ -604,9 +659,13 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
604659
is_src_end,
605660
is_word_end,
606661
non_pad_non_mask,
662+
#[cfg(feature = "dual-bytecode")]
663+
is_first_bytecode_table,
607664
tx_table,
608665
rw_table,
609666
bytecode_table,
667+
#[cfg(feature = "dual-bytecode")]
668+
bytecode_table1,
610669
}
611670
}
612671
}
@@ -624,12 +683,11 @@ impl<F: Field> CopyCircuitConfig<F> {
624683
lt_word_end_chip: &IsEqualChip<F>,
625684
challenges: Challenges<Value<F>>,
626685
copy_event: &CopyEvent,
686+
bytecode_map: Option<&BTreeMap<Word, bool>>,
627687
) -> Result<(), Error> {
628-
for (step_idx, (tag, table_row, circuit_row)) in
629-
CopyTable::assignments(copy_event, challenges)
630-
.iter()
631-
.enumerate()
632-
{
688+
let copy_rows = CopyTable::assignments(copy_event, challenges, bytecode_map);
689+
690+
for (step_idx, (tag, table_row, circuit_row)) in copy_rows.iter().enumerate() {
633691
let is_read = step_idx % 2 == 0;
634692

635693
// Copy table assignments
@@ -670,6 +728,8 @@ impl<F: Field> CopyCircuitConfig<F> {
670728
self.mask,
671729
self.front_mask,
672730
self.word_index,
731+
#[cfg(feature = "dual-bytecode")]
732+
self.is_first_bytecode_table,
673733
]
674734
.iter()
675735
.zip_eq(circuit_row)
@@ -787,6 +847,7 @@ impl<F: Field> CopyCircuitConfig<F> {
787847
copy_events: &[CopyEvent],
788848
max_copy_rows: usize,
789849
challenges: Challenges<Value<F>>,
850+
bytecode_map: Option<&BTreeMap<Word, bool>>,
790851
) -> Result<(), Error> {
791852
let copy_rows_needed = copy_events
792853
.iter()
@@ -846,6 +907,7 @@ impl<F: Field> CopyCircuitConfig<F> {
846907
&lt_word_end_chip,
847908
challenges,
848909
copy_event,
910+
bytecode_map,
849911
)?;
850912
log::trace!("offset after {}th copy event: {}", ev_idx, offset);
851913
}
@@ -909,6 +971,14 @@ impl<F: Field> CopyCircuitConfig<F> {
909971
*offset,
910972
|| Value::known(F::zero()),
911973
)?;
974+
#[cfg(feature = "dual-bytecode")]
975+
// is_first_bytecode_table
976+
region.assign_advice(
977+
|| format!("assign is_first_bytecode_table {}", *offset),
978+
self.is_first_bytecode_table,
979+
*offset,
980+
|| Value::known(F::zero()),
981+
)?;
912982
// is_last
913983
region.assign_advice(
914984
|| format!("assign is_last {}", *offset),
@@ -1105,17 +1175,25 @@ pub struct CopyCircuit<F: Field> {
11051175
pub copy_events: Vec<CopyEvent>,
11061176
/// Max number of rows in copy circuit
11071177
pub max_copy_rows: usize,
1178+
/// map for <code_hash, bool> bool value indicates come from first
1179+
/// bytecode circuit.
1180+
pub bytecode_map: Option<BTreeMap<Word, bool>>,
11081181
_marker: PhantomData<F>,
1109-
/// Data for external lookup tables
1182+
/// Data for external lookup tables, currently this field only used for testing.
11101183
pub external_data: ExternalData,
11111184
}
11121185

11131186
impl<F: Field> CopyCircuit<F> {
11141187
/// Return a new CopyCircuit
1115-
pub fn new(copy_events: Vec<CopyEvent>, max_copy_rows: usize) -> Self {
1188+
pub fn new(
1189+
copy_events: Vec<CopyEvent>,
1190+
max_copy_rows: usize,
1191+
bytecode_map: Option<BTreeMap<Word, bool>>,
1192+
) -> Self {
11161193
Self {
11171194
copy_events,
11181195
max_copy_rows,
1196+
bytecode_map,
11191197
_marker: PhantomData,
11201198
external_data: ExternalData::default(),
11211199
}
@@ -1125,11 +1203,13 @@ impl<F: Field> CopyCircuit<F> {
11251203
pub fn new_with_external_data(
11261204
copy_events: Vec<CopyEvent>,
11271205
max_copy_rows: usize,
1206+
bytecode_map: Option<BTreeMap<Word, bool>>,
11281207
external_data: ExternalData,
11291208
) -> Self {
11301209
Self {
11311210
copy_events,
11321211
max_copy_rows,
1212+
bytecode_map,
11331213
_marker: PhantomData,
11341214
external_data,
11351215
}
@@ -1143,6 +1223,7 @@ impl<F: Field> CopyCircuit<F> {
11431223
Self::new(
11441224
block.copy_events.clone(),
11451225
block.circuits_params.max_copy_rows,
1226+
block.bytecode_map.clone(),
11461227
)
11471228
}
11481229
}
@@ -1160,6 +1241,7 @@ impl<F: Field> SubCircuit<F> for CopyCircuit<F> {
11601241
Self::new_with_external_data(
11611242
block.copy_events.clone(),
11621243
block.circuits_params.max_copy_rows,
1244+
block.bytecode_map.clone(),
11631245
ExternalData {
11641246
max_txs: block.circuits_params.max_txs,
11651247
max_calldata: block.circuits_params.max_calldata,
@@ -1190,7 +1272,13 @@ impl<F: Field> SubCircuit<F> for CopyCircuit<F> {
11901272
challenges: &Challenges<Value<F>>,
11911273
layouter: &mut impl Layouter<F>,
11921274
) -> Result<(), Error> {
1193-
config.assign_copy_events(layouter, &self.copy_events, self.max_copy_rows, *challenges)
1275+
config.assign_copy_events(
1276+
layouter,
1277+
&self.copy_events,
1278+
self.max_copy_rows,
1279+
*challenges,
1280+
self.bytecode_map.as_ref(),
1281+
)
11941282
}
11951283
}
11961284

0 commit comments

Comments
 (0)