Skip to content
This repository was archived by the owner on Jul 5, 2024. It is now read-only.

feat: add shanghai feature and make CI pass with Shanghai geth traces #1424

Merged
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
} else {
GasCost::TX
} + state.tx.tx.call_data_gas_cost();
} + state.tx.tx.call_data_gas_cost()
+ init_code_gas_cost;
exec_step.gas_cost = 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 @@ -14,6 +14,8 @@ pub use opcode_ids::OpcodeId;
pub use stack::{Stack, StackAddress};
pub use storage::Storage;

/// 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 @@ -27,18 +27,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
37 changes: 22 additions & 15 deletions geth-utils/gethutil/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,21 +128,24 @@ 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),
EIP150Hash: common.Hash{},
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),
ChainID: toBigInt(config.ChainID),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: big.NewInt(0),
DAOForkSupport: true,
EIP150Block: big.NewInt(0),
EIP150Hash: common.Hash{},
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),
TerminalTotalDifficulty: big.NewInt(0),
TerminalTotalDifficultyPassed: true,
}

var txsGasLimit uint64
Expand Down Expand Up @@ -180,6 +183,9 @@ func Trace(config TraceConfig) ([]*ExecutionResult, error) {
return nil, fmt.Errorf("txs total gas: %d Exceeds block gas limit: %d", txsGasLimit, blockGasLimit)
}

// For opcode PREVRANDAO
randao := common.BigToHash(toBigInt(config.Block.Difficulty)) // TODO: fix

blockCtx := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
Expand All @@ -195,6 +201,7 @@ func Trace(config TraceConfig) ([]*ExecutionResult, error) {
BlockNumber: toBigInt(config.Block.Number),
Time: toBigInt(config.Block.Timestamp).Uint64(),
Difficulty: toBigInt(config.Block.Difficulty),
Random: &randao,
BaseFee: toBigInt(config.Block.BaseFee),
GasLimit: blockGasLimit,
}
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 @@ -127,13 +136,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 @@ -158,6 +179,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 @@ -262,8 +295,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 @@ -279,7 +313,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 @@ -318,11 +352,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 @@ -362,8 +397,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 @@ -379,7 +415,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 @@ -406,6 +442,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 @@ -415,6 +452,8 @@ impl<F: Field> ExecutionGadget<F> for BeginTxGadget<F> {
create,
callee_not_exists,
is_caller_callee_equal,
coinbase,
is_coinbase_warm,
}
}

Expand All @@ -432,6 +471,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 @@ -504,6 +545,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 @@ -557,6 +600,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