mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 05:06:43 +00:00
fix some stuff. replace the state diff/read calculation in statedb with computation of a block access list
This commit is contained in:
parent
4282087fb0
commit
b2c4d19ca2
17 changed files with 167 additions and 311 deletions
|
|
@ -19,16 +19,16 @@ package beacon
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
state2 "github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/holiman/uint256"
|
||||
|
|
@ -343,7 +343,7 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
|
|||
|
||||
// FinalizeAndAssemble implements consensus.Engine, setting the final state and
|
||||
// assembling the block.
|
||||
func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state state2.BlockProcessingDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
|
||||
func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
|
||||
if !beacon.IsPoSHeader(header) {
|
||||
return beacon.ethone.FinalizeAndAssemble(chain, header, state, body, receipts)
|
||||
}
|
||||
|
|
@ -364,11 +364,6 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
|
|||
// Assign the final state root to header.
|
||||
header.Root = state.IntermediateRoot(true)
|
||||
|
||||
// embed the block access list in the body
|
||||
if chain.Config().IsAmsterdam(header.Number, header.Time) {
|
||||
body.AccessList = state.(*state2.AccessListCreationDB).ConstructedBlockAccessList().ToEncodingObj()
|
||||
}
|
||||
|
||||
// Assemble the final block.
|
||||
block := types.NewBlock(header, body, receipts, trie.NewStackTrie(nil))
|
||||
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@
|
|||
package consensus
|
||||
|
||||
import (
|
||||
state2 "github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"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/core/vm"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
|
|
@ -92,7 +92,7 @@ type Engine interface {
|
|||
//
|
||||
// Note: The block header and state database might be updated to reflect any
|
||||
// consensus rules that happen at finalization (e.g. block rewards).
|
||||
FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state state2.BlockProcessingDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error)
|
||||
FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error)
|
||||
|
||||
// Seal generates a new sealing request for the given input block and pushes
|
||||
// the result into the given channel.
|
||||
|
|
|
|||
|
|
@ -511,7 +511,7 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.
|
|||
|
||||
// FinalizeAndAssemble implements consensus.Engine, accumulating the block and
|
||||
// uncle rewards, setting the final state and assembling the block.
|
||||
func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state state.BlockProcessingDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
|
||||
func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
|
||||
if len(body.Withdrawals) > 0 {
|
||||
return nil, errors.New("ethash does not support withdrawals")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
|
@ -51,6 +52,11 @@ func (a *BlockAccessListTracer) AccessList() *bal.ConstructionBlockAccessList {
|
|||
return a.callAccessLists[0]
|
||||
}
|
||||
|
||||
// StateDiff returns a state diff at the current BAL index
|
||||
func (a *BlockAccessListTracer) StateDiff() *bal.StateDiff {
|
||||
return a.callAccessLists[0].
|
||||
}
|
||||
|
||||
func (a *BlockAccessListTracer) TxEndHook(receipt *types.Receipt, err error) {
|
||||
a.txIdx++
|
||||
}
|
||||
|
|
@ -71,9 +77,14 @@ func (a *BlockAccessListTracer) OnExit(depth int, output []byte, gasUsed uint64,
|
|||
if a.selfdestructedAccount != nil {
|
||||
delete(a.callAccessLists[len(a.callAccessLists)-1].Accounts, *a.selfdestructedAccount)
|
||||
}
|
||||
if !reverted {
|
||||
parentAccessList := a.callAccessLists[len(a.callAccessLists)-2]
|
||||
scopeAccessList := a.callAccessLists[len(a.callAccessLists)-1]
|
||||
parentAccessList := a.callAccessLists[len(a.callAccessLists)-2]
|
||||
scopeAccessList := a.callAccessLists[len(a.callAccessLists)-1]
|
||||
if reverted {
|
||||
fmt.Println("exit reverted")
|
||||
parentAccessList.MergeReads(scopeAccessList)
|
||||
} else {
|
||||
fmt.Println("exit normal")
|
||||
fmt.Printf("scope access list is\n%s\n", scopeAccessList.ToEncodingObj().String())
|
||||
parentAccessList.Merge(scopeAccessList)
|
||||
}
|
||||
|
||||
|
|
@ -101,6 +112,7 @@ func (a *BlockAccessListTracer) OnColdStorageRead(addr common.Address, key commo
|
|||
}
|
||||
|
||||
func (a *BlockAccessListTracer) OnColdAccountRead(addr common.Address) {
|
||||
fmt.Printf("cold account read %x\n", addr)
|
||||
a.callAccessLists[len(a.callAccessLists)-1].AccountRead(addr)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
|
|||
|
||||
// ValidateState validates the various changes that happen after a state transition,
|
||||
// such as amount of used gas, the receipt roots and the state root itself.
|
||||
func (v *BlockValidator) ValidateState(block *types.Block, statedb state.BlockProcessingDB, res *ProcessResult, validateStateRoot, stateless bool) error {
|
||||
func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, res *ProcessResult, validateStateRoot, stateless bool) error {
|
||||
if res == nil {
|
||||
return errors.New("nil ProcessResult value")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2161,6 +2161,9 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
|
|||
}
|
||||
ptime = time.Since(pstart)
|
||||
|
||||
// unset the BAL-creation tracer (dirty)
|
||||
bc.cfg.VmConfig.Tracer = nil
|
||||
|
||||
vstart := time.Now()
|
||||
if err := bc.validator.ValidateState(block, statedb, res, true, false); err != nil {
|
||||
bc.reportBlock(block, res, err)
|
||||
|
|
|
|||
|
|
@ -311,7 +311,7 @@ func (b *BlockGen) collectRequests(readonly bool) (requests [][]byte) {
|
|||
// The system contracts clear themselves on a system-initiated read.
|
||||
// When reading the requests mid-block, we don't want this behavior, so fork
|
||||
// off the statedb before executing the system calls.
|
||||
statedb = statedb.Copy().(*state.StateDB)
|
||||
statedb = statedb.Copy()
|
||||
}
|
||||
|
||||
if b.cm.config.IsPrague(b.header.Number, b.header.Time) {
|
||||
|
|
|
|||
|
|
@ -122,9 +122,7 @@ func (p *ParallelStateProcessor) prepareExecResult(block *types.Block, allStateR
|
|||
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||
p.chain.engine.Finalize(p.chain, header, tracingStateDB, block.Body())
|
||||
// invoke Finalise so that withdrawals are accounted for in the state diff
|
||||
finalDiff, finalAccesses := postTxState.Finalise(true)
|
||||
computedDiff.Merge(finalDiff)
|
||||
computedAccesses.Merge(*finalAccesses)
|
||||
postTxState.Finalise(true)
|
||||
|
||||
if err := postTxState.BlockAccessList().ValidateStateDiff(len(block.Transactions())+1, computedDiff); err != nil {
|
||||
return &ProcessResultWithMetrics{
|
||||
|
|
@ -247,10 +245,8 @@ func (p *ParallelStateProcessor) calcAndVerifyRoot(preState *state.StateDB, bloc
|
|||
// execTx executes single transaction returning a result which includes state accessed/modified
|
||||
func (p *ParallelStateProcessor) execTx(block *types.Block, tx *types.Transaction, idx int, db *state.StateDB, signer types.Signer) *txExecResult {
|
||||
header := block.Header()
|
||||
var tracingStateDB = vm.StateDB(db)
|
||||
if hooks := p.vmCfg.Tracer; hooks != nil {
|
||||
tracingStateDB = state.NewHookedState(db, hooks)
|
||||
}
|
||||
balTracer, hooks := NewBlockAccessListTracer()
|
||||
tracingStateDB := state.NewHookedState(db, hooks)
|
||||
context := NewEVMBlockContext(header, p.chain, nil)
|
||||
evm := vm.NewEVM(context, tracingStateDB, p.config, *p.vmCfg)
|
||||
|
||||
|
|
@ -274,7 +270,7 @@ func (p *ParallelStateProcessor) execTx(block *types.Block, tx *types.Transactio
|
|||
return &txExecResult{err: err}
|
||||
}
|
||||
|
||||
if err := db.BlockAccessList().ValidateStateDiff(idx+1, mutatedState); err != nil {
|
||||
if err := db.BlockAccessList().ValidateStateDiff(idx+1, balTracer.AccessList().DiffAt(uint16(idx)+1)); err != nil {
|
||||
return &txExecResult{err: err}
|
||||
}
|
||||
|
||||
|
|
@ -289,6 +285,7 @@ func (p *ParallelStateProcessor) execTx(block *types.Block, tx *types.Transactio
|
|||
// Process performs EVM execution and state root computation for a block which is known
|
||||
// to contain an access list.
|
||||
func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResultWithMetrics, error) {
|
||||
fmt.Println("start ParallelProcess")
|
||||
var (
|
||||
header = block.Header()
|
||||
resCh = make(chan *ProcessResultWithMetrics)
|
||||
|
|
@ -342,7 +339,7 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat
|
|||
|
||||
// compute the post-tx state prestate (before applying final block system calls and eip-4895 withdrawals)
|
||||
// the post-tx state transition is verified by resultHandler
|
||||
postTxState := statedb.Copy().(*state.StateDB)
|
||||
postTxState := statedb.Copy()
|
||||
|
||||
tPreprocess = time.Since(pStart)
|
||||
|
||||
|
|
@ -356,7 +353,7 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat
|
|||
tx := tx
|
||||
i := i
|
||||
workers.Go(func() error {
|
||||
res := p.execTx(block, tx, i, startingState.Copy().(*state.StateDB), signer)
|
||||
res := p.execTx(block, tx, i, startingState.Copy(), signer)
|
||||
txResCh <- *res
|
||||
return nil
|
||||
})
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -135,10 +134,6 @@ func (s *BALReader) initMutatedObjFromDiff(db *StateDB, addr common.Address, a *
|
|||
return obj
|
||||
}
|
||||
|
||||
var IgnoredBALAddresses map[common.Address]struct{} = map[common.Address]struct{}{
|
||||
params.SystemAddress: {},
|
||||
}
|
||||
|
||||
// BALReader provides methods for reading account state from a block access
|
||||
// list. State values returned from the Reader methods must not be modified.
|
||||
type BALReader struct {
|
||||
|
|
@ -168,7 +163,6 @@ func (r *BALReader) ModifiedAccounts() (res []common.Address) {
|
|||
}
|
||||
|
||||
func (r *BALReader) ValidateStateReads(allReads bal.StateAccesses) error {
|
||||
fmt.Printf("bal is %v\n", r.block.Body().AccessList.String())
|
||||
// 1. remove any slots from 'allReads' which were written
|
||||
// 2. validate that the read set in the BAL matches 'allReads' exactly
|
||||
for addr, reads := range allReads {
|
||||
|
|
@ -178,8 +172,10 @@ func (r *BALReader) ValidateStateReads(allReads bal.StateAccesses) error {
|
|||
delete(reads, writeSlot)
|
||||
}
|
||||
}
|
||||
if _, ok := r.accesses[addr]; !ok {
|
||||
panic(fmt.Sprintf("%x wasn't in BAL", addr))
|
||||
}
|
||||
|
||||
fmt.Printf("addr is %x\n", addr)
|
||||
expectedReads := r.accesses[addr].StorageReads
|
||||
if len(reads) != len(expectedReads) {
|
||||
return fmt.Errorf("mismatch between the number of computed reads and number of expected reads")
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ import (
|
|||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
|
|
@ -53,9 +51,6 @@ type stateObject struct {
|
|||
origin *types.StateAccount // Account original data without any change applied, nil means it was not existent
|
||||
data types.StateAccount // Account data with all mutations applied in the scope of block
|
||||
|
||||
txPreBalance *uint256.Int // the account balance after the last call to finalise
|
||||
txPreNonce uint64 // the account nonce after the last call to finalise
|
||||
|
||||
// Write caches.
|
||||
trie Trie // storage trie, which becomes non-nil on first access
|
||||
code []byte // contract bytecode, which gets set when code is loaded
|
||||
|
|
@ -78,8 +73,6 @@ type stateObject struct {
|
|||
// Cache flags.
|
||||
dirtyCode bool // true if the code was updated
|
||||
|
||||
nonFinalizedCode bool // true if the code was updated since the last call to finalise
|
||||
|
||||
// Flag whether the account was marked as self-destructed. The self-destructed
|
||||
// account is still accessible in the scope of same transaction.
|
||||
selfDestructed bool
|
||||
|
|
@ -111,8 +104,6 @@ func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *s
|
|||
addrHash: crypto.Keccak256Hash(address[:]),
|
||||
origin: origin,
|
||||
data: *acct,
|
||||
txPreBalance: acct.Balance.Clone(),
|
||||
txPreNonce: acct.Nonce,
|
||||
originStorage: make(Storage),
|
||||
dirtyStorage: make(Storage),
|
||||
pendingStorage: make(Storage),
|
||||
|
|
@ -248,29 +239,7 @@ func (s *stateObject) setState(key common.Hash, value common.Hash, origin common
|
|||
|
||||
// finalise moves all dirty storage slots into the pending area to be hashed or
|
||||
// committed later. It is invoked at the end of every transaction.
|
||||
func (s *stateObject) finalise() *bal.AccountState {
|
||||
var accountPost bal.AccountState
|
||||
if s.db.enableStateDiffRecording {
|
||||
if s.Balance().Cmp(s.txPreBalance) != 0 {
|
||||
accountPost.Balance = s.Balance()
|
||||
}
|
||||
|
||||
if s.Nonce() != s.txPreNonce {
|
||||
accountPost.Nonce = new(uint64)
|
||||
*accountPost.Nonce = s.Nonce()
|
||||
}
|
||||
|
||||
// include account code changes: created contracts and 7702 delegation authority code changes
|
||||
if s.nonFinalizedCode {
|
||||
if s.code == nil {
|
||||
// code cleared (7702). code must be non-nil in the post to signal that it's part of the diff vs being unchanged.
|
||||
accountPost.Code = []byte{}
|
||||
} else {
|
||||
accountPost.Code = s.code
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stateObject) finalise() {
|
||||
slotsToPrefetch := make([]common.Hash, 0, len(s.dirtyStorage))
|
||||
for key, value := range s.dirtyStorage {
|
||||
if origin, exist := s.uncommittedStorage[key]; exist && origin == value {
|
||||
|
|
@ -278,32 +247,14 @@ func (s *stateObject) finalise() *bal.AccountState {
|
|||
// to avoid thrashing the data structures.
|
||||
delete(s.uncommittedStorage, key)
|
||||
|
||||
if s.db.enableStateDiffRecording {
|
||||
if accountPost.StorageWrites == nil {
|
||||
accountPost.StorageWrites = make(map[common.Hash]common.Hash)
|
||||
}
|
||||
accountPost.StorageWrites[key] = value
|
||||
}
|
||||
} else if exist {
|
||||
// The slot is modified to another value and the slot has been
|
||||
// tracked for commit in uncommittedStorage.
|
||||
if s.db.enableStateDiffRecording {
|
||||
if accountPost.StorageWrites == nil {
|
||||
accountPost.StorageWrites = make(map[common.Hash]common.Hash)
|
||||
}
|
||||
accountPost.StorageWrites[key] = value
|
||||
}
|
||||
} else {
|
||||
// The slot is different from its original value and hasn't been
|
||||
// tracked for commit yet.
|
||||
s.uncommittedStorage[key] = s.GetCommittedState(key)
|
||||
slotsToPrefetch = append(slotsToPrefetch, key) // Copy needed for closure
|
||||
if s.db.enableStateDiffRecording {
|
||||
if accountPost.StorageWrites == nil {
|
||||
accountPost.StorageWrites = make(map[common.Hash]common.Hash)
|
||||
}
|
||||
accountPost.StorageWrites[key] = value
|
||||
}
|
||||
}
|
||||
// Aggregate the dirty storage slots into the pending area. It might
|
||||
// be possible that the value of tracked slot here is same with the
|
||||
|
|
@ -325,12 +276,6 @@ func (s *stateObject) finalise() *bal.AccountState {
|
|||
// of the newly-created object as it's no longer eligible for self-destruct
|
||||
// by EIP-6780. For non-newly-created objects, it's a no-op.
|
||||
s.newContract = false
|
||||
|
||||
s.nonFinalizedCode = false
|
||||
|
||||
s.txPreBalance = s.data.Balance.Clone()
|
||||
s.txPreNonce = s.data.Nonce
|
||||
return &accountPost
|
||||
}
|
||||
|
||||
// updateTrie is responsible for persisting cached storage changes into the
|
||||
|
|
@ -558,8 +503,6 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject {
|
|||
dirtyCode: s.dirtyCode,
|
||||
selfDestructed: s.selfDestructed,
|
||||
newContract: s.newContract,
|
||||
txPreNonce: s.txPreNonce,
|
||||
txPreBalance: s.txPreBalance.Clone(),
|
||||
}
|
||||
if s.trie != nil {
|
||||
obj.trie = mustCopyTrie(s.trie)
|
||||
|
|
@ -631,7 +574,6 @@ func (s *stateObject) setCode(codeHash common.Hash, code []byte) {
|
|||
func (s *stateObject) setCodeModified(codeHash common.Hash, code []byte) {
|
||||
s.setCode(codeHash, code)
|
||||
s.dirtyCode = true
|
||||
s.nonFinalizedCode = true
|
||||
}
|
||||
|
||||
func (s *stateObject) SetNonce(nonce uint64) {
|
||||
|
|
|
|||
|
|
@ -26,8 +26,6 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
|
|
@ -149,9 +147,6 @@ type StateDB struct {
|
|||
witness *stateless.Witness
|
||||
witnessStats *stateless.WitnessStats
|
||||
|
||||
enableStateDiffRecording bool // if true, calls to Finalise will return the mutated state
|
||||
stateAccesses bal.StateAccesses // accounts/storage accessed during transaction execution
|
||||
|
||||
blockAccessList *BALReader
|
||||
|
||||
// Measurements gathered during execution for debugging purposes
|
||||
|
|
@ -201,7 +196,6 @@ func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, erro
|
|||
journal: newJournal(),
|
||||
accessList: newAccessList(),
|
||||
transientStorage: newTransientStorage(),
|
||||
stateAccesses: make(bal.StateAccesses),
|
||||
}
|
||||
if db.TrieDB().IsVerkle() {
|
||||
sdb.accessEvents = NewAccessEvents(db.PointCache())
|
||||
|
|
@ -209,13 +203,6 @@ func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, erro
|
|||
return sdb, nil
|
||||
}
|
||||
|
||||
// EnableStateDiffRecording enables the recording of state modifications
|
||||
// which are accumulated at each call to Finalise and can be retrieved
|
||||
// using GetStateDiff.
|
||||
func (s *StateDB) EnableStateDiffRecording() {
|
||||
s.enableStateDiffRecording = true
|
||||
}
|
||||
|
||||
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
|
||||
// state trie concurrently while the state is mutated so that when we reach the
|
||||
// commit phase, most of the needed data is already hot.
|
||||
|
|
@ -434,14 +421,6 @@ func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
|
|||
// GetState retrieves the value associated with the specific key.
|
||||
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
|
||||
stateObject := s.getStateObject(addr)
|
||||
if s.enableStateDiffRecording {
|
||||
if _, shouldIgnore := IgnoredBALAddresses[addr]; !shouldIgnore {
|
||||
if _, ok := s.stateAccesses[addr]; !ok {
|
||||
s.stateAccesses[addr] = make(map[common.Hash]struct{})
|
||||
}
|
||||
s.stateAccesses[addr][hash] = struct{}{}
|
||||
}
|
||||
}
|
||||
if stateObject != nil {
|
||||
return stateObject.GetState(hash)
|
||||
}
|
||||
|
|
@ -673,13 +652,6 @@ func (s *StateDB) deleteStateObject(addr common.Address) {
|
|||
// getStateObject retrieves a state object given by the address, returning nil if
|
||||
// the object is not found or was deleted in this execution context.
|
||||
func (s *StateDB) getStateObject(addr common.Address) *stateObject {
|
||||
if s.enableStateDiffRecording {
|
||||
if _, shouldIgnore := IgnoredBALAddresses[addr]; !shouldIgnore {
|
||||
if _, ok := s.stateAccesses[addr]; !ok {
|
||||
s.stateAccesses[addr] = make(map[common.Hash]struct{})
|
||||
}
|
||||
}
|
||||
}
|
||||
// Prefer live objects if any is available
|
||||
if obj := s.stateObjects[addr]; obj != nil {
|
||||
return obj
|
||||
|
|
@ -797,9 +769,7 @@ func (s *StateDB) Copy() *StateDB {
|
|||
preimages: maps.Clone(s.preimages),
|
||||
|
||||
// don't deep-copy these
|
||||
enableStateDiffRecording: s.enableStateDiffRecording,
|
||||
stateAccesses: make(bal.StateAccesses), // Don't deep copy state accesses
|
||||
blockAccessList: s.blockAccessList,
|
||||
blockAccessList: s.blockAccessList,
|
||||
|
||||
// Do we need to copy the access list and transient storage?
|
||||
// In practice: No. At the start of a transaction, these two lists are empty.
|
||||
|
|
@ -865,8 +835,7 @@ func (s *StateDB) GetRefund() uint64 {
|
|||
//
|
||||
// If EnableStateDiffRecording has been called, it returns a state diff containing
|
||||
// the state which was mutated since the previous invocation of Finalise. Otherwise, nil.
|
||||
func (s *StateDB) Finalise(deleteEmptyObjects bool) (mutations *bal.StateDiff, accesses *bal.StateAccesses) {
|
||||
mutations = &bal.StateDiff{Mutations: make(map[common.Address]*bal.AccountState)}
|
||||
func (s *StateDB) Finalise(deleteEmptyObjects bool) {
|
||||
addressesToPrefetch := make([]common.Address, 0, len(s.journal.dirties))
|
||||
for addr := range s.journal.dirties {
|
||||
obj, exist := s.stateObjects[addr]
|
||||
|
|
@ -880,24 +849,6 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) (mutations *bal.StateDiff, a
|
|||
continue
|
||||
}
|
||||
if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) {
|
||||
// record state diffs for any preexisting accounts which became empty
|
||||
if s.enableStateDiffRecording && obj.txPreBalance != nil && !obj.txPreBalance.IsZero() {
|
||||
if _, shouldIgnore := IgnoredBALAddresses[obj.address]; !shouldIgnore {
|
||||
// TODO: IsZero check is somehow needed for coinbase. figure out why.
|
||||
// TODO: the above check should be as easy as checking that the account was not selfdestructed in the
|
||||
// current transaction, but that causes spec tests to fail. need to figure out why.
|
||||
postState := bal.NewEmptyAccountState()
|
||||
postState.Balance = new(uint256.Int)
|
||||
mutations.Mutations[obj.address] = postState
|
||||
|
||||
// note that this account cannot have any storage accesses in the same tx:
|
||||
// * it cannot have code if it was previously-existing and became empty in this tx
|
||||
// * perhaps it could have had code deployed before selfdestruct removal, that set some storage
|
||||
// and the code was removed (pre-selfdestruct removal) leaving the storage. But the storage
|
||||
// won't be cleared from the state here right
|
||||
}
|
||||
}
|
||||
|
||||
delete(s.stateObjects, obj.address)
|
||||
s.markDelete(addr)
|
||||
// We need to maintain account deletions explicitly (will remain
|
||||
|
|
@ -907,26 +858,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) (mutations *bal.StateDiff, a
|
|||
s.stateObjectsDestruct[obj.address] = obj
|
||||
}
|
||||
} else {
|
||||
accountPost := obj.finalise()
|
||||
|
||||
if s.enableStateDiffRecording {
|
||||
if accountPost.Nonce != nil || accountPost.Code != nil || accountPost.StorageWrites != nil || accountPost.Balance != nil {
|
||||
// the account executed SENDALL but did not send a balance, don't include it in the diff
|
||||
// TODO: probably shouldn't include the account in the dirty set in this case (unrelated to the BAL changes)
|
||||
mutations.Mutations[obj.address] = accountPost
|
||||
}
|
||||
if len(accountPost.StorageWrites) > 0 {
|
||||
// remove all the written slots from the accessedState
|
||||
if _, ok := s.stateAccesses[obj.address]; ok {
|
||||
for slot, _ := range accountPost.StorageWrites {
|
||||
delete(s.stateAccesses[obj.address], slot)
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, ok := s.stateAccesses[obj.address]; ok && len(s.stateAccesses[obj.address]) == 0 {
|
||||
delete(s.stateAccesses, obj.address)
|
||||
}
|
||||
}
|
||||
obj.finalise()
|
||||
s.markUpdate(addr)
|
||||
} // At this point, also ship the address off to the precacher. The precacher
|
||||
// will start loading tries, and when the change is eventually committed,
|
||||
|
|
@ -941,11 +873,6 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) (mutations *bal.StateDiff, a
|
|||
|
||||
// Invalidate journal because reverting across transactions is not allowed.
|
||||
s.clearJournalAndRefund()
|
||||
|
||||
accesses = new(bal.StateAccesses)
|
||||
*accesses = s.stateAccesses
|
||||
s.stateAccesses = make(bal.StateAccesses)
|
||||
return mutations, accesses
|
||||
}
|
||||
|
||||
// IntermediateRoot computes the current root hash of the state trie.
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@ package state
|
|||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/stateless"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
|
|
@ -34,12 +32,12 @@ import (
|
|||
// hookedStateDB represents a statedb which emits calls to tracing-hooks
|
||||
// on state operations.
|
||||
type hookedStateDB struct {
|
||||
inner BlockProcessingDB
|
||||
inner *StateDB
|
||||
hooks *tracing.Hooks
|
||||
}
|
||||
|
||||
// NewHookedState wraps the given stateDb with the given hooks
|
||||
func NewHookedState(stateDb BlockProcessingDB, hooks *tracing.Hooks) *hookedStateDB {
|
||||
func NewHookedState(stateDb *StateDB, hooks *tracing.Hooks) *hookedStateDB {
|
||||
s := &hookedStateDB{stateDb, hooks}
|
||||
if s.hooks == nil {
|
||||
s.hooks = new(tracing.Hooks)
|
||||
|
|
@ -277,42 +275,21 @@ func (s *hookedStateDB) AddLog(log *types.Log) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) {
|
||||
defer s.inner.Finalise(deleteEmptyObjects)
|
||||
if s.hooks.OnBalanceChange != nil {
|
||||
for addr := range s.inner.journal.dirties {
|
||||
obj := s.inner.stateObjects[addr]
|
||||
if obj != nil && obj.selfDestructed {
|
||||
// If ether was sent to account post-selfdestruct it is burnt.
|
||||
if bal := obj.Balance(); bal.Sign() != 0 {
|
||||
s.hooks.OnBalanceChange(addr, bal.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestructBurn)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) TxIndex() int {
|
||||
return s.inner.TxIndex()
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) (*bal.StateDiff, *bal.StateAccesses) {
|
||||
/*
|
||||
// TODO: implement this code without peering into statedb internals!!!
|
||||
if s.hooks.OnBalanceChange != nil {
|
||||
for addr := range s.inner.journal.dirties {
|
||||
obj := s.inner.stateObjects[addr]
|
||||
if obj != nil && obj.selfDestructed {
|
||||
// If ether was sent to account post-selfdestruct it is burnt.
|
||||
if bal := obj.Balance(); bal.Sign() != 0 {
|
||||
s.hooks.OnBalanceChange(addr, bal.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestructBurn)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
return s.inner.Finalise(deleteEmptyObjects)
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) GetLogs(hash common.Hash, blockNumber uint64, blockHash common.Hash, blockTime uint64) []*types.Log {
|
||||
return s.inner.GetLogs(hash, blockNumber, blockHash, blockTime)
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) IntermediateRoot(deleteEmpty bool) common.Hash {
|
||||
return s.inner.IntermediateRoot(deleteEmpty)
|
||||
}
|
||||
func (a *hookedStateDB) Database() Database {
|
||||
return a.inner.Database()
|
||||
}
|
||||
func (a *hookedStateDB) GetTrie() Trie {
|
||||
return a.inner.GetTrie()
|
||||
}
|
||||
|
||||
func (a *hookedStateDB) SetTxContext(thash common.Hash, ti int) {
|
||||
a.inner.SetTxContext(thash, ti)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ func NewStateProcessor(config *params.ChainConfig, chain *HeaderChain) *StatePro
|
|||
// Process returns the receipts and logs accumulated during the process and
|
||||
// returns the amount of gas that was used in the process. If any of the
|
||||
// transactions failed to execute due to insufficient gas it will return an error.
|
||||
func (p *StateProcessor) Process(block *types.Block, statedb state.BlockProcessingDB, cfg vm.Config) (*ProcessResult, error) {
|
||||
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) {
|
||||
var (
|
||||
receipts types.Receipts
|
||||
usedGas = new(uint64)
|
||||
|
|
@ -98,10 +98,6 @@ func (p *StateProcessor) Process(block *types.Block, statedb state.BlockProcessi
|
|||
}
|
||||
statedb.SetTxContext(tx.Hash(), i)
|
||||
|
||||
if alDB, ok := statedb.(*state.AccessListCreationDB); ok {
|
||||
alDB.SetAccessListIndex(i + 1)
|
||||
}
|
||||
|
||||
_, _, receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, context.Time, tx, usedGas, evm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||
|
|
@ -110,9 +106,6 @@ func (p *StateProcessor) Process(block *types.Block, statedb state.BlockProcessi
|
|||
allLogs = append(allLogs, receipt.Logs...)
|
||||
}
|
||||
|
||||
if alDB, ok := statedb.(*state.AccessListCreationDB); ok {
|
||||
alDB.SetAccessListIndex(len(block.Transactions()) + 1)
|
||||
}
|
||||
// Read requests if Prague is enabled.
|
||||
var requests [][]byte
|
||||
if p.config.IsPrague(block.Number(), block.Time()) {
|
||||
|
|
@ -134,13 +127,6 @@ func (p *StateProcessor) Process(block *types.Block, statedb state.BlockProcessi
|
|||
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||
p.chain.engine.Finalize(p.chain, header, tracingStateDB, block.Body())
|
||||
|
||||
// if we are building access list, manually call finalise here to ensure
|
||||
// that state diffs due to eip-4895 withdrawals are picked up on the access
|
||||
// list.
|
||||
if alDB, ok := statedb.(*state.AccessListCreationDB); ok {
|
||||
alDB.Finalise(true)
|
||||
}
|
||||
|
||||
return &ProcessResult{
|
||||
Receipts: receipts,
|
||||
Requests: requests,
|
||||
|
|
@ -152,7 +138,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb state.BlockProcessi
|
|||
// ApplyTransactionWithEVM attempts to apply a transaction to the given state database
|
||||
// and uses the input parameters for its environment similar to ApplyTransaction. However,
|
||||
// this method takes an already created EVM instance as input.
|
||||
func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb state.BlockProcessingDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (mutatedState *bal.StateDiff, accessedState *bal.StateAccesses, receipt *types.Receipt, err error) {
|
||||
func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (mutatedState *bal.StateDiff, accessedState *bal.StateAccesses, receipt *types.Receipt, err error) {
|
||||
if hooks := evm.Config.Tracer; hooks != nil {
|
||||
if hooks.OnTxStart != nil {
|
||||
hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||
|
|
@ -185,7 +171,7 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb state.BlockProce
|
|||
}
|
||||
|
||||
// MakeReceipt generates the receipt object for a transaction given its execution result.
|
||||
func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb state.BlockProcessingDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, usedGas uint64, root []byte) *types.Receipt {
|
||||
func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, usedGas uint64, root []byte) *types.Receipt {
|
||||
// Create a new receipt for the transaction, storing the intermediate root and gas used
|
||||
// by the tx.
|
||||
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: usedGas}
|
||||
|
|
@ -220,7 +206,7 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb state.BlockProces
|
|||
// and uses the input parameters for its environment. It returns the receipt
|
||||
// for the transaction, gas used and an error if the transaction failed,
|
||||
// indicating the block was invalid.
|
||||
func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb state.BlockProcessingDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, error) {
|
||||
func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, error) {
|
||||
msg, err := TransactionToMessage(tx, types.MakeSigner(evm.ChainConfig(), header.Number, header.Time), header.BaseFee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ type Validator interface {
|
|||
ValidateBody(block *types.Block) error
|
||||
|
||||
// ValidateState validates the given statedb and optionally the process result.
|
||||
ValidateState(block *types.Block, state state.BlockProcessingDB, res *ProcessResult, validateState, stateless bool) error
|
||||
ValidateState(block *types.Block, state *state.StateDB, res *ProcessResult, validateStateRoot, stateless bool) error
|
||||
}
|
||||
|
||||
// Prefetcher is an interface for pre-caching transaction signatures and state.
|
||||
|
|
@ -48,7 +48,7 @@ type Processor interface {
|
|||
// Process processes the state changes according to the Ethereum rules by running
|
||||
// the transaction messages using the statedb and applying any rewards to both
|
||||
// the processor (coinbase) and any included uncles.
|
||||
Process(block *types.Block, statedb state.BlockProcessingDB, cfg vm.Config) (*ProcessResult, error)
|
||||
Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error)
|
||||
}
|
||||
|
||||
// ProcessResult contains the values computed by Process.
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package bal
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"maps"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -32,6 +33,27 @@ type CodeChange struct {
|
|||
Code []byte `json:"code,omitempty"`
|
||||
}
|
||||
|
||||
var IgnoredBALAddresses map[common.Address]struct{} = map[common.Address]struct{}{
|
||||
params.SystemAddress: {},
|
||||
common.BytesToAddress([]byte{0x01}): {},
|
||||
common.BytesToAddress([]byte{0x02}): {},
|
||||
common.BytesToAddress([]byte{0x03}): {},
|
||||
common.BytesToAddress([]byte{0x04}): {},
|
||||
common.BytesToAddress([]byte{0x05}): {},
|
||||
common.BytesToAddress([]byte{0x06}): {},
|
||||
common.BytesToAddress([]byte{0x07}): {},
|
||||
common.BytesToAddress([]byte{0x08}): {},
|
||||
common.BytesToAddress([]byte{0x09}): {},
|
||||
common.BytesToAddress([]byte{0x0a}): {},
|
||||
common.BytesToAddress([]byte{0x0b}): {},
|
||||
common.BytesToAddress([]byte{0x0c}): {},
|
||||
common.BytesToAddress([]byte{0x0d}): {},
|
||||
common.BytesToAddress([]byte{0x0e}): {},
|
||||
common.BytesToAddress([]byte{0x0f}): {},
|
||||
common.BytesToAddress([]byte{0x10}): {},
|
||||
common.BytesToAddress([]byte{0x11}): {},
|
||||
}
|
||||
|
||||
// ConstructionAccountAccess contains post-block account state for mutations as well as
|
||||
// all storage keys that were read during execution. It is used when building block
|
||||
// access list during execution.
|
||||
|
|
@ -83,6 +105,36 @@ func NewConstructionBlockAccessList() *ConstructionBlockAccessList {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *ConstructionBlockAccessList) DiffAt(idx uint16) *StateDiff {
|
||||
res := &StateDiff{make(map[common.Address]*AccountState)}
|
||||
|
||||
for addr, account := range c.Accounts {
|
||||
accountState := &AccountState{}
|
||||
if balance, ok := account.BalanceChanges[idx]; ok {
|
||||
accountState.Balance = balance
|
||||
}
|
||||
if nonce, ok := account.NonceChanges[idx]; ok {
|
||||
accountState.Nonce = &nonce
|
||||
}
|
||||
if code, ok := account.CodeChanges[idx]; ok {
|
||||
accountState.Code = code.Code
|
||||
}
|
||||
|
||||
storageWrites := make(map[common.Hash]common.Hash)
|
||||
for slot, writes := range account.StorageWrites {
|
||||
if val, ok := writes[idx]; ok {
|
||||
storageWrites[slot] = val
|
||||
}
|
||||
}
|
||||
if len(storageWrites) > 0 {
|
||||
accountState.StorageWrites = make(map[common.Hash]common.Hash)
|
||||
}
|
||||
|
||||
res.Mutations[addr] = accountState
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// AccountRead records the address of an account that has been read during execution.
|
||||
func (c *ConstructionBlockAccessList) AccountRead(addr common.Address) {
|
||||
if _, ok := c.Accounts[addr]; !ok {
|
||||
|
|
@ -163,8 +215,28 @@ func mergeStorageWrites(cur, next map[common.Hash]map[uint16]common.Hash) map[co
|
|||
return cur
|
||||
}
|
||||
|
||||
func (c *ConstructionBlockAccessList) Merge(next *ConstructionBlockAccessList) {
|
||||
for addr, accountAccess := range next.Accounts {
|
||||
// MergeReads combines the account/storage reads from a completed EVM execution scope
|
||||
// into the parent calling scope's access list.
|
||||
// It is intended to be called when the child execution scope terminates in a revert
|
||||
// which means that only the state reads performed by that execution should be reported
|
||||
// in the BAL.
|
||||
func (c *ConstructionBlockAccessList) MergeReads(childScope *ConstructionBlockAccessList) {
|
||||
for addr, accountAccess := range childScope.Accounts {
|
||||
if _, ok := c.Accounts[addr]; !ok {
|
||||
c.Accounts[addr] = &ConstructionAccountAccess{StorageReads: accountAccess.StorageReads}
|
||||
continue
|
||||
}
|
||||
|
||||
for storageRead, _ := range childScope.Accounts[addr].StorageReads {
|
||||
c.Accounts[addr].StorageReads[storageRead] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge combines the state changes from a nested execution with the parent context
|
||||
// It is meant to be invoked after an EVM call completes (without reverting).
|
||||
func (c *ConstructionBlockAccessList) Merge(childScope *ConstructionBlockAccessList) {
|
||||
for addr, accountAccess := range childScope.Accounts {
|
||||
if _, ok := c.Accounts[addr]; !ok {
|
||||
c.Accounts[addr] = accountAccess
|
||||
continue
|
||||
|
|
@ -172,18 +244,18 @@ func (c *ConstructionBlockAccessList) Merge(next *ConstructionBlockAccessList) {
|
|||
|
||||
// copy the entries from 'next' into 'c' overwriting 'c' entries with
|
||||
// 'next entries when the bal index matches.
|
||||
next.Accounts[addr].StorageWrites = mergeStorageWrites(next.Accounts[addr].StorageWrites, c.Accounts[addr].StorageWrites)
|
||||
c.Accounts[addr].StorageWrites = mergeStorageWrites(c.Accounts[addr].StorageWrites, childScope.Accounts[addr].StorageWrites)
|
||||
|
||||
for storageRead, _ := range c.Accounts[addr].StorageReads {
|
||||
next.Accounts[addr].StorageReads[storageRead] = struct{}{}
|
||||
for storageRead, _ := range childScope.Accounts[addr].StorageReads {
|
||||
c.Accounts[addr].StorageReads[storageRead] = struct{}{}
|
||||
}
|
||||
for idx, nonce := range next.Accounts[addr].NonceChanges {
|
||||
for idx, nonce := range childScope.Accounts[addr].NonceChanges {
|
||||
c.Accounts[addr].NonceChanges[idx] = nonce
|
||||
}
|
||||
for idx, code := range next.Accounts[addr].CodeChanges {
|
||||
for idx, code := range childScope.Accounts[addr].CodeChanges {
|
||||
c.Accounts[addr].CodeChanges[idx] = code
|
||||
}
|
||||
for idx, balance := range next.Accounts[addr].BalanceChanges {
|
||||
for idx, balance := range childScope.Accounts[addr].BalanceChanges {
|
||||
c.Accounts[addr].BalanceChanges[idx] = balance
|
||||
}
|
||||
}
|
||||
|
|
@ -221,76 +293,6 @@ func (c *ConstructionBlockAccessList) Copy() *ConstructionBlockAccessList {
|
|||
return res
|
||||
}
|
||||
|
||||
// ApplyDiff includes the given changes in the block access list at the given index.
|
||||
func (c *ConstructionBlockAccessList) ApplyDiff(i uint, diff *StateDiff) {
|
||||
idx := uint16(i)
|
||||
for addr, acctDiff := range diff.Mutations {
|
||||
if _, ok := c.Accounts[addr]; !ok {
|
||||
c.Accounts[addr] = &ConstructionAccountAccess{}
|
||||
}
|
||||
if acctDiff.Balance != nil {
|
||||
if c.Accounts[addr].BalanceChanges == nil {
|
||||
c.Accounts[addr].BalanceChanges = make(map[uint16]*uint256.Int)
|
||||
}
|
||||
c.Accounts[addr].BalanceChanges[idx] = acctDiff.Balance
|
||||
}
|
||||
if acctDiff.Nonce != nil {
|
||||
if c.Accounts[addr].NonceChanges == nil {
|
||||
c.Accounts[addr].NonceChanges = make(map[uint16]uint64)
|
||||
}
|
||||
c.Accounts[addr].NonceChanges[idx] = *acctDiff.Nonce
|
||||
}
|
||||
if acctDiff.Code != nil {
|
||||
if c.Accounts[addr].CodeChanges == nil {
|
||||
c.Accounts[addr].CodeChanges = make(map[uint16]CodeChange)
|
||||
}
|
||||
// TODO: make the CodeChanges value just be []byte
|
||||
c.Accounts[addr].CodeChanges[idx] = CodeChange{idx, acctDiff.Code}
|
||||
}
|
||||
if acctDiff.StorageWrites != nil {
|
||||
if c.Accounts[addr].StorageWrites == nil {
|
||||
// TODO: can we instantiate all these maps in the constructor?
|
||||
c.Accounts[addr].StorageWrites = make(map[common.Hash]map[uint16]common.Hash)
|
||||
}
|
||||
|
||||
for slot, val := range acctDiff.StorageWrites {
|
||||
if c.Accounts[addr].StorageWrites[slot] == nil {
|
||||
c.Accounts[addr].StorageWrites[slot] = make(map[uint16]common.Hash)
|
||||
}
|
||||
|
||||
c.Accounts[addr].StorageWrites[slot][idx] = val
|
||||
|
||||
delete(c.Accounts[addr].StorageReads, slot)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyAccesses records the given account/storage accesses in the BAL.
|
||||
func (c *ConstructionBlockAccessList) ApplyAccesses(accesses StateAccesses) {
|
||||
for addr, acctAccesses := range accesses {
|
||||
if c.Accounts[addr] == nil {
|
||||
c.Accounts[addr] = &ConstructionAccountAccess{}
|
||||
}
|
||||
if len(acctAccesses) > 0 {
|
||||
|
||||
if c.Accounts[addr].StorageReads == nil {
|
||||
c.Accounts[addr].StorageReads = make(map[common.Hash]struct{})
|
||||
}
|
||||
for key, _ := range acctAccesses {
|
||||
// if any of the accessed keys were previously written, they
|
||||
// appear in the written set only and not also in accesses.
|
||||
if len(c.Accounts[addr].StorageWrites) > 0 {
|
||||
if _, ok := c.Accounts[addr].StorageWrites[key]; ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
c.Accounts[addr].StorageReads[key] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type StateDiff struct {
|
||||
Mutations map[common.Address]*AccountState `json:"Mutations,omitempty"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/stateless"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie/utils"
|
||||
"github.com/holiman/uint256"
|
||||
|
|
@ -103,5 +102,5 @@ type StateDB interface {
|
|||
TxIndex() int
|
||||
|
||||
// Finalise must be invoked at the end of a transaction
|
||||
Finalise(bool) (*bal.StateDiff, *bal.StateAccesses)
|
||||
Finalise(bool)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package vm
|
|||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
|
|
@ -109,6 +108,12 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||
// 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)
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnColdStorageRead != nil {
|
||||
// TODO: should these only be called if the cold storage read didn't go OOG?
|
||||
// it's harder to implement, but I lean towards "yes".
|
||||
// need to clarify this in the spec.
|
||||
evm.Config.Tracer.OnColdStorageRead(contract.Address(), slot)
|
||||
}
|
||||
return params.ColdSloadCostEIP2929, nil
|
||||
}
|
||||
return params.WarmStorageReadCostEIP2929, nil
|
||||
|
|
@ -132,7 +137,7 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
|
|||
|
||||
// TODO: same issue as OnColdSStorageRead. See the TODO above near OnColdStorageRead
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnColdAccountRead != nil {
|
||||
evm.Config.Tracer.OnColdAccountRead(contract.Address())
|
||||
evm.Config.Tracer.OnColdAccountRead(addr)
|
||||
}
|
||||
|
||||
var overflow bool
|
||||
|
|
@ -156,6 +161,9 @@ func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
|||
addr := common.Address(stack.peek().Bytes20())
|
||||
// Check slot presence in the access list
|
||||
if !evm.StateDB.AddressInAccessList(addr) {
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnColdAccountRead != nil {
|
||||
evm.Config.Tracer.OnColdAccountRead(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
|
||||
|
|
@ -173,6 +181,9 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
|
|||
// the cost to charge for cold access, if any, is Cold - Warm
|
||||
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
||||
if !warmAccess {
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnColdAccountRead != nil {
|
||||
evm.Config.Tracer.OnColdAccountRead(addr)
|
||||
}
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
// Charge the remaining difference here already, to correctly calculate available
|
||||
// gas for call
|
||||
|
|
@ -239,6 +250,9 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
|||
address = common.Address(stack.peek().Bytes20())
|
||||
)
|
||||
if !evm.StateDB.AddressInAccessList(address) {
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnColdAccountRead != nil {
|
||||
evm.Config.Tracer.OnColdAccountRead(address)
|
||||
}
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddAddressToAccessList(address)
|
||||
gas = params.ColdAccountAccessCostEIP2929
|
||||
|
|
@ -271,6 +285,9 @@ func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
|
|||
|
||||
// Check slot presence in the access list
|
||||
if !evm.StateDB.AddressInAccessList(addr) {
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnColdAccountRead != nil {
|
||||
evm.Config.Tracer.OnColdAccountRead(addr)
|
||||
}
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
// 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
|
||||
|
|
@ -289,6 +306,9 @@ func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
|
|||
if evm.StateDB.AddressInAccessList(target) {
|
||||
cost = params.WarmStorageReadCostEIP2929
|
||||
} else {
|
||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnColdAccountRead != nil {
|
||||
evm.Config.Tracer.OnColdAccountRead(target)
|
||||
}
|
||||
evm.StateDB.AddAddressToAccessList(target)
|
||||
cost = params.ColdAccountAccessCostEIP2929
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue