mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 02:42:27 +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.
|
||||
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) {
|
||||
beacon.ethone.Finalize(chain, header, state, txs, uncles, nil)
|
||||
beacon.ethone.Finalize(chain, header, statedb, txs, uncles, nil)
|
||||
return
|
||||
}
|
||||
// Withdrawals processing.
|
||||
|
|
@ -358,7 +358,11 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
|
|||
// Convert amount from gwei to wei.
|
||||
amount := new(uint256.Int).SetUint64(w.Amount)
|
||||
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.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,10 @@ var (
|
|||
// than init code size limit.
|
||||
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
|
||||
// is higher than the balance of the user's account.
|
||||
ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
|
||||
|
|
|
|||
|
|
@ -18,22 +18,62 @@ package state
|
|||
|
||||
import (
|
||||
"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
|
||||
slots []map[common.Hash]struct{}
|
||||
}
|
||||
|
||||
// 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]
|
||||
return ok
|
||||
}
|
||||
|
||||
// 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.
|
||||
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]
|
||||
if !ok {
|
||||
// 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.
|
||||
func newAccessList() *accessList {
|
||||
return &accessList{
|
||||
func newAccessList() AccessList {
|
||||
return &accessList2929{
|
||||
addresses: make(map[common.Address]int),
|
||||
}
|
||||
}
|
||||
|
||||
// Copy creates an independent copy of an accessList.
|
||||
func (a *accessList) Copy() *accessList {
|
||||
cp := newAccessList()
|
||||
func (a *accessList2929) Copy() AccessList {
|
||||
cp := newAccessList().(*accessList2929)
|
||||
for k, v := range a.addresses {
|
||||
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
|
||||
// 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 {
|
||||
return false
|
||||
return params.WarmStorageReadCostEIP2929
|
||||
}
|
||||
al.addresses[address] = -1
|
||||
return true
|
||||
return params.ColdAccountAccessCostEIP2929
|
||||
}
|
||||
|
||||
// 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
|
||||
// - slot added
|
||||
// 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]
|
||||
if !addrPresent || idx == -1 {
|
||||
// Address not present, or addr present but no slots there
|
||||
al.addresses[address] = len(al.slots)
|
||||
slotmap := map[common.Hash]struct{}{slot: {}}
|
||||
al.slots = append(al.slots, slotmap)
|
||||
return !addrPresent, true
|
||||
return params.WarmStorageReadCostEIP2929
|
||||
}
|
||||
// There is already an (address,slot) mapping
|
||||
slotmap := al.slots[idx]
|
||||
if _, ok := slotmap[slot]; !ok {
|
||||
slotmap[slot] = struct{}{}
|
||||
// Journal add slot change
|
||||
return false, true
|
||||
return params.ColdSloadCostEIP2929
|
||||
}
|
||||
// No changes required
|
||||
return false, false
|
||||
return params.WarmStorageReadCostEIP2929
|
||||
}
|
||||
|
||||
// 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 method is meant to be used by the journal, which maintains ordering of
|
||||
// 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]
|
||||
// There are two ways this can fail
|
||||
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.
|
||||
// This method is meant to be used by the journal, which maintains ordering of
|
||||
// operations.
|
||||
func (al *accessList) DeleteAddress(address common.Address) {
|
||||
func (al *accessList2929) DeleteAddress(address common.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.
|
||||
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||
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)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -99,11 +99,14 @@ type StateDB struct {
|
|||
preimages map[common.Hash][]byte
|
||||
|
||||
// Per-transaction access list
|
||||
accessList *accessList
|
||||
accessList AccessList
|
||||
|
||||
// Transient storage
|
||||
transientStorage transientStorage
|
||||
|
||||
// Global witness
|
||||
witness AccessList
|
||||
|
||||
// Journal of state modifications. This is the backbone of
|
||||
// Snapshot and RevertToSnapshot.
|
||||
journal *journal
|
||||
|
|
@ -1275,26 +1278,30 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er
|
|||
// - Reset transient storage (EIP-1153)
|
||||
func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
|
||||
if rules.IsBerlin {
|
||||
if !rules.IsVerkle {
|
||||
s.accessList = newAccessList()
|
||||
} else {
|
||||
s.accessList = NewAccessWitness()
|
||||
}
|
||||
// Clear out any leftover from previous executions
|
||||
al := newAccessList()
|
||||
s.accessList = al
|
||||
al := s.accessList
|
||||
|
||||
al.AddAddress(sender)
|
||||
al.AddAddress(sender, ALAllItems, AccessListRead)
|
||||
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
|
||||
}
|
||||
for _, addr := range precompiles {
|
||||
al.AddAddress(addr)
|
||||
al.AddAddress(addr, ALAllItems, AccessListRead)
|
||||
}
|
||||
for _, el := range list {
|
||||
al.AddAddress(el.Address)
|
||||
al.AddAddress(el.Address, 0, AccessListRead)
|
||||
for _, key := range el.StorageKeys {
|
||||
al.AddSlot(el.Address, key)
|
||||
al.AddSlot(el.Address, key, AccessListRead)
|
||||
}
|
||||
}
|
||||
if rules.IsShanghai { // EIP-3651: warm coinbase
|
||||
al.AddAddress(coinbase)
|
||||
al.AddAddress(coinbase, ALAllItems, AccessListRead)
|
||||
}
|
||||
}
|
||||
// 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
|
||||
func (s *StateDB) AddAddressToAccessList(addr common.Address) {
|
||||
if s.accessList.AddAddress(addr) {
|
||||
func (s *StateDB) AddAddressToAccessList(addr common.Address, mode ALAccountItem, isWrite ALAccessMode) uint64 {
|
||||
gas := s.accessList.AddAddress(addr, mode, isWrite)
|
||||
if gas > 0 {
|
||||
s.journal.append(accessListAddAccountChange{&addr})
|
||||
}
|
||||
return gas
|
||||
}
|
||||
|
||||
// AddSlotToAccessList adds the given (address, slot)-tuple to the access list
|
||||
func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) {
|
||||
addrMod, slotMod := s.accessList.AddSlot(addr, slot)
|
||||
if addrMod {
|
||||
// In practice, this should not happen, since there is no way to enter the
|
||||
// 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 {
|
||||
func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash, isWrite ALAccessMode) uint64 {
|
||||
gas := s.accessList.AddSlot(addr, slot, isWrite)
|
||||
|
||||
if gas > params.WarmStorageReadCostEIP2929 {
|
||||
s.journal.append(accessListAddSlotChange{
|
||||
address: &addr,
|
||||
slot: &slot,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
return gas
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
s.AddAddressToAccessList(addr)
|
||||
s.AddAddressToAccessList(addr, 0, false)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "AddSlotToAccessList",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
s.AddSlotToAccessList(addr,
|
||||
common.Hash{byte(a.args[0])})
|
||||
common.Hash{byte(a.args[0])}, false)
|
||||
},
|
||||
args: make([]int64, 1),
|
||||
},
|
||||
|
|
@ -879,19 +879,19 @@ func TestStateDBAccessList(t *testing.T) {
|
|||
}
|
||||
// Check that the given addresses are in the access list
|
||||
for _, address := range addresses {
|
||||
if !state.AddressInAccessList(address) {
|
||||
if !state.addressInAccessList(address) {
|
||||
t.Fatalf("expected %x to be in access list", address)
|
||||
}
|
||||
}
|
||||
// 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 {
|
||||
t.Fatalf("extra address %x in access list", address)
|
||||
}
|
||||
}
|
||||
}
|
||||
verifySlots := func(addrString string, slotStrings ...string) {
|
||||
if !state.AddressInAccessList(addr(addrString)) {
|
||||
if !state.addressInAccessList(addr(addrString)) {
|
||||
t.Fatalf("scope missing address/slots %v", addrString)
|
||||
}
|
||||
var address = addr(addrString)
|
||||
|
|
@ -905,14 +905,14 @@ func TestStateDBAccessList(t *testing.T) {
|
|||
}
|
||||
// Check that the expected items are in the access list
|
||||
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)
|
||||
}
|
||||
}
|
||||
// Check that no extra elements are in the access list
|
||||
index := state.accessList.addresses[address]
|
||||
index := state.accessList.(*accessList2929).addresses[address]
|
||||
if index >= 0 {
|
||||
stateSlots := state.accessList.slots[index]
|
||||
stateSlots := state.accessList.(*accessList2929).slots[index]
|
||||
for s := range stateSlots {
|
||||
if _, slotPresent := slotMap[s]; !slotPresent {
|
||||
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.AddSlotToAccessList(addr("bb"), slot("01")) // 2,3
|
||||
state.AddSlotToAccessList(addr("bb"), slot("02")) // 4
|
||||
state.AddAddressToAccessList(addr("aa"), 0, false) // 1
|
||||
state.AddSlotToAccessList(addr("bb"), slot("01"), false) // 2,3
|
||||
state.AddSlotToAccessList(addr("bb"), slot("02"), false) // 4
|
||||
verifyAddrs("aa", "bb")
|
||||
verifySlots("bb", "01", "02")
|
||||
|
||||
|
|
@ -934,17 +934,17 @@ func TestStateDBAccessList(t *testing.T) {
|
|||
}
|
||||
|
||||
// same again, should cause no journal entries
|
||||
state.AddSlotToAccessList(addr("bb"), slot("01"))
|
||||
state.AddSlotToAccessList(addr("bb"), slot("02"))
|
||||
state.AddAddressToAccessList(addr("aa"))
|
||||
state.AddSlotToAccessList(addr("bb"), slot("01"), false)
|
||||
state.AddSlotToAccessList(addr("bb"), slot("02"), false)
|
||||
state.AddAddressToAccessList(addr("aa"), 0, false)
|
||||
if exp, got := 4, state.journal.length(); exp != got {
|
||||
t.Fatalf("journal length mismatch: have %d, want %d", got, exp)
|
||||
}
|
||||
// some new ones
|
||||
state.AddSlotToAccessList(addr("bb"), slot("03")) // 5
|
||||
state.AddSlotToAccessList(addr("aa"), slot("01")) // 6
|
||||
state.AddSlotToAccessList(addr("cc"), slot("01")) // 7,8
|
||||
state.AddAddressToAccessList(addr("cc"))
|
||||
state.AddSlotToAccessList(addr("bb"), slot("03"), false) // 5
|
||||
state.AddSlotToAccessList(addr("aa"), slot("01"), false) // 6
|
||||
state.AddSlotToAccessList(addr("cc"), slot("01"), false) // 7,8
|
||||
state.AddAddressToAccessList(addr("cc"), 0, false)
|
||||
if exp, got := 8, state.journal.length(); exp != got {
|
||||
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
|
||||
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")
|
||||
}
|
||||
verifyAddrs("aa", "bb", "cc")
|
||||
|
|
@ -964,7 +964,7 @@ func TestStateDBAccessList(t *testing.T) {
|
|||
verifySlots("bb", "01", "02", "03")
|
||||
|
||||
state.journal.revert(state, 6)
|
||||
if state.AddressInAccessList(addr("cc")) {
|
||||
if state.addressInAccessList(addr("cc")) {
|
||||
t.Fatalf("addr present, expected missing")
|
||||
}
|
||||
verifyAddrs("aa", "bb")
|
||||
|
|
@ -972,46 +972,46 @@ func TestStateDBAccessList(t *testing.T) {
|
|||
verifySlots("bb", "01", "02", "03")
|
||||
|
||||
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")
|
||||
}
|
||||
verifyAddrs("aa", "bb")
|
||||
verifySlots("bb", "01", "02", "03")
|
||||
|
||||
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")
|
||||
}
|
||||
verifyAddrs("aa", "bb")
|
||||
verifySlots("bb", "01", "02")
|
||||
|
||||
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")
|
||||
}
|
||||
verifyAddrs("aa", "bb")
|
||||
verifySlots("bb", "01")
|
||||
|
||||
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")
|
||||
}
|
||||
verifyAddrs("aa", "bb")
|
||||
|
||||
state.journal.revert(state, 1)
|
||||
if state.AddressInAccessList(addr("bb")) {
|
||||
if state.addressInAccessList(addr("bb")) {
|
||||
t.Fatalf("addr present, expected missing")
|
||||
}
|
||||
verifyAddrs("aa")
|
||||
|
||||
state.journal.revert(state, 0)
|
||||
if state.AddressInAccessList(addr("aa")) {
|
||||
if state.addressInAccessList(addr("aa")) {
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
// Check the copy
|
||||
|
|
@ -1019,10 +1019,10 @@ func TestStateDBAccessList(t *testing.T) {
|
|||
state = stateCopy1
|
||||
verifyAddrs("aa", "bb")
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,6 +145,8 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta
|
|||
receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
|
||||
}
|
||||
|
||||
statedb.MergeTxAccessListIntoBlockWitness()
|
||||
|
||||
// Set the receipt logs and create the bloom filter.
|
||||
receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash)
|
||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||
|
|
@ -185,7 +187,7 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat
|
|||
Data: beaconRoot[:],
|
||||
}
|
||||
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)
|
||||
statedb.Finalise(true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,10 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
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/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
|
|
@ -354,6 +356,19 @@ func (st *StateTransition) preCheck() error {
|
|||
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
|
||||
// returning the evm execution result with following fields.
|
||||
//
|
||||
|
|
@ -404,6 +419,28 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
|||
}
|
||||
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
|
||||
value, overflow := uint256.FromBig(msg.Value)
|
||||
if overflow {
|
||||
|
|
@ -457,6 +494,10 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
|||
fee := new(uint256.Int).SetUint64(st.gasUsed())
|
||||
fee.Mul(fee, effectiveTipU256)
|
||||
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{
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ type Contract struct {
|
|||
CodeAddr *common.Address
|
||||
Input []byte
|
||||
|
||||
IsDeployment bool
|
||||
|
||||
Gas uint64
|
||||
value *uint256.Int
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ var activators = map[int]func(*JumpTable){
|
|||
1884: enable1884,
|
||||
1344: enable1344,
|
||||
1153: enable1153,
|
||||
4762: enable4762,
|
||||
}
|
||||
|
||||
// EnableEIP enables the given EIP on the config.
|
||||
|
|
@ -123,28 +124,28 @@ func enable2929(jt *JumpTable) {
|
|||
jt[SLOAD].constantGas = 0
|
||||
jt[SLOAD].dynamicGas = gasSLoadEIP2929
|
||||
|
||||
jt[EXTCODECOPY].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[EXTCODECOPY].constantGas = 0
|
||||
jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP2929
|
||||
|
||||
jt[EXTCODESIZE].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[EXTCODESIZE].constantGas = 0
|
||||
jt[EXTCODESIZE].dynamicGas = gasEip2929AccountCheck
|
||||
|
||||
jt[EXTCODEHASH].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[EXTCODEHASH].constantGas = 0
|
||||
jt[EXTCODEHASH].dynamicGas = gasEip2929AccountCheck
|
||||
|
||||
jt[BALANCE].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[BALANCE].constantGas = 0
|
||||
jt[BALANCE].dynamicGas = gasEip2929AccountCheck
|
||||
|
||||
jt[CALL].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[CALL].constantGas = 0
|
||||
jt[CALL].dynamicGas = gasCallEIP2929
|
||||
|
||||
jt[CALLCODE].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[CALLCODE].constantGas = 0
|
||||
jt[CALLCODE].dynamicGas = gasCallCodeEIP2929
|
||||
|
||||
jt[STATICCALL].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[STATICCALL].constantGas = 0
|
||||
jt[STATICCALL].dynamicGas = gasStaticCallEIP2929
|
||||
|
||||
jt[DELEGATECALL].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[DELEGATECALL].constantGas = 0
|
||||
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP2929
|
||||
|
||||
// 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),
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
"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/crypto"
|
||||
"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)
|
||||
debug := evm.Config.Tracer != nil
|
||||
|
||||
var creation bool
|
||||
if !evm.StateDB.Exist(addr) {
|
||||
if !isPrecompile && evm.chainRules.IsEIP158 && value.IsZero() {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
if evm.chainRules.IsVerkle {
|
||||
tryConsumeGas(&gas, evm.StateDB.AddAddressToAccessList(addr, state.ALAllItems, state.AccessListRead))
|
||||
}
|
||||
return nil, gas, nil
|
||||
}
|
||||
evm.StateDB.CreateAccount(addr)
|
||||
creation = true
|
||||
}
|
||||
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
|
||||
contract := NewContract(caller, AccountRef(addrCopy), value, gas)
|
||||
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
|
||||
contract.IsDeployment = creation
|
||||
ret, err = evm.interpreter.Run(contract, input, false)
|
||||
gas = contract.Gas
|
||||
}
|
||||
|
|
@ -418,6 +426,20 @@ func (c *codeAndHash) Hash() common.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.
|
||||
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
|
||||
|
|
@ -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,
|
||||
// the access-list change should not be rolled back
|
||||
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
|
||||
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.
|
||||
contract := NewContract(caller, AccountRef(address), value, gas)
|
||||
contract.SetCodeOptionalHash(&address, codeAndHash)
|
||||
contract.IsDeployment = true
|
||||
|
||||
if evm.Config.Tracer != nil {
|
||||
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
|
||||
// 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.
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/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 {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -420,6 +435,17 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
|
|||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -436,6 +462,14 @@ func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if evm.chainRules.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
|
||||
}
|
||||
|
||||
|
|
@ -452,6 +486,14 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
|
|||
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
if evm.chainRules.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
|
||||
}
|
||||
|
||||
|
|
@ -477,3 +519,40 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||
}
|
||||
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()))
|
||||
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 {
|
||||
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()))
|
||||
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
|
||||
gas -= gas / 64
|
||||
scope.Contract.UseGas(gas)
|
||||
|
|
@ -866,6 +881,17 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
|
|||
*pc += 1
|
||||
if *pc < codeLen {
|
||||
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 {
|
||||
scope.Stack.push(integer.Clear())
|
||||
}
|
||||
|
|
@ -887,6 +913,17 @@ func makePush(size uint64, pushByteSize int) executionFunc {
|
|||
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)
|
||||
scope.Stack.push(integer.SetBytes(common.RightPadBytes(
|
||||
scope.Contract.Code[startMin:endMin], pushByteSize)))
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"math/big"
|
||||
|
||||
"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/params"
|
||||
"github.com/holiman/uint256"
|
||||
|
|
@ -64,14 +65,18 @@ type StateDB interface {
|
|||
// is defined according to EIP161 (balance = nonce = code = 0).
|
||||
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
|
||||
// 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
|
||||
// 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)
|
||||
|
||||
RevertToSnapshot(int)
|
||||
|
|
@ -79,6 +84,8 @@ type StateDB interface {
|
|||
|
||||
AddLog(*types.Log)
|
||||
AddPreimage(common.Hash, []byte)
|
||||
|
||||
Witness() state.AccessList
|
||||
}
|
||||
|
||||
// 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.
|
||||
var table *JumpTable
|
||||
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:
|
||||
table = &cancunInstructionSet
|
||||
case evm.chainRules.IsShanghai:
|
||||
|
|
@ -174,6 +179,12 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
// Capture pre-execution values for tracing.
|
||||
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
|
||||
// enough stack items available to perform the operation.
|
||||
op = contract.GetOp(pc)
|
||||
|
|
|
|||
|
|
@ -80,6 +80,12 @@ func validate(jt JumpTable) JumpTable {
|
|||
return jt
|
||||
}
|
||||
|
||||
func newVerkleInstructionSet() JumpTable {
|
||||
instructionSet := newCancunInstructionSet()
|
||||
EnableEIP(4762, &instructionSet)
|
||||
return validate(instructionSet)
|
||||
}
|
||||
|
||||
func newCancunInstructionSet() JumpTable {
|
||||
instructionSet := newShanghaiInstructionSet()
|
||||
enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
|
|
@ -35,26 +36,15 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
|||
y, x = stack.Back(1), stack.peek()
|
||||
slot = common.Hash(x.Bytes32())
|
||||
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
|
||||
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())
|
||||
|
||||
if current == value { // noop (1)
|
||||
// EIP 2200 original clause:
|
||||
// return params.SloadGasEIP2200, nil
|
||||
return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
|
||||
return cost, nil // SLOAD_GAS
|
||||
}
|
||||
original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
|
||||
if original == current {
|
||||
|
|
@ -91,10 +81,27 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
|||
}
|
||||
// EIP-2200 original clause:
|
||||
//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
|
||||
// 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,
|
||||
|
|
@ -104,13 +111,7 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||
loc := stack.peek()
|
||||
slot := common.Hash(loc.Bytes32())
|
||||
// Check slot presence in the access list
|
||||
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
// 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
|
||||
return evm.StateDB.AddSlotToAccessList(contract.Address(), slot, false), nil
|
||||
}
|
||||
|
||||
// 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())
|
||||
// Check slot presence in the access list
|
||||
if !evm.StateDB.AddressInAccessList(addr) {
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
var overflow bool
|
||||
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
||||
if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
algas := evm.StateDB.AddAddressToAccessList(addr, state.ALCodeSize, state.AccessListRead)
|
||||
var overflow bool
|
||||
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
||||
if gas, overflow = math.SafeAdd(gas, algas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
|
||||
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) {
|
||||
addr := common.Address(stack.peek().Bytes20())
|
||||
// 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
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
// The warm storage read cost is already charged as constantGas
|
||||
return params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil
|
||||
}
|
||||
return 0, nil
|
||||
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
return evm.StateDB.AddAddressToAccessList(addr, state.ALCodeSize|state.ALCodeHash|state.ALBalance, state.AccessListRead), nil
|
||||
}
|
||||
|
||||
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
addr := common.Address(stack.Back(1).Bytes20())
|
||||
// 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 cost to charge for cold access, if any, is Cold - Warm
|
||||
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
||||
if !warmAccess {
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
if cost == params.WarmStorageReadCostEIP2929 {
|
||||
// Charge the remaining difference here already, to correctly calculate available
|
||||
// gas for call
|
||||
if !contract.UseGas(coldCost) {
|
||||
if !contract.UseGas(cost) {
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
|
|
@ -179,17 +172,17 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
|
|||
// - memory expansion
|
||||
// - 63/64ths rule
|
||||
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||
if warmAccess || err != nil {
|
||||
if cost == params.WarmStorageReadCostEIP2929 || err != nil {
|
||||
return gas, err
|
||||
}
|
||||
// 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
|
||||
// outside of this function, as part of the dynamic gas, and that will make it
|
||||
// also become correctly reported to tracers.
|
||||
contract.Gas += coldCost
|
||||
contract.Gas += cost
|
||||
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
|
||||
if gas, overflow = math.SafeAdd(gas, cost); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
|
|
@ -231,11 +224,7 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
|||
gas uint64
|
||||
address = common.Address(stack.peek().Bytes20())
|
||||
)
|
||||
if !evm.StateDB.AddressInAccessList(address) {
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddAddressToAccessList(address)
|
||||
gas = params.ColdAccountAccessCostEIP2929
|
||||
}
|
||||
gas = evm.StateDB.AddAddressToAccessList(address, 0, state.AccessListWrite)
|
||||
// if empty and transfers value
|
||||
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
||||
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 (
|
||||
"encoding/binary"
|
||||
"sync"
|
||||
|
||||
"github.com/crate-crypto/go-ipa/bandersnatch/fr"
|
||||
"github.com/ethereum/go-ethereum/common/lru"
|
||||
|
|
@ -35,6 +34,8 @@ const (
|
|||
NonceLeafKey = 2
|
||||
CodeKeccakLeafKey = 3
|
||||
CodeSizeLeafKey = 4
|
||||
|
||||
maxPointCacheByteSize = 100 << 20
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -46,7 +47,7 @@ var (
|
|||
verkleNodeWidth = uint256.NewInt(256)
|
||||
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 = metrics.NewRegisteredGauge("trie/verkle/cache/hit", nil)
|
||||
|
|
@ -57,15 +58,11 @@ var (
|
|||
|
||||
func init() {
|
||||
// The byte array is the Marshalled output of the point computed as such:
|
||||
//
|
||||
// var (
|
||||
// config = verkle.GetConfig()
|
||||
// fr verkle.Fr
|
||||
// )
|
||||
// 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})
|
||||
//cfg, _ := verkle.GetConfig()
|
||||
//verkle.FromLEBytes(&getTreePolyIndex0Fr[0], []byte{2, 64})
|
||||
//= cfg.CommitToPoly(getTreePolyIndex0Fr[:], 1)
|
||||
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})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -73,39 +70,36 @@ func init() {
|
|||
|
||||
// PointCache is the LRU cache for storing evaluated address commitment.
|
||||
type PointCache struct {
|
||||
lru lru.BasicLRU[string, *verkle.Point]
|
||||
lock sync.RWMutex
|
||||
cache *lru.Cache[string, *verkle.Point]
|
||||
}
|
||||
|
||||
// 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{
|
||||
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
|
||||
// it on the flight.
|
||||
func (c *PointCache) Get(addr []byte) *verkle.Point {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
p, ok := c.lru.Get(string(addr))
|
||||
// GetTreeKeyHeader returns the cached commitment for the specified address,
|
||||
// or computing it on the fly.
|
||||
func (pc *PointCache) GetTreeKeyHeader(addr []byte) *verkle.Point {
|
||||
point, ok := pc.cache.Get(string(addr))
|
||||
if ok {
|
||||
cacheHitGauge.Inc(1)
|
||||
return p
|
||||
return point
|
||||
}
|
||||
cacheMissGauge.Inc(1)
|
||||
p = evaluateAddressPoint(addr)
|
||||
c.lru.Add(string(addr), p)
|
||||
return p
|
||||
|
||||
point = EvaluateAddressPoint(addr)
|
||||
pc.cache.Add(string(addr), point)
|
||||
return point
|
||||
}
|
||||
|
||||
// GetStem returns the first 31 bytes of the tree key as the tree stem. It only
|
||||
// works for the account metadata whose treeIndex is 0.
|
||||
func (c *PointCache) GetStem(addr []byte) []byte {
|
||||
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
|
||||
|
|
@ -143,9 +137,9 @@ func GetTreeKey(address []byte, treeIndex *uint256.Int, subIndex byte) []byte {
|
|||
ret := cfg.CommitToPoly(poly[:], 0)
|
||||
|
||||
// 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
|
||||
|
|
@ -174,7 +168,7 @@ func GetTreeKeyWithEvaluatedAddress(evaluated *verkle.Point, treeIndex *uint256.
|
|||
// add the pre-evaluated address
|
||||
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.
|
||||
|
|
@ -305,7 +299,7 @@ func StorageSlotKeyWithEvaluatedAddress(evaluated *verkle.Point, storageKey []by
|
|||
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
|
||||
// introduces an imbalance in the tree, because hashes are
|
||||
// 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[:]
|
||||
}
|
||||
|
||||
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 {
|
||||
var aligned [32]byte
|
||||
address = append(aligned[:32-len(address)], address...)
|
||||
|
|
@ -337,6 +355,43 @@ func evaluateAddressPoint(address []byte) *verkle.Point {
|
|||
ret := cfg.CommitToPoly(poly[:], 0)
|
||||
|
||||
// add a constant point
|
||||
ret.Add(ret, index0Point)
|
||||
ret.Add(ret, getTreePolyIndex0Point)
|
||||
|
||||
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) {
|
||||
var (
|
||||
address = []byte{0x01}
|
||||
addressEval = evaluateAddressPoint(address)
|
||||
addressEval = EvaluateAddressPoint(address)
|
||||
smallIndex = uint256.NewInt(1)
|
||||
largeIndex = uint256.NewInt(10000)
|
||||
smallStorage = []byte{0x1}
|
||||
|
|
@ -91,7 +91,7 @@ func BenchmarkTreeKeyWithEvaluation(b *testing.B) {
|
|||
verkle.GetConfig()
|
||||
|
||||
addr := []byte{0x01}
|
||||
eval := evaluateAddressPoint(addr)
|
||||
eval := EvaluateAddressPoint(addr)
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
|
@ -129,7 +129,7 @@ func BenchmarkStorageKeyWithEvaluation(b *testing.B) {
|
|||
verkle.GetConfig()
|
||||
|
||||
addr := []byte{0x01}
|
||||
eval := evaluateAddressPoint(addr)
|
||||
eval := EvaluateAddressPoint(addr)
|
||||
|
||||
b.ReportAllocs()
|
||||
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
|
||||
// be returned. If the tree is corrupted, an error will be returned.
|
||||
func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error) {
|
||||
versionkey := t.cache.GetTreeKeyVersionCached(addr[:])
|
||||
var (
|
||||
acc = &types.StateAccount{}
|
||||
values [][]byte
|
||||
|
|
@ -86,7 +87,7 @@ func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error
|
|||
)
|
||||
switch n := t.root.(type) {
|
||||
case *verkle.InternalNode:
|
||||
values, err = n.GetValuesAtStem(t.cache.GetStem(addr[:]), t.nodeResolver)
|
||||
values, err = n.GetValuesAtStem(versionkey, t.nodeResolver)
|
||||
if err != nil {
|
||||
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,
|
||||
// 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) {
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -153,7 +154,7 @@ func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount)
|
|||
|
||||
switch n := t.root.(type) {
|
||||
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 {
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
@ -191,7 +192,7 @@ func (t *VerkleTrie) DeleteAccount(addr common.Address) error {
|
|||
}
|
||||
switch n := t.root.(type) {
|
||||
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 {
|
||||
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.
|
||||
func (t *VerkleTrie) DeleteStorage(addr common.Address, key []byte) error {
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
@ -338,7 +339,7 @@ func (t *VerkleTrie) UpdateContractCode(addr common.Address, codeHash common.Has
|
|||
groupOffset := (chunknr + 128) % 256
|
||||
if groupOffset == 0 /* start of new group */ || chunknr == 0 /* first chunk in header group */ {
|
||||
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]
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ var (
|
|||
|
||||
func TestVerkleTreeReadWrite(t *testing.T) {
|
||||
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 {
|
||||
if err := tr.UpdateAccount(addr, acct); err != nil {
|
||||
|
|
|
|||
Loading…
Reference in a new issue