From e72107e3d7810948a3a570ae7723aa62b1358399 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Fri, 15 Mar 2024 17:17:25 +0100
Subject: [PATCH 01/25] simplified gas accounting layer
---
consensus/beacon/consensus.go | 7 +-
consensus/ethash/consensus.go | 11 ---
core/state/access_witness.go | 124 ++++++++++++++++++++++++---------
core/state/accesslist/types.go | 44 ++++++++++++
core/state/statedb.go | 2 +-
core/state_processor.go | 4 +-
core/state_processor_test.go | 2 +-
core/state_transition.go | 12 +---
core/vm/eips.go | 18 +++++
core/vm/evm.go | 35 ++++------
core/vm/gas_table.go | 14 ----
core/vm/instructions.go | 105 ++++------------------------
core/vm/interpreter.go | 2 +-
core/vm/jump_table.go | 1 +
core/vm/operations_acl.go | 13 ----
core/vm/operations_verkle.go | 98 ++++++++++++++++++++++++++
params/config.go | 3 +
params/protocol_params.go | 1 +
18 files changed, 293 insertions(+), 203 deletions(-)
create mode 100644 core/state/accesslist/types.go
create mode 100644 core/vm/operations_verkle.go
diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go
index 35a7ed2b56d0..ad8894cf4db0 100644
--- a/consensus/beacon/consensus.go
+++ b/consensus/beacon/consensus.go
@@ -32,7 +32,6 @@ import (
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/utils"
"github.com/ethereum/go-verkle"
- "github.com/holiman/uint256"
)
// Proof-of-stake protocol constants.
@@ -357,11 +356,7 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
state.AddBalance(w.Address, amount)
// The returned gas is not charged
- state.Witness().TouchAddressOnWriteAndComputeGas(w.Address[:], uint256.Int{}, utils.VersionLeafKey)
- state.Witness().TouchAddressOnWriteAndComputeGas(w.Address[:], uint256.Int{}, utils.BalanceLeafKey)
- state.Witness().TouchAddressOnWriteAndComputeGas(w.Address[:], uint256.Int{}, utils.NonceLeafKey)
- state.Witness().TouchAddressOnWriteAndComputeGas(w.Address[:], uint256.Int{}, utils.CodeKeccakLeafKey)
- state.Witness().TouchAddressOnWriteAndComputeGas(w.Address[:], uint256.Int{}, utils.CodeSizeLeafKey)
+ state.Witness().TouchFullAccount(w.Address[:], true)
}
}
diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go
index 44aec25c1216..720e49b6e0d8 100644
--- a/consensus/ethash/consensus.go
+++ b/consensus/ethash/consensus.go
@@ -33,8 +33,6 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
- "github.com/ethereum/go-ethereum/trie/utils"
- "github.com/holiman/uint256"
"golang.org/x/crypto/sha3"
)
@@ -568,19 +566,10 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header
r.Div(r, big8)
// This should not happen, but it's useful for replay tests
- if config.IsPrague(header.Number, header.Time) {
- state.Witness().TouchAddressOnReadAndComputeGas(uncle.Coinbase.Bytes(), uint256.Int{}, utils.BalanceLeafKey)
- }
state.AddBalance(uncle.Coinbase, r)
r.Div(blockReward, big32)
reward.Add(reward, r)
}
- if config.IsPrague(header.Number, header.Time) {
- state.Witness().TouchAddressOnReadAndComputeGas(header.Coinbase.Bytes(), uint256.Int{}, utils.BalanceLeafKey)
- state.Witness().TouchAddressOnReadAndComputeGas(header.Coinbase.Bytes(), uint256.Int{}, utils.VersionLeafKey)
- state.Witness().TouchAddressOnReadAndComputeGas(header.Coinbase.Bytes(), uint256.Int{}, utils.NonceLeafKey)
- state.Witness().TouchAddressOnReadAndComputeGas(header.Coinbase.Bytes(), uint256.Int{}, utils.CodeKeccakLeafKey)
- }
state.AddBalance(header.Coinbase, reward)
}
diff --git a/core/state/access_witness.go b/core/state/access_witness.go
index 696da15fd1ef..590ef2558dbc 100644
--- a/core/state/access_witness.go
+++ b/core/state/access_witness.go
@@ -18,6 +18,7 @@ package state
import (
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie/utils"
"github.com/holiman/uint256"
@@ -88,27 +89,33 @@ func (aw *AccessWitness) Copy() *AccessWitness {
return naw
}
+func (aw *AccessWitness) TouchFullAccount(addr []byte, isWrite bool) uint64 {
+ var gas uint64
+ for i := utils.VersionLeafKey; i <= utils.CodeSizeLeafKey; i++ {
+ gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, byte(i), isWrite)
+ }
+ return gas
+}
+
func (aw *AccessWitness) TouchAndChargeProofOfAbsence(addr []byte) uint64 {
var gas uint64
- gas += aw.TouchAddressOnReadAndComputeGas(addr, zeroTreeIndex, utils.VersionLeafKey)
- gas += aw.TouchAddressOnReadAndComputeGas(addr, zeroTreeIndex, utils.BalanceLeafKey)
- gas += aw.TouchAddressOnReadAndComputeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey)
- gas += aw.TouchAddressOnReadAndComputeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey)
- gas += aw.TouchAddressOnReadAndComputeGas(addr, zeroTreeIndex, utils.NonceLeafKey)
+ for i := utils.VersionLeafKey; i <= utils.CodeSizeLeafKey; i++ {
+ gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, byte(i), false)
+ }
return gas
}
func (aw *AccessWitness) TouchAndChargeMessageCall(addr []byte) uint64 {
var gas uint64
- gas += aw.TouchAddressOnReadAndComputeGas(addr, zeroTreeIndex, utils.VersionLeafKey)
- gas += aw.TouchAddressOnReadAndComputeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey)
+ gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, false)
+ gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, false)
return gas
}
func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []byte) uint64 {
var gas uint64
- gas += aw.TouchAddressOnWriteAndComputeGas(callerAddr, zeroTreeIndex, utils.BalanceLeafKey)
- gas += aw.TouchAddressOnWriteAndComputeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey)
+ gas += aw.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
+ gas += aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
return gas
}
@@ -116,10 +123,10 @@ func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []by
// a contract creation
func (aw *AccessWitness) TouchAndChargeContractCreateInit(addr []byte, createSendsValue bool) uint64 {
var gas uint64
- gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.VersionLeafKey)
- gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.NonceLeafKey)
+ gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, true)
+ gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, true)
if createSendsValue {
- gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.BalanceLeafKey)
+ gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, true)
}
return gas
}
@@ -129,20 +136,16 @@ func (aw *AccessWitness) TouchAndChargeContractCreateInit(addr []byte, createSen
// the tree
func (aw *AccessWitness) TouchAndChargeContractCreateCompleted(addr []byte) uint64 {
var gas uint64
- gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.VersionLeafKey)
- gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.BalanceLeafKey)
- gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey)
- gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey)
- gas += aw.TouchAddressOnWriteAndComputeGas(addr, zeroTreeIndex, utils.NonceLeafKey)
+ for i := utils.VersionLeafKey; i <= utils.CodeSizeLeafKey; i++ {
+ gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, byte(i), true)
+ }
return gas
}
func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) uint64 {
- aw.TouchAddressOnReadAndComputeGas(originAddr, zeroTreeIndex, utils.VersionLeafKey)
- aw.TouchAddressOnReadAndComputeGas(originAddr, zeroTreeIndex, utils.CodeSizeLeafKey)
- aw.TouchAddressOnReadAndComputeGas(originAddr, zeroTreeIndex, utils.CodeKeccakLeafKey)
- aw.TouchAddressOnWriteAndComputeGas(originAddr, zeroTreeIndex, utils.NonceLeafKey)
- aw.TouchAddressOnWriteAndComputeGas(originAddr, zeroTreeIndex, utils.BalanceLeafKey)
+ for i := utils.VersionLeafKey; i <= utils.CodeSizeLeafKey; i++ {
+ aw.touchAddressAndChargeGas(originAddr, zeroTreeIndex, byte(i), i == utils.BalanceLeafKey || i == utils.NonceLeafKey)
+ }
// Kaustinen note: we're currently experimenting with stop chargin gas for the origin address
// so simple transfer still take 21000 gas. This is to potentially avoid breaking existing tooling.
@@ -152,14 +155,14 @@ func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) uint64 {
}
func (aw *AccessWitness) TouchTxExistingAndComputeGas(targetAddr []byte, sendsValue bool) uint64 {
- aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.VersionLeafKey)
- aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.CodeSizeLeafKey)
- aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.CodeKeccakLeafKey)
- aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.NonceLeafKey)
+ aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.VersionLeafKey, false)
+ aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeSizeLeafKey, false)
+ aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeKeccakLeafKey, false)
+ aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.NonceLeafKey, false)
if sendsValue {
- aw.TouchAddressOnWriteAndComputeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey)
+ aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
} else {
- aw.TouchAddressOnReadAndComputeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey)
+ aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, false)
}
// Kaustinen note: we're currently experimenting with stop chargin gas for the origin address
@@ -169,12 +172,14 @@ func (aw *AccessWitness) TouchTxExistingAndComputeGas(targetAddr []byte, sendsVa
return 0
}
-func (aw *AccessWitness) TouchAddressOnWriteAndComputeGas(addr []byte, treeIndex uint256.Int, subIndex byte) uint64 {
- return aw.touchAddressAndChargeGas(addr, treeIndex, subIndex, true)
+func (aw *AccessWitness) TouchAddressOnWriteAndComputeGas(addr []byte, slot common.Hash) uint64 {
+ treeIndex, subIndex := utils.GetTreeKeyStorageSlotTreeIndexes(slot.Bytes())
+ return aw.touchAddressAndChargeGas(addr, *treeIndex, subIndex, true)
}
-func (aw *AccessWitness) TouchAddressOnReadAndComputeGas(addr []byte, treeIndex uint256.Int, subIndex byte) uint64 {
- return aw.touchAddressAndChargeGas(addr, treeIndex, subIndex, false)
+func (aw *AccessWitness) TouchAddressOnReadAndComputeGas(addr []byte, slot common.Hash) uint64 {
+ treeIndex, subIndex := utils.GetTreeKeyStorageSlotTreeIndexes(slot.Bytes())
+ return aw.touchAddressAndChargeGas(addr, *treeIndex, subIndex, false)
}
func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256.Int, subIndex byte, isWrite bool) uint64 {
@@ -259,3 +264,58 @@ func newChunkAccessKey(branchKey branchAccessKey, leafKey byte) chunkAccessKey {
lk.leafKey = leafKey
return lk
}
+
+// touchCodeChunksRangeOnReadAndChargeGas is a helper function to touch every chunk in a code range and charge witness gas costs
+func (aw *AccessWitness) TouchCodeChunksRangeOnReadAndChargeGas(contractAddr []byte, startPC, size uint64, codeLen uint64) uint64 {
+ // note that in the case where the copied code is outside the range of the
+ // contract code but touches the last leaf with contract code in it,
+ // we don't include the last leaf of code in the AccessWitness. The
+ // reason that we do not need the last leaf is the account's code size
+ // is already in the AccessWitness so a stateless verifier can see that
+ // the code from the last leaf is not needed.
+ if (codeLen == 0 && size == 0) || startPC > codeLen {
+ return 0
+ }
+
+ endPC := startPC + size
+ if endPC > codeLen {
+ endPC = codeLen
+ }
+ if endPC > 0 {
+ endPC -= 1 // endPC is the last bytecode that will be touched.
+ }
+
+ var statelessGasCharged uint64
+ for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ {
+ treeIndex := *uint256.NewInt((chunkNumber + 128) / 256)
+ subIndex := byte((chunkNumber + 128) % 256)
+ gas := aw.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, false)
+ var overflow bool
+ statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, gas)
+ if overflow {
+ panic("overflow when adding gas")
+ }
+ }
+
+ return statelessGasCharged
+}
+
+func (aw *AccessWitness) TouchVersion(addr []byte, isWrite bool) uint64 {
+ return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, isWrite)
+}
+
+func (aw *AccessWitness) TouchBalance(addr []byte, isWrite bool) uint64 {
+ return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, isWrite)
+}
+
+func (aw *AccessWitness) TouchNonce(addr []byte, isWrite bool) uint64 {
+ return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, isWrite)
+}
+
+func (aw *AccessWitness) TouchCodeSize(addr []byte, isWrite bool) uint64 {
+ return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, isWrite)
+}
+
+func (aw *AccessWitness) TouchCodeHash(addr []byte, isWrite bool) uint64 {
+ return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, isWrite)
+}
diff --git a/core/state/accesslist/types.go b/core/state/accesslist/types.go
new file mode 100644
index 000000000000..53d5e03198b7
--- /dev/null
+++ b/core/state/accesslist/types.go
@@ -0,0 +1,44 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package accesslist
+
+// AccessType specifies if a state access is for reading or writing. This
+// is necessary because verkle charges different costs depending on the
+// type of access.
+type AccessType bool
+
+var (
+ AccessListRead = AccessType(false)
+ AccessListWrite = AccessType(true)
+)
+
+// ItemType is used in verkle mode to specify what item of an account
+// is being accessed. This is necessary because verkle charges gas each
+// time a new account item is accessed.
+type ItemType uint64
+
+const (
+ Version = ItemType(1 << iota)
+ Balance
+ Nonce
+ CodeHash
+ CodeSize
+ LastHeaderItem
+)
+
+const ALAllItems = Version | Balance | Nonce | CodeSize | CodeHash
+const ALNoItems = ItemType(0)
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 1b47746458b5..181dbfdd23d8 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -1372,7 +1372,7 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er
// - Add coinbase to access list (EIP-3651)
// - Reset transient storage (EIP-1153)
func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
- if rules.IsBerlin {
+ if rules.IsEIP2929 {
// Clear out any leftover from previous executions
al := newAccessList()
s.accessList = al
diff --git a/core/state_processor.go b/core/state_processor.go
index 6961a873602e..a30cfab60285 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -35,7 +35,6 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
- "github.com/ethereum/go-ethereum/trie/utils"
tutils "github.com/ethereum/go-ethereum/trie/utils"
"github.com/ethereum/go-verkle"
"github.com/holiman/uint256"
@@ -382,6 +381,5 @@ func ProcessParentBlockHash(statedb *state.StateDB, prevNumber uint64, prevHash
var key common.Hash
binary.BigEndian.PutUint64(key[24:], ringIndex)
statedb.SetState(params.HistoryStorageAddress, key, prevHash)
- index, suffix := utils.GetTreeKeyStorageSlotTreeIndexes(key[:])
- statedb.Witness().TouchAddressOnWriteAndComputeGas(params.HistoryStorageAddress[:], *index, suffix)
+ statedb.Witness().TouchAddressOnWriteAndComputeGas(params.HistoryStorageAddress[:], key)
}
diff --git a/core/state_processor_test.go b/core/state_processor_test.go
index a5fd70aada65..b72147d70a98 100644
--- a/core/state_processor_test.go
+++ b/core/state_processor_test.go
@@ -486,7 +486,7 @@ func TestProcessVerkle(t *testing.T) {
txCost1 := params.TxGas
txCost2 := params.TxGas
contractCreationCost := intrinsicContractCreationGas + uint64(5600+700+700+700 /* creation with value */ +2739 /* execution costs */)
- codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(5600+700 /* creation */ +302044 /* execution costs */)
+ codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(5600+700 /* creation */ +198644 /* execution costs */)
blockGasUsagesExpected := []uint64{
txCost1*2 + txCost2,
txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas,
diff --git a/core/state_transition.go b/core/state_transition.go
index 969e7a75fb9b..49b35dd81087 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -29,8 +29,6 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/trie/utils"
- "github.com/holiman/uint256"
)
// ExecutionResult includes all output after executing given evm
@@ -405,7 +403,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
}
st.gasRemaining -= gas
- if rules.IsPrague {
+ if rules.IsEIP4762 {
targetAddr := msg.To
originAddr := msg.From
@@ -480,12 +478,8 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
st.state.AddBalance(st.evm.Context.Coinbase, fee)
// add the coinbase to the witness iff the fee is greater than 0
- if rules.IsPrague && fee.Sign() != 0 {
- st.evm.Accesses.TouchAddressOnWriteAndComputeGas(st.evm.Context.Coinbase[:], uint256.Int{}, utils.VersionLeafKey)
- st.evm.Accesses.TouchAddressOnWriteAndComputeGas(st.evm.Context.Coinbase[:], uint256.Int{}, utils.BalanceLeafKey)
- st.evm.Accesses.TouchAddressOnWriteAndComputeGas(st.evm.Context.Coinbase[:], uint256.Int{}, utils.NonceLeafKey)
- st.evm.Accesses.TouchAddressOnWriteAndComputeGas(st.evm.Context.Coinbase[:], uint256.Int{}, utils.CodeKeccakLeafKey)
- st.evm.Accesses.TouchAddressOnWriteAndComputeGas(st.evm.Context.Coinbase[:], uint256.Int{}, utils.CodeSizeLeafKey)
+ if rules.IsEIP4762 && fee.Sign() != 0 {
+ st.evm.Accesses.TouchFullAccount(st.evm.Context.Coinbase[:], true)
}
}
diff --git a/core/vm/eips.go b/core/vm/eips.go
index 704c1ce12745..2703c8316db2 100644
--- a/core/vm/eips.go
+++ b/core/vm/eips.go
@@ -37,6 +37,7 @@ var activators = map[int]func(*JumpTable){
1884: enable1884,
1344: enable1344,
1153: enable1153,
+ 4762: enable4762,
}
// EnableEIP enables the given EIP on the config.
@@ -303,3 +304,20 @@ func enable6780(jt *JumpTable) {
maxStack: maxStack(1, 0),
}
}
+
+func enable4762(jt *JumpTable) {
+ jt[SSTORE].constantGas = 0
+ jt[SSTORE].dynamicGas = gasSStore4762
+ jt[SLOAD].constantGas = 0
+ jt[SLOAD].dynamicGas = gasSLoad4762
+ jt[BALANCE].dynamicGas = gasBalance4762
+ jt[EXTCODESIZE].dynamicGas = gasExtCodeSize4762
+ jt[EXTCODEHASH].dynamicGas = gasExtCodeHash4762
+ jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP4762
+ jt[CREATE].constantGas = params.CreateNGasEip4762
+ jt[CREATE2].constantGas = params.CreateNGasEip4762
+ jt[CALL].dynamicGas = gasCallEIP4762
+ jt[CALLCODE].dynamicGas = gasCallCodeEIP4762
+ jt[STATICCALL].dynamicGas = gasStaticCallEIP4762
+ jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP4762
+}
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 782fc6d56740..02817fbda40b 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -178,20 +178,6 @@ func (evm *EVM) SetBlockContext(blockCtx BlockContext) {
evm.chainRules = evm.chainConfig.Rules(num, blockCtx.Random != nil, timestamp)
}
-// tryConsumeGas tries to subtract gas from gasPool, setting the result in gasPool
-// if subtracting more gas than remains in gasPool, set gasPool = 0 and return false
-// otherwise, do the subtraction setting the result in gasPool and return true
-func tryConsumeGas(gasPool *uint64, gas uint64) bool {
- // XXX check this is still needed as a func
- if *gasPool < gas {
- *gasPool = 0
- return false
- }
-
- *gasPool -= gas
- return true
-}
-
// Call executes the contract associated with the addr with the given input as
// parameters. It also handles any necessary value transfer required and takes
// the necessary steps to create accounts and reverses the state in case of an
@@ -211,11 +197,18 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
var creation bool
if !evm.StateDB.Exist(addr) {
- if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
- if evm.chainRules.IsPrague {
- // proof of absence
- tryConsumeGas(&gas, evm.Accesses.TouchAndChargeProofOfAbsence(addr.Bytes()))
+ if !isPrecompile && evm.chainRules.IsEIP4762 {
+ // add proof of absence to witness
+ wgas := evm.Accesses.TouchAndChargeProofOfAbsence(addr.Bytes())
+ if gas < wgas {
+ evm.StateDB.RevertToSnapshot(snapshot)
+ return nil, 0, ErrOutOfGas
}
+ gas -= wgas
+ }
+
+ if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
+
// Calling a non existing account, don't do anything, but ping the tracer
if debug {
if evm.depth == 0 {
@@ -463,7 +456,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
evm.StateDB.SetNonce(caller.Address(), nonce+1)
// We add this to the access list _before_ taking a snapshot. Even if the creation fails,
// the access-list change should not be rolled back
- if evm.chainRules.IsBerlin {
+ if evm.chainRules.IsEIP2929 {
evm.StateDB.AddAddressToAccessList(address)
}
// Ensure there's no existing contract already at the designated address
@@ -519,9 +512,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
}
- if err == nil && evm.chainRules.IsPrague {
+ if err == nil && evm.chainRules.IsEIP4762 {
if len(ret) > 0 {
- touchCodeChunksRangeOnReadAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), evm.Accesses)
+ evm.Accesses.TouchCodeChunksRangeOnReadAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)))
}
if !contract.UseGas(evm.Accesses.TouchAndChargeContractCreateCompleted(address.Bytes()[:])) {
err = ErrOutOfGas
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index 50ebf8c05b64..64b6df5cb454 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -23,8 +23,6 @@ import (
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
- trieUtils "github.com/ethereum/go-ethereum/trie/utils"
- "github.com/holiman/uint256"
)
// memoryGasCost calculates the quadratic gas for memory expansion. It does so
@@ -100,24 +98,12 @@ var (
func gasExtCodeSize(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
usedGas := uint64(0)
- slot := stack.Back(0)
- address := slot.Bytes20()
- if evm.chainRules.IsPrague {
- usedGas += evm.TxContext.Accesses.TouchAddressOnReadAndComputeGas(address[:], uint256.Int{}, trieUtils.CodeSizeLeafKey)
- }
-
return usedGas, nil
}
func gasSLoad(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
usedGas := uint64(0)
- if evm.chainRules.IsPrague {
- where := stack.Back(0)
- treeIndex, subIndex := trieUtils.GetTreeKeyStorageSlotTreeIndexes(where.Bytes())
- usedGas += evm.Accesses.TouchAddressOnReadAndComputeGas(contract.Address().Bytes(), *treeIndex, subIndex)
- }
-
return usedGas, nil
}
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 7073f2d7c297..9dc465ce80f9 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -20,12 +20,10 @@ import (
"encoding/binary"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/trie/utils"
"github.com/holiman/uint256"
)
@@ -264,13 +262,6 @@ func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := scope.Stack.peek()
address := common.Address(slot.Bytes20())
- if interpreter.evm.chainRules.IsPrague {
- statelessGas := interpreter.evm.Accesses.TouchAddressOnReadAndComputeGas(address[:], uint256.Int{}, utils.BalanceLeafKey)
- if !scope.Contract.UseGas(statelessGas) {
- scope.Contract.Gas = 0
- return nil, ErrOutOfGas
- }
- }
slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address))
return nil, nil
}
@@ -355,13 +346,6 @@ func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
slot := scope.Stack.peek()
address := slot.Bytes20()
cs := uint64(interpreter.evm.StateDB.GetCodeSize(address))
- if interpreter.evm.chainRules.IsPrague {
- statelessGas := interpreter.evm.Accesses.TouchAddressOnReadAndComputeGas(address[:], uint256.Int{}, utils.CodeSizeLeafKey)
- if !scope.Contract.UseGas(statelessGas) {
- scope.Contract.Gas = 0
- return nil, ErrOutOfGas
- }
- }
slot.SetUint64(cs)
return nil, nil
}
@@ -387,7 +371,7 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
contractAddr := scope.Contract.Address()
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(scope.Contract.Code, uint64CodeOffset, length.Uint64())
if interpreter.evm.chainRules.IsPrague && !scope.Contract.IsDeployment {
- statelessGas := touchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], copyOffset, nonPaddedCopyLength, uint64(len(scope.Contract.Code)), interpreter.evm.Accesses)
+ statelessGas := interpreter.evm.Accesses.TouchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], copyOffset, nonPaddedCopyLength, uint64(len(scope.Contract.Code)))
if !scope.Contract.UseGas(statelessGas) {
scope.Contract.Gas = 0
return nil, ErrOutOfGas
@@ -397,41 +381,6 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
return nil, nil
}
-// touchCodeChunksRangeOnReadAndChargeGas is a helper function to touch every chunk in a code range and charge witness gas costs
-func touchCodeChunksRangeOnReadAndChargeGas(contractAddr []byte, startPC, size uint64, codeLen uint64, accesses *state.AccessWitness) uint64 {
- // note that in the case where the copied code is outside the range of the
- // contract code but touches the last leaf with contract code in it,
- // we don't include the last leaf of code in the AccessWitness. The
- // reason that we do not need the last leaf is the account's code size
- // is already in the AccessWitness so a stateless verifier can see that
- // the code from the last leaf is not needed.
- if (codeLen == 0 && size == 0) || startPC > codeLen {
- return 0
- }
-
- endPC := startPC + size
- if endPC > codeLen {
- endPC = codeLen
- }
- if endPC > 0 {
- endPC -= 1 // endPC is the last bytecode that will be touched.
- }
-
- var statelessGasCharged uint64
- for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ {
- treeIndex := *uint256.NewInt((chunkNumber + 128) / 256)
- subIndex := byte((chunkNumber + 128) % 256)
- gas := accesses.TouchAddressOnReadAndComputeGas(contractAddr, treeIndex, subIndex)
- var overflow bool
- statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, gas)
- if overflow {
- panic("overflow when adding gas")
- }
- }
-
- return statelessGasCharged
-}
-
func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var (
stack = scope.Stack
@@ -452,7 +401,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
self: AccountRef(addr),
}
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
- statelessGas := touchCodeChunksRangeOnReadAndChargeGas(addr[:], copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), interpreter.evm.Accesses)
+ statelessGas := interpreter.evm.Accesses.TouchCodeChunksRangeOnReadAndChargeGas(addr[:], copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)))
if !scope.Contract.UseGas(statelessGas) {
scope.Contract.Gas = 0
return nil, ErrOutOfGas
@@ -495,13 +444,6 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := scope.Stack.peek()
address := common.Address(slot.Bytes20())
- if interpreter.evm.chainRules.IsPrague {
- statelessGas := interpreter.evm.Accesses.TouchAddressOnReadAndComputeGas(address[:], uint256.Int{}, utils.CodeKeccakLeafKey)
- if !scope.Contract.UseGas(statelessGas) {
- scope.Contract.Gas = 0
- return nil, ErrOutOfGas
- }
- }
if interpreter.evm.StateDB.Empty(address) {
slot.Clear()
} else {
@@ -520,8 +462,7 @@ func getBlockHashFromContract(number uint64, statedb StateDB, witness *state.Acc
ringIndex := number % params.Eip2935BlockHashHistorySize
var pnum common.Hash
binary.BigEndian.PutUint64(pnum[24:], ringIndex)
- treeIndex, suffix := utils.GetTreeKeyStorageSlotTreeIndexes(pnum.Bytes())
- witness.TouchAddressOnReadAndComputeGas(params.HistoryStorageAddress[:], *treeIndex, suffix)
+ witness.TouchAddressOnReadAndComputeGas(params.HistoryStorageAddress[:], pnum)
return statedb.GetState(params.HistoryStorageAddress, pnum)
}
@@ -687,16 +628,16 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
gas = scope.Contract.Gas
)
- if interpreter.evm.chainRules.IsPrague {
- contractAddress := crypto.CreateAddress(scope.Contract.Address(), interpreter.evm.StateDB.GetNonce(scope.Contract.Address()))
- statelessGas := interpreter.evm.Accesses.TouchAndChargeContractCreateInit(contractAddress.Bytes()[:], value.Sign() != 0)
- if !tryConsumeGas(&gas, statelessGas) {
- return nil, ErrExecutionReverted
- }
- }
if interpreter.evm.chainRules.IsEIP150 {
gas -= gas / 64
}
+
+ if interpreter.evm.chainRules.IsEIP4762 {
+ contractAddress := crypto.CreateAddress(scope.Contract.Address(), interpreter.evm.StateDB.GetNonce(scope.Contract.Address()))
+ statelessGas := interpreter.evm.Accesses.TouchAndChargeContractCreateInit(contractAddress.Bytes()[:], value.Sign() != 0)
+ scope.Contract.UseGas(statelessGas)
+ }
+
// reuse size int for stackvalue
stackvalue := size
@@ -741,13 +682,11 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
gas = scope.Contract.Gas
)
- if interpreter.evm.chainRules.IsPrague {
+ if interpreter.evm.chainRules.IsEIP4762 {
codeAndHash := &codeAndHash{code: input}
contractAddress := crypto.CreateAddress2(scope.Contract.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
statelessGas := interpreter.evm.Accesses.TouchAndChargeContractCreateInit(contractAddress.Bytes()[:], endowment.Sign() != 0)
- if !tryConsumeGas(&gas, statelessGas) {
- return nil, ErrExecutionReverted
- }
+ scope.Contract.UseGas(statelessGas)
}
// Apply EIP150
@@ -962,22 +901,6 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon
tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
tracer.CaptureExit([]byte{}, 0, nil)
}
- if interpreter.evm.chainRules.IsPrague {
- contractAddr := scope.Contract.Address()
- beneficiaryAddr := beneficiary.Bytes20()
- // If the beneficiary isn't the contract, we need to touch the beneficiary's balance.
- // If the beneficiary is the contract itself, there're two possibilities:
- // 1. The contract was created in the same transaction: the balance is already touched (no need to touch again)
- // 2. The contract wasn't created in the same transaction: there's no net change in balance,
- // and SELFDESTRUCT will perform no action on the account header. (we touch since we did SubBalance+AddBalance above)
- if contractAddr != beneficiaryAddr || interpreter.evm.StateDB.WasCreatedInCurrentTx(contractAddr) {
- statelessGas := interpreter.evm.Accesses.TouchAddressOnReadAndComputeGas(beneficiaryAddr[:], uint256.Int{}, utils.BalanceLeafKey)
- if !scope.Contract.UseGas(statelessGas) {
- scope.Contract.Gas = 0
- return nil, ErrOutOfGas
- }
- }
- }
return nil, errStopToken
}
@@ -1025,7 +948,7 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
// touch next chunk if PUSH1 is at the boundary. if so, *pc has
// advanced past this boundary.
contractAddr := scope.Contract.Address()
- statelessGas := touchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], *pc+1, uint64(1), uint64(len(scope.Contract.Code)), interpreter.evm.Accesses)
+ statelessGas := interpreter.evm.Accesses.TouchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], *pc+1, uint64(1), uint64(len(scope.Contract.Code)))
if !scope.Contract.UseGas(statelessGas) {
scope.Contract.Gas = 0
return nil, ErrOutOfGas
@@ -1054,7 +977,7 @@ func makePush(size uint64, pushByteSize int) executionFunc {
if !scope.Contract.IsDeployment && interpreter.evm.chainRules.IsPrague {
contractAddr := scope.Contract.Address()
- statelessGas := touchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], uint64(startMin), uint64(pushByteSize), uint64(len(scope.Contract.Code)), interpreter.evm.Accesses)
+ statelessGas := interpreter.evm.Accesses.TouchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], uint64(startMin), uint64(pushByteSize), uint64(len(scope.Contract.Code)))
if !scope.Contract.UseGas(statelessGas) {
scope.Contract.Gas = 0
return nil, ErrOutOfGas
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 17b30fae1203..93750de2ca3a 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -183,7 +183,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// if the PC ends up in a new "chunk" of verkleized code, charge the
// associated costs.
contractAddr := contract.Address()
- contract.Gas -= touchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], pc, 1, uint64(len(contract.Code)), in.evm.TxContext.Accesses)
+ contract.Gas -= in.evm.TxContext.Accesses.TouchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], pc, 1, uint64(len(contract.Code)))
}
// Get the operation from the jump table and validate the stack to ensure there are
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index 5dcabe387d6f..11a04a201317 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -84,6 +84,7 @@ func validate(jt JumpTable) JumpTable {
func newPragueInstructionSet() JumpTable {
instructionSet := newShanghaiInstructionSet()
enable6780(&instructionSet)
+ enable4762(&instructionSet)
return validate(instructionSet)
}
diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go
index 4d4fe8aed3e6..dde3338ec18d 100644
--- a/core/vm/operations_acl.go
+++ b/core/vm/operations_acl.go
@@ -22,7 +22,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/trie/utils"
)
func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
@@ -52,11 +51,6 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
}
value := common.Hash(y.Bytes32())
- if evm.chainRules.IsPrague {
- treeIndex, subIndex := utils.GetTreeKeyStorageSlotTreeIndexes(x.Bytes())
- cost += evm.Accesses.TouchAddressOnWriteAndComputeGas(contract.Address().Bytes(), *treeIndex, subIndex)
- }
-
if current == value { // noop (1)
// EIP 2200 original clause:
// return params.SloadGasEIP2200, nil
@@ -111,13 +105,6 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
slot := common.Hash(loc.Bytes32())
var gasUsed uint64
- if evm.chainRules.IsPrague {
- where := stack.Back(0)
- treeIndex, subIndex := utils.GetTreeKeyStorageSlotTreeIndexes(where.Bytes())
- addr := contract.Address()
- gasUsed += evm.Accesses.TouchAddressOnReadAndComputeGas(addr.Bytes(), *treeIndex, subIndex)
- }
-
// Check slot presence in the access list
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
// If the caller cannot afford the cost, this change will be rolled back
diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go
new file mode 100644
index 000000000000..3c4f11121394
--- /dev/null
+++ b/core/vm/operations_verkle.go
@@ -0,0 +1,98 @@
+// Copyright 2024 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package vm
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ gas := evm.StateDB.Witness().TouchAddressOnWriteAndComputeGas(contract.Address().Bytes(), common.Hash(stack.peek().Bytes32()))
+ if gas == 0 {
+ gas = params.WarmStorageReadCostEIP2929
+ }
+ return gas, nil
+}
+
+func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ gas := evm.StateDB.Witness().TouchAddressOnReadAndComputeGas(contract.Address().Bytes(), common.Hash(stack.peek().Bytes32()))
+ if gas == 0 {
+ gas = params.WarmStorageReadCostEIP2929
+ }
+ return gas, nil
+}
+
+func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ address := stack.peek().Bytes20()
+ return evm.StateDB.Witness().TouchBalance(address[:], false), nil
+}
+
+func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ address := stack.peek().Bytes20()
+ return evm.StateDB.Witness().TouchCodeSize(address[:], false), nil
+}
+
+func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ address := stack.peek().Bytes20()
+ return evm.StateDB.Witness().TouchCodeHash(address[:], false), nil
+}
+
+func makeCallVariantGasEIP4762(oldCalculator gasFunc) gasFunc {
+ return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
+ if err != nil {
+ return 0, err
+ }
+ wgas, err := evm.StateDB.Witness().TouchCodeSize(contract.Address().Bytes(), false), nil
+ if err != nil {
+ return 0, err
+ }
+ gas += wgas
+ wgas, err = evm.StateDB.Witness().TouchCodeHash(contract.Address().Bytes(), false), nil
+ if err != nil {
+ return 0, err
+ }
+ return wgas + gas, nil
+ }
+}
+
+var (
+ gasCallEIP4762 = makeCallVariantGasEIP4762(gasCall)
+ gasCallCodeEIP4762 = makeCallVariantGasEIP4762(gasCallCode)
+ gasStaticCallEIP4762 = makeCallVariantGasEIP4762(gasStaticCall)
+ gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall)
+)
+
+func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ beneficiaryAddr := common.Address(stack.peek().Bytes20())
+ contractAddr := contract.Address()
+ // If the beneficiary isn't the contract, we need to touch the beneficiary's balance.
+ // If the beneficiary is the contract itself, there're two possibilities:
+ // 1. The contract was created in the same transaction: the balance is already touched (no need to touch again)
+ // 2. The contract wasn't created in the same transaction: there's no net change in balance,
+ // and SELFDESTRUCT will perform no action on the account header. (we touch since we did SubBalance+AddBalance above)
+ if contractAddr != beneficiaryAddr || evm.StateDB.WasCreatedInCurrentTx(contractAddr) {
+ statelessGas := evm.Accesses.TouchBalance(beneficiaryAddr[:], false)
+ if !contract.UseGas(statelessGas) {
+ contract.Gas = 0
+ return 0, ErrOutOfGas
+ }
+ return statelessGas, nil
+ }
+ return 0, nil
+}
diff --git a/params/config.go b/params/config.go
index 19e633a71def..94dcb57b2fe2 100644
--- a/params/config.go
+++ b/params/config.go
@@ -806,6 +806,7 @@ func (err *ConfigCompatError) Error() string {
type Rules struct {
ChainID *big.Int
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
+ IsEIP2929, IsEIP4762 bool
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
IsBerlin, IsLondon bool
IsMerge, IsShanghai, IsCancun, IsPrague bool
@@ -828,6 +829,8 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules
IsPetersburg: c.IsPetersburg(num),
IsIstanbul: c.IsIstanbul(num),
IsBerlin: c.IsBerlin(num),
+ IsEIP2929: c.IsBerlin(num) && !c.IsPrague(num, timestamp),
+ IsEIP4762: c.IsPrague(num, timestamp),
IsLondon: c.IsLondon(num),
IsMerge: isMerge,
IsShanghai: c.IsShanghai(num, timestamp),
diff --git a/params/protocol_params.go b/params/protocol_params.go
index 8aad52103979..e64836b31417 100644
--- a/params/protocol_params.go
+++ b/params/protocol_params.go
@@ -86,6 +86,7 @@ const (
LogTopicGas uint64 = 375 // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas.
CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction.
Create2Gas uint64 = 32000 // Once per CREATE2 operation
+ CreateNGasEip4762 uint64 = 1000 // Once per CREATEn operations post-verkle
SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation.
MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
From 95da72b6621638dac0c52c8177709d61b8f6fa83 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Sat, 16 Mar 2024 20:31:23 +0100
Subject: [PATCH 02/25] integrate some review feedback
---
core/state/access_witness.go | 32 +++--------------------
core/state/accesslist/types.go | 44 --------------------------------
core/state_processor.go | 4 +--
core/state_processor_test.go | 46 +++++++++++++++++++++++++++++++---
core/vm/evm.go | 4 +--
core/vm/instructions.go | 6 ++---
core/vm/operations_verkle.go | 4 +--
trie/utils/verkle.go | 12 ++++-----
trie/utils/verkle_test.go | 2 +-
trie/verkle.go | 6 ++---
10 files changed, 66 insertions(+), 94 deletions(-)
delete mode 100644 core/state/accesslist/types.go
diff --git a/core/state/access_witness.go b/core/state/access_witness.go
index 590ef2558dbc..61b674f19c4f 100644
--- a/core/state/access_witness.go
+++ b/core/state/access_witness.go
@@ -97,14 +97,6 @@ func (aw *AccessWitness) TouchFullAccount(addr []byte, isWrite bool) uint64 {
return gas
}
-func (aw *AccessWitness) TouchAndChargeProofOfAbsence(addr []byte) uint64 {
- var gas uint64
- for i := utils.VersionLeafKey; i <= utils.CodeSizeLeafKey; i++ {
- gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, byte(i), false)
- }
- return gas
-}
-
func (aw *AccessWitness) TouchAndChargeMessageCall(addr []byte) uint64 {
var gas uint64
gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, false)
@@ -131,17 +123,6 @@ func (aw *AccessWitness) TouchAndChargeContractCreateInit(addr []byte, createSen
return gas
}
-// TouchAndChargeContractCreateCompleted charges access access costs after
-// the completion of a contract creation to populate the created account in
-// the tree
-func (aw *AccessWitness) TouchAndChargeContractCreateCompleted(addr []byte) uint64 {
- var gas uint64
- for i := utils.VersionLeafKey; i <= utils.CodeSizeLeafKey; i++ {
- gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, byte(i), true)
- }
- return gas
-}
-
func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) uint64 {
for i := utils.VersionLeafKey; i <= utils.CodeSizeLeafKey; i++ {
aw.touchAddressAndChargeGas(originAddr, zeroTreeIndex, byte(i), i == utils.BalanceLeafKey || i == utils.NonceLeafKey)
@@ -157,7 +138,7 @@ func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) uint64 {
func (aw *AccessWitness) TouchTxExistingAndComputeGas(targetAddr []byte, sendsValue bool) uint64 {
aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.VersionLeafKey, false)
aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeSizeLeafKey, false)
- aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeKeccakLeafKey, false)
+ aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeHashLeafKey, false)
aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.NonceLeafKey, false)
if sendsValue {
aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
@@ -172,14 +153,9 @@ func (aw *AccessWitness) TouchTxExistingAndComputeGas(targetAddr []byte, sendsVa
return 0
}
-func (aw *AccessWitness) TouchAddressOnWriteAndComputeGas(addr []byte, slot common.Hash) uint64 {
- treeIndex, subIndex := utils.GetTreeKeyStorageSlotTreeIndexes(slot.Bytes())
- return aw.touchAddressAndChargeGas(addr, *treeIndex, subIndex, true)
-}
-
-func (aw *AccessWitness) TouchAddressOnReadAndComputeGas(addr []byte, slot common.Hash) uint64 {
+func (aw *AccessWitness) TouchSlotAndChargeGas(addr []byte, slot common.Hash, isWrite bool) uint64 {
treeIndex, subIndex := utils.GetTreeKeyStorageSlotTreeIndexes(slot.Bytes())
- return aw.touchAddressAndChargeGas(addr, *treeIndex, subIndex, false)
+ return aw.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite)
}
func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256.Int, subIndex byte, isWrite bool) uint64 {
@@ -317,5 +293,5 @@ func (aw *AccessWitness) TouchCodeSize(addr []byte, isWrite bool) uint64 {
}
func (aw *AccessWitness) TouchCodeHash(addr []byte, isWrite bool) uint64 {
- return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, isWrite)
+ return aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite)
}
diff --git a/core/state/accesslist/types.go b/core/state/accesslist/types.go
deleted file mode 100644
index 53d5e03198b7..000000000000
--- a/core/state/accesslist/types.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2020 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package accesslist
-
-// AccessType specifies if a state access is for reading or writing. This
-// is necessary because verkle charges different costs depending on the
-// type of access.
-type AccessType bool
-
-var (
- AccessListRead = AccessType(false)
- AccessListWrite = AccessType(true)
-)
-
-// ItemType is used in verkle mode to specify what item of an account
-// is being accessed. This is necessary because verkle charges gas each
-// time a new account item is accessed.
-type ItemType uint64
-
-const (
- Version = ItemType(1 << iota)
- Balance
- Nonce
- CodeHash
- CodeSize
- LastHeaderItem
-)
-
-const ALAllItems = Version | Balance | Nonce | CodeSize | CodeHash
-const ALNoItems = ItemType(0)
diff --git a/core/state_processor.go b/core/state_processor.go
index a30cfab60285..eb5258f76fdf 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -260,7 +260,7 @@ func (kvm *keyValueMigrator) addAccount(addr []byte, acc *types.StateAccount) {
binary.LittleEndian.PutUint64(nonce[:8], acc.Nonce)
leafNodeData.Values[tutils.NonceLeafKey] = nonce[:]
- leafNodeData.Values[tutils.CodeKeccakLeafKey] = acc.CodeHash[:]
+ leafNodeData.Values[tutils.CodeHashLeafKey] = acc.CodeHash[:]
}
func (kvm *keyValueMigrator) addAccountCode(addr []byte, codeSize uint64, chunks []byte) {
@@ -381,5 +381,5 @@ func ProcessParentBlockHash(statedb *state.StateDB, prevNumber uint64, prevHash
var key common.Hash
binary.BigEndian.PutUint64(key[24:], ringIndex)
statedb.SetState(params.HistoryStorageAddress, key, prevHash)
- statedb.Witness().TouchAddressOnWriteAndComputeGas(params.HistoryStorageAddress[:], key)
+ statedb.Witness().TouchSlotAndChargeGas(params.HistoryStorageAddress[:], key, true)
}
diff --git a/core/state_processor_test.go b/core/state_processor_test.go
index b72147d70a98..623259f03870 100644
--- a/core/state_processor_test.go
+++ b/core/state_processor_test.go
@@ -20,6 +20,9 @@ import (
"bytes"
"crypto/ecdsa"
"encoding/binary"
+ "encoding/json"
+ "fmt"
+ "os"
//"fmt"
"math/big"
@@ -37,7 +40,9 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie/utils"
//"github.com/ethereum/go-ethereum/rlp"
@@ -472,11 +477,19 @@ func TestProcessVerkle(t *testing.T) {
},
},
}
+ loggerCfg = &logger.Config{}
)
+
+ os.MkdirAll("output", 0755)
+ traceFile, err := os.Create("./output/traces.jsonl")
+ if err != nil {
+ t.Fatal(err)
+ }
+
// Verkle trees use the snapshot, which must be enabled before the
// data is saved into the tree+database.
genesis := gspec.MustCommit(bcdb)
- blockchain, _ := NewBlockChain(bcdb, nil, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil, nil)
+ blockchain, _ := NewBlockChain(bcdb, nil, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{Tracer: logger.NewJSONLogger(loggerCfg, traceFile)}, nil, nil)
defer blockchain.Stop()
// Commit the genesis block to the block-generation database as it
@@ -513,6 +526,33 @@ func TestProcessVerkle(t *testing.T) {
}
})
+ kvjson, err := json.Marshal(keyvals)
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = os.WriteFile("./output/statediffs.json", kvjson, 0644)
+ if err != nil {
+ t.Fatal(err)
+ }
+ blockrlp, err := rlp.EncodeToBytes(genesis)
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = os.WriteFile(fmt.Sprintf("./output/block%d.rlp.hex", 0), []byte(fmt.Sprintf("%x", blockrlp)), 0644)
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, block := range chain {
+ blockrlp, err := rlp.EncodeToBytes(block)
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = os.WriteFile(fmt.Sprintf("./output/block%d.rlp.hex", block.NumberU64()), []byte(fmt.Sprintf("%x", blockrlp)), 0644)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+
// Uncomment to extract block #2
//f, _ := os.Create("block2.rlp")
//defer f.Close()
@@ -521,7 +561,7 @@ func TestProcessVerkle(t *testing.T) {
//f.Write(buf.Bytes())
//fmt.Printf("root= %x\n", chain[0].Root())
// check the proof for the last block
- err := trie.DeserializeAndVerifyVerkleProof(proofs[1], chain[0].Root().Bytes(), chain[1].Root().Bytes(), keyvals[1])
+ err = trie.DeserializeAndVerifyVerkleProof(proofs[1], chain[0].Root().Bytes(), chain[1].Root().Bytes(), keyvals[1])
if err != nil {
t.Fatal(err)
}
@@ -913,7 +953,7 @@ func TestProcessVerklExtCodeHashOpcode(t *testing.T) {
}
codeHashStateDiff := statediff[1][stateDiffIdx].SuffixDiffs[0]
- if codeHashStateDiff.Suffix != utils.CodeKeccakLeafKey {
+ if codeHashStateDiff.Suffix != utils.CodeHashLeafKey {
t.Fatalf("code hash invalid suffix")
}
if codeHashStateDiff.CurrentValue == nil {
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 02817fbda40b..06849043873c 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -199,7 +199,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
if !evm.StateDB.Exist(addr) {
if !isPrecompile && evm.chainRules.IsEIP4762 {
// add proof of absence to witness
- wgas := evm.Accesses.TouchAndChargeProofOfAbsence(addr.Bytes())
+ wgas := evm.Accesses.TouchFullAccount(addr.Bytes(), false)
if gas < wgas {
evm.StateDB.RevertToSnapshot(snapshot)
return nil, 0, ErrOutOfGas
@@ -516,7 +516,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if len(ret) > 0 {
evm.Accesses.TouchCodeChunksRangeOnReadAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)))
}
- if !contract.UseGas(evm.Accesses.TouchAndChargeContractCreateCompleted(address.Bytes()[:])) {
+ if !contract.UseGas(evm.Accesses.TouchFullAccount(address.Bytes()[:], true)) {
err = ErrOutOfGas
}
}
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 9dc465ce80f9..a12606844771 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -370,7 +370,7 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
contractAddr := scope.Contract.Address()
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(scope.Contract.Code, uint64CodeOffset, length.Uint64())
- if interpreter.evm.chainRules.IsPrague && !scope.Contract.IsDeployment {
+ if interpreter.evm.chainRules.IsEIP4762 && !scope.Contract.IsDeployment {
statelessGas := interpreter.evm.Accesses.TouchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], copyOffset, nonPaddedCopyLength, uint64(len(scope.Contract.Code)))
if !scope.Contract.UseGas(statelessGas) {
scope.Contract.Gas = 0
@@ -394,7 +394,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
uint64CodeOffset = 0xffffffffffffffff
}
addr := common.Address(a.Bytes20())
- if interpreter.evm.chainRules.IsPrague {
+ if interpreter.evm.chainRules.IsEIP4762 {
code := interpreter.evm.StateDB.GetCode(addr)
contract := &Contract{
Code: code,
@@ -462,7 +462,7 @@ func getBlockHashFromContract(number uint64, statedb StateDB, witness *state.Acc
ringIndex := number % params.Eip2935BlockHashHistorySize
var pnum common.Hash
binary.BigEndian.PutUint64(pnum[24:], ringIndex)
- witness.TouchAddressOnReadAndComputeGas(params.HistoryStorageAddress[:], pnum)
+ witness.TouchSlotAndChargeGas(params.HistoryStorageAddress[:], pnum, false)
return statedb.GetState(params.HistoryStorageAddress, pnum)
}
diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go
index 3c4f11121394..8045c9eb1245 100644
--- a/core/vm/operations_verkle.go
+++ b/core/vm/operations_verkle.go
@@ -22,7 +22,7 @@ import (
)
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
- gas := evm.StateDB.Witness().TouchAddressOnWriteAndComputeGas(contract.Address().Bytes(), common.Hash(stack.peek().Bytes32()))
+ gas := evm.StateDB.Witness().TouchSlotAndChargeGas(contract.Address().Bytes(), common.Hash(stack.peek().Bytes32()), true)
if gas == 0 {
gas = params.WarmStorageReadCostEIP2929
}
@@ -30,7 +30,7 @@ func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
}
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
- gas := evm.StateDB.Witness().TouchAddressOnReadAndComputeGas(contract.Address().Bytes(), common.Hash(stack.peek().Bytes32()))
+ gas := evm.StateDB.Witness().TouchSlotAndChargeGas(contract.Address().Bytes(), common.Hash(stack.peek().Bytes32()), false)
if gas == 0 {
gas = params.WarmStorageReadCostEIP2929
}
diff --git a/trie/utils/verkle.go b/trie/utils/verkle.go
index d1aa9dec57fa..af1240cb16bc 100644
--- a/trie/utils/verkle.go
+++ b/trie/utils/verkle.go
@@ -26,11 +26,11 @@ import (
)
const (
- VersionLeafKey = 0
- BalanceLeafKey = 1
- NonceLeafKey = 2
- CodeKeccakLeafKey = 3
- CodeSizeLeafKey = 4
+ VersionLeafKey = 0
+ BalanceLeafKey = 1
+ NonceLeafKey = 2
+ CodeHashLeafKey = 3
+ CodeSizeLeafKey = 4
maxPointCacheByteSize = 100 << 20
)
@@ -152,7 +152,7 @@ func GetTreeKeyNonce(address []byte) []byte {
}
func GetTreeKeyCodeKeccak(address []byte) []byte {
- return GetTreeKey(address, zero, CodeKeccakLeafKey)
+ return GetTreeKey(address, zero, CodeHashLeafKey)
}
func GetTreeKeyCodeSize(address []byte) []byte {
diff --git a/trie/utils/verkle_test.go b/trie/utils/verkle_test.go
index 02de69c9b712..35cae6e03349 100644
--- a/trie/utils/verkle_test.go
+++ b/trie/utils/verkle_test.go
@@ -77,7 +77,7 @@ func sha256GetTreeKeyCodeSize(addr []byte) []byte {
copy(payload[:len(treeIndexBytes)], treeIndexBytes)
digest.Write(payload[:])
h := digest.Sum(nil)
- h[31] = CodeKeccakLeafKey
+ h[31] = CodeHashLeafKey
return h
}
diff --git a/trie/verkle.go b/trie/verkle.go
index 760e30c8cdaa..ee953c232318 100644
--- a/trie/verkle.go
+++ b/trie/verkle.go
@@ -125,7 +125,7 @@ func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error
// been recreated after that, then its code keccak will NOT be 0. So return `nil` if
// the nonce, and values[10], and code keccak is 0.
- if acc.Nonce == 0 && len(values) > 10 && len(values[10]) > 0 && bytes.Equal(values[utils.CodeKeccakLeafKey], zero[:]) {
+ if acc.Nonce == 0 && len(values) > 10 && len(values[10]) > 0 && bytes.Equal(values[utils.CodeHashLeafKey], zero[:]) {
if !t.ended {
return nil, errDeletedAccount
} else {
@@ -144,7 +144,7 @@ func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error
// }
// }
acc.Balance = new(big.Int).SetBytes(balance[:])
- acc.CodeHash = values[utils.CodeKeccakLeafKey]
+ acc.CodeHash = values[utils.CodeHashLeafKey]
// TODO fix the code size as well
return acc, nil
@@ -164,7 +164,7 @@ func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount)
values[utils.VersionLeafKey] = zero[:]
values[utils.NonceLeafKey] = nonce[:]
values[utils.BalanceLeafKey] = balance[:]
- values[utils.CodeKeccakLeafKey] = acc.CodeHash[:]
+ values[utils.CodeHashLeafKey] = acc.CodeHash[:]
binary.LittleEndian.PutUint64(nonce[:], acc.Nonce)
bbytes := acc.Balance.Bytes()
From b2609706f53ab087d89ff62dbaa4705147ef03fe Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Sun, 17 Mar 2024 20:28:01 +0100
Subject: [PATCH 03/25] Apply suggestions from code review
Co-authored-by: Ignacio Hagopian
---
core/vm/instructions.go | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index a12606844771..b13b28d85787 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -635,7 +635,9 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
if interpreter.evm.chainRules.IsEIP4762 {
contractAddress := crypto.CreateAddress(scope.Contract.Address(), interpreter.evm.StateDB.GetNonce(scope.Contract.Address()))
statelessGas := interpreter.evm.Accesses.TouchAndChargeContractCreateInit(contractAddress.Bytes()[:], value.Sign() != 0)
- scope.Contract.UseGas(statelessGas)
+ if !scope.Contract.UseGas(statelessGas) {
+ return nil, ErrExecutionReverted
+ }
}
// reuse size int for stackvalue
@@ -686,7 +688,9 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
codeAndHash := &codeAndHash{code: input}
contractAddress := crypto.CreateAddress2(scope.Contract.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
statelessGas := interpreter.evm.Accesses.TouchAndChargeContractCreateInit(contractAddress.Bytes()[:], endowment.Sign() != 0)
- scope.Contract.UseGas(statelessGas)
+ if !scope.Contract.UseGas(statelessGas) {
+ return nil, ErrExecutionReverted
+ }
}
// Apply EIP150
From 1df183a7a53e8b07080bfcb7138a09c2daf1edc9 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Sun, 17 Mar 2024 20:28:20 +0100
Subject: [PATCH 04/25] more suggestions from code review
---
core/vm/instructions.go | 2 ++
core/vm/operations_verkle.go | 3 ++-
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index b13b28d85787..0e93ebfba3f4 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -402,6 +402,8 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
}
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
statelessGas := interpreter.evm.Accesses.TouchCodeChunksRangeOnReadAndChargeGas(addr[:], copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)))
+ statelessGas += interpreter.evm.Accesses.TouchVersion(addr[:], false)
+ statelessGas += interpreter.evm.Accesses.TouchCodeSize(addr[:], false)
if !scope.Contract.UseGas(statelessGas) {
scope.Contract.Gas = 0
return nil, ErrOutOfGas
diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go
index 8045c9eb1245..e57175612e18 100644
--- a/core/vm/operations_verkle.go
+++ b/core/vm/operations_verkle.go
@@ -44,7 +44,8 @@ func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
address := stack.peek().Bytes20()
- return evm.StateDB.Witness().TouchCodeSize(address[:], false), nil
+ versiongas := evm.StateDB.Witness().TouchVersion(address[:], false)
+ return versiongas + evm.StateDB.Witness().TouchCodeSize(address[:], false), nil
}
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
From df69fece5c46365db3d36634453a4ce1ae435409 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Tue, 19 Mar 2024 11:24:48 +0100
Subject: [PATCH 05/25] don't charge creation gas + charge code chunks in
create
---
core/state_processor_test.go | 4 ++--
core/vm/evm.go | 28 +++++++++++++++++-----------
core/vm/instructions.go | 8 ++++----
core/vm/interpreter.go | 2 +-
4 files changed, 24 insertions(+), 18 deletions(-)
diff --git a/core/state_processor_test.go b/core/state_processor_test.go
index 623259f03870..20155ba76427 100644
--- a/core/state_processor_test.go
+++ b/core/state_processor_test.go
@@ -498,8 +498,8 @@ func TestProcessVerkle(t *testing.T) {
txCost1 := params.TxGas
txCost2 := params.TxGas
- contractCreationCost := intrinsicContractCreationGas + uint64(5600+700+700+700 /* creation with value */ +2739 /* execution costs */)
- codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(5600+700 /* creation */ +198644 /* execution costs */)
+ contractCreationCost := intrinsicContractCreationGas + uint64(5600+700+700+700 /* creation with value */ +1439 /* execution costs */)
+ codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(5600+700 /* creation */ +56344 /* execution costs */)
blockGasUsagesExpected := []uint64{
txCost1*2 + txCost2,
txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas,
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 06849043873c..2bfc2e233264 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -504,20 +504,26 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// be stored due to not enough gas set an error and let it be handled
// by the error checking condition below.
if err == nil {
- createDataGas := uint64(len(ret)) * params.CreateDataGas
- if contract.UseGas(createDataGas) {
- evm.StateDB.SetCode(address, ret)
+ if !evm.chainRules.IsEIP4762 {
+ createDataGas := uint64(len(ret)) * params.CreateDataGas
+ if contract.UseGas(createDataGas) {
+ } else {
+ err = ErrCodeStoreOutOfGas
+ }
} else {
- err = ErrCodeStoreOutOfGas
+ // Contract creation completed, touch the missing field in the contract
+ if len(ret) > 0 {
+ if !contract.UseGas(evm.Accesses.TouchCodeChunkRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true)) {
+ err = ErrOutOfGas
+ }
+ }
+ if err == nil && !contract.UseGas(evm.Accesses.TouchFullAccount(address.Bytes()[:], true)) {
+ err = ErrOutOfGas
+ }
}
- }
- if err == nil && evm.chainRules.IsEIP4762 {
- if len(ret) > 0 {
- evm.Accesses.TouchCodeChunksRangeOnReadAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)))
- }
- if !contract.UseGas(evm.Accesses.TouchFullAccount(address.Bytes()[:], true)) {
- err = ErrOutOfGas
+ if err == nil {
+ evm.StateDB.SetCode(address, ret)
}
}
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 0e93ebfba3f4..f554ee891ae4 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -371,7 +371,7 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
contractAddr := scope.Contract.Address()
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(scope.Contract.Code, uint64CodeOffset, length.Uint64())
if interpreter.evm.chainRules.IsEIP4762 && !scope.Contract.IsDeployment {
- statelessGas := interpreter.evm.Accesses.TouchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], copyOffset, nonPaddedCopyLength, uint64(len(scope.Contract.Code)))
+ statelessGas := interpreter.evm.Accesses.TouchCodeChunkRangeAndChargeGas(contractAddr[:], copyOffset, nonPaddedCopyLength, uint64(len(scope.Contract.Code)), false)
if !scope.Contract.UseGas(statelessGas) {
scope.Contract.Gas = 0
return nil, ErrOutOfGas
@@ -401,7 +401,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
self: AccountRef(addr),
}
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
- statelessGas := interpreter.evm.Accesses.TouchCodeChunksRangeOnReadAndChargeGas(addr[:], copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)))
+ statelessGas := interpreter.evm.Accesses.TouchCodeChunkRangeAndChargeGas(addr[:], copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false)
statelessGas += interpreter.evm.Accesses.TouchVersion(addr[:], false)
statelessGas += interpreter.evm.Accesses.TouchCodeSize(addr[:], false)
if !scope.Contract.UseGas(statelessGas) {
@@ -954,7 +954,7 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
// touch next chunk if PUSH1 is at the boundary. if so, *pc has
// advanced past this boundary.
contractAddr := scope.Contract.Address()
- statelessGas := interpreter.evm.Accesses.TouchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], *pc+1, uint64(1), uint64(len(scope.Contract.Code)))
+ statelessGas := interpreter.evm.Accesses.TouchCodeChunkRangeAndChargeGas(contractAddr[:], *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false)
if !scope.Contract.UseGas(statelessGas) {
scope.Contract.Gas = 0
return nil, ErrOutOfGas
@@ -983,7 +983,7 @@ func makePush(size uint64, pushByteSize int) executionFunc {
if !scope.Contract.IsDeployment && interpreter.evm.chainRules.IsPrague {
contractAddr := scope.Contract.Address()
- statelessGas := interpreter.evm.Accesses.TouchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], uint64(startMin), uint64(pushByteSize), uint64(len(scope.Contract.Code)))
+ statelessGas := interpreter.evm.Accesses.TouchCodeChunkRangeAndChargeGas(contractAddr[:], uint64(startMin), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false)
if !scope.Contract.UseGas(statelessGas) {
scope.Contract.Gas = 0
return nil, ErrOutOfGas
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 93750de2ca3a..eb52a2080e69 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -183,7 +183,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// if the PC ends up in a new "chunk" of verkleized code, charge the
// associated costs.
contractAddr := contract.Address()
- contract.Gas -= in.evm.TxContext.Accesses.TouchCodeChunksRangeOnReadAndChargeGas(contractAddr[:], pc, 1, uint64(len(contract.Code)))
+ contract.Gas -= in.evm.TxContext.Accesses.TouchCodeChunkRangeAndChargeGas(contractAddr[:], pc, 1, uint64(len(contract.Code)), false)
}
// Get the operation from the jump table and validate the stack to ensure there are
From 93ebf5725e294dc50e39fe8a3fa3d98e8f856ee2 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Mon, 25 Mar 2024 10:43:41 +0100
Subject: [PATCH 06/25] A couple more fixes
---
core/state/access_witness.go | 4 ++--
core/vm/evm.go | 5 ++++-
core/vm/instructions.go | 8 ++++----
core/vm/interpreter.go | 2 +-
4 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/core/state/access_witness.go b/core/state/access_witness.go
index 61b674f19c4f..4c9d3218ad96 100644
--- a/core/state/access_witness.go
+++ b/core/state/access_witness.go
@@ -242,7 +242,7 @@ func newChunkAccessKey(branchKey branchAccessKey, leafKey byte) chunkAccessKey {
}
// touchCodeChunksRangeOnReadAndChargeGas is a helper function to touch every chunk in a code range and charge witness gas costs
-func (aw *AccessWitness) TouchCodeChunksRangeOnReadAndChargeGas(contractAddr []byte, startPC, size uint64, codeLen uint64) uint64 {
+func (aw *AccessWitness) TouchCodeChunksRangeAndChargeGas(contractAddr []byte, startPC, size uint64, codeLen uint64, isWrite bool) uint64 {
// note that in the case where the copied code is outside the range of the
// contract code but touches the last leaf with contract code in it,
// we don't include the last leaf of code in the AccessWitness. The
@@ -265,7 +265,7 @@ func (aw *AccessWitness) TouchCodeChunksRangeOnReadAndChargeGas(contractAddr []b
for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ {
treeIndex := *uint256.NewInt((chunkNumber + 128) / 256)
subIndex := byte((chunkNumber + 128) % 256)
- gas := aw.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, false)
+ gas := aw.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite)
var overflow bool
statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, gas)
if overflow {
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 2bfc2e233264..49cbd1a3b8f5 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -459,6 +459,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if evm.chainRules.IsEIP2929 {
evm.StateDB.AddAddressToAccessList(address)
}
+ if evm.chainRules.IsEIP4762 {
+ evm.Accesses.TouchAndChargeContractCreateInit(address.Bytes(), value.Sign() == 0)
+ }
// Ensure there's no existing contract already at the designated address
contractHash := evm.StateDB.GetCodeHash(address)
if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) {
@@ -513,7 +516,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
} else {
// Contract creation completed, touch the missing field in the contract
if len(ret) > 0 {
- if !contract.UseGas(evm.Accesses.TouchCodeChunkRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true)) {
+ if !contract.UseGas(evm.Accesses.TouchCodeChunksRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true)) {
err = ErrOutOfGas
}
}
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index f554ee891ae4..e8928d561fc3 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -371,7 +371,7 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
contractAddr := scope.Contract.Address()
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(scope.Contract.Code, uint64CodeOffset, length.Uint64())
if interpreter.evm.chainRules.IsEIP4762 && !scope.Contract.IsDeployment {
- statelessGas := interpreter.evm.Accesses.TouchCodeChunkRangeAndChargeGas(contractAddr[:], copyOffset, nonPaddedCopyLength, uint64(len(scope.Contract.Code)), false)
+ statelessGas := interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(contractAddr[:], copyOffset, nonPaddedCopyLength, uint64(len(scope.Contract.Code)), false)
if !scope.Contract.UseGas(statelessGas) {
scope.Contract.Gas = 0
return nil, ErrOutOfGas
@@ -401,7 +401,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
self: AccountRef(addr),
}
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
- statelessGas := interpreter.evm.Accesses.TouchCodeChunkRangeAndChargeGas(addr[:], copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false)
+ statelessGas := interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(addr[:], copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false)
statelessGas += interpreter.evm.Accesses.TouchVersion(addr[:], false)
statelessGas += interpreter.evm.Accesses.TouchCodeSize(addr[:], false)
if !scope.Contract.UseGas(statelessGas) {
@@ -954,7 +954,7 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
// touch next chunk if PUSH1 is at the boundary. if so, *pc has
// advanced past this boundary.
contractAddr := scope.Contract.Address()
- statelessGas := interpreter.evm.Accesses.TouchCodeChunkRangeAndChargeGas(contractAddr[:], *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false)
+ statelessGas := interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(contractAddr[:], *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false)
if !scope.Contract.UseGas(statelessGas) {
scope.Contract.Gas = 0
return nil, ErrOutOfGas
@@ -983,7 +983,7 @@ func makePush(size uint64, pushByteSize int) executionFunc {
if !scope.Contract.IsDeployment && interpreter.evm.chainRules.IsPrague {
contractAddr := scope.Contract.Address()
- statelessGas := interpreter.evm.Accesses.TouchCodeChunkRangeAndChargeGas(contractAddr[:], uint64(startMin), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false)
+ statelessGas := interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(contractAddr[:], uint64(startMin), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false)
if !scope.Contract.UseGas(statelessGas) {
scope.Contract.Gas = 0
return nil, ErrOutOfGas
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index eb52a2080e69..46bbfd0105f7 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -183,7 +183,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// if the PC ends up in a new "chunk" of verkleized code, charge the
// associated costs.
contractAddr := contract.Address()
- contract.Gas -= in.evm.TxContext.Accesses.TouchCodeChunkRangeAndChargeGas(contractAddr[:], pc, 1, uint64(len(contract.Code)), false)
+ contract.Gas -= in.evm.TxContext.Accesses.TouchCodeChunksRangeAndChargeGas(contractAddr[:], pc, 1, uint64(len(contract.Code)), false)
}
// Get the operation from the jump table and validate the stack to ensure there are
From ad0717d9034e7d939b00f67d6b006ab5adda5c46 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Mon, 25 Mar 2024 10:46:05 +0100
Subject: [PATCH 07/25] make linter happy
---
core/vm/evm.go | 1 -
1 file changed, 1 deletion(-)
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 49cbd1a3b8f5..68b654e9eea0 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -208,7 +208,6 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
}
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
-
// Calling a non existing account, don't do anything, but ping the tracer
if debug {
if evm.depth == 0 {
From 5c91ceeeea299047be66454797122e0263f0d034 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Mon, 25 Mar 2024 11:06:49 +0100
Subject: [PATCH 08/25] fix create init gas consumption issue
---
core/vm/evm.go | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 68b654e9eea0..f4b94ececa54 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -458,9 +458,6 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if evm.chainRules.IsEIP2929 {
evm.StateDB.AddAddressToAccessList(address)
}
- if evm.chainRules.IsEIP4762 {
- evm.Accesses.TouchAndChargeContractCreateInit(address.Bytes(), value.Sign() == 0)
- }
// Ensure there's no existing contract already at the designated address
contractHash := evm.StateDB.GetCodeHash(address)
if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) {
@@ -481,6 +478,14 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
contract.SetCodeOptionalHash(&address, codeAndHash)
contract.IsDeployment = true
+ // Charge the contract creation init gas in verkle mode
+ var err error
+ if evm.chainRules.IsEIP4762 {
+ if !contract.UseGas(evm.Accesses.TouchAndChargeContractCreateInit(address.Bytes(), value.Sign() != 0)) {
+ err = ErrOutOfGas
+ }
+ }
+
if evm.Config.Tracer != nil {
if evm.depth == 0 {
evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
@@ -489,7 +494,8 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
}
- ret, err := evm.interpreter.Run(contract, nil, false)
+ var ret []byte
+ ret, err = evm.interpreter.Run(contract, nil, false)
// Check whether the max code size has been exceeded, assign err if the case.
if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
From 0ebb629d3d6f2f095a932570fd5d62d158fa0635 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Mon, 25 Mar 2024 13:16:47 +0100
Subject: [PATCH 09/25] fix: in gas funcs, use tx witness instead of global
witness
---
core/state_processor_test.go | 2 +-
core/vm/operations_verkle.go | 22 ++++++++--------------
2 files changed, 9 insertions(+), 15 deletions(-)
diff --git a/core/state_processor_test.go b/core/state_processor_test.go
index 20155ba76427..25a7d28fea61 100644
--- a/core/state_processor_test.go
+++ b/core/state_processor_test.go
@@ -499,7 +499,7 @@ func TestProcessVerkle(t *testing.T) {
txCost1 := params.TxGas
txCost2 := params.TxGas
contractCreationCost := intrinsicContractCreationGas + uint64(5600+700+700+700 /* creation with value */ +1439 /* execution costs */)
- codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(5600+700 /* creation */ +56344 /* execution costs */)
+ codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(5600+700 /* creation */ +46544 /* execution costs */)
blockGasUsagesExpected := []uint64{
txCost1*2 + txCost2,
txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas,
diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go
index e57175612e18..bc255480fe85 100644
--- a/core/vm/operations_verkle.go
+++ b/core/vm/operations_verkle.go
@@ -22,7 +22,7 @@ import (
)
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
- gas := evm.StateDB.Witness().TouchSlotAndChargeGas(contract.Address().Bytes(), common.Hash(stack.peek().Bytes32()), true)
+ gas := evm.Accesses.TouchSlotAndChargeGas(contract.Address().Bytes(), common.Hash(stack.peek().Bytes32()), true)
if gas == 0 {
gas = params.WarmStorageReadCostEIP2929
}
@@ -30,7 +30,7 @@ func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
}
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
- gas := evm.StateDB.Witness().TouchSlotAndChargeGas(contract.Address().Bytes(), common.Hash(stack.peek().Bytes32()), false)
+ gas := evm.Accesses.TouchSlotAndChargeGas(contract.Address().Bytes(), common.Hash(stack.peek().Bytes32()), false)
if gas == 0 {
gas = params.WarmStorageReadCostEIP2929
}
@@ -39,18 +39,18 @@ func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
address := stack.peek().Bytes20()
- return evm.StateDB.Witness().TouchBalance(address[:], false), nil
+ return evm.Accesses.TouchBalance(address[:], false), nil
}
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
address := stack.peek().Bytes20()
- versiongas := evm.StateDB.Witness().TouchVersion(address[:], false)
- return versiongas + evm.StateDB.Witness().TouchCodeSize(address[:], false), nil
+ versiongas := evm.Accesses.TouchVersion(address[:], false)
+ return versiongas + evm.Accesses.TouchCodeSize(address[:], false), nil
}
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
address := stack.peek().Bytes20()
- return evm.StateDB.Witness().TouchCodeHash(address[:], false), nil
+ return evm.Accesses.TouchCodeHash(address[:], false), nil
}
func makeCallVariantGasEIP4762(oldCalculator gasFunc) gasFunc {
@@ -59,15 +59,9 @@ func makeCallVariantGasEIP4762(oldCalculator gasFunc) gasFunc {
if err != nil {
return 0, err
}
- wgas, err := evm.StateDB.Witness().TouchCodeSize(contract.Address().Bytes(), false), nil
- if err != nil {
- return 0, err
- }
+ wgas := evm.Accesses.TouchCodeSize(contract.Address().Bytes(), false)
gas += wgas
- wgas, err = evm.StateDB.Witness().TouchCodeHash(contract.Address().Bytes(), false), nil
- if err != nil {
- return 0, err
- }
+ wgas = evm.Accesses.TouchCodeHash(contract.Address().Bytes(), false)
return wgas + gas, nil
}
}
From 090794e8b0048d941ee6d28f52aee1b02019733e Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Mon, 25 Mar 2024 13:32:21 +0100
Subject: [PATCH 10/25] fix linter issue
---
core/vm/evm.go | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/core/vm/evm.go b/core/vm/evm.go
index f4b94ececa54..b890ce9723b6 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -495,7 +495,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
var ret []byte
- ret, err = evm.interpreter.Run(contract, nil, false)
+ if err == nil {
+ ret, err = evm.interpreter.Run(contract, nil, false)
+ }
// Check whether the max code size has been exceeded, assign err if the case.
if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
From 87a89da0c1a501f693a5cc8418c11f1dd75d1a2d Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Mon, 25 Mar 2024 13:33:04 +0100
Subject: [PATCH 11/25] Apply suggestions from code review
Co-authored-by: Ignacio Hagopian
---
core/vm/evm.go | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/core/vm/evm.go b/core/vm/evm.go
index b890ce9723b6..2ee5728960d2 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -516,17 +516,14 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if err == nil {
if !evm.chainRules.IsEIP4762 {
createDataGas := uint64(len(ret)) * params.CreateDataGas
- if contract.UseGas(createDataGas) {
- } else {
+ if !contract.UseGas(createDataGas) {
err = ErrCodeStoreOutOfGas
}
} else {
// Contract creation completed, touch the missing field in the contract
- if len(ret) > 0 {
- if !contract.UseGas(evm.Accesses.TouchCodeChunksRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true)) {
+ if len(ret) > 0 && !contract.UseGas(evm.Accesses.TouchCodeChunksRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true)) {
err = ErrOutOfGas
}
- }
if err == nil && !contract.UseGas(evm.Accesses.TouchFullAccount(address.Bytes()[:], true)) {
err = ErrOutOfGas
}
From c5264bd8d2433d473c7904882919aec469c74584 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Mon, 25 Mar 2024 15:17:12 +0100
Subject: [PATCH 12/25] fix: EXTCODECOPY gas consumption
---
core/state_processor_test.go | 2 +-
core/vm/eips.go | 1 +
core/vm/operations_verkle.go | 18 ++++++++++++++++++
3 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/core/state_processor_test.go b/core/state_processor_test.go
index 25a7d28fea61..991f4e2de68e 100644
--- a/core/state_processor_test.go
+++ b/core/state_processor_test.go
@@ -499,7 +499,7 @@ func TestProcessVerkle(t *testing.T) {
txCost1 := params.TxGas
txCost2 := params.TxGas
contractCreationCost := intrinsicContractCreationGas + uint64(5600+700+700+700 /* creation with value */ +1439 /* execution costs */)
- codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(5600+700 /* creation */ +46544 /* execution costs */)
+ codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(5600+700 /* creation */ +44044 /* execution costs */)
blockGasUsagesExpected := []uint64{
txCost1*2 + txCost2,
txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas,
diff --git a/core/vm/eips.go b/core/vm/eips.go
index 2703c8316db2..128bd13044e3 100644
--- a/core/vm/eips.go
+++ b/core/vm/eips.go
@@ -313,6 +313,7 @@ func enable4762(jt *JumpTable) {
jt[BALANCE].dynamicGas = gasBalance4762
jt[EXTCODESIZE].dynamicGas = gasExtCodeSize4762
jt[EXTCODEHASH].dynamicGas = gasExtCodeHash4762
+ jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP4762
jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP4762
jt[CREATE].constantGas = params.CreateNGasEip4762
jt[CREATE2].constantGas = params.CreateNGasEip4762
diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go
index bc255480fe85..ec7c3bea6dfc 100644
--- a/core/vm/operations_verkle.go
+++ b/core/vm/operations_verkle.go
@@ -18,6 +18,7 @@ package vm
import (
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/params"
)
@@ -91,3 +92,20 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem
}
return 0, nil
}
+
+func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ // memory expansion first (dynamic part of pre-2929 implementation)
+ gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
+ if err != nil {
+ return 0, err
+ }
+ addr := common.Address(stack.peek().Bytes20())
+ wgas := evm.Accesses.TouchVersion(addr[:], false)
+ wgas += evm.Accesses.TouchCodeSize(addr[:], false)
+ var overflow bool
+ // We charge (cold-warm), since 'warm' is already charged as constantGas
+ if gas, overflow = math.SafeAdd(gas, wgas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+}
From 26ad438cb6906db8a95a882bf001a3d74ab03601 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Mon, 25 Mar 2024 16:57:04 +0100
Subject: [PATCH 13/25] fix warm gas costs
---
core/vm/eips.go | 8 ++++++++
core/vm/gas_table.go | 12 ++++++------
core/vm/instructions.go | 2 --
core/vm/interpreter.go | 2 +-
core/vm/operations_verkle.go | 30 ++++++++++++++++++++++++------
5 files changed, 39 insertions(+), 15 deletions(-)
diff --git a/core/vm/eips.go b/core/vm/eips.go
index 128bd13044e3..1df79f6cffd0 100644
--- a/core/vm/eips.go
+++ b/core/vm/eips.go
@@ -311,14 +311,22 @@ func enable4762(jt *JumpTable) {
jt[SLOAD].constantGas = 0
jt[SLOAD].dynamicGas = gasSLoad4762
jt[BALANCE].dynamicGas = gasBalance4762
+ jt[BALANCE].constantGas = 0
+ jt[EXTCODESIZE].constantGas = 0
jt[EXTCODESIZE].dynamicGas = gasExtCodeSize4762
+ jt[EXTCODEHASH].constantGas = 0
jt[EXTCODEHASH].dynamicGas = gasExtCodeHash4762
+ jt[EXTCODECOPY].constantGas = 0
jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP4762
jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP4762
jt[CREATE].constantGas = params.CreateNGasEip4762
jt[CREATE2].constantGas = params.CreateNGasEip4762
+ jt[CALL].constantGas = 0
jt[CALL].dynamicGas = gasCallEIP4762
+ jt[CALLCODE].constantGas = 0
jt[CALLCODE].dynamicGas = gasCallCodeEIP4762
+ jt[STATICCALL].constantGas = 0
jt[STATICCALL].dynamicGas = gasStaticCallEIP4762
+ jt[DELEGATECALL].constantGas = 0
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP4762
}
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index 64b6df5cb454..d1ffaf6b60f9 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -391,7 +391,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
} else if !evm.StateDB.Exist(address) {
gas += params.CallNewAccountGas
}
- if transfersValue {
+ if transfersValue && !evm.chainRules.IsEIP4762 {
gas += params.CallValueTransferGas
}
memoryGas, err := memoryGasCost(mem, memorySize)
@@ -410,7 +410,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
return 0, ErrGasUintOverflow
}
- if evm.chainRules.IsPrague {
+ if evm.chainRules.IsEIP4762 {
if _, isPrecompile := evm.precompile(address); !isPrecompile {
gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes()[:]))
if overflow {
@@ -437,7 +437,7 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
gas uint64
overflow bool
)
- if stack.Back(2).Sign() != 0 {
+ if stack.Back(2).Sign() != 0 && !evm.chainRules.IsEIP4762 {
gas += params.CallValueTransferGas
}
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
@@ -450,7 +450,7 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
return 0, ErrGasUintOverflow
}
- if evm.chainRules.IsPrague {
+ if evm.chainRules.IsEIP4762 {
address := common.Address(stack.Back(1).Bytes20())
if _, isPrecompile := evm.precompile(address); !isPrecompile {
gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes()))
@@ -475,7 +475,7 @@ func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
return 0, ErrGasUintOverflow
}
- if evm.chainRules.IsPrague {
+ if evm.chainRules.IsEIP4762 {
address := common.Address(stack.Back(1).Bytes20())
if _, isPrecompile := evm.precompile(address); !isPrecompile {
gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes()))
@@ -500,7 +500,7 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
return 0, ErrGasUintOverflow
}
- if evm.chainRules.IsPrague {
+ if evm.chainRules.IsEIP4762 {
address := common.Address(stack.Back(1).Bytes20())
if _, isPrecompile := evm.precompile(address); !isPrecompile {
gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes()))
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index e8928d561fc3..7760916e1635 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -402,8 +402,6 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
}
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
statelessGas := interpreter.evm.Accesses.TouchCodeChunksRangeAndChargeGas(addr[:], copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false)
- statelessGas += interpreter.evm.Accesses.TouchVersion(addr[:], false)
- statelessGas += interpreter.evm.Accesses.TouchCodeSize(addr[:], false)
if !scope.Contract.UseGas(statelessGas) {
scope.Contract.Gas = 0
return nil, ErrOutOfGas
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 46bbfd0105f7..427ac4e44fc5 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -179,7 +179,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
logged, pcCopy, gasCopy = false, pc, contract.Gas
}
- if in.evm.chainRules.IsPrague && !contract.IsDeployment {
+ if in.evm.chainRules.IsEIP4762 && !contract.IsDeployment {
// if the PC ends up in a new "chunk" of verkleized code, charge the
// associated costs.
contractAddr := contract.Address()
diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go
index ec7c3bea6dfc..a43a86e71159 100644
--- a/core/vm/operations_verkle.go
+++ b/core/vm/operations_verkle.go
@@ -40,18 +40,31 @@ func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
address := stack.peek().Bytes20()
- return evm.Accesses.TouchBalance(address[:], false), nil
+ gas := evm.Accesses.TouchBalance(address[:], false)
+ if gas == 0 {
+ gas = params.WarmStorageReadCostEIP2929
+ }
+ return gas, nil
}
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
address := stack.peek().Bytes20()
- versiongas := evm.Accesses.TouchVersion(address[:], false)
- return versiongas + evm.Accesses.TouchCodeSize(address[:], false), nil
+ wgas := evm.Accesses.TouchVersion(address[:], false)
+ wgas += evm.Accesses.TouchCodeSize(address[:], false)
+ if wgas == 0 {
+ wgas = params.WarmStorageReadCostEIP2929
+ }
+
+ return wgas, nil
}
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
address := stack.peek().Bytes20()
- return evm.Accesses.TouchCodeHash(address[:], false), nil
+ codehashgas := evm.Accesses.TouchCodeHash(address[:], false)
+ if codehashgas == 0 {
+ codehashgas = params.WarmStorageReadCostEIP2929
+ }
+ return codehashgas, nil
}
func makeCallVariantGasEIP4762(oldCalculator gasFunc) gasFunc {
@@ -61,8 +74,10 @@ func makeCallVariantGasEIP4762(oldCalculator gasFunc) gasFunc {
return 0, err
}
wgas := evm.Accesses.TouchCodeSize(contract.Address().Bytes(), false)
- gas += wgas
- wgas = evm.Accesses.TouchCodeHash(contract.Address().Bytes(), false)
+ wgas += evm.Accesses.TouchCodeHash(contract.Address().Bytes(), false)
+ if wgas == 0 {
+ wgas = params.WarmStorageReadCostEIP2929
+ }
return wgas + gas, nil
}
}
@@ -102,6 +117,9 @@ func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memo
addr := common.Address(stack.peek().Bytes20())
wgas := evm.Accesses.TouchVersion(addr[:], false)
wgas += evm.Accesses.TouchCodeSize(addr[:], false)
+ if wgas == 0 {
+ wgas = params.WarmStorageReadCostEIP2929
+ }
var overflow bool
// We charge (cold-warm), since 'warm' is already charged as constantGas
if gas, overflow = math.SafeAdd(gas, wgas); overflow {
From 9d7d5bc9bc3f0ea27617c918a0fb4c939005954b Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Mon, 25 Mar 2024 17:10:34 +0100
Subject: [PATCH 14/25] fix the order gas is charged in during contract
creation epilogue
---
core/vm/evm.go | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 2ee5728960d2..c95401582050 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -520,11 +520,12 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
err = ErrCodeStoreOutOfGas
}
} else {
- // Contract creation completed, touch the missing field in the contract
- if len(ret) > 0 && !contract.UseGas(evm.Accesses.TouchCodeChunksRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true)) {
- err = ErrOutOfGas
- }
- if err == nil && !contract.UseGas(evm.Accesses.TouchFullAccount(address.Bytes()[:], true)) {
+ // Contract creation completed, touch the missing fields in the contract
+ if !contract.UseGas(evm.Accesses.TouchFullAccount(address.Bytes()[:], true)) {
+ err = ErrOutOfGas
+ }
+
+ if err != nil && len(ret) > 0 && !contract.UseGas(evm.Accesses.TouchCodeChunksRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true)) {
err = ErrOutOfGas
}
}
From 30eea50ad090e56028603db148d7032a21c94f29 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Tue, 26 Mar 2024 15:38:33 +0100
Subject: [PATCH 15/25] fix selfdestruct
---
core/vm/operations_verkle.go | 25 +++++++++++++------------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go
index a43a86e71159..70219a5a316d 100644
--- a/core/vm/operations_verkle.go
+++ b/core/vm/operations_verkle.go
@@ -92,20 +92,20 @@ var (
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
beneficiaryAddr := common.Address(stack.peek().Bytes20())
contractAddr := contract.Address()
- // If the beneficiary isn't the contract, we need to touch the beneficiary's balance.
- // If the beneficiary is the contract itself, there're two possibilities:
- // 1. The contract was created in the same transaction: the balance is already touched (no need to touch again)
- // 2. The contract wasn't created in the same transaction: there's no net change in balance,
- // and SELFDESTRUCT will perform no action on the account header. (we touch since we did SubBalance+AddBalance above)
- if contractAddr != beneficiaryAddr || evm.StateDB.WasCreatedInCurrentTx(contractAddr) {
- statelessGas := evm.Accesses.TouchBalance(beneficiaryAddr[:], false)
- if !contract.UseGas(statelessGas) {
- contract.Gas = 0
- return 0, ErrOutOfGas
+ statelessGas := evm.Accesses.TouchVersion(contractAddr[:], false)
+ statelessGas += evm.Accesses.TouchCodeSize(contractAddr[:], false)
+ statelessGas += evm.Accesses.TouchBalance(contractAddr[:], false)
+ if contractAddr != beneficiaryAddr {
+ statelessGas += evm.Accesses.TouchBalance(beneficiaryAddr[:], false)
+ }
+ // Charge write costs if it transfers value
+ if evm.StateDB.GetBalance(contractAddr).Sign() != 0 {
+ statelessGas += evm.Accesses.TouchBalance(contractAddr[:], true)
+ if contractAddr != beneficiaryAddr {
+ statelessGas += evm.Accesses.TouchBalance(beneficiaryAddr[:], true)
}
- return statelessGas, nil
}
- return 0, nil
+ return statelessGas, nil
}
func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
@@ -127,3 +127,4 @@ func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memo
}
return gas, nil
}
+
From f89dc814e6d3de79fc807514f41b026cf3c3222c Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Thu, 28 Mar 2024 17:32:16 +0100
Subject: [PATCH 16/25] fix #365 in eip rewrite (#407)
---
core/state_processor.go | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/core/state_processor.go b/core/state_processor.go
index eb5258f76fdf..fbc6beda4a08 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -368,7 +368,13 @@ func (kvm *keyValueMigrator) migrateCollectedKeyValues(tree *trie.VerkleTrie) er
return nil
}
+// InsertBlockHashHistoryAtEip2935Fork handles the insertion of all previous 256
+// blocks on the eip2935 activation block. It also adds the account header of the
+// history contract to the witness.
func InsertBlockHashHistoryAtEip2935Fork(statedb *state.StateDB, prevNumber uint64, prevHash common.Hash, chain consensus.ChainHeaderReader) {
+ // Make sure that the historical contract is added to the witness
+ statedb.Witness().TouchFullAccount(params.HistoryStorageAddress[:], true)
+
ancestor := chain.GetHeader(prevHash, prevNumber)
for i := prevNumber; i > 0 && i >= prevNumber-params.Eip2935BlockHashHistorySize; i-- {
ProcessParentBlockHash(statedb, i, ancestor.Hash())
From b5e2ba62c39924f70f780a044e06461e36732d48 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Wed, 3 Apr 2024 18:30:11 +0200
Subject: [PATCH 17/25] fix: OOG type in code creation OOG (#408)
---
core/vm/evm.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/core/vm/evm.go b/core/vm/evm.go
index c95401582050..a8ec9f7a061a 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -522,11 +522,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
} else {
// Contract creation completed, touch the missing fields in the contract
if !contract.UseGas(evm.Accesses.TouchFullAccount(address.Bytes()[:], true)) {
- err = ErrOutOfGas
+ err = ErrCodeStoreOutOfGas
}
if err != nil && len(ret) > 0 && !contract.UseGas(evm.Accesses.TouchCodeChunksRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true)) {
- err = ErrOutOfGas
+ err = ErrCodeStoreOutOfGas
}
}
From ccb4692ceb386d2cfe1270672b9fbb9b4efa1e5b Mon Sep 17 00:00:00 2001
From: Ignacio Hagopian
Date: Fri, 5 Apr 2024 12:23:40 -0300
Subject: [PATCH 18/25] core/vm: charge BLOCKHASH witness cost (#409)
* core/vm: charge BLOCKHASH witness cost
Signed-off-by: Ignacio Hagopian
* remove gas optimization for now
Signed-off-by: Ignacio Hagopian
---------
Signed-off-by: Ignacio Hagopian
---
core/vm/instructions.go | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 7760916e1635..ef3472d8acd9 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -458,12 +458,12 @@ func opGasprice(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
return nil, nil
}
-func getBlockHashFromContract(number uint64, statedb StateDB, witness *state.AccessWitness) common.Hash {
+func getBlockHashFromContract(number uint64, statedb StateDB, witness *state.AccessWitness) (common.Hash, uint64) {
ringIndex := number % params.Eip2935BlockHashHistorySize
var pnum common.Hash
binary.BigEndian.PutUint64(pnum[24:], ringIndex)
- witness.TouchSlotAndChargeGas(params.HistoryStorageAddress[:], pnum, false)
- return statedb.GetState(params.HistoryStorageAddress, pnum)
+ statelessGas := witness.TouchSlotAndChargeGas(params.HistoryStorageAddress[:], pnum, false)
+ return statedb.GetState(params.HistoryStorageAddress, pnum), statelessGas
}
func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
@@ -486,7 +486,13 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
if num64 >= lower && num64 < upper {
// if Prague is active, read it from the history contract (EIP 2935).
if evm.chainRules.IsPrague {
- num.SetBytes(getBlockHashFromContract(num64, evm.StateDB, evm.Accesses).Bytes())
+ blockHash, statelessGas := getBlockHashFromContract(num64, evm.StateDB, evm.Accesses)
+ if interpreter.evm.chainRules.IsEIP4762 {
+ if !scope.Contract.UseGas(statelessGas) {
+ return nil, ErrExecutionReverted
+ }
+ }
+ num.SetBytes(blockHash.Bytes())
} else {
num.SetBytes(interpreter.evm.Context.GetHash(num64).Bytes())
}
From 48c51edbb21b17765e670d9aa71fffe732241a39 Mon Sep 17 00:00:00 2001
From: Ignacio Hagopian
Date: Mon, 8 Apr 2024 16:12:36 -0300
Subject: [PATCH 19/25] remove redundant logic for contract creation (#413)
Signed-off-by: Ignacio Hagopian
---
core/state_transition.go | 7 -------
1 file changed, 7 deletions(-)
diff --git a/core/state_transition.go b/core/state_transition.go
index 49b35dd81087..0aa4a369018b 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -27,7 +27,6 @@ import (
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
@@ -411,7 +410,6 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
if !tryConsumeGas(&st.gasRemaining, statelessGasOrigin) {
return nil, fmt.Errorf("%w: Insufficient funds to cover witness access costs for transaction: have %d, want %d", ErrInsufficientBalanceWitness, st.gasRemaining, gas)
}
- originNonce := st.evm.StateDB.GetNonce(originAddr)
if msg.To != nil {
statelessGasDest := st.evm.Accesses.TouchTxExistingAndComputeGas(targetAddr.Bytes(), msg.Value.Sign() != 0)
@@ -421,11 +419,6 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
// ensure the code size ends up in the access witness
st.evm.StateDB.GetCodeSize(*targetAddr)
- } else {
- contractAddr := crypto.CreateAddress(originAddr, originNonce)
- if !tryConsumeGas(&st.gasRemaining, st.evm.Accesses.TouchAndChargeContractCreateInit(contractAddr.Bytes(), msg.Value.Sign() != 0)) {
- return nil, fmt.Errorf("%w: Insufficient funds to cover witness access costs for transaction: have %d, want %d", ErrInsufficientBalanceWitness, st.gasRemaining, gas)
- }
}
}
From 33ff60f7ab4a2fe5062ef659ebd46e6125b7cd22 Mon Sep 17 00:00:00 2001
From: Ignacio Hagopian
Date: Tue, 9 Apr 2024 06:34:13 -0300
Subject: [PATCH 20/25] fix precompile address check for charging witness costs
& fix missing value-bearing rule (#412)
Signed-off-by: Ignacio Hagopian
---
core/vm/gas_table.go | 29 +++--------------------------
core/vm/operations_verkle.go | 18 ++++++++++++++----
2 files changed, 17 insertions(+), 30 deletions(-)
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index d1ffaf6b60f9..5f8183801f1c 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -411,12 +411,6 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
return 0, ErrGasUintOverflow
}
if evm.chainRules.IsEIP4762 {
- if _, isPrecompile := evm.precompile(address); !isPrecompile {
- gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes()[:]))
- if overflow {
- return 0, ErrGasUintOverflow
- }
- }
if transfersValue {
gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeValueTransfer(contract.Address().Bytes()[:], address.Bytes()[:]))
if overflow {
@@ -452,8 +446,9 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
}
if evm.chainRules.IsEIP4762 {
address := common.Address(stack.Back(1).Bytes20())
- if _, isPrecompile := evm.precompile(address); !isPrecompile {
- gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes()))
+ transfersValue := !stack.Back(2).IsZero()
+ if transfersValue {
+ gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeValueTransfer(contract.Address().Bytes()[:], address.Bytes()[:]))
if overflow {
return 0, ErrGasUintOverflow
}
@@ -475,15 +470,6 @@ func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
return 0, ErrGasUintOverflow
}
- if evm.chainRules.IsEIP4762 {
- address := common.Address(stack.Back(1).Bytes20())
- if _, isPrecompile := evm.precompile(address); !isPrecompile {
- gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes()))
- if overflow {
- return 0, ErrGasUintOverflow
- }
- }
- }
return gas, nil
}
@@ -500,15 +486,6 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
return 0, ErrGasUintOverflow
}
- if evm.chainRules.IsEIP4762 {
- address := common.Address(stack.Back(1).Bytes20())
- if _, isPrecompile := evm.precompile(address); !isPrecompile {
- gas, overflow = math.SafeAdd(gas, evm.Accesses.TouchAndChargeMessageCall(address.Bytes()))
- if overflow {
- return 0, ErrGasUintOverflow
- }
- }
- }
return gas, nil
}
diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go
index 70219a5a316d..152b8a9646a2 100644
--- a/core/vm/operations_verkle.go
+++ b/core/vm/operations_verkle.go
@@ -49,17 +49,22 @@ func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
address := stack.peek().Bytes20()
+ if _, isPrecompile := evm.precompile(address); isPrecompile {
+ return 0, nil
+ }
wgas := evm.Accesses.TouchVersion(address[:], false)
wgas += evm.Accesses.TouchCodeSize(address[:], false)
if wgas == 0 {
wgas = params.WarmStorageReadCostEIP2929
}
-
return wgas, nil
}
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
address := stack.peek().Bytes20()
+ if _, isPrecompile := evm.precompile(address); isPrecompile {
+ return 0, nil
+ }
codehashgas := evm.Accesses.TouchCodeHash(address[:], false)
if codehashgas == 0 {
codehashgas = params.WarmStorageReadCostEIP2929
@@ -73,8 +78,10 @@ func makeCallVariantGasEIP4762(oldCalculator gasFunc) gasFunc {
if err != nil {
return 0, err
}
- wgas := evm.Accesses.TouchCodeSize(contract.Address().Bytes(), false)
- wgas += evm.Accesses.TouchCodeHash(contract.Address().Bytes(), false)
+ if _, isPrecompile := evm.precompile(contract.Address()); isPrecompile {
+ return gas, nil
+ }
+ wgas := evm.Accesses.TouchAndChargeMessageCall(contract.Address().Bytes())
if wgas == 0 {
wgas = params.WarmStorageReadCostEIP2929
}
@@ -91,6 +98,10 @@ var (
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
beneficiaryAddr := common.Address(stack.peek().Bytes20())
+ if _, isPrecompile := evm.precompile(beneficiaryAddr); isPrecompile {
+ return 0, nil
+ }
+
contractAddr := contract.Address()
statelessGas := evm.Accesses.TouchVersion(contractAddr[:], false)
statelessGas += evm.Accesses.TouchCodeSize(contractAddr[:], false)
@@ -127,4 +138,3 @@ func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memo
}
return gas, nil
}
-
From 342722e96614de57820f6b31d1b9e5ebccf906b0 Mon Sep 17 00:00:00 2001
From: Ignacio Hagopian
Date: Tue, 9 Apr 2024 09:42:18 -0300
Subject: [PATCH 21/25] core/vm: fix wrong check (#416)
Signed-off-by: Ignacio Hagopian
---
core/vm/evm.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core/vm/evm.go b/core/vm/evm.go
index a8ec9f7a061a..3278856f39a8 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -525,7 +525,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
err = ErrCodeStoreOutOfGas
}
- if err != nil && len(ret) > 0 && !contract.UseGas(evm.Accesses.TouchCodeChunksRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true)) {
+ if err == nil && len(ret) > 0 && !contract.UseGas(evm.Accesses.TouchCodeChunksRangeAndChargeGas(address.Bytes(), 0, uint64(len(ret)), uint64(len(ret)), true)) {
err = ErrCodeStoreOutOfGas
}
}
From ea20444ea3e554e21e32621bb9a380417ca6d5cb Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Wed, 10 Apr 2024 15:21:30 +0200
Subject: [PATCH 22/25] charge for account creation if selfdestruct creates a
new account (#417)
---
core/vm/operations_verkle.go | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go
index 152b8a9646a2..8b55b83bb21f 100644
--- a/core/vm/operations_verkle.go
+++ b/core/vm/operations_verkle.go
@@ -115,6 +115,13 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem
if contractAddr != beneficiaryAddr {
statelessGas += evm.Accesses.TouchBalance(beneficiaryAddr[:], true)
}
+
+ // Case when the beneficiary does not exist: touch the account
+ // but leave code hash and size alone.
+ if evm.StateDB.Empty(beneficiaryAddr) {
+ statelessGas += evm.Accesses.TouchVersion(beneficiaryAddr[:], true)
+ statelessGas += evm.Accesses.TouchNonce(beneficiaryAddr[:], true)
+ }
}
return statelessGas, nil
}
From 3a40da05e00e74f94dbbe519739be8069666d024 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Thu, 11 Apr 2024 21:10:44 +0200
Subject: [PATCH 23/25] add key comparison test (#418)
---
trie/utils/verkle_test.go | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/trie/utils/verkle_test.go b/trie/utils/verkle_test.go
index 35cae6e03349..ea6b733feddb 100644
--- a/trie/utils/verkle_test.go
+++ b/trie/utils/verkle_test.go
@@ -17,10 +17,11 @@
package utils
import (
+ "bytes"
+ "crypto/rand"
"crypto/sha256"
"encoding/hex"
"math/big"
- "math/rand"
"testing"
"github.com/ethereum/go-verkle"
@@ -93,3 +94,22 @@ func BenchmarkSha256Hash(b *testing.B) {
sha256GetTreeKeyCodeSize(addr[:])
}
}
+
+func TestCompareGetTreeKeyWithEvaluated(t *testing.T) {
+ var addr [32]byte
+ rand.Read(addr[:])
+ addrpoint := EvaluateAddressPoint(addr[:])
+ for i := 0; i < 100; i++ {
+ var val [32]byte
+ rand.Read(val[:])
+ n := uint256.NewInt(0).SetBytes(val[:])
+ n.Lsh(n, 8)
+ subindex := byte(val[0])
+ tk1 := GetTreeKey(addr[:], n, subindex)
+ tk2 := GetTreeKeyWithEvaluatedAddess(addrpoint, n, subindex)
+
+ if !bytes.Equal(tk1, tk2) {
+ t.Fatalf("differing key: slot=%x, addr=%x", val, addr)
+ }
+ }
+}
From 29337311247ad8b923f61018de02835b9f5c4b86 Mon Sep 17 00:00:00 2001
From: Ignacio Hagopian
Date: Fri, 12 Apr 2024 05:20:34 -0300
Subject: [PATCH 24/25] core/vm: charge contract init before execution logic
(#419)
* core/vm: charge contract init before execution logic
Signed-off-by: Ignacio Hagopian
* fix CREATE2 as well
---------
Signed-off-by: Ignacio Hagopian
Co-authored-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
---
core/vm/instructions.go | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index ef3472d8acd9..7a6db2e8eaaa 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -628,16 +628,8 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
if interpreter.readOnly {
return nil, ErrWriteProtection
}
- var (
- value = scope.Stack.pop()
- offset, size = scope.Stack.pop(), scope.Stack.pop()
- input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
- gas = scope.Contract.Gas
- )
- if interpreter.evm.chainRules.IsEIP150 {
- gas -= gas / 64
- }
+ value := scope.Stack.pop()
if interpreter.evm.chainRules.IsEIP4762 {
contractAddress := crypto.CreateAddress(scope.Contract.Address(), interpreter.evm.StateDB.GetNonce(scope.Contract.Address()))
statelessGas := interpreter.evm.Accesses.TouchAndChargeContractCreateInit(contractAddress.Bytes()[:], value.Sign() != 0)
@@ -646,6 +638,15 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
}
}
+ var (
+ offset, size = scope.Stack.pop(), scope.Stack.pop()
+ input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
+ gas = scope.Contract.Gas
+ )
+ if interpreter.evm.chainRules.IsEIP150 {
+ gas -= gas / 64
+ }
+
// reuse size int for stackvalue
stackvalue := size
@@ -688,7 +689,6 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
offset, size = scope.Stack.pop(), scope.Stack.pop()
salt = scope.Stack.pop()
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
- gas = scope.Contract.Gas
)
if interpreter.evm.chainRules.IsEIP4762 {
codeAndHash := &codeAndHash{code: input}
@@ -699,6 +699,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
}
}
+ var gas = scope.Contract.Gas
// Apply EIP150
gas -= gas / 64
scope.Contract.UseGas(gas)
From 06a92923b306fa4d669caafc92994d3433a09bd2 Mon Sep 17 00:00:00 2001
From: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
Date: Mon, 15 Apr 2024 13:38:01 +0200
Subject: [PATCH 25/25] quell linter
---
trie/utils/verkle_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/trie/utils/verkle_test.go b/trie/utils/verkle_test.go
index ea6b733feddb..a5aec55a0660 100644
--- a/trie/utils/verkle_test.go
+++ b/trie/utils/verkle_test.go
@@ -104,7 +104,7 @@ func TestCompareGetTreeKeyWithEvaluated(t *testing.T) {
rand.Read(val[:])
n := uint256.NewInt(0).SetBytes(val[:])
n.Lsh(n, 8)
- subindex := byte(val[0])
+ subindex := val[0]
tk1 := GetTreeKey(addr[:], n, subindex)
tk2 := GetTreeKeyWithEvaluatedAddess(addrpoint, n, subindex)