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

Commit 4996eb6

Browse files
curryrasuled255
andauthored
fix(zkevm-circuits/begin_tx): add missing constraints (privacy-scaling-explorations#1776)
### Description This PR aims to fix privacy-scaling-explorations#1475 by adding missing constraints. ### Issue Link privacy-scaling-explorations#1475 ### Type of change - [X] Bug fix (non-breaking change which fixes an issue) ### Questions / Need Help 1. Meaning of `value_prev` argument in `account_access_list_write_unchecked`. Why is it sometimes set to 0, and sometimes to bool values such as `is_coinbase_warm` or `is_caller_callee_equal`. What is `is_caller_callee_equal` for? 2. There's expression: https://github.com/privacy-scaling-explorations/zkevm-circuits/blob/main/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs#L163 Why do we have summation - if `is_empty_code_hash.expr()` is enough (as well as `callee_not_exists` is enough)? 3. What's [caller_nonce_hash_bytes](https://github.com/privacy-scaling-explorations/zkevm-circuits/blob/main/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs#L186)? Is it Keccak(sender, nonce) and we have to constrain this exact value? --------- Co-authored-by: Eduard S <[email protected]>
1 parent b82ea41 commit 4996eb6

File tree

3 files changed

+94
-15
lines changed

3 files changed

+94
-15
lines changed

bus-mapping/src/circuit_input_builder/block.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ pub struct Block {
8484
pub block_steps: BlockSteps,
8585
/// Copy events in this block.
8686
pub copy_events: Vec<CopyEvent>,
87-
/// Inputs to the SHA3 opcode
87+
/// Inputs to the SHA3 opcode as well as data hashed during the EVM execution like init code
88+
/// and address creation inputs.
8889
pub sha3_inputs: Vec<Vec<u8>>,
8990
/// Exponentiation events in the block.
9091
pub exp_events: Vec<ExpEvent>,

bus-mapping/src/evm/opcodes/begin_end_tx.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use super::TxExecSteps;
22
use crate::{
3-
circuit_input_builder::{Call, CircuitInputStateRef, ExecState, ExecStep},
3+
circuit_input_builder::{
4+
Call, CircuitInputStateRef, CopyDataType, CopyEvent, ExecState, ExecStep, NumberOrHash,
5+
},
46
operation::{AccountField, AccountOp, CallContextField, TxReceiptField, TxRefundOp, RW},
57
state_db::CodeDB,
68
Error,
@@ -148,6 +150,28 @@ fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result<ExecStep, Erro
148150
stream.append(&nonce_prev);
149151
stream.out().to_vec()
150152
});
153+
// We also hash the call_data as it will be used as init code, and the
154+
// call_context.code_hash needs to be checked against the hash of this call_data.
155+
state.block.sha3_inputs.push(state.tx.call_data.to_vec());
156+
157+
// Append the copy for the CopyCircuit to calculate RLC(call_data) for the keccack lookup
158+
if state.tx.call_data.len() > 0 {
159+
state.push_copy(
160+
&mut exec_step,
161+
CopyEvent {
162+
src_addr: 0,
163+
src_addr_end: state.tx.call_data.len() as u64,
164+
src_type: CopyDataType::TxCalldata,
165+
src_id: NumberOrHash::Number(state.tx.id as usize),
166+
dst_addr: 0,
167+
dst_type: CopyDataType::RlcAcc,
168+
dst_id: NumberOrHash::Number(0),
169+
log_id: None,
170+
rw_counter_start: state.block_ctx.rwc,
171+
bytes: state.tx.call_data.iter().map(|b| (*b, false)).collect(),
172+
},
173+
);
174+
}
151175
}
152176

153177
// There are 4 branches from here.

zkevm-circuits/src/evm_circuit/execution/begin_tx.rs

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,22 @@ use crate::{
1212
},
1313
is_precompiled,
1414
math_gadget::{
15-
ContractCreateGadget, IsEqualWordGadget, IsZeroWordGadget, RangeCheckGadget,
15+
ContractCreateGadget, IsEqualWordGadget, IsZeroGadget, IsZeroWordGadget,
16+
RangeCheckGadget,
1617
},
17-
not,
18+
not, rlc,
1819
tx::{BeginTxHelperGadget, TxDataGadget},
1920
AccountAddress, CachedRegion, Cell, StepRws,
2021
},
2122
witness::{Block, Call, ExecStep, Transaction},
2223
},
23-
table::{AccountFieldTag, BlockContextFieldTag, CallContextFieldTag},
24+
table::{AccountFieldTag, BlockContextFieldTag, CallContextFieldTag, TxContextFieldTag},
2425
util::{
2526
word::{Word32Cell, WordExpr, WordLoHi, WordLoHiCell},
2627
Expr,
2728
},
2829
};
29-
use bus_mapping::state_db::CodeDB;
30+
use bus_mapping::{circuit_input_builder::CopyDataType, state_db::CodeDB};
3031
use eth_types::{evm_types::PRECOMPILE_COUNT, keccak256, Field, OpsIdentity, ToWord, U256};
3132
use halo2_proofs::{
3233
circuit::Value,
@@ -45,6 +46,9 @@ pub(crate) struct BeginTxGadget<F> {
4546
code_hash: WordLoHiCell<F>,
4647
is_empty_code_hash: IsEqualWordGadget<F, WordLoHi<Expression<F>>, WordLoHi<Expression<F>>>,
4748
caller_nonce_hash_bytes: Word32Cell<F>,
49+
calldata_length: Cell<F>,
50+
calldata_length_is_zero: IsZeroGadget<F>,
51+
calldata_rlc: Cell<F>,
4852
create: ContractCreateGadget<F, false>,
4953
callee_not_exists: IsZeroWordGadget<F, WordLoHiCell<F>>,
5054
is_caller_callee_equal: Cell<F>,
@@ -183,12 +187,23 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
183187
);
184188

185189
let caller_nonce_hash_bytes = cb.query_word32();
190+
let calldata_length = cb.query_cell();
191+
let calldata_length_is_zero = cb.is_zero(calldata_length.expr());
192+
let calldata_rlc = cb.query_cell_phase2();
186193
let create = ContractCreateGadget::construct(cb);
187194
cb.require_equal_word(
188195
"tx caller address equivalence",
189196
tx.caller_address.to_word(),
190197
create.caller_address(),
191198
);
199+
200+
cb.require_equal(
201+
"tx nonce equivalence",
202+
tx.nonce.expr(),
203+
create.caller_nonce(),
204+
);
205+
206+
// 1. Handle contract creation transaction.
192207
cb.condition(tx.is_create.expr(), |cb| {
193208
cb.require_equal_word(
194209
"call callee address equivalence",
@@ -201,21 +216,45 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
201216
)
202217
.to_word(),
203218
);
204-
});
205-
cb.require_equal(
206-
"tx nonce equivalence",
207-
tx.nonce.expr(),
208-
create.caller_nonce(),
209-
);
210-
211-
// 1. Handle contract creation transaction.
212-
cb.condition(tx.is_create.expr(), |cb| {
213219
cb.keccak_table_lookup(
214220
create.input_rlc(cb),
215221
create.input_length(),
216222
caller_nonce_hash_bytes.to_word(),
217223
);
218224

225+
cb.tx_context_lookup(
226+
tx_id.expr(),
227+
TxContextFieldTag::CallDataLength,
228+
None,
229+
WordLoHi::from_lo_unchecked(calldata_length.expr()),
230+
);
231+
// If calldata_length > 0 we use the copy circuit to calculate the calldata_rlc for the
232+
// keccack input.
233+
cb.condition(not::expr(calldata_length_is_zero.expr()), |cb| {
234+
cb.copy_table_lookup(
235+
WordLoHi::from_lo_unchecked(tx_id.expr()),
236+
CopyDataType::TxCalldata.expr(),
237+
WordLoHi::zero(),
238+
CopyDataType::RlcAcc.expr(),
239+
0.expr(),
240+
calldata_length.expr(),
241+
0.expr(),
242+
calldata_length.expr(),
243+
calldata_rlc.expr(),
244+
0.expr(),
245+
)
246+
});
247+
// If calldata_length == 0, the copy circuit will not contain any entry, so we skip the
248+
// lookup and instead force calldata_rlc to be 0 for the keccack input.
249+
cb.condition(calldata_length_is_zero.expr(), |cb| {
250+
cb.require_equal("calldata_rlc = 0", calldata_rlc.expr(), 0.expr());
251+
});
252+
cb.keccak_table_lookup(
253+
calldata_rlc.expr(),
254+
calldata_length.expr(),
255+
cb.curr.state.code_hash.to_word(),
256+
);
257+
219258
cb.account_write(
220259
call_callee_address.to_word(),
221260
AccountFieldTag::Nonce,
@@ -434,6 +473,9 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
434473
code_hash,
435474
is_empty_code_hash,
436475
caller_nonce_hash_bytes,
476+
calldata_length,
477+
calldata_length_is_zero,
478+
calldata_rlc,
437479
create,
438480
callee_not_exists,
439481
is_caller_callee_equal,
@@ -522,6 +564,18 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
522564
offset,
523565
U256::from_big_endian(&untrimmed_contract_addr),
524566
)?;
567+
self.calldata_length.assign(
568+
region,
569+
offset,
570+
Value::known(F::from(tx.call_data.len() as u64)),
571+
)?;
572+
self.calldata_length_is_zero
573+
.assign(region, offset, F::from(tx.call_data.len() as u64))?;
574+
let calldata_rlc = region
575+
.challenges()
576+
.keccak_input()
577+
.map(|randomness| rlc::value(tx.call_data.iter().rev(), randomness));
578+
self.calldata_rlc.assign(region, offset, calldata_rlc)?;
525579
self.create.assign(
526580
region,
527581
offset,

0 commit comments

Comments
 (0)