mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
consensus/beacon,core, params, trie: verkle-compatible access list
Co-authored-by: Ignacio Hagopian <jsign.uy@gmail.com>
This commit is contained in:
parent
ebf9e11af2
commit
361a1a6686
23 changed files with 874 additions and 194 deletions
|
|
@ -348,9 +348,9 @@ func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.H
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize implements consensus.Engine and processes withdrawals on top.
|
// Finalize implements consensus.Engine and processes withdrawals on top.
|
||||||
func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) {
|
func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, statedb *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) {
|
||||||
if !beacon.IsPoSHeader(header) {
|
if !beacon.IsPoSHeader(header) {
|
||||||
beacon.ethone.Finalize(chain, header, state, txs, uncles, nil)
|
beacon.ethone.Finalize(chain, header, statedb, txs, uncles, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Withdrawals processing.
|
// Withdrawals processing.
|
||||||
|
|
@ -358,7 +358,11 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
|
||||||
// Convert amount from gwei to wei.
|
// Convert amount from gwei to wei.
|
||||||
amount := new(uint256.Int).SetUint64(w.Amount)
|
amount := new(uint256.Int).SetUint64(w.Amount)
|
||||||
amount = amount.Mul(amount, uint256.NewInt(params.GWei))
|
amount = amount.Mul(amount, uint256.NewInt(params.GWei))
|
||||||
state.AddBalance(w.Address, amount)
|
statedb.AddBalance(w.Address, amount)
|
||||||
|
|
||||||
|
if chain.Config().IsVerkle(header.Number, header.Time) {
|
||||||
|
statedb.Witness().AddAddress(w.Address, state.ALAllItems, state.AccessListWrite)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// No block reward which is issued by consensus layer instead.
|
// No block reward which is issued by consensus layer instead.
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,10 @@ 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 does not
|
||||||
|
// have enough funds to cover the witness access costs.
|
||||||
|
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")
|
||||||
|
|
|
||||||
|
|
@ -18,22 +18,62 @@ package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
type accessList struct {
|
type ALAccessMode bool
|
||||||
|
|
||||||
|
var (
|
||||||
|
AccessListRead = ALAccessMode(false)
|
||||||
|
AccessListWrite = ALAccessMode(true)
|
||||||
|
)
|
||||||
|
|
||||||
|
type ALAccountItem uint64
|
||||||
|
|
||||||
|
const (
|
||||||
|
ALVersion = ALAccountItem(1 << iota)
|
||||||
|
ALBalance
|
||||||
|
ALNonce
|
||||||
|
ALCodeHash
|
||||||
|
ALCodeSize
|
||||||
|
ALLastHeaderItem
|
||||||
|
)
|
||||||
|
|
||||||
|
const ALAllItems = ALVersion | ALBalance | ALNonce | ALCodeSize | ALCodeHash
|
||||||
|
|
||||||
|
type AccessList interface {
|
||||||
|
ContainsAddress(address common.Address) bool
|
||||||
|
Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool)
|
||||||
|
Copy() AccessList
|
||||||
|
AddAddress(address common.Address, items ALAccountItem, isWrite ALAccessMode) uint64
|
||||||
|
AddSlot(address common.Address, slot common.Hash, isWrite ALAccessMode) uint64
|
||||||
|
DeleteSlot(address common.Address, slot common.Hash)
|
||||||
|
DeleteAddress(address common.Address)
|
||||||
|
|
||||||
|
TouchAndChargeValueTransfer(callerAddr, targetAddr []byte) uint64
|
||||||
|
TouchAndChargeContractCreateInit(addr []byte, createSendsValue bool) uint64
|
||||||
|
TouchTxOriginAndComputeGas(originAddr []byte) uint64
|
||||||
|
TouchTxExistingAndComputeGas(targetAddr []byte, sendsValue bool) uint64
|
||||||
|
TouchAddressOnReadAndComputeGas(addr []byte, index uint256.Int, suffix byte) uint64
|
||||||
|
Merge(AccessList)
|
||||||
|
Keys() [][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type accessList2929 struct {
|
||||||
addresses map[common.Address]int
|
addresses map[common.Address]int
|
||||||
slots []map[common.Hash]struct{}
|
slots []map[common.Hash]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainsAddress returns true if the address is in the access list.
|
// ContainsAddress returns true if the address is in the access list.
|
||||||
func (al *accessList) ContainsAddress(address common.Address) bool {
|
func (al *accessList2929) ContainsAddress(address common.Address) bool {
|
||||||
_, ok := al.addresses[address]
|
_, ok := al.addresses[address]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contains checks if a slot within an account is present in the access list, returning
|
// Contains checks if a slot within an account is present in the access list, returning
|
||||||
// separate flags for the presence of the account and the slot respectively.
|
// separate flags for the presence of the account and the slot respectively.
|
||||||
func (al *accessList) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
|
func (al *accessList2929) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
|
||||||
idx, ok := al.addresses[address]
|
idx, ok := al.addresses[address]
|
||||||
if !ok {
|
if !ok {
|
||||||
// no such address (and hence zero slots)
|
// no such address (and hence zero slots)
|
||||||
|
|
@ -48,15 +88,15 @@ func (al *accessList) Contains(address common.Address, slot common.Hash) (addres
|
||||||
}
|
}
|
||||||
|
|
||||||
// newAccessList creates a new accessList.
|
// newAccessList creates a new accessList.
|
||||||
func newAccessList() *accessList {
|
func newAccessList() AccessList {
|
||||||
return &accessList{
|
return &accessList2929{
|
||||||
addresses: make(map[common.Address]int),
|
addresses: make(map[common.Address]int),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy creates an independent copy of an accessList.
|
// Copy creates an independent copy of an accessList.
|
||||||
func (a *accessList) Copy() *accessList {
|
func (a *accessList2929) Copy() AccessList {
|
||||||
cp := newAccessList()
|
cp := newAccessList().(*accessList2929)
|
||||||
for k, v := range a.addresses {
|
for k, v := range a.addresses {
|
||||||
cp.addresses[k] = v
|
cp.addresses[k] = v
|
||||||
}
|
}
|
||||||
|
|
@ -73,12 +113,12 @@ func (a *accessList) Copy() *accessList {
|
||||||
|
|
||||||
// AddAddress adds an address to the access list, and returns 'true' if the operation
|
// AddAddress adds an address to the access list, and returns 'true' if the operation
|
||||||
// caused a change (addr was not previously in the list).
|
// caused a change (addr was not previously in the list).
|
||||||
func (al *accessList) AddAddress(address common.Address) bool {
|
func (al *accessList2929) AddAddress(address common.Address, _ ALAccountItem, _ ALAccessMode) uint64 {
|
||||||
if _, present := al.addresses[address]; present {
|
if _, present := al.addresses[address]; present {
|
||||||
return false
|
return params.WarmStorageReadCostEIP2929
|
||||||
}
|
}
|
||||||
al.addresses[address] = -1
|
al.addresses[address] = -1
|
||||||
return true
|
return params.ColdAccountAccessCostEIP2929
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddSlot adds the specified (addr, slot) combo to the access list.
|
// AddSlot adds the specified (addr, slot) combo to the access list.
|
||||||
|
|
@ -86,31 +126,31 @@ func (al *accessList) AddAddress(address common.Address) bool {
|
||||||
// - address added
|
// - address added
|
||||||
// - slot added
|
// - slot added
|
||||||
// For any 'true' value returned, a corresponding journal entry must be made.
|
// For any 'true' value returned, a corresponding journal entry must be made.
|
||||||
func (al *accessList) AddSlot(address common.Address, slot common.Hash) (addrChange bool, slotChange bool) {
|
func (al *accessList2929) AddSlot(address common.Address, slot common.Hash, _ ALAccessMode) (gas uint64) {
|
||||||
idx, addrPresent := al.addresses[address]
|
idx, addrPresent := al.addresses[address]
|
||||||
if !addrPresent || idx == -1 {
|
if !addrPresent || idx == -1 {
|
||||||
// Address not present, or addr present but no slots there
|
// Address not present, or addr present but no slots there
|
||||||
al.addresses[address] = len(al.slots)
|
al.addresses[address] = len(al.slots)
|
||||||
slotmap := map[common.Hash]struct{}{slot: {}}
|
slotmap := map[common.Hash]struct{}{slot: {}}
|
||||||
al.slots = append(al.slots, slotmap)
|
al.slots = append(al.slots, slotmap)
|
||||||
return !addrPresent, true
|
return params.WarmStorageReadCostEIP2929
|
||||||
}
|
}
|
||||||
// There is already an (address,slot) mapping
|
// There is already an (address,slot) mapping
|
||||||
slotmap := al.slots[idx]
|
slotmap := al.slots[idx]
|
||||||
if _, ok := slotmap[slot]; !ok {
|
if _, ok := slotmap[slot]; !ok {
|
||||||
slotmap[slot] = struct{}{}
|
slotmap[slot] = struct{}{}
|
||||||
// Journal add slot change
|
// Journal add slot change
|
||||||
return false, true
|
return params.ColdSloadCostEIP2929
|
||||||
}
|
}
|
||||||
// No changes required
|
// No changes required
|
||||||
return false, false
|
return params.WarmStorageReadCostEIP2929
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteSlot removes an (address, slot)-tuple from the access list.
|
// DeleteSlot removes an (address, slot)-tuple from the access list.
|
||||||
// This operation needs to be performed in the same order as the addition happened.
|
// This operation needs to be performed in the same order as the addition happened.
|
||||||
// This method is meant to be used by the journal, which maintains ordering of
|
// This method is meant to be used by the journal, which maintains ordering of
|
||||||
// operations.
|
// operations.
|
||||||
func (al *accessList) DeleteSlot(address common.Address, slot common.Hash) {
|
func (al *accessList2929) DeleteSlot(address common.Address, slot common.Hash) {
|
||||||
idx, addrOk := al.addresses[address]
|
idx, addrOk := al.addresses[address]
|
||||||
// There are two ways this can fail
|
// There are two ways this can fail
|
||||||
if !addrOk {
|
if !addrOk {
|
||||||
|
|
@ -131,6 +171,29 @@ func (al *accessList) DeleteSlot(address common.Address, slot common.Hash) {
|
||||||
// needs to be performed in the same order as the addition happened.
|
// needs to be performed in the same order as the addition happened.
|
||||||
// This method is meant to be used by the journal, which maintains ordering of
|
// This method is meant to be used by the journal, which maintains ordering of
|
||||||
// operations.
|
// operations.
|
||||||
func (al *accessList) DeleteAddress(address common.Address) {
|
func (al *accessList2929) DeleteAddress(address common.Address) {
|
||||||
delete(al.addresses, address)
|
delete(al.addresses, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (al *accessList2929) TouchAndChargeValueTransfer(callerAddr []byte, targetAddr []byte) uint64 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (al *accessList2929) TouchAndChargeContractCreateInit(addr []byte, createSendsValue bool) uint64 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (al *accessList2929) TouchTxOriginAndComputeGas(originAddr []byte) uint64 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (al *accessList2929) TouchTxExistingAndComputeGas(targetAddr []byte, sendsValue bool) uint64 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (al *accessList2929) TouchAddressOnReadAndComputeGas(addr []byte, index uint256.Int, subIndex byte) uint64 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (al *accessList2929) Merge(other AccessList) {}
|
||||||
|
func (al *accessList2929) Keys() [][]byte { return nil }
|
||||||
|
|
|
||||||
260
core/state/access_witness.go
Normal file
260
core/state/access_witness.go
Normal file
|
|
@ -0,0 +1,260 @@
|
||||||
|
// 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 (
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"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
|
||||||
|
|
||||||
|
// AccessWitness lists the locations of the state that are being accessed
|
||||||
|
// during the production of a block.
|
||||||
|
type AccessWitness struct {
|
||||||
|
branches map[branchAccessKey]mode
|
||||||
|
chunks map[chunkAccessKey]mode
|
||||||
|
|
||||||
|
pointCache *utils.PointCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAccessWitness() *AccessWitness {
|
||||||
|
return &AccessWitness{
|
||||||
|
branches: make(map[branchAccessKey]mode),
|
||||||
|
chunks: make(map[chunkAccessKey]mode),
|
||||||
|
pointCache: utils.NewPointCache(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge is used to merge the witness that got generated during the execution
|
||||||
|
// of a tx, with the accumulation of witnesses that were generated during the
|
||||||
|
// execution of all the txs preceding this one in a given block.
|
||||||
|
func (aw *AccessWitness) Merge(o AccessList) {
|
||||||
|
other := o.(*AccessWitness)
|
||||||
|
for k := range other.branches {
|
||||||
|
aw.branches[k] |= other.branches[k]
|
||||||
|
}
|
||||||
|
for k, chunk := range other.chunks {
|
||||||
|
aw.chunks[k] |= chunk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key returns, predictably, the list of keys that were touched during the
|
||||||
|
// buildup of the access witness.
|
||||||
|
func (aw *AccessWitness) Keys() [][]byte {
|
||||||
|
// TODO: consider if parallelizing this is worth it, probably depending on len(aw.chunks).
|
||||||
|
keys := make([][]byte, 0, len(aw.chunks))
|
||||||
|
for chunk := range aw.chunks {
|
||||||
|
basePoint := aw.pointCache.GetTreeKeyHeader(chunk.addr[:])
|
||||||
|
key := utils.GetTreeKeyWithEvaluatedAddess(basePoint, &chunk.treeIndex, chunk.leafKey)
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aw *AccessWitness) Copy() AccessList {
|
||||||
|
naw := &AccessWitness{
|
||||||
|
branches: make(map[branchAccessKey]mode),
|
||||||
|
chunks: make(map[chunkAccessKey]mode),
|
||||||
|
pointCache: aw.pointCache,
|
||||||
|
}
|
||||||
|
naw.Merge(aw)
|
||||||
|
return naw
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aw *AccessWitness) TouchAndChargeValueTransfer(callerAddr, targetAddr []byte) uint64 {
|
||||||
|
var gas uint64
|
||||||
|
gas += aw.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BalanceLeafKey, AccessListWrite)
|
||||||
|
gas += aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, AccessListWrite)
|
||||||
|
return gas
|
||||||
|
}
|
||||||
|
|
||||||
|
// TouchAndChargeContractCreateInit charges access costs to initiate
|
||||||
|
// a contract creation
|
||||||
|
func (aw *AccessWitness) TouchAndChargeContractCreateInit(addr []byte, createSendsValue bool) uint64 {
|
||||||
|
var gas uint64
|
||||||
|
gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, AccessListWrite)
|
||||||
|
gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, AccessListWrite)
|
||||||
|
// FIXME note that this is incompatible with the spec
|
||||||
|
if createSendsValue {
|
||||||
|
gas += aw.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, AccessListWrite)
|
||||||
|
}
|
||||||
|
return gas
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aw *AccessWitness) TouchTxOriginAndComputeGas(originAddr []byte) uint64 {
|
||||||
|
aw.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.VersionLeafKey, AccessListRead)
|
||||||
|
aw.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeSizeLeafKey, AccessListRead)
|
||||||
|
aw.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeKeccakLeafKey, AccessListRead)
|
||||||
|
aw.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.NonceLeafKey, AccessListWrite)
|
||||||
|
aw.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BalanceLeafKey, AccessListWrite)
|
||||||
|
|
||||||
|
// Kaustinen note: we're currently experimenting with stop chargin gas for the origin address
|
||||||
|
// so simple transfer still take 21000 gas. This is to potentially avoid breaking existing tooling.
|
||||||
|
// This is the reason why we return 0 instead of `gas`.
|
||||||
|
// Note that we still have to touch the addresses to make sure the witness is correct.
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aw *AccessWitness) TouchTxExistingAndComputeGas(targetAddr []byte, sendsValue bool) uint64 {
|
||||||
|
aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.VersionLeafKey, AccessListRead)
|
||||||
|
aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeSizeLeafKey, AccessListRead)
|
||||||
|
aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.CodeKeccakLeafKey, AccessListRead)
|
||||||
|
aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.NonceLeafKey, AccessListRead)
|
||||||
|
if sendsValue {
|
||||||
|
aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, AccessListWrite)
|
||||||
|
} else {
|
||||||
|
aw.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, AccessListRead)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kaustinen note: we're currently experimenting with stop chargin gas for the origin address
|
||||||
|
// so simple transfer still take 21000 gas. This is to potentially avoid breaking existing tooling.
|
||||||
|
// This is the reason why we return 0 instead of `gas`.
|
||||||
|
// Note that we still have to touch the addresses to make sure the witness is correct.
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aw *AccessWitness) TouchAddressOnReadAndComputeGas(addr []byte, treeIndex uint256.Int, subIndex byte) uint64 {
|
||||||
|
return aw.touchAddressAndChargeGas(addr, treeIndex, subIndex, AccessListRead)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aw *AccessWitness) touchAddressAndChargeGas(addr []byte, treeIndex uint256.Int, subIndex byte, isWrite ALAccessMode) uint64 {
|
||||||
|
stemRead, suffixRead, stemWrite, selectorWrite, selectorFill := aw.touchAddress(addr, treeIndex, subIndex, isWrite)
|
||||||
|
|
||||||
|
var gas uint64
|
||||||
|
if stemRead {
|
||||||
|
gas += params.WitnessBranchReadCost
|
||||||
|
}
|
||||||
|
if suffixRead {
|
||||||
|
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 witness.
|
||||||
|
func (aw *AccessWitness) touchAddress(addr []byte, treeIndex uint256.Int, subIndex byte, isWrite ALAccessMode) (bool, bool, bool, bool, bool) {
|
||||||
|
branchKey := newBranchAccessKey(addr, treeIndex)
|
||||||
|
chunkKey := newChunkAccessKey(branchKey, subIndex)
|
||||||
|
|
||||||
|
// Read access.
|
||||||
|
var branchRead, chunkRead bool
|
||||||
|
if _, hasStem := aw.branches[branchKey]; !hasStem {
|
||||||
|
branchRead = true
|
||||||
|
aw.branches[branchKey] = AccessWitnessReadFlag
|
||||||
|
}
|
||||||
|
if _, hasSelector := aw.chunks[chunkKey]; !hasSelector {
|
||||||
|
chunkRead = true
|
||||||
|
aw.chunks[chunkKey] = AccessWitnessReadFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write access.
|
||||||
|
var branchWrite, chunkWrite, chunkFill bool
|
||||||
|
if isWrite {
|
||||||
|
if (aw.branches[branchKey] & AccessWitnessWriteFlag) == 0 {
|
||||||
|
branchWrite = true
|
||||||
|
aw.branches[branchKey] |= AccessWitnessWriteFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkValue := aw.chunks[chunkKey]
|
||||||
|
if (chunkValue & AccessWitnessWriteFlag) == 0 {
|
||||||
|
chunkWrite = true
|
||||||
|
aw.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 []byte, treeIndex uint256.Int) branchAccessKey {
|
||||||
|
var sk branchAccessKey
|
||||||
|
copy(sk.addr[20-len(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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aw *AccessWitness) AddAddress(addr common.Address, items ALAccountItem, isWrite ALAccessMode) uint64 {
|
||||||
|
var gas uint64
|
||||||
|
for index := utils.VersionLeafKey; index <= utils.CodeSizeLeafKey; index++ {
|
||||||
|
if (1<<index)&items != 0 {
|
||||||
|
gas += aw.touchAddressAndChargeGas(addr[:], zeroTreeIndex, byte(index), isWrite)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return gas
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aw *AccessWitness) AddSlot(address common.Address, slot common.Hash, isWrite ALAccessMode) uint64 {
|
||||||
|
treeIndex, suffix := utils.GetTreeKeyStorageSlotTreeIndexes(slot.Bytes())
|
||||||
|
return aw.touchAddressAndChargeGas(address[:], *treeIndex, suffix, isWrite)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aw *AccessWitness) DeleteSlot(address common.Address, slot common.Hash) {
|
||||||
|
// Should remain in the witness
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aw *AccessWitness) DeleteAddress(address common.Address) {
|
||||||
|
// Should remain in the witness
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aw *AccessWitness) ContainsAddress(address common.Address) bool {
|
||||||
|
panic("not implemented") // TODO: Implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aw *AccessWitness) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
|
||||||
|
panic("not implemented") // TODO: Implement
|
||||||
|
}
|
||||||
|
|
@ -180,7 +180,7 @@ type cachingDB struct {
|
||||||
// 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, utils.NewPointCache())
|
||||||
}
|
}
|
||||||
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
|
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -99,11 +99,14 @@ type StateDB struct {
|
||||||
preimages map[common.Hash][]byte
|
preimages map[common.Hash][]byte
|
||||||
|
|
||||||
// Per-transaction access list
|
// Per-transaction access list
|
||||||
accessList *accessList
|
accessList AccessList
|
||||||
|
|
||||||
// Transient storage
|
// Transient storage
|
||||||
transientStorage transientStorage
|
transientStorage transientStorage
|
||||||
|
|
||||||
|
// Global witness
|
||||||
|
witness AccessList
|
||||||
|
|
||||||
// Journal of state modifications. This is the backbone of
|
// Journal of state modifications. This is the backbone of
|
||||||
// Snapshot and RevertToSnapshot.
|
// Snapshot and RevertToSnapshot.
|
||||||
journal *journal
|
journal *journal
|
||||||
|
|
@ -1275,26 +1278,30 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er
|
||||||
// - 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.IsBerlin {
|
||||||
|
if !rules.IsVerkle {
|
||||||
|
s.accessList = newAccessList()
|
||||||
|
} else {
|
||||||
|
s.accessList = NewAccessWitness()
|
||||||
|
}
|
||||||
// Clear out any leftover from previous executions
|
// Clear out any leftover from previous executions
|
||||||
al := newAccessList()
|
al := s.accessList
|
||||||
s.accessList = al
|
|
||||||
|
|
||||||
al.AddAddress(sender)
|
al.AddAddress(sender, ALAllItems, AccessListRead)
|
||||||
if dst != nil {
|
if dst != nil {
|
||||||
al.AddAddress(*dst)
|
al.AddAddress(*dst, ALAllItems, AccessListRead)
|
||||||
// If it's a create-tx, the destination will be added inside evm.create
|
// If it's a create-tx, the destination will be added inside evm.create
|
||||||
}
|
}
|
||||||
for _, addr := range precompiles {
|
for _, addr := range precompiles {
|
||||||
al.AddAddress(addr)
|
al.AddAddress(addr, ALAllItems, AccessListRead)
|
||||||
}
|
}
|
||||||
for _, el := range list {
|
for _, el := range list {
|
||||||
al.AddAddress(el.Address)
|
al.AddAddress(el.Address, 0, AccessListRead)
|
||||||
for _, key := range el.StorageKeys {
|
for _, key := range el.StorageKeys {
|
||||||
al.AddSlot(el.Address, key)
|
al.AddSlot(el.Address, key, AccessListRead)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rules.IsShanghai { // EIP-3651: warm coinbase
|
if rules.IsShanghai { // EIP-3651: warm coinbase
|
||||||
al.AddAddress(coinbase)
|
al.AddAddress(coinbase, ALAllItems, AccessListRead)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Reset transient storage at the beginning of transaction execution
|
// Reset transient storage at the beginning of transaction execution
|
||||||
|
|
@ -1302,38 +1309,25 @@ func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, d
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddAddressToAccessList adds the given address to the access list
|
// AddAddressToAccessList adds the given address to the access list
|
||||||
func (s *StateDB) AddAddressToAccessList(addr common.Address) {
|
func (s *StateDB) AddAddressToAccessList(addr common.Address, mode ALAccountItem, isWrite ALAccessMode) uint64 {
|
||||||
if s.accessList.AddAddress(addr) {
|
gas := s.accessList.AddAddress(addr, mode, isWrite)
|
||||||
|
if gas > 0 {
|
||||||
s.journal.append(accessListAddAccountChange{&addr})
|
s.journal.append(accessListAddAccountChange{&addr})
|
||||||
}
|
}
|
||||||
|
return gas
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddSlotToAccessList adds the given (address, slot)-tuple to the access list
|
// AddSlotToAccessList adds the given (address, slot)-tuple to the access list
|
||||||
func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) {
|
func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash, isWrite ALAccessMode) uint64 {
|
||||||
addrMod, slotMod := s.accessList.AddSlot(addr, slot)
|
gas := s.accessList.AddSlot(addr, slot, isWrite)
|
||||||
if addrMod {
|
|
||||||
// In practice, this should not happen, since there is no way to enter the
|
if gas > params.WarmStorageReadCostEIP2929 {
|
||||||
// scope of 'address' without having the 'address' become already added
|
|
||||||
// to the access list (via call-variant, create, etc).
|
|
||||||
// Better safe than sorry, though
|
|
||||||
s.journal.append(accessListAddAccountChange{&addr})
|
|
||||||
}
|
|
||||||
if slotMod {
|
|
||||||
s.journal.append(accessListAddSlotChange{
|
s.journal.append(accessListAddSlotChange{
|
||||||
address: &addr,
|
address: &addr,
|
||||||
slot: &slot,
|
slot: &slot,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
return gas
|
||||||
|
|
||||||
// AddressInAccessList returns true if the given address is in the access list.
|
|
||||||
func (s *StateDB) AddressInAccessList(addr common.Address) bool {
|
|
||||||
return s.accessList.ContainsAddress(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SlotInAccessList returns true if the given (address, slot)-tuple is in the access list.
|
|
||||||
func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
|
|
||||||
return s.accessList.Contains(addr, slot)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertAccountSet converts a provided account set from address keyed to hash keyed.
|
// convertAccountSet converts a provided account set from address keyed to hash keyed.
|
||||||
|
|
@ -1370,3 +1364,45 @@ func copy2DSet[k comparable](set map[k]map[common.Hash][]byte) map[k]map[common.
|
||||||
}
|
}
|
||||||
return copied
|
return copied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) TouchTxOriginAndComputeGas(addr common.Address) uint64 {
|
||||||
|
return s.accessList.TouchTxOriginAndComputeGas(addr.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) TouchTxExistingAndComputeGas(addr common.Address, sendsValue bool) uint64 {
|
||||||
|
return s.accessList.TouchTxExistingAndComputeGas(addr.Bytes(), sendsValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) TouchAndChargeContractCreateInit(addr common.Address, sendsValue bool) uint64 {
|
||||||
|
return s.accessList.TouchAndChargeContractCreateInit(addr.Bytes(), sendsValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) TouchAndChargeValueTransfer(from, to common.Address) uint64 {
|
||||||
|
return s.accessList.TouchAndChargeValueTransfer(from.Bytes(), to.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) TouchAddressOnReadAndComputeGas(from common.Address, index uint64) uint64 {
|
||||||
|
treeIndex := uint256.NewInt((index + 128) / 256)
|
||||||
|
subIndex := byte((index + 128) % 256)
|
||||||
|
return s.accessList.TouchAddressOnReadAndComputeGas(from.Bytes(), *treeIndex, subIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) MergeTxAccessListIntoBlockWitness() {
|
||||||
|
if _, ok := s.accessList.(*AccessWitness); ok {
|
||||||
|
s.witness.Merge(s.accessList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addressInAccessList returns true if the given address is in the access list.
|
||||||
|
func (s *StateDB) addressInAccessList(addr common.Address) bool {
|
||||||
|
return s.accessList.ContainsAddress(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// slotInAccessList returns true if the given (address, slot)-tuple is in the access list.
|
||||||
|
func (s *StateDB) slotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
|
||||||
|
return s.accessList.Contains(addr, slot)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) Witness() AccessList {
|
||||||
|
return s.witness
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -345,14 +345,14 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
|
||||||
{
|
{
|
||||||
name: "AddAddressToAccessList",
|
name: "AddAddressToAccessList",
|
||||||
fn: func(a testAction, s *StateDB) {
|
fn: func(a testAction, s *StateDB) {
|
||||||
s.AddAddressToAccessList(addr)
|
s.AddAddressToAccessList(addr, 0, false)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "AddSlotToAccessList",
|
name: "AddSlotToAccessList",
|
||||||
fn: func(a testAction, s *StateDB) {
|
fn: func(a testAction, s *StateDB) {
|
||||||
s.AddSlotToAccessList(addr,
|
s.AddSlotToAccessList(addr,
|
||||||
common.Hash{byte(a.args[0])})
|
common.Hash{byte(a.args[0])}, false)
|
||||||
},
|
},
|
||||||
args: make([]int64, 1),
|
args: make([]int64, 1),
|
||||||
},
|
},
|
||||||
|
|
@ -879,19 +879,19 @@ func TestStateDBAccessList(t *testing.T) {
|
||||||
}
|
}
|
||||||
// Check that the given addresses are in the access list
|
// Check that the given addresses are in the access list
|
||||||
for _, address := range addresses {
|
for _, address := range addresses {
|
||||||
if !state.AddressInAccessList(address) {
|
if !state.addressInAccessList(address) {
|
||||||
t.Fatalf("expected %x to be in access list", address)
|
t.Fatalf("expected %x to be in access list", address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check that only the expected addresses are present in the access list
|
// Check that only the expected addresses are present in the access list
|
||||||
for address := range state.accessList.addresses {
|
for address := range state.accessList.(*accessList2929).addresses {
|
||||||
if _, exist := addressMap[address]; !exist {
|
if _, exist := addressMap[address]; !exist {
|
||||||
t.Fatalf("extra address %x in access list", address)
|
t.Fatalf("extra address %x in access list", address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
verifySlots := func(addrString string, slotStrings ...string) {
|
verifySlots := func(addrString string, slotStrings ...string) {
|
||||||
if !state.AddressInAccessList(addr(addrString)) {
|
if !state.addressInAccessList(addr(addrString)) {
|
||||||
t.Fatalf("scope missing address/slots %v", addrString)
|
t.Fatalf("scope missing address/slots %v", addrString)
|
||||||
}
|
}
|
||||||
var address = addr(addrString)
|
var address = addr(addrString)
|
||||||
|
|
@ -905,14 +905,14 @@ func TestStateDBAccessList(t *testing.T) {
|
||||||
}
|
}
|
||||||
// Check that the expected items are in the access list
|
// Check that the expected items are in the access list
|
||||||
for i, s := range slots {
|
for i, s := range slots {
|
||||||
if _, slotPresent := state.SlotInAccessList(address, s); !slotPresent {
|
if _, slotPresent := state.slotInAccessList(address, s); !slotPresent {
|
||||||
t.Fatalf("input %d: scope missing slot %v (address %v)", i, s, addrString)
|
t.Fatalf("input %d: scope missing slot %v (address %v)", i, s, addrString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check that no extra elements are in the access list
|
// Check that no extra elements are in the access list
|
||||||
index := state.accessList.addresses[address]
|
index := state.accessList.(*accessList2929).addresses[address]
|
||||||
if index >= 0 {
|
if index >= 0 {
|
||||||
stateSlots := state.accessList.slots[index]
|
stateSlots := state.accessList.(*accessList2929).slots[index]
|
||||||
for s := range stateSlots {
|
for s := range stateSlots {
|
||||||
if _, slotPresent := slotMap[s]; !slotPresent {
|
if _, slotPresent := slotMap[s]; !slotPresent {
|
||||||
t.Fatalf("scope has extra slot %v (address %v)", s, addrString)
|
t.Fatalf("scope has extra slot %v (address %v)", s, addrString)
|
||||||
|
|
@ -921,9 +921,9 @@ func TestStateDBAccessList(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.AddAddressToAccessList(addr("aa")) // 1
|
state.AddAddressToAccessList(addr("aa"), 0, false) // 1
|
||||||
state.AddSlotToAccessList(addr("bb"), slot("01")) // 2,3
|
state.AddSlotToAccessList(addr("bb"), slot("01"), false) // 2,3
|
||||||
state.AddSlotToAccessList(addr("bb"), slot("02")) // 4
|
state.AddSlotToAccessList(addr("bb"), slot("02"), false) // 4
|
||||||
verifyAddrs("aa", "bb")
|
verifyAddrs("aa", "bb")
|
||||||
verifySlots("bb", "01", "02")
|
verifySlots("bb", "01", "02")
|
||||||
|
|
||||||
|
|
@ -934,17 +934,17 @@ func TestStateDBAccessList(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// same again, should cause no journal entries
|
// same again, should cause no journal entries
|
||||||
state.AddSlotToAccessList(addr("bb"), slot("01"))
|
state.AddSlotToAccessList(addr("bb"), slot("01"), false)
|
||||||
state.AddSlotToAccessList(addr("bb"), slot("02"))
|
state.AddSlotToAccessList(addr("bb"), slot("02"), false)
|
||||||
state.AddAddressToAccessList(addr("aa"))
|
state.AddAddressToAccessList(addr("aa"), 0, false)
|
||||||
if exp, got := 4, state.journal.length(); exp != got {
|
if exp, got := 4, state.journal.length(); exp != got {
|
||||||
t.Fatalf("journal length mismatch: have %d, want %d", got, exp)
|
t.Fatalf("journal length mismatch: have %d, want %d", got, exp)
|
||||||
}
|
}
|
||||||
// some new ones
|
// some new ones
|
||||||
state.AddSlotToAccessList(addr("bb"), slot("03")) // 5
|
state.AddSlotToAccessList(addr("bb"), slot("03"), false) // 5
|
||||||
state.AddSlotToAccessList(addr("aa"), slot("01")) // 6
|
state.AddSlotToAccessList(addr("aa"), slot("01"), false) // 6
|
||||||
state.AddSlotToAccessList(addr("cc"), slot("01")) // 7,8
|
state.AddSlotToAccessList(addr("cc"), slot("01"), false) // 7,8
|
||||||
state.AddAddressToAccessList(addr("cc"))
|
state.AddAddressToAccessList(addr("cc"), 0, false)
|
||||||
if exp, got := 8, state.journal.length(); exp != got {
|
if exp, got := 8, state.journal.length(); exp != got {
|
||||||
t.Fatalf("journal length mismatch: have %d, want %d", got, exp)
|
t.Fatalf("journal length mismatch: have %d, want %d", got, exp)
|
||||||
}
|
}
|
||||||
|
|
@ -956,7 +956,7 @@ func TestStateDBAccessList(t *testing.T) {
|
||||||
|
|
||||||
// now start rolling back changes
|
// now start rolling back changes
|
||||||
state.journal.revert(state, 7)
|
state.journal.revert(state, 7)
|
||||||
if _, ok := state.SlotInAccessList(addr("cc"), slot("01")); ok {
|
if _, ok := state.slotInAccessList(addr("cc"), slot("01")); ok {
|
||||||
t.Fatalf("slot present, expected missing")
|
t.Fatalf("slot present, expected missing")
|
||||||
}
|
}
|
||||||
verifyAddrs("aa", "bb", "cc")
|
verifyAddrs("aa", "bb", "cc")
|
||||||
|
|
@ -964,7 +964,7 @@ func TestStateDBAccessList(t *testing.T) {
|
||||||
verifySlots("bb", "01", "02", "03")
|
verifySlots("bb", "01", "02", "03")
|
||||||
|
|
||||||
state.journal.revert(state, 6)
|
state.journal.revert(state, 6)
|
||||||
if state.AddressInAccessList(addr("cc")) {
|
if state.addressInAccessList(addr("cc")) {
|
||||||
t.Fatalf("addr present, expected missing")
|
t.Fatalf("addr present, expected missing")
|
||||||
}
|
}
|
||||||
verifyAddrs("aa", "bb")
|
verifyAddrs("aa", "bb")
|
||||||
|
|
@ -972,46 +972,46 @@ func TestStateDBAccessList(t *testing.T) {
|
||||||
verifySlots("bb", "01", "02", "03")
|
verifySlots("bb", "01", "02", "03")
|
||||||
|
|
||||||
state.journal.revert(state, 5)
|
state.journal.revert(state, 5)
|
||||||
if _, ok := state.SlotInAccessList(addr("aa"), slot("01")); ok {
|
if _, ok := state.slotInAccessList(addr("aa"), slot("01")); ok {
|
||||||
t.Fatalf("slot present, expected missing")
|
t.Fatalf("slot present, expected missing")
|
||||||
}
|
}
|
||||||
verifyAddrs("aa", "bb")
|
verifyAddrs("aa", "bb")
|
||||||
verifySlots("bb", "01", "02", "03")
|
verifySlots("bb", "01", "02", "03")
|
||||||
|
|
||||||
state.journal.revert(state, 4)
|
state.journal.revert(state, 4)
|
||||||
if _, ok := state.SlotInAccessList(addr("bb"), slot("03")); ok {
|
if _, ok := state.slotInAccessList(addr("bb"), slot("03")); ok {
|
||||||
t.Fatalf("slot present, expected missing")
|
t.Fatalf("slot present, expected missing")
|
||||||
}
|
}
|
||||||
verifyAddrs("aa", "bb")
|
verifyAddrs("aa", "bb")
|
||||||
verifySlots("bb", "01", "02")
|
verifySlots("bb", "01", "02")
|
||||||
|
|
||||||
state.journal.revert(state, 3)
|
state.journal.revert(state, 3)
|
||||||
if _, ok := state.SlotInAccessList(addr("bb"), slot("02")); ok {
|
if _, ok := state.slotInAccessList(addr("bb"), slot("02")); ok {
|
||||||
t.Fatalf("slot present, expected missing")
|
t.Fatalf("slot present, expected missing")
|
||||||
}
|
}
|
||||||
verifyAddrs("aa", "bb")
|
verifyAddrs("aa", "bb")
|
||||||
verifySlots("bb", "01")
|
verifySlots("bb", "01")
|
||||||
|
|
||||||
state.journal.revert(state, 2)
|
state.journal.revert(state, 2)
|
||||||
if _, ok := state.SlotInAccessList(addr("bb"), slot("01")); ok {
|
if _, ok := state.slotInAccessList(addr("bb"), slot("01")); ok {
|
||||||
t.Fatalf("slot present, expected missing")
|
t.Fatalf("slot present, expected missing")
|
||||||
}
|
}
|
||||||
verifyAddrs("aa", "bb")
|
verifyAddrs("aa", "bb")
|
||||||
|
|
||||||
state.journal.revert(state, 1)
|
state.journal.revert(state, 1)
|
||||||
if state.AddressInAccessList(addr("bb")) {
|
if state.addressInAccessList(addr("bb")) {
|
||||||
t.Fatalf("addr present, expected missing")
|
t.Fatalf("addr present, expected missing")
|
||||||
}
|
}
|
||||||
verifyAddrs("aa")
|
verifyAddrs("aa")
|
||||||
|
|
||||||
state.journal.revert(state, 0)
|
state.journal.revert(state, 0)
|
||||||
if state.AddressInAccessList(addr("aa")) {
|
if state.addressInAccessList(addr("aa")) {
|
||||||
t.Fatalf("addr present, expected missing")
|
t.Fatalf("addr present, expected missing")
|
||||||
}
|
}
|
||||||
if got, exp := len(state.accessList.addresses), 0; got != exp {
|
if got, exp := len(state.accessList.(*accessList2929).addresses), 0; got != exp {
|
||||||
t.Fatalf("expected empty, got %d", got)
|
t.Fatalf("expected empty, got %d", got)
|
||||||
}
|
}
|
||||||
if got, exp := len(state.accessList.slots), 0; got != exp {
|
if got, exp := len(state.accessList.(*accessList2929).slots), 0; got != exp {
|
||||||
t.Fatalf("expected empty, got %d", got)
|
t.Fatalf("expected empty, got %d", got)
|
||||||
}
|
}
|
||||||
// Check the copy
|
// Check the copy
|
||||||
|
|
@ -1019,10 +1019,10 @@ func TestStateDBAccessList(t *testing.T) {
|
||||||
state = stateCopy1
|
state = stateCopy1
|
||||||
verifyAddrs("aa", "bb")
|
verifyAddrs("aa", "bb")
|
||||||
verifySlots("bb", "01", "02")
|
verifySlots("bb", "01", "02")
|
||||||
if got, exp := len(state.accessList.addresses), 2; got != exp {
|
if got, exp := len(state.accessList.(*accessList2929).addresses), 2; got != exp {
|
||||||
t.Fatalf("expected empty, got %d", got)
|
t.Fatalf("expected empty, got %d", got)
|
||||||
}
|
}
|
||||||
if got, exp := len(state.accessList.slots), 1; got != exp {
|
if got, exp := len(state.accessList.(*accessList2929).slots), 1; got != exp {
|
||||||
t.Fatalf("expected empty, got %d", got)
|
t.Fatalf("expected empty, got %d", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,8 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta
|
||||||
receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
|
receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
statedb.MergeTxAccessListIntoBlockWitness()
|
||||||
|
|
||||||
// Set the receipt logs and create the bloom filter.
|
// Set the receipt logs and create the bloom filter.
|
||||||
receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash)
|
receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash)
|
||||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||||
|
|
@ -185,7 +187,7 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat
|
||||||
Data: beaconRoot[:],
|
Data: beaconRoot[:],
|
||||||
}
|
}
|
||||||
vmenv.Reset(NewEVMTxContext(msg), statedb)
|
vmenv.Reset(NewEVMTxContext(msg), statedb)
|
||||||
statedb.AddAddressToAccessList(params.BeaconRootsStorageAddress)
|
statedb.AddAddressToAccessList(params.BeaconRootsStorageAddress, state.ALAllItems, state.AccessListWrite)
|
||||||
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
|
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
|
||||||
statedb.Finalise(true)
|
statedb.Finalise(true)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,10 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
cmath "github.com/ethereum/go-ethereum/common/math"
|
cmath "github.com/ethereum/go-ethereum/common/math"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
|
|
@ -354,6 +356,19 @@ func (st *StateTransition) preCheck() error {
|
||||||
return st.buyGas()
|
return st.buyGas()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tryConsumeGas tries to subtract gas from gasPool, setting the result in gasPool
|
||||||
|
// if subtracting more gas than remains in gasPool, set gasPool = 0 and return false
|
||||||
|
// otherwise, do the subtraction setting the result in gasPool and return true
|
||||||
|
func tryConsumeGas(gasPool *uint64, gas uint64) bool {
|
||||||
|
if *gasPool < gas {
|
||||||
|
*gasPool = 0
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
*gasPool -= gas
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// TransitionDb will transition the state by applying the current message and
|
// TransitionDb will transition the state by applying the current message and
|
||||||
// returning the evm execution result with following fields.
|
// returning the evm execution result with following fields.
|
||||||
//
|
//
|
||||||
|
|
@ -404,6 +419,28 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||||
}
|
}
|
||||||
st.gasRemaining -= gas
|
st.gasRemaining -= gas
|
||||||
|
|
||||||
|
if rules.IsVerkle {
|
||||||
|
statelessGasOrigin := st.evm.StateDB.TouchTxOriginAndComputeGas(msg.From)
|
||||||
|
if !tryConsumeGas(&st.gasRemaining, statelessGasOrigin) {
|
||||||
|
return nil, fmt.Errorf("%w: Insufficient funds to cover witness access costs for transaction: have %d, want %d", ErrInsufficientBalanceWitness, st.gasRemaining, gas)
|
||||||
|
}
|
||||||
|
originNonce := st.evm.StateDB.GetNonce(msg.From)
|
||||||
|
if msg.To != nil {
|
||||||
|
statelessGasDest := st.evm.StateDB.TouchTxExistingAndComputeGas(*msg.To, msg.Value.Sign() != 0)
|
||||||
|
if !tryConsumeGas(&st.gasRemaining, statelessGasDest) {
|
||||||
|
return nil, fmt.Errorf("%w: Insufficient funds to cover witness access costs for transaction: have %d, want %d", ErrInsufficientBalanceWitness, st.gasRemaining, gas)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure the code size ends up in the access witness
|
||||||
|
st.evm.StateDB.GetCodeSize(*msg.To)
|
||||||
|
} else {
|
||||||
|
contractAddr := crypto.CreateAddress(msg.From, originNonce)
|
||||||
|
if !tryConsumeGas(&st.gasRemaining, st.evm.StateDB.TouchAndChargeContractCreateInit(contractAddr, msg.Value.Sign() != 0)) {
|
||||||
|
return nil, fmt.Errorf("%w: Insufficient funds to cover witness access costs for transaction: have %d, want %d", ErrInsufficientBalanceWitness, st.gasRemaining, gas)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check clause 6
|
// Check clause 6
|
||||||
value, overflow := uint256.FromBig(msg.Value)
|
value, overflow := uint256.FromBig(msg.Value)
|
||||||
if overflow {
|
if overflow {
|
||||||
|
|
@ -457,6 +494,10 @@ 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)
|
st.state.AddBalance(st.evm.Context.Coinbase, fee)
|
||||||
|
|
||||||
|
if rules.IsVerkle && fee.Sign() != 0 {
|
||||||
|
st.evm.StateDB.AddAddressToAccessList(st.evm.Context.Coinbase, state.ALAllItems, state.AccessListWrite)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ExecutionResult{
|
return &ExecutionResult{
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,8 @@ type Contract struct {
|
||||||
CodeAddr *common.Address
|
CodeAddr *common.Address
|
||||||
Input []byte
|
Input []byte
|
||||||
|
|
||||||
|
IsDeployment bool
|
||||||
|
|
||||||
Gas uint64
|
Gas uint64
|
||||||
value *uint256.Int
|
value *uint256.Int
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,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.
|
||||||
|
|
@ -123,28 +124,28 @@ func enable2929(jt *JumpTable) {
|
||||||
jt[SLOAD].constantGas = 0
|
jt[SLOAD].constantGas = 0
|
||||||
jt[SLOAD].dynamicGas = gasSLoadEIP2929
|
jt[SLOAD].dynamicGas = gasSLoadEIP2929
|
||||||
|
|
||||||
jt[EXTCODECOPY].constantGas = params.WarmStorageReadCostEIP2929
|
jt[EXTCODECOPY].constantGas = 0
|
||||||
jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP2929
|
jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP2929
|
||||||
|
|
||||||
jt[EXTCODESIZE].constantGas = params.WarmStorageReadCostEIP2929
|
jt[EXTCODESIZE].constantGas = 0
|
||||||
jt[EXTCODESIZE].dynamicGas = gasEip2929AccountCheck
|
jt[EXTCODESIZE].dynamicGas = gasEip2929AccountCheck
|
||||||
|
|
||||||
jt[EXTCODEHASH].constantGas = params.WarmStorageReadCostEIP2929
|
jt[EXTCODEHASH].constantGas = 0
|
||||||
jt[EXTCODEHASH].dynamicGas = gasEip2929AccountCheck
|
jt[EXTCODEHASH].dynamicGas = gasEip2929AccountCheck
|
||||||
|
|
||||||
jt[BALANCE].constantGas = params.WarmStorageReadCostEIP2929
|
jt[BALANCE].constantGas = 0
|
||||||
jt[BALANCE].dynamicGas = gasEip2929AccountCheck
|
jt[BALANCE].dynamicGas = gasEip2929AccountCheck
|
||||||
|
|
||||||
jt[CALL].constantGas = params.WarmStorageReadCostEIP2929
|
jt[CALL].constantGas = 0
|
||||||
jt[CALL].dynamicGas = gasCallEIP2929
|
jt[CALL].dynamicGas = gasCallEIP2929
|
||||||
|
|
||||||
jt[CALLCODE].constantGas = params.WarmStorageReadCostEIP2929
|
jt[CALLCODE].constantGas = 0
|
||||||
jt[CALLCODE].dynamicGas = gasCallCodeEIP2929
|
jt[CALLCODE].dynamicGas = gasCallCodeEIP2929
|
||||||
|
|
||||||
jt[STATICCALL].constantGas = params.WarmStorageReadCostEIP2929
|
jt[STATICCALL].constantGas = 0
|
||||||
jt[STATICCALL].dynamicGas = gasStaticCallEIP2929
|
jt[STATICCALL].dynamicGas = gasStaticCallEIP2929
|
||||||
|
|
||||||
jt[DELEGATECALL].constantGas = params.WarmStorageReadCostEIP2929
|
jt[DELEGATECALL].constantGas = 0
|
||||||
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP2929
|
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP2929
|
||||||
|
|
||||||
// This was previously part of the dynamic cost, but we're using it as a constantGas
|
// This was previously part of the dynamic cost, but we're using it as a constantGas
|
||||||
|
|
@ -319,3 +320,14 @@ func enable6780(jt *JumpTable) {
|
||||||
maxStack: maxStack(1, 0),
|
maxStack: maxStack(1, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func enable4762(jt *JumpTable) {
|
||||||
|
jt[SSTORE].dynamicGas = gasSStore4762
|
||||||
|
jt[BALANCE].dynamicGas = gasBalance4762
|
||||||
|
jt[EXTCODESIZE].dynamicGas = gasExtCodeSize4762
|
||||||
|
jt[EXTCODEHASH].dynamicGas = gasExtCodeHash4762
|
||||||
|
jt[CALL].dynamicGas = nil
|
||||||
|
jt[CALLCODE].dynamicGas = nil
|
||||||
|
jt[DELEGATECALL].dynamicGas = nil
|
||||||
|
jt[STATICCALL].dynamicGas = nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,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/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -189,6 +190,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
p, isPrecompile := evm.precompile(addr)
|
p, isPrecompile := evm.precompile(addr)
|
||||||
debug := evm.Config.Tracer != nil
|
debug := evm.Config.Tracer != nil
|
||||||
|
|
||||||
|
var creation bool
|
||||||
if !evm.StateDB.Exist(addr) {
|
if !evm.StateDB.Exist(addr) {
|
||||||
if !isPrecompile && evm.chainRules.IsEIP158 && value.IsZero() {
|
if !isPrecompile && evm.chainRules.IsEIP158 && value.IsZero() {
|
||||||
// Calling a non existing account, don't do anything, but ping the tracer
|
// Calling a non existing account, don't do anything, but ping the tracer
|
||||||
|
|
@ -201,9 +203,14 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
evm.Config.Tracer.CaptureExit(ret, 0, nil)
|
evm.Config.Tracer.CaptureExit(ret, 0, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if evm.chainRules.IsVerkle {
|
||||||
|
tryConsumeGas(&gas, evm.StateDB.AddAddressToAccessList(addr, state.ALAllItems, state.AccessListRead))
|
||||||
|
}
|
||||||
return nil, gas, nil
|
return nil, gas, nil
|
||||||
}
|
}
|
||||||
evm.StateDB.CreateAccount(addr)
|
evm.StateDB.CreateAccount(addr)
|
||||||
|
creation = true
|
||||||
}
|
}
|
||||||
evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value)
|
evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value)
|
||||||
|
|
||||||
|
|
@ -237,6 +244,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
// The depth-check is already done, and precompiles handled above
|
// The depth-check is already done, and precompiles handled above
|
||||||
contract := NewContract(caller, AccountRef(addrCopy), value, gas)
|
contract := NewContract(caller, AccountRef(addrCopy), value, gas)
|
||||||
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
|
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
|
||||||
|
contract.IsDeployment = creation
|
||||||
ret, err = evm.interpreter.Run(contract, input, false)
|
ret, err = evm.interpreter.Run(contract, input, false)
|
||||||
gas = contract.Gas
|
gas = contract.Gas
|
||||||
}
|
}
|
||||||
|
|
@ -418,6 +426,20 @@ func (c *codeAndHash) Hash() common.Hash {
|
||||||
return c.hash
|
return c.hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tryConsumeGas tries to subtract gas from gasPool, setting the result in gasPool
|
||||||
|
// if subtracting more gas than remains in gasPool, set gasPool = 0 and return false
|
||||||
|
// otherwise, do the subtraction setting the result in gasPool and return true
|
||||||
|
func tryConsumeGas(gasPool *uint64, gas uint64) bool {
|
||||||
|
// TODO remove this function, it should no longer be needed
|
||||||
|
if *gasPool < gas {
|
||||||
|
*gasPool = 0
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
*gasPool -= gas
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// create creates a new contract using code as deployment code.
|
// create creates a new contract using code as deployment code.
|
||||||
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) {
|
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) {
|
||||||
// Depth check execution. Fail if we're trying to execute above the
|
// Depth check execution. Fail if we're trying to execute above the
|
||||||
|
|
@ -436,7 +458,10 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||||
// We add this to the access list _before_ taking a snapshot. Even if the creation fails,
|
// 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
|
// the access-list change should not be rolled back
|
||||||
if evm.chainRules.IsBerlin {
|
if evm.chainRules.IsBerlin {
|
||||||
evm.StateDB.AddAddressToAccessList(address)
|
evm.StateDB.AddAddressToAccessList(address, state.ALVersion|state.ALNonce, state.AccessListWrite)
|
||||||
|
if value.Sign() != 0 {
|
||||||
|
evm.StateDB.AddAddressToAccessList(address, state.ALBalance, state.AccessListWrite)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Ensure there's no existing contract already at the designated address
|
// Ensure there's no existing contract already at the designated address
|
||||||
contractHash := evm.StateDB.GetCodeHash(address)
|
contractHash := evm.StateDB.GetCodeHash(address)
|
||||||
|
|
@ -455,6 +480,7 @@ 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
|
||||||
|
|
||||||
if evm.Config.Tracer != nil {
|
if evm.Config.Tracer != nil {
|
||||||
if evm.depth == 0 {
|
if evm.depth == 0 {
|
||||||
|
|
@ -489,6 +515,15 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err == nil && evm.chainRules.IsVerkle {
|
||||||
|
if len(ret) > 0 {
|
||||||
|
touchCodeChunksRangeOnReadAndChargeGas(address, 0, uint64(len(ret)), uint64(len(ret)), evm.StateDB)
|
||||||
|
}
|
||||||
|
if !contract.UseGas(evm.StateDB.AddAddressToAccessList(address, state.ALAllItems, state.AccessListWrite)) {
|
||||||
|
err = ErrOutOfGas
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// When an error was returned by the EVM or when setting the creation code
|
// When an error was returned by the EVM or when setting the creation code
|
||||||
// above we revert to the snapshot and consume any gas remaining. Additionally
|
// above we revert to the snapshot and consume any gas remaining. Additionally
|
||||||
// when we're in homestead this also counts for code storage gas errors.
|
// when we're in homestead this also counts for code storage gas errors.
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -395,6 +396,20 @@ 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
|
||||||
}
|
}
|
||||||
|
if evm.chainRules.IsVerkle {
|
||||||
|
if _, isPrecompile := evm.precompile(address); !isPrecompile {
|
||||||
|
gas, overflow = math.SafeAdd(gas, evm.StateDB.AddAddressToAccessList(address, state.ALVersion|state.ALCodeSize, state.AccessListRead))
|
||||||
|
if overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
if transfersValue {
|
||||||
|
gas, overflow = math.SafeAdd(gas, evm.StateDB.AddAddressToAccessList(address, state.ALBalance, state.AccessListRead))
|
||||||
|
if overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -420,6 +435,17 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
|
||||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
if evm.chainRules.IsVerkle {
|
||||||
|
if _, isPrecompile := evm.precompile(contract.Address()); !isPrecompile {
|
||||||
|
gas, overflow = math.SafeAdd(gas, evm.StateDB.AddAddressToAccessList(contract.Address(), state.ALVersion|state.ALCodeSize, state.AccessListRead))
|
||||||
|
if overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -436,6 +462,14 @@ func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
||||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
if evm.chainRules.IsVerkle {
|
||||||
|
if _, isPrecompile := evm.precompile(contract.Address()); !isPrecompile {
|
||||||
|
gas, overflow = math.SafeAdd(gas, evm.StateDB.AddAddressToAccessList(contract.Address(), state.ALVersion|state.ALCodeSize, state.AccessListRead))
|
||||||
|
if overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -452,6 +486,14 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
|
||||||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
if evm.chainRules.IsVerkle {
|
||||||
|
if _, isPrecompile := evm.precompile(contract.Address()); !isPrecompile {
|
||||||
|
gas, overflow = math.SafeAdd(gas, evm.StateDB.AddAddressToAccessList(contract.Address(), state.ALVersion|state.ALCodeSize, state.AccessListRead))
|
||||||
|
if overflow {
|
||||||
|
return 0, ErrGasUintOverflow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -477,3 +519,40 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// touchCodeChunksRangeOnReadAndChargeGas is a helper function to touch every chunk in a code range and charge witness gas costs
|
||||||
|
func touchCodeChunksRangeOnReadAndChargeGas(contractAddr common.Address, startPC, size uint64, codeLen uint64, sdb StateDB) 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++ {
|
||||||
|
gas := sdb.TouchAddressOnReadAndComputeGas(contractAddr, chunkNumber)
|
||||||
|
var overflow bool
|
||||||
|
statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, gas)
|
||||||
|
if overflow {
|
||||||
|
panic("overflow when adding gas")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return statelessGasCharged
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -584,6 +584,13 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
|
||||||
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
|
||||||
)
|
)
|
||||||
|
if interpreter.evm.chainRules.IsVerkle {
|
||||||
|
contractAddress := crypto.CreateAddress(scope.Contract.Address(), interpreter.evm.StateDB.GetNonce(scope.Contract.Address()))
|
||||||
|
statelessGas := interpreter.evm.StateDB.TouchAndChargeContractCreateInit(contractAddress, value.Sign() != 0)
|
||||||
|
if !tryConsumeGas(&gas, statelessGas) {
|
||||||
|
return nil, ErrOutOfGas
|
||||||
|
}
|
||||||
|
}
|
||||||
if interpreter.evm.chainRules.IsEIP150 {
|
if interpreter.evm.chainRules.IsEIP150 {
|
||||||
gas -= gas / 64
|
gas -= gas / 64
|
||||||
}
|
}
|
||||||
|
|
@ -626,6 +633,14 @@ 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
|
||||||
)
|
)
|
||||||
|
if interpreter.evm.chainRules.IsVerkle {
|
||||||
|
codeAndHash := &codeAndHash{code: input}
|
||||||
|
contractAddress := crypto.CreateAddress2(scope.Contract.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
|
||||||
|
statelessGas := interpreter.evm.StateDB.TouchAndChargeContractCreateInit(contractAddress, endowment.Sign() != 0)
|
||||||
|
if !tryConsumeGas(&gas, statelessGas) {
|
||||||
|
return nil, ErrOutOfGas
|
||||||
|
}
|
||||||
|
}
|
||||||
// Apply EIP150
|
// Apply EIP150
|
||||||
gas -= gas / 64
|
gas -= gas / 64
|
||||||
scope.Contract.UseGas(gas)
|
scope.Contract.UseGas(gas)
|
||||||
|
|
@ -866,6 +881,17 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
|
||||||
*pc += 1
|
*pc += 1
|
||||||
if *pc < codeLen {
|
if *pc < codeLen {
|
||||||
scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc])))
|
scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc])))
|
||||||
|
|
||||||
|
if !scope.Contract.IsDeployment && interpreter.evm.chainRules.IsVerkle && *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 := touchCodeChunksRangeOnReadAndChargeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), interpreter.evm.StateDB)
|
||||||
|
if !scope.Contract.UseGas(statelessGas) {
|
||||||
|
scope.Contract.Gas = 0
|
||||||
|
return nil, ErrOutOfGas
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
scope.Stack.push(integer.Clear())
|
scope.Stack.push(integer.Clear())
|
||||||
}
|
}
|
||||||
|
|
@ -887,6 +913,17 @@ func makePush(size uint64, pushByteSize int) executionFunc {
|
||||||
endMin = startMin + pushByteSize
|
endMin = startMin + pushByteSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !scope.Contract.IsDeployment && interpreter.evm.chainRules.IsVerkle && *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 := touchCodeChunksRangeOnReadAndChargeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), interpreter.evm.StateDB)
|
||||||
|
if !scope.Contract.UseGas(statelessGas) {
|
||||||
|
scope.Contract.Gas = 0
|
||||||
|
return nil, ErrOutOfGas
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
integer := new(uint256.Int)
|
integer := new(uint256.Int)
|
||||||
scope.Stack.push(integer.SetBytes(common.RightPadBytes(
|
scope.Stack.push(integer.SetBytes(common.RightPadBytes(
|
||||||
scope.Contract.Code[startMin:endMin], pushByteSize)))
|
scope.Contract.Code[startMin:endMin], pushByteSize)))
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"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/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
|
|
@ -64,14 +65,18 @@ type StateDB interface {
|
||||||
// is defined according to EIP161 (balance = nonce = code = 0).
|
// is defined according to EIP161 (balance = nonce = code = 0).
|
||||||
Empty(common.Address) bool
|
Empty(common.Address) bool
|
||||||
|
|
||||||
AddressInAccessList(addr common.Address) bool
|
|
||||||
SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool)
|
|
||||||
// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
|
// AddAddressToAccessList adds the given address 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
|
||||||
AddAddressToAccessList(addr common.Address)
|
AddAddressToAccessList(addr common.Address, item state.ALAccountItem, isWrite state.ALAccessMode) uint64
|
||||||
// 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, write state.ALAccessMode) uint64
|
||||||
|
TouchTxOriginAndComputeGas(addr common.Address) uint64
|
||||||
|
TouchTxExistingAndComputeGas(addr common.Address, sendsValue bool) uint64
|
||||||
|
TouchAndChargeContractCreateInit(addr common.Address, sendsValue bool) uint64
|
||||||
|
TouchAndChargeValueTransfer(from, to common.Address) uint64
|
||||||
|
TouchAddressOnReadAndComputeGas(from common.Address, chunk uint64) uint64
|
||||||
|
|
||||||
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)
|
||||||
|
|
@ -79,6 +84,8 @@ type StateDB interface {
|
||||||
|
|
||||||
AddLog(*types.Log)
|
AddLog(*types.Log)
|
||||||
AddPreimage(common.Hash, []byte)
|
AddPreimage(common.Hash, []byte)
|
||||||
|
|
||||||
|
Witness() state.AccessList
|
||||||
}
|
}
|
||||||
|
|
||||||
// CallContext provides a basic interface for the EVM calling conventions. The EVM
|
// CallContext provides a basic interface for the EVM calling conventions. The EVM
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,11 @@ 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:
|
||||||
|
table = &cancunInstructionSet
|
||||||
|
if err := EnableEIP(4762, table); err != nil {
|
||||||
|
log.Error("EIP activation failed", "eip", 4762, "error", err)
|
||||||
|
}
|
||||||
case evm.chainRules.IsCancun:
|
case evm.chainRules.IsCancun:
|
||||||
table = &cancunInstructionSet
|
table = &cancunInstructionSet
|
||||||
case evm.chainRules.IsShanghai:
|
case evm.chainRules.IsShanghai:
|
||||||
|
|
@ -174,6 +179,12 @@ 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.IsVerkle && !contract.IsDeployment {
|
||||||
|
// if the PC ends up in a new "chunk" of verkleized code, charge the
|
||||||
|
// associated costs.
|
||||||
|
contractAddr := contract.Address()
|
||||||
|
contract.Gas -= touchCodeChunksRangeOnReadAndChargeGas(contractAddr, pc, 1, uint64(len(contract.Code)), in.evm.StateDB)
|
||||||
|
}
|
||||||
// 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)
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,12 @@ func validate(jt JumpTable) JumpTable {
|
||||||
return jt
|
return jt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newVerkleInstructionSet() JumpTable {
|
||||||
|
instructionSet := newCancunInstructionSet()
|
||||||
|
EnableEIP(4762, &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)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -35,26 +36,15 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
||||||
y, x = stack.Back(1), stack.peek()
|
y, x = stack.Back(1), stack.peek()
|
||||||
slot = common.Hash(x.Bytes32())
|
slot = common.Hash(x.Bytes32())
|
||||||
current = evm.StateDB.GetState(contract.Address(), slot)
|
current = evm.StateDB.GetState(contract.Address(), slot)
|
||||||
cost = uint64(0)
|
cost = evm.StateDB.AddSlotToAccessList(contract.Address(), slot, state.AccessListWrite)
|
||||||
)
|
)
|
||||||
// Check slot presence in the access list
|
// Check slot presence in the access list
|
||||||
if addrPresent, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
|
||||||
cost = params.ColdSloadCostEIP2929
|
|
||||||
// If the caller cannot afford the cost, this change will be rolled back
|
|
||||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
|
||||||
if !addrPresent {
|
|
||||||
// Once we're done with YOLOv2 and schedule this for mainnet, might
|
|
||||||
// be good to remove this panic here, which is just really a
|
|
||||||
// canary to have during testing
|
|
||||||
panic("impossible case: address was not present in access list during sstore op")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
value := common.Hash(y.Bytes32())
|
value := common.Hash(y.Bytes32())
|
||||||
|
|
||||||
if current == value { // noop (1)
|
if current == value { // noop (1)
|
||||||
// EIP 2200 original clause:
|
// EIP 2200 original clause:
|
||||||
// return params.SloadGasEIP2200, nil
|
// return params.SloadGasEIP2200, nil
|
||||||
return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
|
return cost, nil // SLOAD_GAS
|
||||||
}
|
}
|
||||||
original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
|
original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
|
||||||
if original == current {
|
if original == current {
|
||||||
|
|
@ -91,10 +81,27 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
||||||
}
|
}
|
||||||
// EIP-2200 original clause:
|
// EIP-2200 original clause:
|
||||||
//return params.SloadGasEIP2200, nil // dirty update (2.2)
|
//return params.SloadGasEIP2200, nil // dirty update (2.2)
|
||||||
return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2)
|
return cost, nil // dirty update (2.2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
gas := evm.StateDB.AddSlotToAccessList(contract.Address(), common.Hash(stack.peek().Bytes32()), state.AccessListWrite)
|
||||||
|
return gas, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
return evm.StateDB.AddAddressToAccessList(contract.Address(), state.ALBalance, state.AccessListRead), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
return evm.StateDB.AddAddressToAccessList(contract.Address(), state.ALCodeSize, state.AccessListRead), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
|
return evm.StateDB.AddAddressToAccessList(contract.Address(), state.ALCodeHash, state.AccessListRead), nil
|
||||||
|
}
|
||||||
|
|
||||||
// gasSLoadEIP2929 calculates dynamic gas for SLOAD according to EIP-2929
|
// gasSLoadEIP2929 calculates dynamic gas for SLOAD according to EIP-2929
|
||||||
// For SLOAD, if the (address, storage_key) pair (where address is the address of the contract
|
// For SLOAD, if the (address, storage_key) pair (where address is the address of the contract
|
||||||
// whose storage is being read) is not yet in accessed_storage_keys,
|
// whose storage is being read) is not yet in accessed_storage_keys,
|
||||||
|
|
@ -104,13 +111,7 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
||||||
loc := stack.peek()
|
loc := stack.peek()
|
||||||
slot := common.Hash(loc.Bytes32())
|
slot := common.Hash(loc.Bytes32())
|
||||||
// Check slot presence in the access list
|
// Check slot presence in the access list
|
||||||
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
return evm.StateDB.AddSlotToAccessList(contract.Address(), slot, false), nil
|
||||||
// If the caller cannot afford the cost, this change will be rolled back
|
|
||||||
// If he does afford it, we can skip checking the same thing later on, during execution
|
|
||||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
|
||||||
return params.ColdSloadCostEIP2929, nil
|
|
||||||
}
|
|
||||||
return params.WarmStorageReadCostEIP2929, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929
|
// gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929
|
||||||
|
|
@ -126,15 +127,13 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
|
||||||
}
|
}
|
||||||
addr := common.Address(stack.peek().Bytes20())
|
addr := common.Address(stack.peek().Bytes20())
|
||||||
// Check slot presence in the access list
|
// Check slot presence in the access list
|
||||||
if !evm.StateDB.AddressInAccessList(addr) {
|
algas := evm.StateDB.AddAddressToAccessList(addr, state.ALCodeSize, state.AccessListRead)
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
|
||||||
var overflow bool
|
var overflow bool
|
||||||
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
||||||
if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
|
if gas, overflow = math.SafeAdd(gas, algas); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
return gas, nil
|
|
||||||
}
|
|
||||||
return gas, nil
|
return gas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,28 +147,22 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
|
||||||
func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
addr := common.Address(stack.peek().Bytes20())
|
addr := common.Address(stack.peek().Bytes20())
|
||||||
// Check slot presence in the access list
|
// Check slot presence in the access list
|
||||||
if !evm.StateDB.AddressInAccessList(addr) {
|
|
||||||
// If the caller cannot afford the cost, this change will be rolled back
|
// If the caller cannot afford the cost, this change will be rolled back
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
return evm.StateDB.AddAddressToAccessList(addr, state.ALCodeSize|state.ALCodeHash|state.ALBalance, state.AccessListRead), nil
|
||||||
// The warm storage read cost is already charged as constantGas
|
|
||||||
return params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil
|
|
||||||
}
|
|
||||||
return 0, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
|
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
|
||||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
addr := common.Address(stack.Back(1).Bytes20())
|
addr := common.Address(stack.Back(1).Bytes20())
|
||||||
// Check slot presence in the access list
|
// Check slot presence in the access list
|
||||||
warmAccess := evm.StateDB.AddressInAccessList(addr)
|
cost := evm.StateDB.AddAddressToAccessList(addr, 0, state.AccessListRead)
|
||||||
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
|
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
|
||||||
// the cost to charge for cold access, if any, is Cold - Warm
|
// the cost to charge for cold access, if any, is Cold - Warm
|
||||||
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
if cost == params.WarmStorageReadCostEIP2929 {
|
||||||
if !warmAccess {
|
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
|
||||||
// Charge the remaining difference here already, to correctly calculate available
|
// Charge the remaining difference here already, to correctly calculate available
|
||||||
// gas for call
|
// gas for call
|
||||||
if !contract.UseGas(coldCost) {
|
if !contract.UseGas(cost) {
|
||||||
return 0, ErrOutOfGas
|
return 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -179,17 +172,17 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
|
||||||
// - memory expansion
|
// - memory expansion
|
||||||
// - 63/64ths rule
|
// - 63/64ths rule
|
||||||
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||||
if warmAccess || err != nil {
|
if cost == params.WarmStorageReadCostEIP2929 || err != nil {
|
||||||
return gas, err
|
return gas, err
|
||||||
}
|
}
|
||||||
// In case of a cold access, we temporarily add the cold charge back, and also
|
// In case of a cold access, we temporarily add the cold charge back, and also
|
||||||
// add it to the returned gas. By adding it to the return, it will be charged
|
// add it to the returned gas. By adding it to the return, it will be charged
|
||||||
// outside of this function, as part of the dynamic gas, and that will make it
|
// outside of this function, as part of the dynamic gas, and that will make it
|
||||||
// also become correctly reported to tracers.
|
// also become correctly reported to tracers.
|
||||||
contract.Gas += coldCost
|
contract.Gas += cost
|
||||||
|
|
||||||
var overflow bool
|
var overflow bool
|
||||||
if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
|
if gas, overflow = math.SafeAdd(gas, cost); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
|
|
@ -231,11 +224,7 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
||||||
gas uint64
|
gas uint64
|
||||||
address = common.Address(stack.peek().Bytes20())
|
address = common.Address(stack.peek().Bytes20())
|
||||||
)
|
)
|
||||||
if !evm.StateDB.AddressInAccessList(address) {
|
gas = evm.StateDB.AddAddressToAccessList(address, 0, state.AccessListWrite)
|
||||||
// If the caller cannot afford the cost, this change will be rolled back
|
|
||||||
evm.StateDB.AddAddressToAccessList(address)
|
|
||||||
gas = params.ColdAccountAccessCostEIP2929
|
|
||||||
}
|
|
||||||
// if empty and transfers value
|
// if empty and transfers value
|
||||||
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
||||||
gas += params.CreateBySelfdestructGas
|
gas += params.CreateBySelfdestructGas
|
||||||
|
|
|
||||||
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
|
||||||
|
}
|
||||||
|
|
@ -18,7 +18,6 @@ package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"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"
|
||||||
|
|
@ -35,6 +34,8 @@ const (
|
||||||
NonceLeafKey = 2
|
NonceLeafKey = 2
|
||||||
CodeKeccakLeafKey = 3
|
CodeKeccakLeafKey = 3
|
||||||
CodeSizeLeafKey = 4
|
CodeSizeLeafKey = 4
|
||||||
|
|
||||||
|
maxPointCacheByteSize = 100 << 20
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -46,7 +47,7 @@ var (
|
||||||
verkleNodeWidth = uint256.NewInt(256)
|
verkleNodeWidth = uint256.NewInt(256)
|
||||||
codeStorageDelta = uint256.NewInt(0).Sub(codeOffset, headerStorageOffset)
|
codeStorageDelta = uint256.NewInt(0).Sub(codeOffset, headerStorageOffset)
|
||||||
|
|
||||||
index0Point *verkle.Point // pre-computed commitment of polynomial [2+256*64]
|
getTreePolyIndex0Point *verkle.Point // pre-computed commitment of polynomial [2+256*64]
|
||||||
|
|
||||||
// cacheHitGauge is the metric to track how many cache hit occurred.
|
// cacheHitGauge is the metric to track how many cache hit occurred.
|
||||||
cacheHitGauge = metrics.NewRegisteredGauge("trie/verkle/cache/hit", nil)
|
cacheHitGauge = metrics.NewRegisteredGauge("trie/verkle/cache/hit", nil)
|
||||||
|
|
@ -57,15 +58,11 @@ var (
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// The byte array is the Marshalled output of the point computed as such:
|
// The byte array is the Marshalled output of the point computed as such:
|
||||||
//
|
//cfg, _ := verkle.GetConfig()
|
||||||
// var (
|
//verkle.FromLEBytes(&getTreePolyIndex0Fr[0], []byte{2, 64})
|
||||||
// config = verkle.GetConfig()
|
//= cfg.CommitToPoly(getTreePolyIndex0Fr[:], 1)
|
||||||
// fr verkle.Fr
|
getTreePolyIndex0Point = new(verkle.Point)
|
||||||
// )
|
err := getTreePolyIndex0Point.SetBytes([]byte{34, 25, 109, 242, 193, 5, 144, 224, 76, 52, 189, 92, 197, 126, 9, 145, 27, 152, 199, 130, 165, 3, 210, 27, 193, 131, 142, 28, 110, 26, 16, 191})
|
||||||
// verkle.FromLEBytes(&fr, []byte{2, 64})
|
|
||||||
// point := config.CommitToPoly([]verkle.Fr{fr}, 1)
|
|
||||||
index0Point = new(verkle.Point)
|
|
||||||
err := index0Point.SetBytes([]byte{34, 25, 109, 242, 193, 5, 144, 224, 76, 52, 189, 92, 197, 126, 9, 145, 27, 152, 199, 130, 165, 3, 210, 27, 193, 131, 142, 28, 110, 26, 16, 191})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -73,39 +70,36 @@ func init() {
|
||||||
|
|
||||||
// PointCache is the LRU cache for storing evaluated address commitment.
|
// PointCache is the LRU cache for storing evaluated address commitment.
|
||||||
type PointCache struct {
|
type PointCache struct {
|
||||||
lru lru.BasicLRU[string, *verkle.Point]
|
cache *lru.Cache[string, *verkle.Point]
|
||||||
lock sync.RWMutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPointCache returns the cache with specified size.
|
// NewPointCache returns the cache with specified size.
|
||||||
func NewPointCache(maxItems int) *PointCache {
|
func NewPointCache() *PointCache {
|
||||||
|
// Each verkle.Point is 96 bytes.
|
||||||
|
verklePointSize := 96
|
||||||
|
capacity := maxPointCacheByteSize / verklePointSize
|
||||||
return &PointCache{
|
return &PointCache{
|
||||||
lru: lru.NewBasicLRU[string, *verkle.Point](maxItems),
|
cache: lru.NewCache[string, *verkle.Point](capacity),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the cached commitment for the specified address, or computing
|
// GetTreeKeyHeader returns the cached commitment for the specified address,
|
||||||
// it on the flight.
|
// or computing it on the fly.
|
||||||
func (c *PointCache) Get(addr []byte) *verkle.Point {
|
func (pc *PointCache) GetTreeKeyHeader(addr []byte) *verkle.Point {
|
||||||
c.lock.Lock()
|
point, ok := pc.cache.Get(string(addr))
|
||||||
defer c.lock.Unlock()
|
|
||||||
|
|
||||||
p, ok := c.lru.Get(string(addr))
|
|
||||||
if ok {
|
if ok {
|
||||||
cacheHitGauge.Inc(1)
|
return point
|
||||||
return p
|
|
||||||
}
|
|
||||||
cacheMissGauge.Inc(1)
|
|
||||||
p = evaluateAddressPoint(addr)
|
|
||||||
c.lru.Add(string(addr), p)
|
|
||||||
return p
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStem returns the first 31 bytes of the tree key as the tree stem. It only
|
point = EvaluateAddressPoint(addr)
|
||||||
// works for the account metadata whose treeIndex is 0.
|
pc.cache.Add(string(addr), point)
|
||||||
func (c *PointCache) GetStem(addr []byte) []byte {
|
return point
|
||||||
p := c.Get(addr)
|
}
|
||||||
return pointToHash(p, 0)[:31]
|
|
||||||
|
func (pc *PointCache) GetTreeKeyVersionCached(addr []byte) []byte {
|
||||||
|
p := pc.GetTreeKeyHeader(addr)
|
||||||
|
v := PointToHash(p, VersionLeafKey)
|
||||||
|
return v[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTreeKey performs both the work of the spec's get_tree_key function, and that
|
// GetTreeKey performs both the work of the spec's get_tree_key function, and that
|
||||||
|
|
@ -143,9 +137,9 @@ func GetTreeKey(address []byte, treeIndex *uint256.Int, subIndex byte) []byte {
|
||||||
ret := cfg.CommitToPoly(poly[:], 0)
|
ret := cfg.CommitToPoly(poly[:], 0)
|
||||||
|
|
||||||
// add a constant point corresponding to poly[0]=[2+256*64].
|
// add a constant point corresponding to poly[0]=[2+256*64].
|
||||||
ret.Add(ret, index0Point)
|
ret.Add(ret, getTreePolyIndex0Point)
|
||||||
|
|
||||||
return pointToHash(ret, subIndex)
|
return PointToHash(ret, subIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTreeKeyWithEvaluatedAddress is basically identical to GetTreeKey, the only
|
// GetTreeKeyWithEvaluatedAddress is basically identical to GetTreeKey, the only
|
||||||
|
|
@ -174,7 +168,7 @@ func GetTreeKeyWithEvaluatedAddress(evaluated *verkle.Point, treeIndex *uint256.
|
||||||
// add the pre-evaluated address
|
// add the pre-evaluated address
|
||||||
ret.Add(ret, evaluated)
|
ret.Add(ret, evaluated)
|
||||||
|
|
||||||
return pointToHash(ret, subIndex)
|
return PointToHash(ret, subIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VersionKey returns the verkle tree key of the version field for the specified account.
|
// VersionKey returns the verkle tree key of the version field for the specified account.
|
||||||
|
|
@ -305,7 +299,7 @@ func StorageSlotKeyWithEvaluatedAddress(evaluated *verkle.Point, storageKey []by
|
||||||
return GetTreeKeyWithEvaluatedAddress(evaluated, treeIndex, subIndex)
|
return GetTreeKeyWithEvaluatedAddress(evaluated, treeIndex, subIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
func pointToHash(evaluated *verkle.Point, suffix byte) []byte {
|
func PointToHash(evaluated *verkle.Point, suffix byte) []byte {
|
||||||
// The output of Byte() is big endian for banderwagon. This
|
// The output of Byte() is big endian for banderwagon. This
|
||||||
// introduces an imbalance in the tree, because hashes are
|
// introduces an imbalance in the tree, because hashes are
|
||||||
// elements of a 253-bit field. This means more than half the
|
// elements of a 253-bit field. This means more than half the
|
||||||
|
|
@ -319,7 +313,31 @@ func pointToHash(evaluated *verkle.Point, suffix byte) []byte {
|
||||||
return bytes[:]
|
return bytes[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
func evaluateAddressPoint(address []byte) *verkle.Point {
|
func GetTreeKeyWithEvaluatedAddess(evaluated *verkle.Point, treeIndex *uint256.Int, subIndex byte) []byte {
|
||||||
|
var poly [5]fr.Element
|
||||||
|
|
||||||
|
poly[0].SetZero()
|
||||||
|
poly[1].SetZero()
|
||||||
|
poly[2].SetZero()
|
||||||
|
|
||||||
|
// little-endian, 32-byte aligned treeIndex
|
||||||
|
var index [32]byte
|
||||||
|
for i := 0; i < len(treeIndex); i++ {
|
||||||
|
binary.LittleEndian.PutUint64(index[i*8:(i+1)*8], treeIndex[i])
|
||||||
|
}
|
||||||
|
verkle.FromLEBytes(&poly[3], index[:16])
|
||||||
|
verkle.FromLEBytes(&poly[4], index[16:])
|
||||||
|
|
||||||
|
cfg := verkle.GetConfig()
|
||||||
|
ret := cfg.CommitToPoly(poly[:], 0)
|
||||||
|
|
||||||
|
// add the pre-evaluated address
|
||||||
|
ret.Add(ret, evaluated)
|
||||||
|
|
||||||
|
return PointToHash(ret, subIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EvaluateAddressPoint(address []byte) *verkle.Point {
|
||||||
if len(address) < 32 {
|
if len(address) < 32 {
|
||||||
var aligned [32]byte
|
var aligned [32]byte
|
||||||
address = append(aligned[:32-len(address)], address...)
|
address = append(aligned[:32-len(address)], address...)
|
||||||
|
|
@ -337,6 +355,43 @@ func evaluateAddressPoint(address []byte) *verkle.Point {
|
||||||
ret := cfg.CommitToPoly(poly[:], 0)
|
ret := cfg.CommitToPoly(poly[:], 0)
|
||||||
|
|
||||||
// add a constant point
|
// add a constant point
|
||||||
ret.Add(ret, index0Point)
|
ret.Add(ret, getTreePolyIndex0Point)
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetTreeKeyStorageSlotTreeIndexes(storageKey []byte) (*uint256.Int, byte) {
|
||||||
|
var pos uint256.Int
|
||||||
|
pos.SetBytes(storageKey)
|
||||||
|
|
||||||
|
// If the storage slot is in the header, we need to add the header offset.
|
||||||
|
if pos.Cmp(codeStorageDelta) < 0 {
|
||||||
|
// This addition is always safe; it can't ever overflow since pos<codeStorageDelta.
|
||||||
|
pos.Add(headerStorageOffset, &pos)
|
||||||
|
|
||||||
|
// In this branch, the tree-index is zero since we're in the account header,
|
||||||
|
// and the sub-index is the LSB of the modified storage key.
|
||||||
|
return zero, byte(pos[0] & 0xFF)
|
||||||
|
}
|
||||||
|
// If the storage slot is in the main storage, we need to add the main storage offset.
|
||||||
|
|
||||||
|
// The first MAIN_STORAGE_OFFSET group will see its
|
||||||
|
// first 64 slots unreachable. This is either a typo in the
|
||||||
|
// spec or intended to conserve the 256-u256
|
||||||
|
// aligment. If we decide to ever access these 64
|
||||||
|
// slots, uncomment this.
|
||||||
|
// // Get the new offset since we now know that we are above 64.
|
||||||
|
// pos.Sub(&pos, codeStorageDelta)
|
||||||
|
// suffix := byte(pos[0] & 0xFF)
|
||||||
|
suffix := storageKey[len(storageKey)-1]
|
||||||
|
|
||||||
|
// We first divide by VerkleNodeWidth to create room to avoid an overflow next.
|
||||||
|
pos.Rsh(&pos, uint(verkleNodeWidthLog2))
|
||||||
|
|
||||||
|
// We add mainStorageOffset/VerkleNodeWidth which can't overflow.
|
||||||
|
pos.Add(&pos, mainStorageOffsetLshVerkleNodeWidth)
|
||||||
|
|
||||||
|
// The sub-index is the LSB of the original storage key, since mainStorageOffset
|
||||||
|
// doesn't affect this byte, so we can avoid masks or shifts.
|
||||||
|
return &pos, suffix
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import (
|
||||||
func TestTreeKey(t *testing.T) {
|
func TestTreeKey(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
address = []byte{0x01}
|
address = []byte{0x01}
|
||||||
addressEval = evaluateAddressPoint(address)
|
addressEval = EvaluateAddressPoint(address)
|
||||||
smallIndex = uint256.NewInt(1)
|
smallIndex = uint256.NewInt(1)
|
||||||
largeIndex = uint256.NewInt(10000)
|
largeIndex = uint256.NewInt(10000)
|
||||||
smallStorage = []byte{0x1}
|
smallStorage = []byte{0x1}
|
||||||
|
|
@ -91,7 +91,7 @@ func BenchmarkTreeKeyWithEvaluation(b *testing.B) {
|
||||||
verkle.GetConfig()
|
verkle.GetConfig()
|
||||||
|
|
||||||
addr := []byte{0x01}
|
addr := []byte{0x01}
|
||||||
eval := evaluateAddressPoint(addr)
|
eval := EvaluateAddressPoint(addr)
|
||||||
|
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
@ -129,7 +129,7 @@ func BenchmarkStorageKeyWithEvaluation(b *testing.B) {
|
||||||
verkle.GetConfig()
|
verkle.GetConfig()
|
||||||
|
|
||||||
addr := []byte{0x01}
|
addr := []byte{0x01}
|
||||||
eval := evaluateAddressPoint(addr)
|
eval := EvaluateAddressPoint(addr)
|
||||||
|
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ func (t *VerkleTrie) GetKey(key []byte) []byte {
|
||||||
// account address. If the specified account is not in the verkle tree, nil will
|
// account address. If the specified account is not in the verkle tree, nil will
|
||||||
// be returned. If the tree is corrupted, an error will be returned.
|
// be returned. If the tree is corrupted, an error will be returned.
|
||||||
func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error) {
|
func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error) {
|
||||||
|
versionkey := t.cache.GetTreeKeyVersionCached(addr[:])
|
||||||
var (
|
var (
|
||||||
acc = &types.StateAccount{}
|
acc = &types.StateAccount{}
|
||||||
values [][]byte
|
values [][]byte
|
||||||
|
|
@ -86,7 +87,7 @@ func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error
|
||||||
)
|
)
|
||||||
switch n := t.root.(type) {
|
switch n := t.root.(type) {
|
||||||
case *verkle.InternalNode:
|
case *verkle.InternalNode:
|
||||||
values, err = n.GetValuesAtStem(t.cache.GetStem(addr[:]), t.nodeResolver)
|
values, err = n.GetValuesAtStem(versionkey, t.nodeResolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("GetAccount (%x) error: %v", addr, err)
|
return nil, fmt.Errorf("GetAccount (%x) error: %v", addr, err)
|
||||||
}
|
}
|
||||||
|
|
@ -119,7 +120,7 @@ func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error
|
||||||
// account address and storage key. If the specified slot is not in the verkle tree,
|
// account address and storage key. If the specified slot is not in the verkle tree,
|
||||||
// nil will be returned. If the tree is corrupted, an error will be returned.
|
// nil will be returned. If the tree is corrupted, an error will be returned.
|
||||||
func (t *VerkleTrie) GetStorage(addr common.Address, key []byte) ([]byte, error) {
|
func (t *VerkleTrie) GetStorage(addr common.Address, key []byte) ([]byte, error) {
|
||||||
k := utils.StorageSlotKeyWithEvaluatedAddress(t.cache.Get(addr.Bytes()), key)
|
k := utils.StorageSlotKeyWithEvaluatedAddress(t.cache.GetTreeKeyHeader(addr.Bytes()), key)
|
||||||
val, err := t.root.Get(k, t.nodeResolver)
|
val, err := t.root.Get(k, t.nodeResolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -153,7 +154,7 @@ func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount)
|
||||||
|
|
||||||
switch n := t.root.(type) {
|
switch n := t.root.(type) {
|
||||||
case *verkle.InternalNode:
|
case *verkle.InternalNode:
|
||||||
err = n.InsertValuesAtStem(t.cache.GetStem(addr[:]), values, t.nodeResolver)
|
err = n.InsertValuesAtStem(t.cache.GetTreeKeyVersionCached(addr[:]), values, t.nodeResolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("UpdateAccount (%x) error: %v", addr, err)
|
return fmt.Errorf("UpdateAccount (%x) error: %v", addr, err)
|
||||||
}
|
}
|
||||||
|
|
@ -174,7 +175,7 @@ func (t *VerkleTrie) UpdateStorage(address common.Address, key, value []byte) er
|
||||||
} else {
|
} else {
|
||||||
copy(v[32-len(value):], value[:])
|
copy(v[32-len(value):], value[:])
|
||||||
}
|
}
|
||||||
k := utils.StorageSlotKeyWithEvaluatedAddress(t.cache.Get(address.Bytes()), key)
|
k := utils.StorageSlotKeyWithEvaluatedAddress(t.cache.GetTreeKeyHeader(address.Bytes()), key)
|
||||||
return t.root.Insert(k, v[:], t.nodeResolver)
|
return t.root.Insert(k, v[:], t.nodeResolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -191,7 +192,7 @@ func (t *VerkleTrie) DeleteAccount(addr common.Address) error {
|
||||||
}
|
}
|
||||||
switch n := t.root.(type) {
|
switch n := t.root.(type) {
|
||||||
case *verkle.InternalNode:
|
case *verkle.InternalNode:
|
||||||
err = n.InsertValuesAtStem(t.cache.GetStem(addr.Bytes()), values, t.nodeResolver)
|
err = n.InsertValuesAtStem(t.cache.GetTreeKeyVersionCached(addr.Bytes()), values, t.nodeResolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("DeleteAccount (%x) error: %v", addr, err)
|
return fmt.Errorf("DeleteAccount (%x) error: %v", addr, err)
|
||||||
}
|
}
|
||||||
|
|
@ -206,7 +207,7 @@ func (t *VerkleTrie) DeleteAccount(addr common.Address) error {
|
||||||
// returned. If the trie is corrupted, an error will be returned.
|
// returned. If the trie is corrupted, an error will be returned.
|
||||||
func (t *VerkleTrie) DeleteStorage(addr common.Address, key []byte) error {
|
func (t *VerkleTrie) DeleteStorage(addr common.Address, key []byte) error {
|
||||||
var zero [32]byte
|
var zero [32]byte
|
||||||
k := utils.StorageSlotKeyWithEvaluatedAddress(t.cache.Get(addr.Bytes()), key)
|
k := utils.StorageSlotKeyWithEvaluatedAddress(t.cache.GetTreeKeyHeader(addr.Bytes()), key)
|
||||||
return t.root.Insert(k, zero[:], t.nodeResolver)
|
return t.root.Insert(k, zero[:], t.nodeResolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -338,7 +339,7 @@ func (t *VerkleTrie) UpdateContractCode(addr common.Address, codeHash common.Has
|
||||||
groupOffset := (chunknr + 128) % 256
|
groupOffset := (chunknr + 128) % 256
|
||||||
if groupOffset == 0 /* start of new group */ || chunknr == 0 /* first chunk in header group */ {
|
if groupOffset == 0 /* start of new group */ || chunknr == 0 /* first chunk in header group */ {
|
||||||
values = make([][]byte, verkle.NodeWidth)
|
values = make([][]byte, verkle.NodeWidth)
|
||||||
key = utils.CodeChunkKeyWithEvaluatedAddress(t.cache.Get(addr.Bytes()), uint256.NewInt(chunknr))
|
key = utils.CodeChunkKeyWithEvaluatedAddress(t.cache.GetTreeKeyHeader(addr.Bytes()), uint256.NewInt(chunknr))
|
||||||
}
|
}
|
||||||
values[groupOffset] = chunks[i : i+32]
|
values[groupOffset] = chunks[i : i+32]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ var (
|
||||||
|
|
||||||
func TestVerkleTreeReadWrite(t *testing.T) {
|
func TestVerkleTreeReadWrite(t *testing.T) {
|
||||||
db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.PathScheme)
|
db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.PathScheme)
|
||||||
tr, _ := NewVerkleTrie(types.EmptyVerkleHash, db, utils.NewPointCache(100))
|
tr, _ := NewVerkleTrie(types.EmptyVerkleHash, db, utils.NewPointCache())
|
||||||
|
|
||||||
for addr, acct := range accounts {
|
for addr, acct := range accounts {
|
||||||
if err := tr.UpdateAccount(addr, acct); err != nil {
|
if err := tr.UpdateAccount(addr, acct); err != nil {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue