cmd, core, params, trie: add verkle access witness gas charging (#29338)
Implements some of the changes required to charge and do gas accounting in verkle testnet.
This commit is contained in:
parent
47af69c2bc
commit
44a50c9f96
@ -103,17 +103,17 @@ func TestAttachWelcome(t *testing.T) {
|
|||||||
"--http", "--http.port", httpPort,
|
"--http", "--http.port", httpPort,
|
||||||
"--ws", "--ws.port", wsPort)
|
"--ws", "--ws.port", wsPort)
|
||||||
t.Run("ipc", func(t *testing.T) {
|
t.Run("ipc", func(t *testing.T) {
|
||||||
waitForEndpoint(t, ipc, 3*time.Second)
|
waitForEndpoint(t, ipc, 4*time.Second)
|
||||||
testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs)
|
testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs)
|
||||||
})
|
})
|
||||||
t.Run("http", func(t *testing.T) {
|
t.Run("http", func(t *testing.T) {
|
||||||
endpoint := "http://127.0.0.1:" + httpPort
|
endpoint := "http://127.0.0.1:" + httpPort
|
||||||
waitForEndpoint(t, endpoint, 3*time.Second)
|
waitForEndpoint(t, endpoint, 4*time.Second)
|
||||||
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
||||||
})
|
})
|
||||||
t.Run("ws", func(t *testing.T) {
|
t.Run("ws", func(t *testing.T) {
|
||||||
endpoint := "ws://127.0.0.1:" + wsPort
|
endpoint := "ws://127.0.0.1:" + wsPort
|
||||||
waitForEndpoint(t, endpoint, 3*time.Second)
|
waitForEndpoint(t, endpoint, 4*time.Second)
|
||||||
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
||||||
})
|
})
|
||||||
geth.Kill()
|
geth.Kill()
|
||||||
|
@ -28,7 +28,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/gballet/go-verkle"
|
"github.com/ethereum/go-verkle"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
"github.com/gballet/go-verkle"
|
"github.com/ethereum/go-verkle"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -64,6 +64,11 @@ var (
|
|||||||
// than init code size limit.
|
// than init code size limit.
|
||||||
ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded")
|
ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded")
|
||||||
|
|
||||||
|
// ErrInsufficientBalanceWitness is returned if the transaction sender has enough
|
||||||
|
// funds to cover the transfer, but not enough to pay for witness access/modification
|
||||||
|
// costs for the transaction
|
||||||
|
ErrInsufficientBalanceWitness = errors.New("insufficient funds to cover witness access costs for transaction")
|
||||||
|
|
||||||
// ErrInsufficientFunds is returned if the total cost of executing a transaction
|
// ErrInsufficientFunds is returned if the total cost of executing a transaction
|
||||||
// is higher than the balance of the user's account.
|
// is higher than the balance of the user's account.
|
||||||
ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
|
ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
|
||||||
|
320
core/state/access_events.go
Normal file
320
core/state/access_events.go
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
// Copyright 2021 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"maps"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// mode specifies how a tree location has been accessed
|
||||||
|
// for the byte value:
|
||||||
|
// * the first bit is set if the branch has been edited
|
||||||
|
// * the second bit is set if the branch has been read
|
||||||
|
type mode byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
AccessWitnessReadFlag = mode(1)
|
||||||
|
AccessWitnessWriteFlag = mode(2)
|
||||||
|
)
|
||||||
|
|
||||||
|
var zeroTreeIndex uint256.Int
|
||||||
|
|
||||||
|
// AccessEvents lists the locations of the state that are being accessed
|
||||||
|
// during the production of a block.
|
||||||
|
type AccessEvents struct {
|
||||||
|
branches map[branchAccessKey]mode
|
||||||
|
chunks map[chunkAccessKey]mode
|
||||||
|
|
||||||
|
pointCache *utils.PointCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAccessEvents(pointCache *utils.PointCache) *AccessEvents {
|
||||||
|
return &AccessEvents{
|
||||||
|
branches: make(map[branchAccessKey]mode),
|
||||||
|
chunks: make(map[chunkAccessKey]mode),
|
||||||
|
pointCache: pointCache,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge is used to merge the access events that were generated during the
|
||||||
|
// execution of a tx, with the accumulation of all access events that were
|
||||||
|
// generated during the execution of all txs preceding this one in a block.
|
||||||
|
func (ae *AccessEvents) Merge(other *AccessEvents) {
|
||||||
|
for k := range other.branches {
|
||||||
|
ae.branches[k] |= other.branches[k]
|
||||||
|
}
|
||||||
|
for k, chunk := range other.chunks {
|
||||||
|
ae.chunks[k] |= chunk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns, predictably, the list of keys that were touched during the
|
||||||
|
// buildup of the access witness.
|
||||||
|
func (ae *AccessEvents) Keys() [][]byte {
|
||||||
|
// TODO: consider if parallelizing this is worth it, probably depending on len(ae.chunks).
|
||||||
|
keys := make([][]byte, 0, len(ae.chunks))
|
||||||
|
for chunk := range ae.chunks {
|
||||||
|
basePoint := ae.pointCache.Get(chunk.addr[:])
|
||||||
|
key := utils.GetTreeKeyWithEvaluatedAddress(basePoint, &chunk.treeIndex, chunk.leafKey)
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ae *AccessEvents) Copy() *AccessEvents {
|
||||||
|
cpy := &AccessEvents{
|
||||||
|
branches: maps.Clone(ae.branches),
|
||||||
|
chunks: maps.Clone(ae.chunks),
|
||||||
|
pointCache: ae.pointCache,
|
||||||
|
}
|
||||||
|
return cpy
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAccount returns the gas to be charged for each of the currently cold
|
||||||
|
// member fields of an account.
|
||||||
|
func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 {
|
||||||
|
var gas uint64
|
||||||
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, isWrite)
|
||||||
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, isWrite)
|
||||||
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, isWrite)
|
||||||
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, isWrite)
|
||||||
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, isWrite)
|
||||||
|
return gas
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageCallGas returns the gas to be charged for each of the currently
|
||||||
|
// cold member fields of an account, that need to be touched when making a message
|
||||||
|
// call to that account.
|
||||||
|
func (ae *AccessEvents) MessageCallGas(destination common.Address) uint64 {
|
||||||
|
var gas uint64
|
||||||
|
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.VersionLeafKey, false)
|
||||||
|
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.CodeSizeLeafKey, false)
|
||||||
|
return gas
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueTransferGas returns the gas to be charged for each of the currently
|
||||||
|
// cold balance member fields of the caller and the callee accounts.
|
||||||
|
func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address) uint64 {
|
||||||
|
var gas uint64
|
||||||
|
gas += ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
|
||||||
|
gas += ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
|
||||||
|
return gas
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContractCreateInitGas returns the access gas costs for the initialization of
|
||||||
|
// a contract creation.
|
||||||
|
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, createSendsValue bool) uint64 {
|
||||||
|
var gas uint64
|
||||||
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, true)
|
||||||
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, true)
|
||||||
|
if createSendsValue {
|
||||||
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, true)
|
||||||
|
}
|
||||||
|
return gas
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTxOrigin adds the member fields of the sender account to the access event list,
|
||||||
|
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
||||||
|
func (ae *AccessEvents) AddTxOrigin(originAddr common.Address) {
|
||||||
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.VersionLeafKey, false)
|
||||||
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
|
||||||
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.NonceLeafKey, true)
|
||||||
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeKeccakLeafKey, false)
|
||||||
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeSizeLeafKey, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTxDestination adds the member fields of the sender account to the access event list,
|
||||||
|
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
||||||
|
func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue bool) {
|
||||||
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, false)
|
||||||
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, sendsValue)
|
||||||
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, false)
|
||||||
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, false)
|
||||||
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SlotGas returns the amount of gas to be charged for a cold storage access.
|
||||||
|
func (ae *AccessEvents) SlotGas(addr common.Address, slot common.Hash, isWrite bool) uint64 {
|
||||||
|
treeIndex, subIndex := utils.StorageIndex(slot.Bytes())
|
||||||
|
return ae.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite)
|
||||||
|
}
|
||||||
|
|
||||||
|
// touchAddressAndChargeGas adds any missing access event to the access event list, and returns the cold
|
||||||
|
// access cost to be charged, if need be.
|
||||||
|
func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) uint64 {
|
||||||
|
stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := ae.touchAddress(addr, treeIndex, subIndex, isWrite)
|
||||||
|
|
||||||
|
var gas uint64
|
||||||
|
if stemRead {
|
||||||
|
gas += params.WitnessBranchReadCost
|
||||||
|
}
|
||||||
|
if selectorRead {
|
||||||
|
gas += params.WitnessChunkReadCost
|
||||||
|
}
|
||||||
|
if stemWrite {
|
||||||
|
gas += params.WitnessBranchWriteCost
|
||||||
|
}
|
||||||
|
if selectorWrite {
|
||||||
|
gas += params.WitnessChunkWriteCost
|
||||||
|
}
|
||||||
|
if selectorFill {
|
||||||
|
gas += params.WitnessChunkFillCost
|
||||||
|
}
|
||||||
|
return gas
|
||||||
|
}
|
||||||
|
|
||||||
|
// touchAddress adds any missing access event to the access event list.
|
||||||
|
func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) (bool, bool, bool, bool, bool) {
|
||||||
|
branchKey := newBranchAccessKey(addr, treeIndex)
|
||||||
|
chunkKey := newChunkAccessKey(branchKey, subIndex)
|
||||||
|
|
||||||
|
// Read access.
|
||||||
|
var branchRead, chunkRead bool
|
||||||
|
if _, hasStem := ae.branches[branchKey]; !hasStem {
|
||||||
|
branchRead = true
|
||||||
|
ae.branches[branchKey] = AccessWitnessReadFlag
|
||||||
|
}
|
||||||
|
if _, hasSelector := ae.chunks[chunkKey]; !hasSelector {
|
||||||
|
chunkRead = true
|
||||||
|
ae.chunks[chunkKey] = AccessWitnessReadFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write access.
|
||||||
|
var branchWrite, chunkWrite, chunkFill bool
|
||||||
|
if isWrite {
|
||||||
|
if (ae.branches[branchKey] & AccessWitnessWriteFlag) == 0 {
|
||||||
|
branchWrite = true
|
||||||
|
ae.branches[branchKey] |= AccessWitnessWriteFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkValue := ae.chunks[chunkKey]
|
||||||
|
if (chunkValue & AccessWitnessWriteFlag) == 0 {
|
||||||
|
chunkWrite = true
|
||||||
|
ae.chunks[chunkKey] |= AccessWitnessWriteFlag
|
||||||
|
}
|
||||||
|
// TODO: charge chunk filling costs if the leaf was previously empty in the state
|
||||||
|
}
|
||||||
|
return branchRead, chunkRead, branchWrite, chunkWrite, chunkFill
|
||||||
|
}
|
||||||
|
|
||||||
|
type branchAccessKey struct {
|
||||||
|
addr common.Address
|
||||||
|
treeIndex uint256.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBranchAccessKey(addr common.Address, treeIndex uint256.Int) branchAccessKey {
|
||||||
|
var sk branchAccessKey
|
||||||
|
sk.addr = addr
|
||||||
|
sk.treeIndex = treeIndex
|
||||||
|
return sk
|
||||||
|
}
|
||||||
|
|
||||||
|
type chunkAccessKey struct {
|
||||||
|
branchAccessKey
|
||||||
|
leafKey byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func newChunkAccessKey(branchKey branchAccessKey, leafKey byte) chunkAccessKey {
|
||||||
|
var lk chunkAccessKey
|
||||||
|
lk.branchAccessKey = branchKey
|
||||||
|
lk.leafKey = leafKey
|
||||||
|
return lk
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodeChunksRangeGas is a helper function to touch every chunk in a code range and charge witness gas costs
|
||||||
|
func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, 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
|
||||||
|
// 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 := ae.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite)
|
||||||
|
var overflow bool
|
||||||
|
statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, gas)
|
||||||
|
if overflow {
|
||||||
|
panic("overflow when adding gas")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return statelessGasCharged
|
||||||
|
}
|
||||||
|
|
||||||
|
// VersionGas adds the account's version to the accessed data, and returns the
|
||||||
|
// amount of gas that it costs.
|
||||||
|
// Note that an access in write mode implies an access in read mode, whereas an
|
||||||
|
// access in read mode does not imply an access in write mode.
|
||||||
|
func (ae *AccessEvents) VersionGas(addr common.Address, isWrite bool) uint64 {
|
||||||
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, isWrite)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceGas adds the account's balance to the accessed data, and returns the
|
||||||
|
// amount of gas that it costs.
|
||||||
|
// in write mode. If false, the charged gas corresponds to an access in read mode.
|
||||||
|
// Note that an access in write mode implies an access in read mode, whereas an access in
|
||||||
|
// read mode does not imply an access in write mode.
|
||||||
|
func (ae *AccessEvents) BalanceGas(addr common.Address, isWrite bool) uint64 {
|
||||||
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, isWrite)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NonceGas adds the account's nonce to the accessed data, and returns the
|
||||||
|
// amount of gas that it costs.
|
||||||
|
// in write mode. If false, the charged gas corresponds to an access in read mode.
|
||||||
|
// Note that an access in write mode implies an access in read mode, whereas an access in
|
||||||
|
// read mode does not imply an access in write mode.
|
||||||
|
func (ae *AccessEvents) NonceGas(addr common.Address, isWrite bool) uint64 {
|
||||||
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, isWrite)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodeSizeGas adds the account's code size to the accessed data, and returns the
|
||||||
|
// amount of gas that it costs.
|
||||||
|
// in write mode. If false, the charged gas corresponds to an access in read mode.
|
||||||
|
// Note that an access in write mode implies an access in read mode, whereas an access in
|
||||||
|
// read mode does not imply an access in write mode.
|
||||||
|
func (ae *AccessEvents) CodeSizeGas(addr common.Address, isWrite bool) uint64 {
|
||||||
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, isWrite)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodeHashGas adds the account's code hash to the accessed data, and returns the
|
||||||
|
// amount of gas that it costs.
|
||||||
|
// in write mode. If false, the charged gas corresponds to an access in read mode.
|
||||||
|
// Note that an access in write mode implies an access in read mode, whereas an access in
|
||||||
|
// read mode does not imply an access in write mode.
|
||||||
|
func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool) uint64 {
|
||||||
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, isWrite)
|
||||||
|
}
|
153
core/state/access_events_test.go
Normal file
153
core/state/access_events_test.go
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
// Copyright 2021 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/trie/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
testAddr [20]byte
|
||||||
|
testAddr2 [20]byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for i := byte(0); i < 20; i++ {
|
||||||
|
testAddr[i] = i
|
||||||
|
testAddr[2] = 2 * i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccountHeaderGas(t *testing.T) {
|
||||||
|
ae := NewAccessEvents(utils.NewPointCache(1024))
|
||||||
|
|
||||||
|
// Check cold read cost
|
||||||
|
gas := ae.VersionGas(testAddr, false)
|
||||||
|
if gas != params.WitnessBranchReadCost+params.WitnessChunkReadCost {
|
||||||
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessBranchReadCost+params.WitnessChunkReadCost)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check warm read cost
|
||||||
|
gas = ae.VersionGas(testAddr, false)
|
||||||
|
if gas != 0 {
|
||||||
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check cold read costs in the same group no longer incur the branch read cost
|
||||||
|
gas = ae.BalanceGas(testAddr, false)
|
||||||
|
if gas != params.WitnessChunkReadCost {
|
||||||
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
||||||
|
}
|
||||||
|
gas = ae.NonceGas(testAddr, false)
|
||||||
|
if gas != params.WitnessChunkReadCost {
|
||||||
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
||||||
|
}
|
||||||
|
gas = ae.CodeSizeGas(testAddr, false)
|
||||||
|
if gas != params.WitnessChunkReadCost {
|
||||||
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
||||||
|
}
|
||||||
|
gas = ae.CodeHashGas(testAddr, false)
|
||||||
|
if gas != params.WitnessChunkReadCost {
|
||||||
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check cold write cost
|
||||||
|
gas = ae.VersionGas(testAddr, true)
|
||||||
|
if gas != params.WitnessBranchWriteCost+params.WitnessChunkWriteCost {
|
||||||
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessBranchReadCost+params.WitnessBranchWriteCost)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check warm write cost
|
||||||
|
gas = ae.VersionGas(testAddr, true)
|
||||||
|
if gas != 0 {
|
||||||
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check a write without a read charges both read and write costs
|
||||||
|
gas = ae.BalanceGas(testAddr2, true)
|
||||||
|
if gas != params.WitnessBranchReadCost+params.WitnessBranchWriteCost+params.WitnessChunkWriteCost+params.WitnessChunkReadCost {
|
||||||
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessBranchReadCost+params.WitnessBranchWriteCost+params.WitnessChunkWriteCost+params.WitnessChunkReadCost)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that a write followed by a read charges nothing
|
||||||
|
gas = ae.BalanceGas(testAddr2, false)
|
||||||
|
if gas != 0 {
|
||||||
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that reading a slot from the account header only charges the
|
||||||
|
// chunk read cost.
|
||||||
|
gas = ae.SlotGas(testAddr, common.Hash{}, false)
|
||||||
|
if gas != params.WitnessChunkReadCost {
|
||||||
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestContractCreateInitGas checks that the gas cost of contract creation is correctly
|
||||||
|
// calculated.
|
||||||
|
func TestContractCreateInitGas(t *testing.T) {
|
||||||
|
ae := NewAccessEvents(utils.NewPointCache(1024))
|
||||||
|
|
||||||
|
var testAddr [20]byte
|
||||||
|
for i := byte(0); i < 20; i++ {
|
||||||
|
testAddr[i] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check cold read cost, without a value
|
||||||
|
gas := ae.ContractCreateInitGas(testAddr, false)
|
||||||
|
if gas != params.WitnessBranchWriteCost+params.WitnessBranchReadCost+params.WitnessChunkWriteCost*2+params.WitnessChunkReadCost*2 {
|
||||||
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessBranchWriteCost+params.WitnessBranchReadCost+params.WitnessChunkWriteCost*3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check warm read cost
|
||||||
|
gas = ae.ContractCreateInitGas(testAddr, false)
|
||||||
|
if gas != 0 {
|
||||||
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMessageCallGas checks that the gas cost of message calls is correctly
|
||||||
|
// calculated.
|
||||||
|
func TestMessageCallGas(t *testing.T) {
|
||||||
|
ae := NewAccessEvents(utils.NewPointCache(1024))
|
||||||
|
|
||||||
|
// Check cold read cost, without a value
|
||||||
|
gas := ae.MessageCallGas(testAddr)
|
||||||
|
if gas != params.WitnessBranchReadCost+params.WitnessChunkReadCost*2 {
|
||||||
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessBranchReadCost+params.WitnessChunkReadCost*2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that reading the version and code size of the same account does not incur the branch read cost
|
||||||
|
gas = ae.VersionGas(testAddr, false)
|
||||||
|
if gas != 0 {
|
||||||
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
|
}
|
||||||
|
gas = ae.CodeSizeGas(testAddr, false)
|
||||||
|
if gas != 0 {
|
||||||
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check warm read cost
|
||||||
|
gas = ae.MessageCallGas(testAddr)
|
||||||
|
if gas != 0 {
|
||||||
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/crate-crypto/go-ipa/banderwagon"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/lru"
|
"github.com/ethereum/go-ethereum/common/lru"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
@ -40,11 +39,8 @@ const (
|
|||||||
// Cache size granted for caching clean code.
|
// Cache size granted for caching clean code.
|
||||||
codeCacheSize = 64 * 1024 * 1024
|
codeCacheSize = 64 * 1024 * 1024
|
||||||
|
|
||||||
// commitmentSize is the size of commitment stored in cache.
|
// Number of address->curve point associations to keep.
|
||||||
commitmentSize = banderwagon.UncompressedSize
|
pointCacheSize = 4096
|
||||||
|
|
||||||
// Cache item granted for caching commitment results.
|
|
||||||
commitmentCacheItems = 64 * 1024 * 1024 / (commitmentSize + common.AddressLength)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Database wraps access to tries and contract code.
|
// Database wraps access to tries and contract code.
|
||||||
@ -67,6 +63,9 @@ type Database interface {
|
|||||||
// DiskDB returns the underlying key-value disk database.
|
// DiskDB returns the underlying key-value disk database.
|
||||||
DiskDB() ethdb.KeyValueStore
|
DiskDB() ethdb.KeyValueStore
|
||||||
|
|
||||||
|
// PointCache returns the cache holding points used in verkle tree key computation
|
||||||
|
PointCache() *utils.PointCache
|
||||||
|
|
||||||
// TrieDB returns the underlying trie database for managing trie nodes.
|
// TrieDB returns the underlying trie database for managing trie nodes.
|
||||||
TrieDB() *triedb.Database
|
TrieDB() *triedb.Database
|
||||||
}
|
}
|
||||||
@ -139,6 +138,9 @@ type Trie interface {
|
|||||||
// nodes of the longest existing prefix of the key (at least the root), ending
|
// nodes of the longest existing prefix of the key (at least the root), ending
|
||||||
// with the node that proves the absence of the key.
|
// with the node that proves the absence of the key.
|
||||||
Prove(key []byte, proofDb ethdb.KeyValueWriter) error
|
Prove(key []byte, proofDb ethdb.KeyValueWriter) error
|
||||||
|
|
||||||
|
// IsVerkle returns true if the trie is verkle-tree based
|
||||||
|
IsVerkle() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDatabase creates a backing store for state. The returned database is safe for
|
// NewDatabase creates a backing store for state. The returned database is safe for
|
||||||
@ -157,6 +159,7 @@ func NewDatabaseWithConfig(db ethdb.Database, config *triedb.Config) Database {
|
|||||||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
||||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
||||||
triedb: triedb.NewDatabase(db, config),
|
triedb: triedb.NewDatabase(db, config),
|
||||||
|
pointCache: utils.NewPointCache(pointCacheSize),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,6 +170,7 @@ func NewDatabaseWithNodeDB(db ethdb.Database, triedb *triedb.Database) Database
|
|||||||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
||||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
||||||
triedb: triedb,
|
triedb: triedb,
|
||||||
|
pointCache: utils.NewPointCache(pointCacheSize),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,12 +179,13 @@ type cachingDB struct {
|
|||||||
codeSizeCache *lru.Cache[common.Hash, int]
|
codeSizeCache *lru.Cache[common.Hash, int]
|
||||||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
||||||
triedb *triedb.Database
|
triedb *triedb.Database
|
||||||
|
pointCache *utils.PointCache
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenTrie opens the main account trie at a specific root hash.
|
// OpenTrie opens the main account trie at a specific root hash.
|
||||||
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||||
if db.triedb.IsVerkle() {
|
if db.triedb.IsVerkle() {
|
||||||
return trie.NewVerkleTrie(root, db.triedb, utils.NewPointCache(commitmentCacheItems))
|
return trie.NewVerkleTrie(root, db.triedb, db.pointCache)
|
||||||
}
|
}
|
||||||
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
|
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -266,3 +271,8 @@ func (db *cachingDB) DiskDB() ethdb.KeyValueStore {
|
|||||||
func (db *cachingDB) TrieDB() *triedb.Database {
|
func (db *cachingDB) TrieDB() *triedb.Database {
|
||||||
return db.triedb
|
return db.triedb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PointCache returns the cache of evaluated curve points.
|
||||||
|
func (db *cachingDB) PointCache() *utils.PointCache {
|
||||||
|
return db.pointCache
|
||||||
|
}
|
||||||
|
@ -37,6 +37,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||||
"github.com/ethereum/go-ethereum/trie/triestate"
|
"github.com/ethereum/go-ethereum/trie/triestate"
|
||||||
|
"github.com/ethereum/go-ethereum/trie/utils"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
@ -1327,7 +1328,10 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er
|
|||||||
// - Add coinbase to access list (EIP-3651)
|
// - Add coinbase to access list (EIP-3651)
|
||||||
// - Reset transient storage (EIP-1153)
|
// - 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) {
|
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 && rules.IsEIP4762 {
|
||||||
|
panic("eip2929 and eip4762 are both activated")
|
||||||
|
}
|
||||||
|
if rules.IsEIP2929 {
|
||||||
// Clear out any leftover from previous executions
|
// Clear out any leftover from previous executions
|
||||||
al := newAccessList()
|
al := newAccessList()
|
||||||
s.accessList = al
|
s.accessList = al
|
||||||
@ -1439,3 +1443,7 @@ func (s *StateDB) markUpdate(addr common.Address) {
|
|||||||
s.mutations[addr].applied = false
|
s.mutations[addr].applied = false
|
||||||
s.mutations[addr].typ = update
|
s.mutations[addr].typ = update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) PointCache() *utils.PointCache {
|
||||||
|
return s.db.PointCache()
|
||||||
|
}
|
||||||
|
@ -482,7 +482,7 @@ func TestProcessVerkle(t *testing.T) {
|
|||||||
txCost1 := params.TxGas
|
txCost1 := params.TxGas
|
||||||
txCost2 := params.TxGas
|
txCost2 := params.TxGas
|
||||||
contractCreationCost := intrinsicContractCreationGas + uint64(2039 /* execution costs */)
|
contractCreationCost := intrinsicContractCreationGas + uint64(2039 /* execution costs */)
|
||||||
codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(293644 /* execution costs */)
|
codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(57444 /* execution costs */)
|
||||||
blockGasUsagesExpected := []uint64{
|
blockGasUsagesExpected := []uint64{
|
||||||
txCost1*2 + txCost2,
|
txCost1*2 + txCost2,
|
||||||
txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas,
|
txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas,
|
||||||
|
@ -68,7 +68,7 @@ func (result *ExecutionResult) Revert() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
||||||
func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) {
|
func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) {
|
||||||
// Set the starting gas for the raw transaction
|
// Set the starting gas for the raw transaction
|
||||||
var gas uint64
|
var gas uint64
|
||||||
if isContractCreation && isHomestead {
|
if isContractCreation && isHomestead {
|
||||||
@ -405,6 +405,14 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
|||||||
}
|
}
|
||||||
st.gasRemaining -= gas
|
st.gasRemaining -= gas
|
||||||
|
|
||||||
|
if rules.IsEIP4762 {
|
||||||
|
st.evm.AccessEvents.AddTxOrigin(msg.From)
|
||||||
|
|
||||||
|
if targetAddr := msg.To; targetAddr != nil {
|
||||||
|
st.evm.AccessEvents.AddTxDestination(*targetAddr, msg.Value.Sign() != 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check clause 6
|
// Check clause 6
|
||||||
value, overflow := uint256.FromBig(msg.Value)
|
value, overflow := uint256.FromBig(msg.Value)
|
||||||
if overflow {
|
if overflow {
|
||||||
@ -458,6 +466,11 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
|||||||
fee := new(uint256.Int).SetUint64(st.gasUsed())
|
fee := new(uint256.Int).SetUint64(st.gasUsed())
|
||||||
fee.Mul(fee, effectiveTipU256)
|
fee.Mul(fee, effectiveTipU256)
|
||||||
st.state.AddBalance(st.evm.Context.Coinbase, fee, tracing.BalanceIncreaseRewardTransactionFee)
|
st.state.AddBalance(st.evm.Context.Coinbase, fee, tracing.BalanceIncreaseRewardTransactionFee)
|
||||||
|
|
||||||
|
// add the coinbase to the witness iff the fee is greater than 0
|
||||||
|
if rules.IsEIP4762 && fee.Sign() != 0 {
|
||||||
|
st.evm.AccessEvents.BalanceGas(st.evm.Context.Coinbase, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ExecutionResult{
|
return &ExecutionResult{
|
||||||
|
@ -298,6 +298,12 @@ const (
|
|||||||
GasChangeCallStorageColdAccess GasChangeReason = 13
|
GasChangeCallStorageColdAccess GasChangeReason = 13
|
||||||
// GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert.
|
// GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert.
|
||||||
GasChangeCallFailedExecution GasChangeReason = 14
|
GasChangeCallFailedExecution GasChangeReason = 14
|
||||||
|
// GasChangeWitnessContractInit is the amount charged for adding to the witness during the contract creation initialization step
|
||||||
|
GasChangeWitnessContractInit GasChangeReason = 15
|
||||||
|
// GasChangeWitnessContractCreation is the amount charged for adding to the witness during the contract creation finalization step
|
||||||
|
GasChangeWitnessContractCreation GasChangeReason = 16
|
||||||
|
// GasChangeWitnessCodeChunk is the amount charged for touching one or more contract code chunks
|
||||||
|
GasChangeWitnessCodeChunk GasChangeReason = 17
|
||||||
|
|
||||||
// GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as
|
// GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as
|
||||||
// it will be "manually" tracked by a direct emit of the gas change event.
|
// it will be "manually" tracked by a direct emit of the gas change event.
|
||||||
|
@ -63,6 +63,18 @@ func getData(data []byte, start uint64, size uint64) []byte {
|
|||||||
return common.RightPadBytes(data[start:end], int(size))
|
return common.RightPadBytes(data[start:end], int(size))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getDataAndAdjustedBounds(data []byte, start uint64, size uint64) (codeCopyPadded []byte, actualStart uint64, sizeNonPadded uint64) {
|
||||||
|
length := uint64(len(data))
|
||||||
|
if start > length {
|
||||||
|
start = length
|
||||||
|
}
|
||||||
|
end := start + size
|
||||||
|
if end > length {
|
||||||
|
end = length
|
||||||
|
}
|
||||||
|
return common.RightPadBytes(data[start:end], int(size)), start, end - start
|
||||||
|
}
|
||||||
|
|
||||||
// toWordSize returns the ceiled word size required for memory expansion.
|
// toWordSize returns the ceiled word size required for memory expansion.
|
||||||
func toWordSize(size uint64) uint64 {
|
func toWordSize(size uint64) uint64 {
|
||||||
if size > math.MaxUint64-31 {
|
if size > math.MaxUint64-31 {
|
||||||
|
@ -57,6 +57,9 @@ type Contract struct {
|
|||||||
CodeAddr *common.Address
|
CodeAddr *common.Address
|
||||||
Input []byte
|
Input []byte
|
||||||
|
|
||||||
|
// is the execution frame represented by this object a contract deployment
|
||||||
|
IsDeployment bool
|
||||||
|
|
||||||
Gas uint64
|
Gas uint64
|
||||||
value *uint256.Int
|
value *uint256.Int
|
||||||
}
|
}
|
||||||
|
@ -137,6 +137,8 @@ var PrecompiledContractsPrague = map[common.Address]PrecompiledContract{
|
|||||||
|
|
||||||
var PrecompiledContractsBLS = PrecompiledContractsPrague
|
var PrecompiledContractsBLS = PrecompiledContractsPrague
|
||||||
|
|
||||||
|
var PrecompiledContractsVerkle = PrecompiledContractsPrague
|
||||||
|
|
||||||
var (
|
var (
|
||||||
PrecompiledAddressesPrague []common.Address
|
PrecompiledAddressesPrague []common.Address
|
||||||
PrecompiledAddressesCancun []common.Address
|
PrecompiledAddressesCancun []common.Address
|
||||||
|
214
core/vm/eips.go
214
core/vm/eips.go
@ -18,9 +18,11 @@ package vm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
@ -37,6 +39,7 @@ var activators = map[int]func(*JumpTable){
|
|||||||
1884: enable1884,
|
1884: enable1884,
|
||||||
1344: enable1344,
|
1344: enable1344,
|
||||||
1153: enable1153,
|
1153: enable1153,
|
||||||
|
4762: enable4762,
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnableEIP enables the given EIP on the config.
|
// EnableEIP enables the given EIP on the config.
|
||||||
@ -319,3 +322,214 @@ func enable6780(jt *JumpTable) {
|
|||||||
maxStack: maxStack(1, 0),
|
maxStack: maxStack(1, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
var (
|
||||||
|
stack = scope.Stack
|
||||||
|
a = stack.pop()
|
||||||
|
memOffset = stack.pop()
|
||||||
|
codeOffset = stack.pop()
|
||||||
|
length = stack.pop()
|
||||||
|
)
|
||||||
|
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
|
||||||
|
if overflow {
|
||||||
|
uint64CodeOffset = math.MaxUint64
|
||||||
|
}
|
||||||
|
addr := common.Address(a.Bytes20())
|
||||||
|
code := interpreter.evm.StateDB.GetCode(addr)
|
||||||
|
contract := &Contract{
|
||||||
|
Code: code,
|
||||||
|
self: AccountRef(addr),
|
||||||
|
}
|
||||||
|
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
|
||||||
|
statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false)
|
||||||
|
if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) {
|
||||||
|
scope.Contract.Gas = 0
|
||||||
|
return nil, ErrOutOfGas
|
||||||
|
}
|
||||||
|
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy)
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// opPush1EIP4762 handles the special case of PUSH1 opcode for EIP-4762, which
|
||||||
|
// need not worry about the adjusted bound logic when adding the PUSHDATA to
|
||||||
|
// the list of access events.
|
||||||
|
func opPush1EIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
var (
|
||||||
|
codeLen = uint64(len(scope.Contract.Code))
|
||||||
|
integer = new(uint256.Int)
|
||||||
|
)
|
||||||
|
*pc += 1
|
||||||
|
if *pc < codeLen {
|
||||||
|
scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc])))
|
||||||
|
|
||||||
|
if !scope.Contract.IsDeployment && *pc%31 == 0 {
|
||||||
|
// touch next chunk if PUSH1 is at the boundary. if so, *pc has
|
||||||
|
// advanced past this boundary.
|
||||||
|
contractAddr := scope.Contract.Address()
|
||||||
|
statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false)
|
||||||
|
if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) {
|
||||||
|
scope.Contract.Gas = 0
|
||||||
|
return nil, ErrOutOfGas
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scope.Stack.push(integer.Clear())
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
|
||||||
|
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
|
var (
|
||||||
|
codeLen = len(scope.Contract.Code)
|
||||||
|
start = min(codeLen, int(*pc+1))
|
||||||
|
end = min(codeLen, start+pushByteSize)
|
||||||
|
)
|
||||||
|
scope.Stack.push(new(uint256.Int).SetBytes(
|
||||||
|
common.RightPadBytes(
|
||||||
|
scope.Contract.Code[start:end],
|
||||||
|
pushByteSize,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
|
||||||
|
if !scope.Contract.IsDeployment {
|
||||||
|
contractAddr := scope.Contract.Address()
|
||||||
|
statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false)
|
||||||
|
if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) {
|
||||||
|
scope.Contract.Gas = 0
|
||||||
|
return nil, ErrOutOfGas
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*pc += size
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func enable4762(jt *JumpTable) {
|
||||||
|
jt[SSTORE] = &operation{
|
||||||
|
dynamicGas: gasSStore4762,
|
||||||
|
execute: opSstore,
|
||||||
|
minStack: minStack(2, 0),
|
||||||
|
maxStack: maxStack(2, 0),
|
||||||
|
}
|
||||||
|
jt[SLOAD] = &operation{
|
||||||
|
dynamicGas: gasSLoad4762,
|
||||||
|
execute: opSload,
|
||||||
|
minStack: minStack(1, 1),
|
||||||
|
maxStack: maxStack(1, 1),
|
||||||
|
}
|
||||||
|
|
||||||
|
jt[BALANCE] = &operation{
|
||||||
|
execute: opBalance,
|
||||||
|
dynamicGas: gasBalance4762,
|
||||||
|
minStack: minStack(1, 1),
|
||||||
|
maxStack: maxStack(1, 1),
|
||||||
|
}
|
||||||
|
|
||||||
|
jt[EXTCODESIZE] = &operation{
|
||||||
|
execute: opExtCodeSize,
|
||||||
|
dynamicGas: gasExtCodeSize4762,
|
||||||
|
minStack: minStack(1, 1),
|
||||||
|
maxStack: maxStack(1, 1),
|
||||||
|
}
|
||||||
|
|
||||||
|
jt[EXTCODEHASH] = &operation{
|
||||||
|
execute: opExtCodeHash,
|
||||||
|
dynamicGas: gasExtCodeHash4762,
|
||||||
|
minStack: minStack(1, 1),
|
||||||
|
maxStack: maxStack(1, 1),
|
||||||
|
}
|
||||||
|
|
||||||
|
jt[EXTCODECOPY] = &operation{
|
||||||
|
execute: opExtCodeCopyEIP4762,
|
||||||
|
dynamicGas: gasExtCodeCopyEIP4762,
|
||||||
|
minStack: minStack(4, 0),
|
||||||
|
maxStack: maxStack(4, 0),
|
||||||
|
memorySize: memoryExtCodeCopy,
|
||||||
|
}
|
||||||
|
|
||||||
|
jt[CODECOPY] = &operation{
|
||||||
|
execute: opCodeCopy,
|
||||||
|
constantGas: GasFastestStep,
|
||||||
|
dynamicGas: gasCodeCopyEip4762,
|
||||||
|
minStack: minStack(3, 0),
|
||||||
|
maxStack: maxStack(3, 0),
|
||||||
|
memorySize: memoryCodeCopy,
|
||||||
|
}
|
||||||
|
|
||||||
|
jt[SELFDESTRUCT] = &operation{
|
||||||
|
execute: opSelfdestruct6780,
|
||||||
|
dynamicGas: gasSelfdestructEIP4762,
|
||||||
|
constantGas: params.SelfdestructGasEIP150,
|
||||||
|
minStack: minStack(1, 0),
|
||||||
|
maxStack: maxStack(1, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
jt[CREATE] = &operation{
|
||||||
|
execute: opCreate,
|
||||||
|
constantGas: params.CreateNGasEip4762,
|
||||||
|
dynamicGas: gasCreateEip3860,
|
||||||
|
minStack: minStack(3, 1),
|
||||||
|
maxStack: maxStack(3, 1),
|
||||||
|
memorySize: memoryCreate,
|
||||||
|
}
|
||||||
|
|
||||||
|
jt[CREATE2] = &operation{
|
||||||
|
execute: opCreate2,
|
||||||
|
constantGas: params.CreateNGasEip4762,
|
||||||
|
dynamicGas: gasCreate2Eip3860,
|
||||||
|
minStack: minStack(4, 1),
|
||||||
|
maxStack: maxStack(4, 1),
|
||||||
|
memorySize: memoryCreate2,
|
||||||
|
}
|
||||||
|
|
||||||
|
jt[CALL] = &operation{
|
||||||
|
execute: opCall,
|
||||||
|
dynamicGas: gasCallEIP4762,
|
||||||
|
minStack: minStack(7, 1),
|
||||||
|
maxStack: maxStack(7, 1),
|
||||||
|
memorySize: memoryCall,
|
||||||
|
}
|
||||||
|
|
||||||
|
jt[CALLCODE] = &operation{
|
||||||
|
execute: opCallCode,
|
||||||
|
dynamicGas: gasCallCodeEIP4762,
|
||||||
|
minStack: minStack(7, 1),
|
||||||
|
maxStack: maxStack(7, 1),
|
||||||
|
memorySize: memoryCall,
|
||||||
|
}
|
||||||
|
|
||||||
|
jt[STATICCALL] = &operation{
|
||||||
|
execute: opStaticCall,
|
||||||
|
dynamicGas: gasStaticCallEIP4762,
|
||||||
|
minStack: minStack(6, 1),
|
||||||
|
maxStack: maxStack(6, 1),
|
||||||
|
memorySize: memoryStaticCall,
|
||||||
|
}
|
||||||
|
|
||||||
|
jt[DELEGATECALL] = &operation{
|
||||||
|
execute: opDelegateCall,
|
||||||
|
dynamicGas: gasDelegateCallEIP4762,
|
||||||
|
minStack: minStack(6, 1),
|
||||||
|
maxStack: maxStack(6, 1),
|
||||||
|
memorySize: memoryDelegateCall,
|
||||||
|
}
|
||||||
|
|
||||||
|
jt[PUSH1] = &operation{
|
||||||
|
execute: opPush1EIP4762,
|
||||||
|
constantGas: GasFastestStep,
|
||||||
|
minStack: minStack(0, 1),
|
||||||
|
maxStack: maxStack(0, 1),
|
||||||
|
}
|
||||||
|
for i := 1; i < 32; i++ {
|
||||||
|
jt[PUSH1+OpCode(i)] = &operation{
|
||||||
|
execute: makePushEIP4762(uint64(i+1), i+1),
|
||||||
|
constantGas: GasFastestStep,
|
||||||
|
minStack: minStack(0, 1),
|
||||||
|
maxStack: maxStack(0, 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
@ -42,6 +43,8 @@ type (
|
|||||||
func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
|
func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
|
||||||
var precompiles map[common.Address]PrecompiledContract
|
var precompiles map[common.Address]PrecompiledContract
|
||||||
switch {
|
switch {
|
||||||
|
case evm.chainRules.IsVerkle:
|
||||||
|
precompiles = PrecompiledContractsVerkle
|
||||||
case evm.chainRules.IsPrague:
|
case evm.chainRules.IsPrague:
|
||||||
precompiles = PrecompiledContractsPrague
|
precompiles = PrecompiledContractsPrague
|
||||||
case evm.chainRules.IsCancun:
|
case evm.chainRules.IsCancun:
|
||||||
@ -85,10 +88,11 @@ type BlockContext struct {
|
|||||||
// All fields can change between transactions.
|
// All fields can change between transactions.
|
||||||
type TxContext struct {
|
type TxContext struct {
|
||||||
// Message information
|
// Message information
|
||||||
Origin common.Address // Provides information for ORIGIN
|
Origin common.Address // Provides information for ORIGIN
|
||||||
GasPrice *big.Int // Provides information for GASPRICE (and is used to zero the basefee if NoBaseFee is set)
|
GasPrice *big.Int // Provides information for GASPRICE (and is used to zero the basefee if NoBaseFee is set)
|
||||||
BlobHashes []common.Hash // Provides information for BLOBHASH
|
BlobHashes []common.Hash // Provides information for BLOBHASH
|
||||||
BlobFeeCap *big.Int // Is used to zero the blobbasefee if NoBaseFee is set
|
BlobFeeCap *big.Int // Is used to zero the blobbasefee if NoBaseFee is set
|
||||||
|
AccessEvents *state.AccessEvents // Capture all state accesses for this tx
|
||||||
}
|
}
|
||||||
|
|
||||||
// EVM is the Ethereum Virtual Machine base object and provides
|
// EVM is the Ethereum Virtual Machine base object and provides
|
||||||
@ -156,6 +160,9 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig
|
|||||||
// Reset resets the EVM with a new transaction context.Reset
|
// Reset resets the EVM with a new transaction context.Reset
|
||||||
// This is not threadsafe and should only be done very cautiously.
|
// This is not threadsafe and should only be done very cautiously.
|
||||||
func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) {
|
func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) {
|
||||||
|
if evm.chainRules.IsEIP4762 {
|
||||||
|
txCtx.AccessEvents = state.NewAccessEvents(statedb.PointCache())
|
||||||
|
}
|
||||||
evm.TxContext = txCtx
|
evm.TxContext = txCtx
|
||||||
evm.StateDB = statedb
|
evm.StateDB = statedb
|
||||||
}
|
}
|
||||||
@ -200,6 +207,16 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
|||||||
p, isPrecompile := evm.precompile(addr)
|
p, isPrecompile := evm.precompile(addr)
|
||||||
|
|
||||||
if !evm.StateDB.Exist(addr) {
|
if !evm.StateDB.Exist(addr) {
|
||||||
|
if !isPrecompile && evm.chainRules.IsEIP4762 {
|
||||||
|
// add proof of absence to witness
|
||||||
|
wgas := evm.AccessEvents.AddAccount(addr, false)
|
||||||
|
if gas < wgas {
|
||||||
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
|
return nil, 0, ErrOutOfGas
|
||||||
|
}
|
||||||
|
gas -= wgas
|
||||||
|
}
|
||||||
|
|
||||||
if !isPrecompile && evm.chainRules.IsEIP158 && value.IsZero() {
|
if !isPrecompile && evm.chainRules.IsEIP158 && value.IsZero() {
|
||||||
// Calling a non-existing account, don't do anything.
|
// Calling a non-existing account, don't do anything.
|
||||||
return nil, gas, nil
|
return nil, gas, nil
|
||||||
@ -439,7 +456,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
|||||||
|
|
||||||
// We add this to the access list _before_ taking a snapshot. Even if the
|
// 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.
|
// creation fails, the access-list change should not be rolled back.
|
||||||
if evm.chainRules.IsBerlin {
|
if evm.chainRules.IsEIP2929 {
|
||||||
evm.StateDB.AddAddressToAccessList(address)
|
evm.StateDB.AddAddressToAccessList(address)
|
||||||
}
|
}
|
||||||
// Ensure there's no existing contract already at the designated address.
|
// Ensure there's no existing contract already at the designated address.
|
||||||
@ -479,8 +496,18 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
|||||||
// The contract is a scoped environment for this execution context only.
|
// The contract is a scoped environment for this execution context only.
|
||||||
contract := NewContract(caller, AccountRef(address), value, gas)
|
contract := NewContract(caller, AccountRef(address), value, gas)
|
||||||
contract.SetCodeOptionalHash(&address, codeAndHash)
|
contract.SetCodeOptionalHash(&address, codeAndHash)
|
||||||
|
contract.IsDeployment = true
|
||||||
|
|
||||||
ret, err = evm.interpreter.Run(contract, nil, false)
|
// Charge the contract creation init gas in verkle mode
|
||||||
|
if evm.chainRules.IsEIP4762 {
|
||||||
|
if !contract.UseGas(evm.AccessEvents.ContractCreateInitGas(address, value.Sign() != 0), evm.Config.Tracer, tracing.GasChangeWitnessContractInit) {
|
||||||
|
err = ErrOutOfGas
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
// Check whether the max code size has been exceeded, assign err if the case.
|
||||||
if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
|
if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
|
||||||
@ -497,11 +524,24 @@ 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
|
// be stored due to not enough gas set an error and let it be handled
|
||||||
// by the error checking condition below.
|
// by the error checking condition below.
|
||||||
if err == nil {
|
if err == nil {
|
||||||
createDataGas := uint64(len(ret)) * params.CreateDataGas
|
if !evm.chainRules.IsEIP4762 {
|
||||||
if contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
|
createDataGas := uint64(len(ret)) * params.CreateDataGas
|
||||||
evm.StateDB.SetCode(address, ret)
|
if !contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
|
||||||
|
err = ErrCodeStoreOutOfGas
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
err = ErrCodeStoreOutOfGas
|
// Contract creation completed, touch the missing fields in the contract
|
||||||
|
if !contract.UseGas(evm.AccessEvents.AddAccount(address, true), evm.Config.Tracer, tracing.GasChangeWitnessContractCreation) {
|
||||||
|
err = ErrCodeStoreOutOfGas
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil && len(ret) > 0 && !contract.UseGas(evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true), evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) {
|
||||||
|
err = ErrCodeStoreOutOfGas
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
evm.StateDB.SetCode(address, ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,7 +383,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
|
|||||||
} else if !evm.StateDB.Exist(address) {
|
} else if !evm.StateDB.Exist(address) {
|
||||||
gas += params.CallNewAccountGas
|
gas += params.CallNewAccountGas
|
||||||
}
|
}
|
||||||
if transfersValue {
|
if transfersValue && !evm.chainRules.IsEIP4762 {
|
||||||
gas += params.CallValueTransferGas
|
gas += params.CallValueTransferGas
|
||||||
}
|
}
|
||||||
memoryGas, err := memoryGasCost(mem, memorySize)
|
memoryGas, err := memoryGasCost(mem, memorySize)
|
||||||
@ -394,7 +394,14 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
|
|||||||
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
if evm.chainRules.IsEIP4762 {
|
||||||
|
if transfersValue {
|
||||||
|
gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address))
|
||||||
|
if overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@ -402,6 +409,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
|
|||||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,12 +422,22 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
|
|||||||
gas uint64
|
gas uint64
|
||||||
overflow bool
|
overflow bool
|
||||||
)
|
)
|
||||||
if stack.Back(2).Sign() != 0 {
|
if stack.Back(2).Sign() != 0 && !evm.chainRules.IsEIP4762 {
|
||||||
gas += params.CallValueTransferGas
|
gas += params.CallValueTransferGas
|
||||||
}
|
}
|
||||||
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
if evm.chainRules.IsEIP4762 {
|
||||||
|
address := common.Address(stack.Back(1).Bytes20())
|
||||||
|
transfersValue := !stack.Back(2).IsZero()
|
||||||
|
if transfersValue {
|
||||||
|
gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address))
|
||||||
|
if overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
@ -359,9 +359,9 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
|
|||||||
if overflow {
|
if overflow {
|
||||||
uint64CodeOffset = math.MaxUint64
|
uint64CodeOffset = math.MaxUint64
|
||||||
}
|
}
|
||||||
|
|
||||||
codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64())
|
codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64())
|
||||||
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
|
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -434,6 +434,7 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
|
|||||||
num.Clear()
|
num.Clear()
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var upper, lower uint64
|
var upper, lower uint64
|
||||||
upper = interpreter.evm.Context.BlockNumber.Uint64()
|
upper = interpreter.evm.Context.BlockNumber.Uint64()
|
||||||
if upper < 257 {
|
if upper < 257 {
|
||||||
@ -583,6 +584,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
|
|||||||
if interpreter.evm.chainRules.IsEIP150 {
|
if interpreter.evm.chainRules.IsEIP150 {
|
||||||
gas -= gas / 64
|
gas -= gas / 64
|
||||||
}
|
}
|
||||||
|
|
||||||
// reuse size int for stackvalue
|
// reuse size int for stackvalue
|
||||||
stackvalue := size
|
stackvalue := size
|
||||||
|
|
||||||
@ -623,6 +625,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
|
|||||||
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
||||||
gas = scope.Contract.Gas
|
gas = scope.Contract.Gas
|
||||||
)
|
)
|
||||||
|
|
||||||
// Apply EIP150
|
// Apply EIP150
|
||||||
gas -= gas / 64
|
gas -= gas / 64
|
||||||
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
|
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
|
||||||
@ -637,7 +640,6 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
|
|||||||
stackvalue.SetBytes(addr.Bytes())
|
stackvalue.SetBytes(addr.Bytes())
|
||||||
}
|
}
|
||||||
scope.Stack.push(&stackvalue)
|
scope.Stack.push(&stackvalue)
|
||||||
|
|
||||||
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
if suberr == ErrExecutionReverted {
|
if suberr == ErrExecutionReverted {
|
||||||
@ -896,6 +898,7 @@ func makePush(size uint64, pushByteSize int) executionFunc {
|
|||||||
pushByteSize,
|
pushByteSize,
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
|
|
||||||
*pc += size
|
*pc += size
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/trie/utils"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -75,6 +76,10 @@ type StateDB interface {
|
|||||||
// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
|
// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
|
||||||
// even if the feature/fork is not active yet
|
// even if the feature/fork is not active yet
|
||||||
AddSlotToAccessList(addr common.Address, slot common.Hash)
|
AddSlotToAccessList(addr common.Address, slot common.Hash)
|
||||||
|
|
||||||
|
// PointCache returns the point cache used in computations
|
||||||
|
PointCache() *utils.PointCache
|
||||||
|
|
||||||
Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
|
Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
|
||||||
|
|
||||||
RevertToSnapshot(int)
|
RevertToSnapshot(int)
|
||||||
|
@ -99,6 +99,9 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
|
|||||||
// If jump table was not initialised we set the default one.
|
// If jump table was not initialised we set the default one.
|
||||||
var table *JumpTable
|
var table *JumpTable
|
||||||
switch {
|
switch {
|
||||||
|
case evm.chainRules.IsVerkle:
|
||||||
|
// TODO replace with proper instruction set when fork is specified
|
||||||
|
table = &verkleInstructionSet
|
||||||
case evm.chainRules.IsCancun:
|
case evm.chainRules.IsCancun:
|
||||||
table = &cancunInstructionSet
|
table = &cancunInstructionSet
|
||||||
case evm.chainRules.IsShanghai:
|
case evm.chainRules.IsShanghai:
|
||||||
@ -219,6 +222,14 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||||||
// Capture pre-execution values for tracing.
|
// Capture pre-execution values for tracing.
|
||||||
logged, pcCopy, gasCopy = false, pc, contract.Gas
|
logged, pcCopy, gasCopy = false, pc, contract.Gas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
contract.Gas -= in.evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false)
|
||||||
|
}
|
||||||
|
|
||||||
// Get the operation from the jump table and validate the stack to ensure there are
|
// Get the operation from the jump table and validate the stack to ensure there are
|
||||||
// enough stack items available to perform the operation.
|
// enough stack items available to perform the operation.
|
||||||
op = contract.GetOp(pc)
|
op = contract.GetOp(pc)
|
||||||
|
@ -57,6 +57,7 @@ var (
|
|||||||
mergeInstructionSet = newMergeInstructionSet()
|
mergeInstructionSet = newMergeInstructionSet()
|
||||||
shanghaiInstructionSet = newShanghaiInstructionSet()
|
shanghaiInstructionSet = newShanghaiInstructionSet()
|
||||||
cancunInstructionSet = newCancunInstructionSet()
|
cancunInstructionSet = newCancunInstructionSet()
|
||||||
|
verkleInstructionSet = newVerkleInstructionSet()
|
||||||
)
|
)
|
||||||
|
|
||||||
// JumpTable contains the EVM opcodes supported at a given fork.
|
// JumpTable contains the EVM opcodes supported at a given fork.
|
||||||
@ -80,6 +81,12 @@ func validate(jt JumpTable) JumpTable {
|
|||||||
return jt
|
return jt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newVerkleInstructionSet() JumpTable {
|
||||||
|
instructionSet := newCancunInstructionSet()
|
||||||
|
enable4762(&instructionSet)
|
||||||
|
return validate(instructionSet)
|
||||||
|
}
|
||||||
|
|
||||||
func newCancunInstructionSet() JumpTable {
|
func newCancunInstructionSet() JumpTable {
|
||||||
instructionSet := newShanghaiInstructionSet()
|
instructionSet := newShanghaiInstructionSet()
|
||||||
enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode)
|
enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode)
|
||||||
|
159
core/vm/operations_verkle.go
Normal file
159
core/vm/operations_verkle.go
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true)
|
||||||
|
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.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false)
|
||||||
|
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()
|
||||||
|
gas := evm.AccessEvents.BalanceGas(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()
|
||||||
|
if _, isPrecompile := evm.precompile(address); isPrecompile {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
gas := evm.AccessEvents.VersionGas(address, false)
|
||||||
|
gas += evm.AccessEvents.CodeSizeGas(address, false)
|
||||||
|
if gas == 0 {
|
||||||
|
gas = params.WarmStorageReadCostEIP2929
|
||||||
|
}
|
||||||
|
return gas, 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
|
||||||
|
}
|
||||||
|
gas := evm.AccessEvents.CodeHashGas(address, false)
|
||||||
|
if gas == 0 {
|
||||||
|
gas = params.WarmStorageReadCostEIP2929
|
||||||
|
}
|
||||||
|
return gas, 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
|
||||||
|
}
|
||||||
|
if _, isPrecompile := evm.precompile(contract.Address()); isPrecompile {
|
||||||
|
return gas, nil
|
||||||
|
}
|
||||||
|
witnessGas := evm.AccessEvents.MessageCallGas(contract.Address())
|
||||||
|
if witnessGas == 0 {
|
||||||
|
witnessGas = params.WarmStorageReadCostEIP2929
|
||||||
|
}
|
||||||
|
return witnessGas + 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())
|
||||||
|
if _, isPrecompile := evm.precompile(beneficiaryAddr); isPrecompile {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
contractAddr := contract.Address()
|
||||||
|
statelessGas := evm.AccessEvents.VersionGas(contractAddr, false)
|
||||||
|
statelessGas += evm.AccessEvents.CodeSizeGas(contractAddr, false)
|
||||||
|
statelessGas += evm.AccessEvents.BalanceGas(contractAddr, false)
|
||||||
|
if contractAddr != beneficiaryAddr {
|
||||||
|
statelessGas += evm.AccessEvents.BalanceGas(beneficiaryAddr, false)
|
||||||
|
}
|
||||||
|
// Charge write costs if it transfers value
|
||||||
|
if evm.StateDB.GetBalance(contractAddr).Sign() != 0 {
|
||||||
|
statelessGas += evm.AccessEvents.BalanceGas(contractAddr, true)
|
||||||
|
if contractAddr != beneficiaryAddr {
|
||||||
|
statelessGas += evm.AccessEvents.BalanceGas(beneficiaryAddr, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return statelessGas, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
gas, err := gasCodeCopy(evm, contract, stack, mem, memorySize)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
codeOffset = stack.Back(1)
|
||||||
|
length = stack.Back(2)
|
||||||
|
)
|
||||||
|
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
|
||||||
|
if overflow {
|
||||||
|
uint64CodeOffset = math.MaxUint64
|
||||||
|
}
|
||||||
|
_, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64())
|
||||||
|
if !contract.IsDeployment {
|
||||||
|
gas += evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false)
|
||||||
|
}
|
||||||
|
return gas, 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.AccessEvents.VersionGas(addr, false)
|
||||||
|
wgas += evm.AccessEvents.CodeSizeGas(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 {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
return gas, nil
|
||||||
|
}
|
4
go.mod
4
go.mod
@ -15,20 +15,20 @@ require (
|
|||||||
github.com/cloudflare/cloudflare-go v0.79.0
|
github.com/cloudflare/cloudflare-go v0.79.0
|
||||||
github.com/cockroachdb/pebble v1.1.0
|
github.com/cockroachdb/pebble v1.1.0
|
||||||
github.com/consensys/gnark-crypto v0.12.1
|
github.com/consensys/gnark-crypto v0.12.1
|
||||||
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233
|
github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c
|
||||||
github.com/crate-crypto/go-kzg-4844 v1.0.0
|
github.com/crate-crypto/go-kzg-4844 v1.0.0
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/deckarep/golang-set/v2 v2.1.0
|
github.com/deckarep/golang-set/v2 v2.1.0
|
||||||
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0
|
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0
|
||||||
github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3
|
github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3
|
||||||
github.com/ethereum/c-kzg-4844 v1.0.0
|
github.com/ethereum/c-kzg-4844 v1.0.0
|
||||||
|
github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0
|
||||||
github.com/fatih/color v1.13.0
|
github.com/fatih/color v1.13.0
|
||||||
github.com/ferranbt/fastssz v0.1.2
|
github.com/ferranbt/fastssz v0.1.2
|
||||||
github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e
|
github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e
|
||||||
github.com/fjl/memsize v0.0.2
|
github.com/fjl/memsize v0.0.2
|
||||||
github.com/fsnotify/fsnotify v1.6.0
|
github.com/fsnotify/fsnotify v1.6.0
|
||||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff
|
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff
|
||||||
github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46
|
|
||||||
github.com/gofrs/flock v0.8.1
|
github.com/gofrs/flock v0.8.1
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/golang/protobuf v1.5.4
|
github.com/golang/protobuf v1.5.4
|
||||||
|
8
go.sum
8
go.sum
@ -133,8 +133,8 @@ github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJ
|
|||||||
github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
|
github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ=
|
github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I=
|
||||||
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs=
|
github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs=
|
||||||
github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI=
|
github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI=
|
||||||
github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc=
|
github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
@ -169,6 +169,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
|||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA=
|
github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA=
|
||||||
github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
|
github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
|
||||||
|
github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 h1:KrE8I4reeVvf7C1tm8elRjj4BdscTYzz/WAbYyf/JI4=
|
||||||
|
github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0/go.mod h1:D9AJLVXSyZQXJQVk8oh1EwjISE+sJTn2duYIZC0dy3w=
|
||||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||||
github.com/ferranbt/fastssz v0.1.2 h1:Dky6dXlngF6Qjc+EfDipAkE83N5I5DE68bY6O0VLNPk=
|
github.com/ferranbt/fastssz v0.1.2 h1:Dky6dXlngF6Qjc+EfDipAkE83N5I5DE68bY6O0VLNPk=
|
||||||
@ -185,8 +187,6 @@ github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 h1:IZqZOB2fydHte3kUgx
|
|||||||
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8=
|
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8=
|
||||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
|
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
|
||||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
|
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
|
||||||
github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE=
|
|
||||||
github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc=
|
|
||||||
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
|
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
|
||||||
github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0=
|
github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0=
|
||||||
github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ=
|
github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ=
|
||||||
|
@ -581,6 +581,11 @@ func (c *ChainConfig) IsVerkle(num *big.Int, time uint64) bool {
|
|||||||
return c.IsLondon(num) && isTimestampForked(c.VerkleTime, time)
|
return c.IsLondon(num) && isTimestampForked(c.VerkleTime, time)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsEIP4762 returns whether eip 4762 has been activated at given block.
|
||||||
|
func (c *ChainConfig) IsEIP4762(num *big.Int, time uint64) bool {
|
||||||
|
return c.IsVerkle(num, time)
|
||||||
|
}
|
||||||
|
|
||||||
// CheckCompatible checks whether scheduled fork transitions have been imported
|
// CheckCompatible checks whether scheduled fork transitions have been imported
|
||||||
// with a mismatching chain configuration.
|
// with a mismatching chain configuration.
|
||||||
func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64, time uint64) *ConfigCompatError {
|
func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64, time uint64) *ConfigCompatError {
|
||||||
@ -909,6 +914,7 @@ func (err *ConfigCompatError) Error() string {
|
|||||||
type Rules struct {
|
type Rules struct {
|
||||||
ChainID *big.Int
|
ChainID *big.Int
|
||||||
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
|
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
|
||||||
|
IsEIP2929, IsEIP4762 bool
|
||||||
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
|
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
|
||||||
IsBerlin, IsLondon bool
|
IsBerlin, IsLondon bool
|
||||||
IsMerge, IsShanghai, IsCancun, IsPrague bool
|
IsMerge, IsShanghai, IsCancun, IsPrague bool
|
||||||
@ -923,6 +929,7 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules
|
|||||||
}
|
}
|
||||||
// disallow setting Merge out of order
|
// disallow setting Merge out of order
|
||||||
isMerge = isMerge && c.IsLondon(num)
|
isMerge = isMerge && c.IsLondon(num)
|
||||||
|
isVerkle := isMerge && c.IsVerkle(num, timestamp)
|
||||||
return Rules{
|
return Rules{
|
||||||
ChainID: new(big.Int).Set(chainID),
|
ChainID: new(big.Int).Set(chainID),
|
||||||
IsHomestead: c.IsHomestead(num),
|
IsHomestead: c.IsHomestead(num),
|
||||||
@ -934,11 +941,13 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules
|
|||||||
IsPetersburg: c.IsPetersburg(num),
|
IsPetersburg: c.IsPetersburg(num),
|
||||||
IsIstanbul: c.IsIstanbul(num),
|
IsIstanbul: c.IsIstanbul(num),
|
||||||
IsBerlin: c.IsBerlin(num),
|
IsBerlin: c.IsBerlin(num),
|
||||||
|
IsEIP2929: c.IsBerlin(num) && !isVerkle,
|
||||||
IsLondon: c.IsLondon(num),
|
IsLondon: c.IsLondon(num),
|
||||||
IsMerge: isMerge,
|
IsMerge: isMerge,
|
||||||
IsShanghai: isMerge && c.IsShanghai(num, timestamp),
|
IsShanghai: isMerge && c.IsShanghai(num, timestamp),
|
||||||
IsCancun: isMerge && c.IsCancun(num, timestamp),
|
IsCancun: isMerge && c.IsCancun(num, timestamp),
|
||||||
IsPrague: isMerge && c.IsPrague(num, timestamp),
|
IsPrague: isMerge && c.IsPrague(num, timestamp),
|
||||||
IsVerkle: isMerge && c.IsVerkle(num, timestamp),
|
IsVerkle: isVerkle,
|
||||||
|
IsEIP4762: isVerkle,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
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.
|
CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction.
|
||||||
Create2Gas uint64 = 32000 // Once per CREATE2 operation
|
Create2Gas uint64 = 32000 // Once per CREATE2 operation
|
||||||
|
CreateNGasEip4762 uint64 = 1000 // Once per CREATEn operations post-verkle
|
||||||
SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation.
|
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.
|
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.
|
||||||
|
|
||||||
|
36
params/verkle_params.go
Normal file
36
params/verkle_params.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2023 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package params
|
||||||
|
|
||||||
|
// Verkle tree EIP: costs associated to witness accesses
|
||||||
|
var (
|
||||||
|
WitnessBranchReadCost uint64 = 1900
|
||||||
|
WitnessChunkReadCost uint64 = 200
|
||||||
|
WitnessBranchWriteCost uint64 = 3000
|
||||||
|
WitnessChunkWriteCost uint64 = 500
|
||||||
|
WitnessChunkFillCost uint64 = 6200
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClearVerkleWitnessCosts sets all witness costs to 0, which is necessary
|
||||||
|
// for historical block replay simulations.
|
||||||
|
func ClearVerkleWitnessCosts() {
|
||||||
|
WitnessBranchReadCost = 0
|
||||||
|
WitnessChunkReadCost = 0
|
||||||
|
WitnessBranchWriteCost = 0
|
||||||
|
WitnessChunkWriteCost = 0
|
||||||
|
WitnessChunkFillCost = 0
|
||||||
|
}
|
@ -284,3 +284,7 @@ func (t *StateTrie) getSecKeyCache() map[string][]byte {
|
|||||||
}
|
}
|
||||||
return t.secKeyCache
|
return t.secKeyCache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *StateTrie) IsVerkle() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -23,7 +23,7 @@ import (
|
|||||||
"github.com/crate-crypto/go-ipa/bandersnatch/fr"
|
"github.com/crate-crypto/go-ipa/bandersnatch/fr"
|
||||||
"github.com/ethereum/go-ethereum/common/lru"
|
"github.com/ethereum/go-ethereum/common/lru"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/gballet/go-verkle"
|
"github.com/ethereum/go-verkle"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -219,7 +219,7 @@ func CodeChunkKey(address []byte, chunk *uint256.Int) []byte {
|
|||||||
return GetTreeKey(address, treeIndex, subIndex)
|
return GetTreeKey(address, treeIndex, subIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
func storageIndex(bytes []byte) (*uint256.Int, byte) {
|
func StorageIndex(bytes []byte) (*uint256.Int, byte) {
|
||||||
// If the storage slot is in the header, we need to add the header offset.
|
// If the storage slot is in the header, we need to add the header offset.
|
||||||
var key uint256.Int
|
var key uint256.Int
|
||||||
key.SetBytes(bytes)
|
key.SetBytes(bytes)
|
||||||
@ -245,7 +245,7 @@ func storageIndex(bytes []byte) (*uint256.Int, byte) {
|
|||||||
// StorageSlotKey returns the verkle tree key of the storage slot for the
|
// StorageSlotKey returns the verkle tree key of the storage slot for the
|
||||||
// specified account.
|
// specified account.
|
||||||
func StorageSlotKey(address []byte, storageKey []byte) []byte {
|
func StorageSlotKey(address []byte, storageKey []byte) []byte {
|
||||||
treeIndex, subIndex := storageIndex(storageKey)
|
treeIndex, subIndex := StorageIndex(storageKey)
|
||||||
return GetTreeKey(address, treeIndex, subIndex)
|
return GetTreeKey(address, treeIndex, subIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,7 +296,7 @@ func CodeChunkKeyWithEvaluatedAddress(addressPoint *verkle.Point, chunk *uint256
|
|||||||
// slot for the specified account. The difference between StorageSlotKey is the
|
// slot for the specified account. The difference between StorageSlotKey is the
|
||||||
// address evaluation is already computed to minimize the computational overhead.
|
// address evaluation is already computed to minimize the computational overhead.
|
||||||
func StorageSlotKeyWithEvaluatedAddress(evaluated *verkle.Point, storageKey []byte) []byte {
|
func StorageSlotKeyWithEvaluatedAddress(evaluated *verkle.Point, storageKey []byte) []byte {
|
||||||
treeIndex, subIndex := storageIndex(storageKey)
|
treeIndex, subIndex := StorageIndex(storageKey)
|
||||||
return GetTreeKeyWithEvaluatedAddress(evaluated, treeIndex, subIndex)
|
return GetTreeKeyWithEvaluatedAddress(evaluated, treeIndex, subIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/gballet/go-verkle"
|
"github.com/ethereum/go-verkle"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||||
"github.com/ethereum/go-ethereum/trie/utils"
|
"github.com/ethereum/go-ethereum/trie/utils"
|
||||||
"github.com/ethereum/go-ethereum/triedb/database"
|
"github.com/ethereum/go-ethereum/triedb/database"
|
||||||
"github.com/gballet/go-verkle"
|
"github.com/ethereum/go-verkle"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user