mirror of https://github.com/ethereum/go-ethereum
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.pull/29757/head
parent
47af69c2bc
commit
44a50c9f96
@ -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) |
||||
} |
@ -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) |
||||
} |
||||
} |
@ -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 |
||||
} |
@ -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 |
||||
} |
Loading…
Reference in new issue