Skip to content

Commit

Permalink
feat: add shanghai feature and make CI pass with Shanghai geth trac…
Browse files Browse the repository at this point in the history
…es (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]>
  • Loading branch information
2 people authored and johntaiko committed Aug 20, 2023
1 parent 1c8cb90 commit 46c5125
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 44 deletions.
14 changes: 11 additions & 3 deletions bus-mapping/src/evm/opcodes/begin_end_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result<ExecStep, Erro
nonce_prev.into(),
)?;

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

let init_code_gas_cost = if state.tx.is_create() {
// Calculate gas cost of init code for EIP-3860.
(state.tx.tx.call_data.len() as u64 + 31) / 32 * eth_types::evm_types::INIT_CODE_WORD_GAS
} else {
0
};

let intrinsic_gas_cost = if state.tx.is_create() {
GasCost::CREATION_TX.as_u64()
} else {
GasCost::TX.as_u64()
} + state.tx.tx.call_data_gas_cost();
} + state.tx.tx.call_data_gas_cost()
+ init_code_gas_cost;
exec_step.gas_cost = GasCost(intrinsic_gas_cost);

// Get code_hash of callee
Expand Down
4 changes: 2 additions & 2 deletions bus-mapping/src/evm/opcodes/extcodesize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ mod extcodesize_tests {
geth_types::{Account, GethData},
Bytecode, U256,
};
use mock::{TestContext, MOCK_1_ETH, MOCK_ACCOUNTS, MOCK_CODES};
use mock::{TestContext, MOCK_1_ETH, MOCK_ACCOUNTS, MOCK_CODES, MOCK_COINBASE};
use pretty_assertions::assert_eq;

#[test]
Expand Down Expand Up @@ -151,7 +151,7 @@ mod extcodesize_tests {
|mut txs, accs| {
txs[0].to(accs[0].address).from(accs[2].address);
},
|block, _tx| block.number(0xcafeu64),
|block, _tx| block.author(*MOCK_COINBASE).number(0xcafeu64),
)
.unwrap()
.into();
Expand Down
2 changes: 2 additions & 0 deletions eth-types/src/evm_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ impl From<Gas> for u64 {
}
}

/// Once per word of the init code when creating a contract.
pub const INIT_CODE_WORD_GAS: u64 = 2;
/// Quotient for max refund of gas used
pub const MAX_REFUND_QUOTIENT_OF_GAS_USED: usize = 5;
/// Gas stipend when CALL or CALLCODE is attached with value.
Expand Down
5 changes: 1 addition & 4 deletions external-tracer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,15 @@ pub struct TraceConfig {

/// Configuration structure for `logger.Config`
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct LoggerConfig {
/// enable memory capture
#[serde(rename = "EnableMemory")]
pub enable_memory: bool,
/// disable stack capture
#[serde(rename = "DisableStack")]
pub disable_stack: bool,
/// disable storage capture
#[serde(rename = "DisableStorage")]
pub disable_storage: bool,
/// enable return data capture
#[serde(rename = "EnableReturnData")]
pub enable_return_data: bool,
}

Expand Down
31 changes: 15 additions & 16 deletions geth-utils/gethutil/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,22 +130,21 @@ func newUint64(val uint64) *uint64 { return &val }

func Trace(config TraceConfig) ([]*ExecutionResult, error) {
chainConfig := params.ChainConfig{
ChainID: toBigInt(config.ChainID),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: big.NewInt(0),
DAOForkSupport: true,
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
// FIXME: EIP-3860
// ShanghaiTime: newUint64(0),
ChainID: toBigInt(config.ChainID),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: big.NewInt(0),
DAOForkSupport: true,
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
ShanghaiTime: newUint64(0),
MergeNetsplitBlock: nil,
TerminalTotalDifficulty: common.Big0,
TerminalTotalDifficultyPassed: true,
Expand Down
3 changes: 1 addition & 2 deletions geth-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,7 @@ mod test {
]
}"#,
] {
let _trace = trace(config);
assert!(_trace.is_err())
assert!(trace(config).is_err())
}
}
}
6 changes: 4 additions & 2 deletions mock/src/anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use eth_types::{
sign_types::{biguint_to_32bytes_le, ct_option_ok_or, sign as eth_sign, SignData, SECP256K1_Q},
word, Bytes, ToBigEndian, ToLittleEndian, ToWord, Word, U256,
};
use ethers_core::types::Eip1559TransactionRequest;
use ethers_core::types::{transaction::eip2718::TypedTransaction, Eip1559TransactionRequest};
use halo2_proofs::{
arithmetic::Field as _,
halo2curves::{
Expand All @@ -29,7 +29,9 @@ static GX2: Lazy<Word> =
fn fixd_k_sign(anchor_tx: &Transaction, chain_id: u64) -> Result<SignData, eth_types::Error> {
// msg = rlp([nonce, gasPrice, gas, to, value, data, sig_v, r, s])
let req: Eip1559TransactionRequest = anchor_tx.into();
let msg = req.chain_id(chain_id).rlp();
let req = req.chain_id(chain_id);
let req: TypedTransaction = req.into();
let msg = req.rlp();
let msg_hash: [u8; 32] = Keccak256::digest(&msg)
.as_slice()
.to_vec()
Expand Down
2 changes: 1 addition & 1 deletion mock/src/test_ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ impl<const NACC: usize, const NTX: usize> TestContext<NACC, NTX> {
/// account_0_code_account_1_no_code`]. Extra accounts, txs and/or block
/// configs are set as [`Default`].
pub fn simple_ctx_with_bytecode(bytecode: Bytecode) -> Result<TestContext<2, 1>, Error> {
TestContext::<2, 1>::new(
TestContext::new(
None,
account_0_code_account_1_no_code(bytecode),
tx_from_1_to_0,
Expand Down
88 changes: 74 additions & 14 deletions zkevm-circuits/src/evm_circuit/execution/begin_tx.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
evm_circuit::{
execution::ExecutionGadget,
param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_GAS, N_BYTES_WORD},
param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_GAS, N_BYTES_U64, N_BYTES_WORD},
step::ExecutionState,
util::{
and,
Expand All @@ -12,14 +12,16 @@ use crate::{
},
is_precompiled,
math_gadget::{
ContractCreateGadget, IsEqualGadget, IsZeroGadget, MulWordByU64Gadget,
RangeCheckGadget,
ConstantDivisionGadget, ContractCreateGadget, IsEqualGadget, IsZeroGadget,
MulWordByU64Gadget, RangeCheckGadget,
},
not, or, select, CachedRegion, Cell, StepRws, Word,
},
witness::{Block, Call, ExecStep, Transaction},
},
table::{AccountFieldTag, CallContextFieldTag, TxFieldTag as TxContextFieldTag},
table::{
AccountFieldTag, BlockContextFieldTag, CallContextFieldTag, TxFieldTag as TxContextFieldTag,
},
util::Expr,
};
use eth_types::{evm_types::GasCost, Field, ToLittleEndian, ToScalar};
Expand All @@ -42,6 +44,7 @@ pub(crate) struct BeginTxGadget<F> {
tx_value: Word<F>,
tx_call_data_length: Cell<F>,
tx_call_data_gas_cost: Cell<F>,
tx_call_data_word_length: ConstantDivisionGadget<F, N_BYTES_U64>,
reversion_info: ReversionInfo<F>,
sufficient_gas_left: RangeCheckGadget<F, N_BYTES_GAS>,
transfer_with_gas_fee: TransferWithGasFeeGadget<F>,
Expand All @@ -51,6 +54,12 @@ pub(crate) struct BeginTxGadget<F> {
create: ContractCreateGadget<F, false>,
callee_not_exists: IsZeroGadget<F>,
is_caller_callee_equal: Cell<F>,
// EIP-3651 (Warm COINBASE)
coinbase: Cell<F>,
// Caller, callee and a list addresses are added to the access list before
// coinbase, and may be duplicate.
// <https://github.com/ethereum/go-ethereum/blob/604e215d1bb070dff98fb76aa965064c74e3633f/core/state/statedb.go#LL1119C9-L1119C9>
is_coinbase_warm: Cell<F>,
}

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

let tx_call_data_word_length =
ConstantDivisionGadget::construct(cb, tx_call_data_length.expr() + 31.expr(), 32);

// Calculate gas cost of init code for EIP-3860.
let init_code_gas_cost = select::expr(
tx_is_create.expr(),
tx_call_data_word_length.quotient().expr()
* eth_types::evm_types::INIT_CODE_WORD_GAS.expr(),
0.expr(),
);

// TODO: Take gas cost of access list (EIP 2930) into consideration.
// Use intrinsic gas
let intrinsic_gas_cost = select::expr(
tx_is_create.expr(),
GasCost::CREATION_TX.expr(),
GasCost::TX.expr(),
) + tx_call_data_gas_cost.expr();
) + tx_call_data_gas_cost.expr()
+ init_code_gas_cost;

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

// Query coinbase address.
let coinbase = cb.query_cell();
let is_coinbase_warm = cb.query_bool();
cb.block_lookup(BlockContextFieldTag::Coinbase.expr(), None, coinbase.expr());
cb.account_access_list_write(
tx_id.expr(),
coinbase.expr(),
1.expr(),
is_coinbase_warm.expr(),
None,
); // rwc_delta += 1

// Read code_hash of callee
let phase2_code_hash = cb.query_cell_phase2();
let is_empty_code_hash =
Expand Down Expand Up @@ -261,8 +294,9 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
// - Write CallContext IsPersistent
// - Write CallContext IsSuccess
// - Write Account (Caller) Nonce
// - Write TxAccessListAccount
// - Write TxAccessListAccount
// - Write TxAccessListAccount (Caller)
// - Write TxAccessListAccount (Callee)
// - Write TxAccessListAccount (Coinbase) for EIP-3651
// - a TransferWithGasFeeGadget
// - Write Account (Callee) Nonce (Reversible)
// - Write CallContext Depth
Expand All @@ -278,7 +312,7 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
// - Write CallContext IsRoot
// - Write CallContext IsCreate
// - Write CallContext CodeHash
rw_counter: Delta(21.expr() + transfer_with_gas_fee.rw_delta()),
rw_counter: Delta(22.expr() + transfer_with_gas_fee.rw_delta()),
call_id: To(call_id.expr()),
is_root: To(true.expr()),
is_create: To(tx_is_create.expr()),
Expand Down Expand Up @@ -317,11 +351,12 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
// - Write CallContext IsPersistent
// - Write CallContext IsSuccess
// - Write Account Nonce
// - Write TxAccessListAccount
// - Write TxAccessListAccount
// - Write TxAccessListAccount (Caller)
// - Write TxAccessListAccount (Callee)
// - Write TxAccessListAccount (Coinbase) for EIP-3651
// - Read Account CodeHash
// - a TransferWithGasFeeGadget
rw_counter: Delta(8.expr() + transfer_with_gas_fee.rw_delta()),
rw_counter: Delta(9.expr() + transfer_with_gas_fee.rw_delta()),
call_id: To(call_id.expr()),
..StepStateTransition::any()
});
Expand Down Expand Up @@ -361,8 +396,9 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
// - Write CallContext IsPersistent
// - Write CallContext IsSuccess
// - Write Account Nonce
// - Write TxAccessListAccount
// - Write TxAccessListAccount
// - Write TxAccessListAccount (Caller)
// - Write TxAccessListAccount (Callee)
// - Write TxAccessListAccount (Coinbase) for EIP-3651
// - Read Account CodeHash
// - a TransferWithGasFeeGadget
// - Write CallContext Depth
Expand All @@ -378,7 +414,7 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
// - Write CallContext IsRoot
// - Write CallContext IsCreate
// - Write CallContext CodeHash
rw_counter: Delta(21.expr() + transfer_with_gas_fee.rw_delta()),
rw_counter: Delta(22.expr() + transfer_with_gas_fee.rw_delta()),
call_id: To(call_id.expr()),
is_root: To(true.expr()),
is_create: To(tx_is_create.expr()),
Expand All @@ -405,6 +441,7 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
tx_value,
tx_call_data_length,
tx_call_data_gas_cost,
tx_call_data_word_length,
reversion_info,
sufficient_gas_left,
transfer_with_gas_fee,
Expand All @@ -414,6 +451,8 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
create,
callee_not_exists,
is_caller_callee_equal,
coinbase,
is_coinbase_warm,
}
}

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

let mut rws = StepRws::new(block, step);
rws.offset_add(7);

let is_coinbase_warm = rws.next().tx_access_list_value_pair().1;
let mut callee_code_hash = zero;
if !is_precompiled(&tx.callee_address) && !tx.is_create {
callee_code_hash = rws.next().account_value_pair().1;
Expand Down Expand Up @@ -503,6 +544,8 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
offset,
Value::known(F::from(tx.call_data_gas_cost)),
)?;
self.tx_call_data_word_length
.assign(region, offset, tx.call_data_length as u128 + 31)?;
self.reversion_info.assign(
region,
offset,
Expand Down Expand Up @@ -556,6 +599,23 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
None,
)?;

self.coinbase.assign(
region,
offset,
Value::known(
block
.context
.coinbase
.to_scalar()
.expect("unexpected Address -> Scalar conversion failure"),
),
)?;
self.is_coinbase_warm.assign(
region,
offset,
Value::known(F::from(is_coinbase_warm as u64)),
)?;

Ok(())
}
}
Expand Down

0 comments on commit 46c5125

Please sign in to comment.