mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 13:16:42 +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 (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
state2 "github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
"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/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/holiman/uint256"
|
"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
|
// FinalizeAndAssemble implements consensus.Engine, setting the final state and
|
||||||
// assembling the block.
|
// 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) {
|
if !beacon.IsPoSHeader(header) {
|
||||||
return beacon.ethone.FinalizeAndAssemble(chain, header, state, body, receipts)
|
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.
|
// Assign the final state root to header.
|
||||||
header.Root = state.IntermediateRoot(true)
|
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.
|
// Assemble the final block.
|
||||||
block := types.NewBlock(header, body, receipts, trie.NewStackTrie(nil))
|
block := types.NewBlock(header, body, receipts, trie.NewStackTrie(nil))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,12 @@
|
||||||
package consensus
|
package consensus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
state2 "github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"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
|
// Note: The block header and state database might be updated to reflect any
|
||||||
// consensus rules that happen at finalization (e.g. block rewards).
|
// 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
|
// Seal generates a new sealing request for the given input block and pushes
|
||||||
// the result into the given channel.
|
// 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
|
// FinalizeAndAssemble implements consensus.Engine, accumulating the block and
|
||||||
// uncle rewards, setting the final state and assembling the block.
|
// 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 {
|
if len(body.Withdrawals) > 0 {
|
||||||
return nil, errors.New("ethash does not support withdrawals")
|
return nil, errors.New("ethash does not support withdrawals")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
@ -51,6 +52,11 @@ func (a *BlockAccessListTracer) AccessList() *bal.ConstructionBlockAccessList {
|
||||||
return a.callAccessLists[0]
|
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) {
|
func (a *BlockAccessListTracer) TxEndHook(receipt *types.Receipt, err error) {
|
||||||
a.txIdx++
|
a.txIdx++
|
||||||
}
|
}
|
||||||
|
|
@ -71,9 +77,14 @@ func (a *BlockAccessListTracer) OnExit(depth int, output []byte, gasUsed uint64,
|
||||||
if a.selfdestructedAccount != nil {
|
if a.selfdestructedAccount != nil {
|
||||||
delete(a.callAccessLists[len(a.callAccessLists)-1].Accounts, *a.selfdestructedAccount)
|
delete(a.callAccessLists[len(a.callAccessLists)-1].Accounts, *a.selfdestructedAccount)
|
||||||
}
|
}
|
||||||
if !reverted {
|
parentAccessList := a.callAccessLists[len(a.callAccessLists)-2]
|
||||||
parentAccessList := a.callAccessLists[len(a.callAccessLists)-2]
|
scopeAccessList := a.callAccessLists[len(a.callAccessLists)-1]
|
||||||
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)
|
parentAccessList.Merge(scopeAccessList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,6 +112,7 @@ func (a *BlockAccessListTracer) OnColdStorageRead(addr common.Address, key commo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *BlockAccessListTracer) OnColdAccountRead(addr common.Address) {
|
func (a *BlockAccessListTracer) OnColdAccountRead(addr common.Address) {
|
||||||
|
fmt.Printf("cold account read %x\n", addr)
|
||||||
a.callAccessLists[len(a.callAccessLists)-1].AccountRead(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,
|
// 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.
|
// 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 {
|
if res == nil {
|
||||||
return errors.New("nil ProcessResult value")
|
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)
|
ptime = time.Since(pstart)
|
||||||
|
|
||||||
|
// unset the BAL-creation tracer (dirty)
|
||||||
|
bc.cfg.VmConfig.Tracer = nil
|
||||||
|
|
||||||
vstart := time.Now()
|
vstart := time.Now()
|
||||||
if err := bc.validator.ValidateState(block, statedb, res, true, false); err != nil {
|
if err := bc.validator.ValidateState(block, statedb, res, true, false); err != nil {
|
||||||
bc.reportBlock(block, res, err)
|
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.
|
// The system contracts clear themselves on a system-initiated read.
|
||||||
// When reading the requests mid-block, we don't want this behavior, so fork
|
// When reading the requests mid-block, we don't want this behavior, so fork
|
||||||
// off the statedb before executing the system calls.
|
// 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) {
|
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)
|
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||||
p.chain.engine.Finalize(p.chain, header, tracingStateDB, block.Body())
|
p.chain.engine.Finalize(p.chain, header, tracingStateDB, block.Body())
|
||||||
// invoke Finalise so that withdrawals are accounted for in the state diff
|
// invoke Finalise so that withdrawals are accounted for in the state diff
|
||||||
finalDiff, finalAccesses := postTxState.Finalise(true)
|
postTxState.Finalise(true)
|
||||||
computedDiff.Merge(finalDiff)
|
|
||||||
computedAccesses.Merge(*finalAccesses)
|
|
||||||
|
|
||||||
if err := postTxState.BlockAccessList().ValidateStateDiff(len(block.Transactions())+1, computedDiff); err != nil {
|
if err := postTxState.BlockAccessList().ValidateStateDiff(len(block.Transactions())+1, computedDiff); err != nil {
|
||||||
return &ProcessResultWithMetrics{
|
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
|
// 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 {
|
func (p *ParallelStateProcessor) execTx(block *types.Block, tx *types.Transaction, idx int, db *state.StateDB, signer types.Signer) *txExecResult {
|
||||||
header := block.Header()
|
header := block.Header()
|
||||||
var tracingStateDB = vm.StateDB(db)
|
balTracer, hooks := NewBlockAccessListTracer()
|
||||||
if hooks := p.vmCfg.Tracer; hooks != nil {
|
tracingStateDB := state.NewHookedState(db, hooks)
|
||||||
tracingStateDB = state.NewHookedState(db, hooks)
|
|
||||||
}
|
|
||||||
context := NewEVMBlockContext(header, p.chain, nil)
|
context := NewEVMBlockContext(header, p.chain, nil)
|
||||||
evm := vm.NewEVM(context, tracingStateDB, p.config, *p.vmCfg)
|
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}
|
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}
|
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
|
// Process performs EVM execution and state root computation for a block which is known
|
||||||
// to contain an access list.
|
// to contain an access list.
|
||||||
func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResultWithMetrics, error) {
|
func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResultWithMetrics, error) {
|
||||||
|
fmt.Println("start ParallelProcess")
|
||||||
var (
|
var (
|
||||||
header = block.Header()
|
header = block.Header()
|
||||||
resCh = make(chan *ProcessResultWithMetrics)
|
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)
|
// 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
|
// the post-tx state transition is verified by resultHandler
|
||||||
postTxState := statedb.Copy().(*state.StateDB)
|
postTxState := statedb.Copy()
|
||||||
|
|
||||||
tPreprocess = time.Since(pStart)
|
tPreprocess = time.Since(pStart)
|
||||||
|
|
||||||
|
|
@ -356,7 +353,7 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat
|
||||||
tx := tx
|
tx := tx
|
||||||
i := i
|
i := i
|
||||||
workers.Go(func() error {
|
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
|
txResCh <- *res
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -135,10 +134,6 @@ func (s *BALReader) initMutatedObjFromDiff(db *StateDB, addr common.Address, a *
|
||||||
return obj
|
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
|
// BALReader provides methods for reading account state from a block access
|
||||||
// list. State values returned from the Reader methods must not be modified.
|
// list. State values returned from the Reader methods must not be modified.
|
||||||
type BALReader struct {
|
type BALReader struct {
|
||||||
|
|
@ -168,7 +163,6 @@ func (r *BALReader) ModifiedAccounts() (res []common.Address) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *BALReader) ValidateStateReads(allReads bal.StateAccesses) error {
|
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
|
// 1. remove any slots from 'allReads' which were written
|
||||||
// 2. validate that the read set in the BAL matches 'allReads' exactly
|
// 2. validate that the read set in the BAL matches 'allReads' exactly
|
||||||
for addr, reads := range allReads {
|
for addr, reads := range allReads {
|
||||||
|
|
@ -178,8 +172,10 @@ func (r *BALReader) ValidateStateReads(allReads bal.StateAccesses) error {
|
||||||
delete(reads, writeSlot)
|
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
|
expectedReads := r.accesses[addr].StorageReads
|
||||||
if len(reads) != len(expectedReads) {
|
if len(reads) != len(expectedReads) {
|
||||||
return fmt.Errorf("mismatch between the number of computed reads and number of expected reads")
|
return fmt.Errorf("mismatch between the number of computed reads and number of expected reads")
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,6 @@ import (
|
||||||
"slices"
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
@ -53,9 +51,6 @@ type stateObject struct {
|
||||||
origin *types.StateAccount // Account original data without any change applied, nil means it was not existent
|
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
|
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.
|
// Write caches.
|
||||||
trie Trie // storage trie, which becomes non-nil on first access
|
trie Trie // storage trie, which becomes non-nil on first access
|
||||||
code []byte // contract bytecode, which gets set when code is loaded
|
code []byte // contract bytecode, which gets set when code is loaded
|
||||||
|
|
@ -78,8 +73,6 @@ type stateObject struct {
|
||||||
// Cache flags.
|
// Cache flags.
|
||||||
dirtyCode bool // true if the code was updated
|
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
|
// Flag whether the account was marked as self-destructed. The self-destructed
|
||||||
// account is still accessible in the scope of same transaction.
|
// account is still accessible in the scope of same transaction.
|
||||||
selfDestructed bool
|
selfDestructed bool
|
||||||
|
|
@ -111,8 +104,6 @@ func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *s
|
||||||
addrHash: crypto.Keccak256Hash(address[:]),
|
addrHash: crypto.Keccak256Hash(address[:]),
|
||||||
origin: origin,
|
origin: origin,
|
||||||
data: *acct,
|
data: *acct,
|
||||||
txPreBalance: acct.Balance.Clone(),
|
|
||||||
txPreNonce: acct.Nonce,
|
|
||||||
originStorage: make(Storage),
|
originStorage: make(Storage),
|
||||||
dirtyStorage: make(Storage),
|
dirtyStorage: make(Storage),
|
||||||
pendingStorage: 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
|
// 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.
|
// committed later. It is invoked at the end of every transaction.
|
||||||
func (s *stateObject) finalise() *bal.AccountState {
|
func (s *stateObject) finalise() {
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
slotsToPrefetch := make([]common.Hash, 0, len(s.dirtyStorage))
|
slotsToPrefetch := make([]common.Hash, 0, len(s.dirtyStorage))
|
||||||
for key, value := range s.dirtyStorage {
|
for key, value := range s.dirtyStorage {
|
||||||
if origin, exist := s.uncommittedStorage[key]; exist && origin == value {
|
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.
|
// to avoid thrashing the data structures.
|
||||||
delete(s.uncommittedStorage, key)
|
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 {
|
} else if exist {
|
||||||
// The slot is modified to another value and the slot has been
|
// The slot is modified to another value and the slot has been
|
||||||
// tracked for commit in uncommittedStorage.
|
// 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 {
|
} else {
|
||||||
// The slot is different from its original value and hasn't been
|
// The slot is different from its original value and hasn't been
|
||||||
// tracked for commit yet.
|
// tracked for commit yet.
|
||||||
s.uncommittedStorage[key] = s.GetCommittedState(key)
|
s.uncommittedStorage[key] = s.GetCommittedState(key)
|
||||||
slotsToPrefetch = append(slotsToPrefetch, key) // Copy needed for closure
|
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
|
// Aggregate the dirty storage slots into the pending area. It might
|
||||||
// be possible that the value of tracked slot here is same with the
|
// 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
|
// 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.
|
// by EIP-6780. For non-newly-created objects, it's a no-op.
|
||||||
s.newContract = false
|
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
|
// updateTrie is responsible for persisting cached storage changes into the
|
||||||
|
|
@ -558,8 +503,6 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject {
|
||||||
dirtyCode: s.dirtyCode,
|
dirtyCode: s.dirtyCode,
|
||||||
selfDestructed: s.selfDestructed,
|
selfDestructed: s.selfDestructed,
|
||||||
newContract: s.newContract,
|
newContract: s.newContract,
|
||||||
txPreNonce: s.txPreNonce,
|
|
||||||
txPreBalance: s.txPreBalance.Clone(),
|
|
||||||
}
|
}
|
||||||
if s.trie != nil {
|
if s.trie != nil {
|
||||||
obj.trie = mustCopyTrie(s.trie)
|
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) {
|
func (s *stateObject) setCodeModified(codeHash common.Hash, code []byte) {
|
||||||
s.setCode(codeHash, code)
|
s.setCode(codeHash, code)
|
||||||
s.dirtyCode = true
|
s.dirtyCode = true
|
||||||
s.nonFinalizedCode = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) SetNonce(nonce uint64) {
|
func (s *stateObject) SetNonce(nonce uint64) {
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,6 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||||
|
|
@ -149,9 +147,6 @@ type StateDB struct {
|
||||||
witness *stateless.Witness
|
witness *stateless.Witness
|
||||||
witnessStats *stateless.WitnessStats
|
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
|
blockAccessList *BALReader
|
||||||
|
|
||||||
// Measurements gathered during execution for debugging purposes
|
// Measurements gathered during execution for debugging purposes
|
||||||
|
|
@ -201,7 +196,6 @@ func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, erro
|
||||||
journal: newJournal(),
|
journal: newJournal(),
|
||||||
accessList: newAccessList(),
|
accessList: newAccessList(),
|
||||||
transientStorage: newTransientStorage(),
|
transientStorage: newTransientStorage(),
|
||||||
stateAccesses: make(bal.StateAccesses),
|
|
||||||
}
|
}
|
||||||
if db.TrieDB().IsVerkle() {
|
if db.TrieDB().IsVerkle() {
|
||||||
sdb.accessEvents = NewAccessEvents(db.PointCache())
|
sdb.accessEvents = NewAccessEvents(db.PointCache())
|
||||||
|
|
@ -209,13 +203,6 @@ func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, erro
|
||||||
return sdb, nil
|
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
|
// 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
|
// state trie concurrently while the state is mutated so that when we reach the
|
||||||
// commit phase, most of the needed data is already hot.
|
// 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.
|
// GetState retrieves the value associated with the specific key.
|
||||||
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
|
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
|
||||||
stateObject := s.getStateObject(addr)
|
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 {
|
if stateObject != nil {
|
||||||
return stateObject.GetState(hash)
|
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
|
// getStateObject retrieves a state object given by the address, returning nil if
|
||||||
// the object is not found or was deleted in this execution context.
|
// the object is not found or was deleted in this execution context.
|
||||||
func (s *StateDB) getStateObject(addr common.Address) *stateObject {
|
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
|
// Prefer live objects if any is available
|
||||||
if obj := s.stateObjects[addr]; obj != nil {
|
if obj := s.stateObjects[addr]; obj != nil {
|
||||||
return obj
|
return obj
|
||||||
|
|
@ -797,9 +769,7 @@ func (s *StateDB) Copy() *StateDB {
|
||||||
preimages: maps.Clone(s.preimages),
|
preimages: maps.Clone(s.preimages),
|
||||||
|
|
||||||
// don't deep-copy these
|
// don't deep-copy these
|
||||||
enableStateDiffRecording: s.enableStateDiffRecording,
|
blockAccessList: s.blockAccessList,
|
||||||
stateAccesses: make(bal.StateAccesses), // Don't deep copy state accesses
|
|
||||||
blockAccessList: s.blockAccessList,
|
|
||||||
|
|
||||||
// Do we need to copy the access list and transient storage?
|
// 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.
|
// 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
|
// If EnableStateDiffRecording has been called, it returns a state diff containing
|
||||||
// the state which was mutated since the previous invocation of Finalise. Otherwise, nil.
|
// 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) {
|
func (s *StateDB) Finalise(deleteEmptyObjects bool) {
|
||||||
mutations = &bal.StateDiff{Mutations: make(map[common.Address]*bal.AccountState)}
|
|
||||||
addressesToPrefetch := make([]common.Address, 0, len(s.journal.dirties))
|
addressesToPrefetch := make([]common.Address, 0, len(s.journal.dirties))
|
||||||
for addr := range s.journal.dirties {
|
for addr := range s.journal.dirties {
|
||||||
obj, exist := s.stateObjects[addr]
|
obj, exist := s.stateObjects[addr]
|
||||||
|
|
@ -880,24 +849,6 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) (mutations *bal.StateDiff, a
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) {
|
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)
|
delete(s.stateObjects, obj.address)
|
||||||
s.markDelete(addr)
|
s.markDelete(addr)
|
||||||
// We need to maintain account deletions explicitly (will remain
|
// 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
|
s.stateObjectsDestruct[obj.address] = obj
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
accountPost := obj.finalise()
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.markUpdate(addr)
|
s.markUpdate(addr)
|
||||||
} // At this point, also ship the address off to the precacher. The precacher
|
} // At this point, also ship the address off to the precacher. The precacher
|
||||||
// will start loading tries, and when the change is eventually committed,
|
// 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.
|
// Invalidate journal because reverting across transactions is not allowed.
|
||||||
s.clearJournalAndRefund()
|
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.
|
// IntermediateRoot computes the current root hash of the state trie.
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,6 @@ package state
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/stateless"
|
"github.com/ethereum/go-ethereum/core/stateless"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
|
|
@ -34,12 +32,12 @@ import (
|
||||||
// hookedStateDB represents a statedb which emits calls to tracing-hooks
|
// hookedStateDB represents a statedb which emits calls to tracing-hooks
|
||||||
// on state operations.
|
// on state operations.
|
||||||
type hookedStateDB struct {
|
type hookedStateDB struct {
|
||||||
inner BlockProcessingDB
|
inner *StateDB
|
||||||
hooks *tracing.Hooks
|
hooks *tracing.Hooks
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHookedState wraps the given stateDb with the given 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}
|
s := &hookedStateDB{stateDb, hooks}
|
||||||
if s.hooks == nil {
|
if s.hooks == nil {
|
||||||
s.hooks = new(tracing.Hooks)
|
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 {
|
func (s *hookedStateDB) TxIndex() int {
|
||||||
return s.inner.TxIndex()
|
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
|
// 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
|
// 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.
|
// 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 (
|
var (
|
||||||
receipts types.Receipts
|
receipts types.Receipts
|
||||||
usedGas = new(uint64)
|
usedGas = new(uint64)
|
||||||
|
|
@ -98,10 +98,6 @@ func (p *StateProcessor) Process(block *types.Block, statedb state.BlockProcessi
|
||||||
}
|
}
|
||||||
statedb.SetTxContext(tx.Hash(), i)
|
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)
|
_, _, receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, context.Time, tx, usedGas, evm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
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...)
|
allLogs = append(allLogs, receipt.Logs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if alDB, ok := statedb.(*state.AccessListCreationDB); ok {
|
|
||||||
alDB.SetAccessListIndex(len(block.Transactions()) + 1)
|
|
||||||
}
|
|
||||||
// Read requests if Prague is enabled.
|
// Read requests if Prague is enabled.
|
||||||
var requests [][]byte
|
var requests [][]byte
|
||||||
if p.config.IsPrague(block.Number(), block.Time()) {
|
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)
|
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||||
p.chain.engine.Finalize(p.chain, header, tracingStateDB, block.Body())
|
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{
|
return &ProcessResult{
|
||||||
Receipts: receipts,
|
Receipts: receipts,
|
||||||
Requests: requests,
|
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
|
// ApplyTransactionWithEVM attempts to apply a transaction to the given state database
|
||||||
// and uses the input parameters for its environment similar to ApplyTransaction. However,
|
// and uses the input parameters for its environment similar to ApplyTransaction. However,
|
||||||
// this method takes an already created EVM instance as input.
|
// 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 := evm.Config.Tracer; hooks != nil {
|
||||||
if hooks.OnTxStart != nil {
|
if hooks.OnTxStart != nil {
|
||||||
hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
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.
|
// 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
|
// Create a new receipt for the transaction, storing the intermediate root and gas used
|
||||||
// by the tx.
|
// by the tx.
|
||||||
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: usedGas}
|
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
|
// and uses the input parameters for its environment. It returns the receipt
|
||||||
// for the transaction, gas used and an error if the transaction failed,
|
// for the transaction, gas used and an error if the transaction failed,
|
||||||
// indicating the block was invalid.
|
// 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)
|
msg, err := TransactionToMessage(tx, types.MakeSigner(evm.ChainConfig(), header.Number, header.Time), header.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ type Validator interface {
|
||||||
ValidateBody(block *types.Block) error
|
ValidateBody(block *types.Block) error
|
||||||
|
|
||||||
// ValidateState validates the given statedb and optionally the process result.
|
// 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.
|
// 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
|
// 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 transaction messages using the statedb and applying any rewards to both
|
||||||
// the processor (coinbase) and any included uncles.
|
// 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.
|
// ProcessResult contains the values computed by Process.
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package bal
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"maps"
|
"maps"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -32,6 +33,27 @@ type CodeChange struct {
|
||||||
Code []byte `json:"code,omitempty"`
|
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
|
// 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
|
// all storage keys that were read during execution. It is used when building block
|
||||||
// access list during execution.
|
// 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.
|
// AccountRead records the address of an account that has been read during execution.
|
||||||
func (c *ConstructionBlockAccessList) AccountRead(addr common.Address) {
|
func (c *ConstructionBlockAccessList) AccountRead(addr common.Address) {
|
||||||
if _, ok := c.Accounts[addr]; !ok {
|
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
|
return cur
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConstructionBlockAccessList) Merge(next *ConstructionBlockAccessList) {
|
// MergeReads combines the account/storage reads from a completed EVM execution scope
|
||||||
for addr, accountAccess := range next.Accounts {
|
// 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 {
|
if _, ok := c.Accounts[addr]; !ok {
|
||||||
c.Accounts[addr] = accountAccess
|
c.Accounts[addr] = accountAccess
|
||||||
continue
|
continue
|
||||||
|
|
@ -172,18 +244,18 @@ func (c *ConstructionBlockAccessList) Merge(next *ConstructionBlockAccessList) {
|
||||||
|
|
||||||
// copy the entries from 'next' into 'c' overwriting 'c' entries with
|
// copy the entries from 'next' into 'c' overwriting 'c' entries with
|
||||||
// 'next entries when the bal index matches.
|
// '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 {
|
for storageRead, _ := range childScope.Accounts[addr].StorageReads {
|
||||||
next.Accounts[addr].StorageReads[storageRead] = struct{}{}
|
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
|
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
|
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
|
c.Accounts[addr].BalanceChanges[idx] = balance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -221,76 +293,6 @@ func (c *ConstructionBlockAccessList) Copy() *ConstructionBlockAccessList {
|
||||||
return res
|
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 {
|
type StateDiff struct {
|
||||||
Mutations map[common.Address]*AccountState `json:"Mutations,omitempty"`
|
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/stateless"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie/utils"
|
"github.com/ethereum/go-ethereum/trie/utils"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
|
|
@ -103,5 +102,5 @@ type StateDB interface {
|
||||||
TxIndex() int
|
TxIndex() int
|
||||||
|
|
||||||
// Finalise must be invoked at the end of a transaction
|
// 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 (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"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 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
|
// If he does afford it, we can skip checking the same thing later on, during execution
|
||||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
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.ColdSloadCostEIP2929, nil
|
||||||
}
|
}
|
||||||
return params.WarmStorageReadCostEIP2929, 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
|
// TODO: same issue as OnColdSStorageRead. See the TODO above near OnColdStorageRead
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnColdAccountRead != nil {
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnColdAccountRead != nil {
|
||||||
evm.Config.Tracer.OnColdAccountRead(contract.Address())
|
evm.Config.Tracer.OnColdAccountRead(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
var overflow bool
|
var overflow bool
|
||||||
|
|
@ -156,6 +161,9 @@ func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
||||||
addr := common.Address(stack.peek().Bytes20())
|
addr := common.Address(stack.peek().Bytes20())
|
||||||
// Check slot presence in the access list
|
// Check slot presence in the access list
|
||||||
if !evm.StateDB.AddressInAccessList(addr) {
|
if !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
|
// If the caller cannot afford the cost, this change will be rolled back
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
// The warm storage read cost is already charged as constantGas
|
// 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
|
// the cost to charge for cold access, if any, is Cold - Warm
|
||||||
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
||||||
if !warmAccess {
|
if !warmAccess {
|
||||||
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnColdAccountRead != nil {
|
||||||
|
evm.Config.Tracer.OnColdAccountRead(addr)
|
||||||
|
}
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
// Charge the remaining difference here already, to correctly calculate available
|
// Charge the remaining difference here already, to correctly calculate available
|
||||||
// gas for call
|
// gas for call
|
||||||
|
|
@ -239,6 +250,9 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
||||||
address = common.Address(stack.peek().Bytes20())
|
address = common.Address(stack.peek().Bytes20())
|
||||||
)
|
)
|
||||||
if !evm.StateDB.AddressInAccessList(address) {
|
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
|
// If the caller cannot afford the cost, this change will be rolled back
|
||||||
evm.StateDB.AddAddressToAccessList(address)
|
evm.StateDB.AddAddressToAccessList(address)
|
||||||
gas = params.ColdAccountAccessCostEIP2929
|
gas = params.ColdAccountAccessCostEIP2929
|
||||||
|
|
@ -271,6 +285,9 @@ func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
|
||||||
|
|
||||||
// Check slot presence in the access list
|
// Check slot presence in the access list
|
||||||
if !evm.StateDB.AddressInAccessList(addr) {
|
if !evm.StateDB.AddressInAccessList(addr) {
|
||||||
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnColdAccountRead != nil {
|
||||||
|
evm.Config.Tracer.OnColdAccountRead(addr)
|
||||||
|
}
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
|
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
|
||||||
// the cost to charge for cold access, if any, is Cold - Warm
|
// the cost to charge for cold access, if any, is Cold - Warm
|
||||||
|
|
@ -289,6 +306,9 @@ func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
|
||||||
if evm.StateDB.AddressInAccessList(target) {
|
if evm.StateDB.AddressInAccessList(target) {
|
||||||
cost = params.WarmStorageReadCostEIP2929
|
cost = params.WarmStorageReadCostEIP2929
|
||||||
} else {
|
} else {
|
||||||
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnColdAccountRead != nil {
|
||||||
|
evm.Config.Tracer.OnColdAccountRead(target)
|
||||||
|
}
|
||||||
evm.StateDB.AddAddressToAccessList(target)
|
evm.StateDB.AddAddressToAccessList(target)
|
||||||
cost = params.ColdAccountAccessCostEIP2929
|
cost = params.ColdAccountAccessCostEIP2929
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue