Skip to content

Commit 46c5125

Browse files
silathdiirlispc
authored andcommitted
feat: add shanghai feature and make CI pass with Shanghai geth traces (privacy-scaling-explorations#1424)
1. Add Shanghai related fields to chain config in geth-utils. 2. EIP-3651 (Warm COINBASE): add a new access-list write for coinbase to Begin TX. 3. Part of EIP-3860 (Limit and meter initcode): only add gas cost of init code to Begin TX (missing gas cost changes in Create and OOG Create). Issue privacy-scaling-explorations#1362 Local related PRs: scroll-tech#497 scroll-tech#500 scroll-tech#507 Reference previous PR: privacy-scaling-explorations#1361 - [x] Breaking change (fix or feature that would cause existing functionality to not work as expected) Unit test cases of CI could pass with Shanghai geth traces. --------- Co-authored-by: Zhang Zhuo <[email protected]>
1 parent 1c8cb90 commit 46c5125

File tree

9 files changed

+111
-44
lines changed

9 files changed

+111
-44
lines changed

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result<ExecStep, Erro
5959
nonce_prev.into(),
6060
)?;
6161

62-
// Add caller and callee into access list
63-
for address in [call.caller_address, call.address] {
62+
// Add caller, callee and coinbase (for EIP-3651) to access list.
63+
for address in [call.caller_address, call.address, state.block.coinbase] {
6464
let is_warm_prev = !state.sdb.add_account_to_access_list(address);
6565
state.tx_accesslist_account_write(
6666
&mut exec_step,
@@ -71,11 +71,19 @@ fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result<ExecStep, Erro
7171
)?;
7272
}
7373

74+
let init_code_gas_cost = if state.tx.is_create() {
75+
// Calculate gas cost of init code for EIP-3860.
76+
(state.tx.tx.call_data.len() as u64 + 31) / 32 * eth_types::evm_types::INIT_CODE_WORD_GAS
77+
} else {
78+
0
79+
};
80+
7481
let intrinsic_gas_cost = if state.tx.is_create() {
7582
GasCost::CREATION_TX.as_u64()
7683
} else {
7784
GasCost::TX.as_u64()
78-
} + state.tx.tx.call_data_gas_cost();
85+
} + state.tx.tx.call_data_gas_cost()
86+
+ init_code_gas_cost;
7987
exec_step.gas_cost = GasCost(intrinsic_gas_cost);
8088

8189
// Get code_hash of callee

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ mod extcodesize_tests {
9797
geth_types::{Account, GethData},
9898
Bytecode, U256,
9999
};
100-
use mock::{TestContext, MOCK_1_ETH, MOCK_ACCOUNTS, MOCK_CODES};
100+
use mock::{TestContext, MOCK_1_ETH, MOCK_ACCOUNTS, MOCK_CODES, MOCK_COINBASE};
101101
use pretty_assertions::assert_eq;
102102

103103
#[test]
@@ -151,7 +151,7 @@ mod extcodesize_tests {
151151
|mut txs, accs| {
152152
txs[0].to(accs[0].address).from(accs[2].address);
153153
},
154-
|block, _tx| block.number(0xcafeu64),
154+
|block, _tx| block.author(*MOCK_COINBASE).number(0xcafeu64),
155155
)
156156
.unwrap()
157157
.into();

eth-types/src/evm_types.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ impl From<Gas> for u64 {
7272
}
7373
}
7474

75+
/// Once per word of the init code when creating a contract.
76+
pub const INIT_CODE_WORD_GAS: u64 = 2;
7577
/// Quotient for max refund of gas used
7678
pub const MAX_REFUND_QUOTIENT_OF_GAS_USED: usize = 5;
7779
/// Gas stipend when CALL or CALLCODE is attached with value.

external-tracer/src/lib.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,15 @@ pub struct TraceConfig {
2929

3030
/// Configuration structure for `logger.Config`
3131
#[derive(Debug, Clone, Serialize)]
32+
#[serde(rename_all = "PascalCase")]
3233
pub struct LoggerConfig {
3334
/// enable memory capture
34-
#[serde(rename = "EnableMemory")]
3535
pub enable_memory: bool,
3636
/// disable stack capture
37-
#[serde(rename = "DisableStack")]
3837
pub disable_stack: bool,
3938
/// disable storage capture
40-
#[serde(rename = "DisableStorage")]
4139
pub disable_storage: bool,
4240
/// enable return data capture
43-
#[serde(rename = "EnableReturnData")]
4441
pub enable_return_data: bool,
4542
}
4643

geth-utils/gethutil/trace.go

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -130,22 +130,21 @@ func newUint64(val uint64) *uint64 { return &val }
130130

131131
func Trace(config TraceConfig) ([]*ExecutionResult, error) {
132132
chainConfig := params.ChainConfig{
133-
ChainID: toBigInt(config.ChainID),
134-
HomesteadBlock: big.NewInt(0),
135-
DAOForkBlock: big.NewInt(0),
136-
DAOForkSupport: true,
137-
EIP150Block: big.NewInt(0),
138-
EIP155Block: big.NewInt(0),
139-
EIP158Block: big.NewInt(0),
140-
ByzantiumBlock: big.NewInt(0),
141-
ConstantinopleBlock: big.NewInt(0),
142-
PetersburgBlock: big.NewInt(0),
143-
IstanbulBlock: big.NewInt(0),
144-
MuirGlacierBlock: big.NewInt(0),
145-
BerlinBlock: big.NewInt(0),
146-
LondonBlock: big.NewInt(0),
147-
// FIXME: EIP-3860
148-
// ShanghaiTime: newUint64(0),
133+
ChainID: toBigInt(config.ChainID),
134+
HomesteadBlock: big.NewInt(0),
135+
DAOForkBlock: big.NewInt(0),
136+
DAOForkSupport: true,
137+
EIP150Block: big.NewInt(0),
138+
EIP155Block: big.NewInt(0),
139+
EIP158Block: big.NewInt(0),
140+
ByzantiumBlock: big.NewInt(0),
141+
ConstantinopleBlock: big.NewInt(0),
142+
PetersburgBlock: big.NewInt(0),
143+
IstanbulBlock: big.NewInt(0),
144+
MuirGlacierBlock: big.NewInt(0),
145+
BerlinBlock: big.NewInt(0),
146+
LondonBlock: big.NewInt(0),
147+
ShanghaiTime: newUint64(0),
149148
MergeNetsplitBlock: nil,
150149
TerminalTotalDifficulty: common.Big0,
151150
TerminalTotalDifficultyPassed: true,

geth-utils/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,7 @@ mod test {
152152
]
153153
}"#,
154154
] {
155-
let _trace = trace(config);
156-
assert!(_trace.is_err())
155+
assert!(trace(config).is_err())
157156
}
158157
}
159158
}

mock/src/anchor.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use eth_types::{
77
sign_types::{biguint_to_32bytes_le, ct_option_ok_or, sign as eth_sign, SignData, SECP256K1_Q},
88
word, Bytes, ToBigEndian, ToLittleEndian, ToWord, Word, U256,
99
};
10-
use ethers_core::types::Eip1559TransactionRequest;
10+
use ethers_core::types::{transaction::eip2718::TypedTransaction, Eip1559TransactionRequest};
1111
use halo2_proofs::{
1212
arithmetic::Field as _,
1313
halo2curves::{
@@ -29,7 +29,9 @@ static GX2: Lazy<Word> =
2929
fn fixd_k_sign(anchor_tx: &Transaction, chain_id: u64) -> Result<SignData, eth_types::Error> {
3030
// msg = rlp([nonce, gasPrice, gas, to, value, data, sig_v, r, s])
3131
let req: Eip1559TransactionRequest = anchor_tx.into();
32-
let msg = req.chain_id(chain_id).rlp();
32+
let req = req.chain_id(chain_id);
33+
let req: TypedTransaction = req.into();
34+
let msg = req.rlp();
3335
let msg_hash: [u8; 32] = Keccak256::digest(&msg)
3436
.as_slice()
3537
.to_vec()

mock/src/test_ctx.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ impl<const NACC: usize, const NTX: usize> TestContext<NACC, NTX> {
286286
/// account_0_code_account_1_no_code`]. Extra accounts, txs and/or block
287287
/// configs are set as [`Default`].
288288
pub fn simple_ctx_with_bytecode(bytecode: Bytecode) -> Result<TestContext<2, 1>, Error> {
289-
TestContext::<2, 1>::new(
289+
TestContext::new(
290290
None,
291291
account_0_code_account_1_no_code(bytecode),
292292
tx_from_1_to_0,

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

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{
22
evm_circuit::{
33
execution::ExecutionGadget,
4-
param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_GAS, N_BYTES_WORD},
4+
param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_GAS, N_BYTES_U64, N_BYTES_WORD},
55
step::ExecutionState,
66
util::{
77
and,
@@ -12,14 +12,16 @@ use crate::{
1212
},
1313
is_precompiled,
1414
math_gadget::{
15-
ContractCreateGadget, IsEqualGadget, IsZeroGadget, MulWordByU64Gadget,
16-
RangeCheckGadget,
15+
ConstantDivisionGadget, ContractCreateGadget, IsEqualGadget, IsZeroGadget,
16+
MulWordByU64Gadget, RangeCheckGadget,
1717
},
1818
not, or, select, CachedRegion, Cell, StepRws, Word,
1919
},
2020
witness::{Block, Call, ExecStep, Transaction},
2121
},
22-
table::{AccountFieldTag, CallContextFieldTag, TxFieldTag as TxContextFieldTag},
22+
table::{
23+
AccountFieldTag, BlockContextFieldTag, CallContextFieldTag, TxFieldTag as TxContextFieldTag,
24+
},
2325
util::Expr,
2426
};
2527
use eth_types::{evm_types::GasCost, Field, ToLittleEndian, ToScalar};
@@ -42,6 +44,7 @@ pub(crate) struct BeginTxGadget<F> {
4244
tx_value: Word<F>,
4345
tx_call_data_length: Cell<F>,
4446
tx_call_data_gas_cost: Cell<F>,
47+
tx_call_data_word_length: ConstantDivisionGadget<F, N_BYTES_U64>,
4548
reversion_info: ReversionInfo<F>,
4649
sufficient_gas_left: RangeCheckGadget<F, N_BYTES_GAS>,
4750
transfer_with_gas_fee: TransferWithGasFeeGadget<F>,
@@ -51,6 +54,12 @@ pub(crate) struct BeginTxGadget<F> {
5154
create: ContractCreateGadget<F, false>,
5255
callee_not_exists: IsZeroGadget<F>,
5356
is_caller_callee_equal: Cell<F>,
57+
// EIP-3651 (Warm COINBASE)
58+
coinbase: Cell<F>,
59+
// Caller, callee and a list addresses are added to the access list before
60+
// coinbase, and may be duplicate.
61+
// <https://github.com/ethereum/go-ethereum/blob/604e215d1bb070dff98fb76aa965064c74e3633f/core/state/statedb.go#LL1119C9-L1119C9>
62+
is_coinbase_warm: Cell<F>,
5463
}
5564

5665
impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
@@ -126,13 +135,25 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
126135
let mul_gas_fee_by_gas =
127136
MulWordByU64Gadget::construct(cb, tx_gas_price.clone(), tx_gas.expr());
128137

138+
let tx_call_data_word_length =
139+
ConstantDivisionGadget::construct(cb, tx_call_data_length.expr() + 31.expr(), 32);
140+
141+
// Calculate gas cost of init code for EIP-3860.
142+
let init_code_gas_cost = select::expr(
143+
tx_is_create.expr(),
144+
tx_call_data_word_length.quotient().expr()
145+
* eth_types::evm_types::INIT_CODE_WORD_GAS.expr(),
146+
0.expr(),
147+
);
148+
129149
// TODO: Take gas cost of access list (EIP 2930) into consideration.
130150
// Use intrinsic gas
131151
let intrinsic_gas_cost = select::expr(
132152
tx_is_create.expr(),
133153
GasCost::CREATION_TX.expr(),
134154
GasCost::TX.expr(),
135-
) + tx_call_data_gas_cost.expr();
155+
) + tx_call_data_gas_cost.expr()
156+
+ init_code_gas_cost;
136157

137158
// Check gas_left is sufficient
138159
let gas_left = tx_gas.expr() - intrinsic_gas_cost;
@@ -157,6 +178,18 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
157178
None,
158179
); // rwc_delta += 1
159180

181+
// Query coinbase address.
182+
let coinbase = cb.query_cell();
183+
let is_coinbase_warm = cb.query_bool();
184+
cb.block_lookup(BlockContextFieldTag::Coinbase.expr(), None, coinbase.expr());
185+
cb.account_access_list_write(
186+
tx_id.expr(),
187+
coinbase.expr(),
188+
1.expr(),
189+
is_coinbase_warm.expr(),
190+
None,
191+
); // rwc_delta += 1
192+
160193
// Read code_hash of callee
161194
let phase2_code_hash = cb.query_cell_phase2();
162195
let is_empty_code_hash =
@@ -261,8 +294,9 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
261294
// - Write CallContext IsPersistent
262295
// - Write CallContext IsSuccess
263296
// - Write Account (Caller) Nonce
264-
// - Write TxAccessListAccount
265-
// - Write TxAccessListAccount
297+
// - Write TxAccessListAccount (Caller)
298+
// - Write TxAccessListAccount (Callee)
299+
// - Write TxAccessListAccount (Coinbase) for EIP-3651
266300
// - a TransferWithGasFeeGadget
267301
// - Write Account (Callee) Nonce (Reversible)
268302
// - Write CallContext Depth
@@ -278,7 +312,7 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
278312
// - Write CallContext IsRoot
279313
// - Write CallContext IsCreate
280314
// - Write CallContext CodeHash
281-
rw_counter: Delta(21.expr() + transfer_with_gas_fee.rw_delta()),
315+
rw_counter: Delta(22.expr() + transfer_with_gas_fee.rw_delta()),
282316
call_id: To(call_id.expr()),
283317
is_root: To(true.expr()),
284318
is_create: To(tx_is_create.expr()),
@@ -317,11 +351,12 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
317351
// - Write CallContext IsPersistent
318352
// - Write CallContext IsSuccess
319353
// - Write Account Nonce
320-
// - Write TxAccessListAccount
321-
// - Write TxAccessListAccount
354+
// - Write TxAccessListAccount (Caller)
355+
// - Write TxAccessListAccount (Callee)
356+
// - Write TxAccessListAccount (Coinbase) for EIP-3651
322357
// - Read Account CodeHash
323358
// - a TransferWithGasFeeGadget
324-
rw_counter: Delta(8.expr() + transfer_with_gas_fee.rw_delta()),
359+
rw_counter: Delta(9.expr() + transfer_with_gas_fee.rw_delta()),
325360
call_id: To(call_id.expr()),
326361
..StepStateTransition::any()
327362
});
@@ -361,8 +396,9 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
361396
// - Write CallContext IsPersistent
362397
// - Write CallContext IsSuccess
363398
// - Write Account Nonce
364-
// - Write TxAccessListAccount
365-
// - Write TxAccessListAccount
399+
// - Write TxAccessListAccount (Caller)
400+
// - Write TxAccessListAccount (Callee)
401+
// - Write TxAccessListAccount (Coinbase) for EIP-3651
366402
// - Read Account CodeHash
367403
// - a TransferWithGasFeeGadget
368404
// - Write CallContext Depth
@@ -378,7 +414,7 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
378414
// - Write CallContext IsRoot
379415
// - Write CallContext IsCreate
380416
// - Write CallContext CodeHash
381-
rw_counter: Delta(21.expr() + transfer_with_gas_fee.rw_delta()),
417+
rw_counter: Delta(22.expr() + transfer_with_gas_fee.rw_delta()),
382418
call_id: To(call_id.expr()),
383419
is_root: To(true.expr()),
384420
is_create: To(tx_is_create.expr()),
@@ -405,6 +441,7 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
405441
tx_value,
406442
tx_call_data_length,
407443
tx_call_data_gas_cost,
444+
tx_call_data_word_length,
408445
reversion_info,
409446
sufficient_gas_left,
410447
transfer_with_gas_fee,
@@ -414,6 +451,8 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
414451
create,
415452
callee_not_exists,
416453
is_caller_callee_equal,
454+
coinbase,
455+
is_coinbase_warm,
417456
}
418457
}
419458

@@ -431,6 +470,8 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
431470

432471
let mut rws = StepRws::new(block, step);
433472
rws.offset_add(7);
473+
474+
let is_coinbase_warm = rws.next().tx_access_list_value_pair().1;
434475
let mut callee_code_hash = zero;
435476
if !is_precompiled(&tx.callee_address) && !tx.is_create {
436477
callee_code_hash = rws.next().account_value_pair().1;
@@ -503,6 +544,8 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
503544
offset,
504545
Value::known(F::from(tx.call_data_gas_cost)),
505546
)?;
547+
self.tx_call_data_word_length
548+
.assign(region, offset, tx.call_data_length as u128 + 31)?;
506549
self.reversion_info.assign(
507550
region,
508551
offset,
@@ -556,6 +599,23 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
556599
None,
557600
)?;
558601

602+
self.coinbase.assign(
603+
region,
604+
offset,
605+
Value::known(
606+
block
607+
.context
608+
.coinbase
609+
.to_scalar()
610+
.expect("unexpected Address -> Scalar conversion failure"),
611+
),
612+
)?;
613+
self.is_coinbase_warm.assign(
614+
region,
615+
offset,
616+
Value::known(F::from(is_coinbase_warm as u64)),
617+
)?;
618+
559619
Ok(())
560620
}
561621
}

0 commit comments

Comments
 (0)