mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 14:16:44 +00:00
Add simple parallel executor with sim prestate and pass TestEIP155Transition test
This commit is contained in:
parent
10f600aef4
commit
45501be321
5 changed files with 268 additions and 1 deletions
|
|
@ -342,7 +342,8 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
bc.statedb = state.NewDatabase(bc.triedb, nil)
|
bc.statedb = state.NewDatabase(bc.triedb, nil)
|
||||||
bc.validator = NewBlockValidator(chainConfig, bc)
|
bc.validator = NewBlockValidator(chainConfig, bc)
|
||||||
bc.prefetcher = newStatePrefetcher(chainConfig, bc.hc)
|
bc.prefetcher = newStatePrefetcher(chainConfig, bc.hc)
|
||||||
bc.processor = NewStateProcessor(chainConfig, bc.hc)
|
// bc.processor = NewStateProcessor(chainConfig, bc.hc)
|
||||||
|
bc.processor = NewParallelStateProcessor(chainConfig, bc.hc)
|
||||||
|
|
||||||
genesisHeader := bc.GetHeaderByNumber(0)
|
genesisHeader := bc.GetHeaderByNumber(0)
|
||||||
if genesisHeader == nil {
|
if genesisHeader == nil {
|
||||||
|
|
@ -1899,6 +1900,7 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s
|
||||||
)
|
)
|
||||||
defer interrupt.Store(true) // terminate the prefetch at the end
|
defer interrupt.Store(true) // terminate the prefetch at the end
|
||||||
|
|
||||||
|
bc.cacheConfig.TrieCleanNoPrefetch = true
|
||||||
if bc.cacheConfig.TrieCleanNoPrefetch {
|
if bc.cacheConfig.TrieCleanNoPrefetch {
|
||||||
statedb, err = state.New(parentRoot, bc.statedb)
|
statedb, err = state.New(parentRoot, bc.statedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
198
core/parallel_state_processor.go
Normal file
198
core/parallel_state_processor.go
Normal file
|
|
@ -0,0 +1,198 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ParallelStateProcessor struct {
|
||||||
|
config *params.ChainConfig // Chain configuration options
|
||||||
|
chain *HeaderChain // Canonical header chain
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewParallelStateProcessor(config *params.ChainConfig, chain *HeaderChain) *ParallelStateProcessor {
|
||||||
|
return &ParallelStateProcessor{
|
||||||
|
config: config,
|
||||||
|
chain: chain,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) {
|
||||||
|
fmt.Println("ParallelStateProcessor.Process called")
|
||||||
|
var (
|
||||||
|
header = block.Header()
|
||||||
|
gp = new(GasPool).AddGas(block.GasLimit())
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mutate the block and state according to any hard-fork specs
|
||||||
|
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
|
||||||
|
misc.ApplyDAOHardFork(statedb)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
context vm.BlockContext
|
||||||
|
signer = types.MakeSigner(p.config, header.Number, header.Time)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Apply pre-execution system calls.
|
||||||
|
var tracingStateDB = vm.StateDB(statedb)
|
||||||
|
if hooks := cfg.Tracer; hooks != nil {
|
||||||
|
tracingStateDB = state.NewHookedState(statedb, hooks)
|
||||||
|
}
|
||||||
|
context = NewEVMBlockContext(header, p.chain, nil)
|
||||||
|
evm := vm.NewEVM(context, tracingStateDB, p.config, cfg)
|
||||||
|
|
||||||
|
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||||
|
ProcessBeaconBlockRoot(*beaconRoot, evm)
|
||||||
|
}
|
||||||
|
if p.config.IsPrague(block.Number(), block.Time()) || p.config.IsVerkle(block.Number(), block.Time()) {
|
||||||
|
ProcessParentBlockHash(block.ParentHash(), evm)
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.executeParallel(block, statedb, &context, cfg, gp, signer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParallelStateProcessor) executeParallel(block *types.Block, statedb *state.StateDB, blockContext *vm.BlockContext, cfg vm.Config, gp *GasPool, signer types.Signer) (*ProcessResult, error) {
|
||||||
|
var (
|
||||||
|
receipts = make(types.Receipts, len(block.Transactions()))
|
||||||
|
header = block.Header()
|
||||||
|
blockHash = block.Hash()
|
||||||
|
blockNumber = block.Number()
|
||||||
|
allLogs []*types.Log
|
||||||
|
wg sync.WaitGroup
|
||||||
|
preStatedb = statedb.Copy()
|
||||||
|
sequentialEvm = vm.NewEVM(*blockContext, preStatedb, p.config, cfg)
|
||||||
|
seqUsedGas = new(uint64)
|
||||||
|
)
|
||||||
|
// Fetch prestate for each tx
|
||||||
|
|
||||||
|
// Parallel executing the transaction
|
||||||
|
postStates := make([]*state.StateDB, len(block.Transactions()))
|
||||||
|
postEntries := make([][]state.JournalEntry, len(block.Transactions()))
|
||||||
|
for i, tx := range block.Transactions() {
|
||||||
|
postStates[i] = preStatedb.Copy() // Copy the state for each transaction
|
||||||
|
cleanStatedb := postStates[i]
|
||||||
|
i := i
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
usedGas := new(uint64)
|
||||||
|
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
||||||
|
// todo: handle error: break all routines and return error
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("could not apply tx %d [%v]: %v", i, tx.Hash().Hex(), err)
|
||||||
|
}
|
||||||
|
cleanStatedb.SetTxContext(tx.Hash(), i)
|
||||||
|
|
||||||
|
evm := vm.NewEVM(*blockContext, cleanStatedb, p.config, cfg)
|
||||||
|
|
||||||
|
receipt, entries, err := ApplyTransactionWithParallelEVM(msg, gp, cleanStatedb, blockNumber, blockHash, tx, usedGas, evm)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("could not apply parallel tx %d [%v]: %v", i, tx.Hash().Hex(), err)
|
||||||
|
}
|
||||||
|
receipts[i] = receipt
|
||||||
|
postEntries[i] = entries
|
||||||
|
}()
|
||||||
|
|
||||||
|
// execute the transaction again to simulate the state changes
|
||||||
|
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not apply tx to msg %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||||
|
}
|
||||||
|
preStatedb.SetTxContext(tx.Hash(), i)
|
||||||
|
|
||||||
|
_, err = ApplyTransactionWithEVM(msg, gp, preStatedb, blockNumber, blockHash, tx, seqUsedGas, sequentialEvm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
// Merge state changes
|
||||||
|
// - Append receipts
|
||||||
|
// - Sum usedGas
|
||||||
|
// - Collect state state changes: simple overwrite
|
||||||
|
// - Ommit preimages for now
|
||||||
|
usedGas := uint64(0)
|
||||||
|
for i, receipt := range receipts {
|
||||||
|
if receipt == nil {
|
||||||
|
continue // Skip nil receipts
|
||||||
|
}
|
||||||
|
receipt.CumulativeGasUsed = usedGas + receipt.GasUsed
|
||||||
|
usedGas += receipt.GasUsed
|
||||||
|
allLogs = append(allLogs, receipt.Logs...)
|
||||||
|
statedb.MergeState(postEntries[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read requests if Prague is enabled.
|
||||||
|
evm := vm.NewEVM(*blockContext, statedb, p.config, cfg)
|
||||||
|
var requests [][]byte
|
||||||
|
if p.config.IsPrague(block.Number(), block.Time()) {
|
||||||
|
requests = [][]byte{}
|
||||||
|
// EIP-6110
|
||||||
|
if err := ParseDepositLogs(&requests, allLogs, p.config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// EIP-7002
|
||||||
|
if err := ProcessWithdrawalQueue(&requests, evm); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// EIP-7251
|
||||||
|
if err := ProcessConsolidationQueue(&requests, evm); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||||
|
p.chain.engine.Finalize(p.chain, header, statedb, block.Body())
|
||||||
|
|
||||||
|
return &ProcessResult{
|
||||||
|
Receipts: receipts,
|
||||||
|
Requests: requests,
|
||||||
|
Logs: allLogs,
|
||||||
|
GasUsed: usedGas,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplyTransactionWithParallelEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, entries []state.JournalEntry, err error) {
|
||||||
|
if hooks := evm.Config.Tracer; hooks != nil {
|
||||||
|
if hooks.OnTxStart != nil {
|
||||||
|
hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||||
|
}
|
||||||
|
if hooks.OnTxEnd != nil {
|
||||||
|
defer func() { hooks.OnTxEnd(receipt, err) }()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Apply the transaction to the current state (included in the env).
|
||||||
|
result, err := ApplyMessage(evm, msg, gp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
// copy changed state
|
||||||
|
entries = statedb.JournalEntriesCopy()
|
||||||
|
// Update the state with pending changes.
|
||||||
|
var root []byte
|
||||||
|
if evm.ChainConfig().IsByzantium(blockNumber) {
|
||||||
|
evm.StateDB.Finalise(true)
|
||||||
|
} else {
|
||||||
|
root = statedb.IntermediateRoot(evm.ChainConfig().IsEIP158(blockNumber)).Bytes()
|
||||||
|
}
|
||||||
|
*usedGas += result.UsedGas
|
||||||
|
|
||||||
|
// Merge the tx-local access event into the "block-local" one, in order to collect
|
||||||
|
// all values, so that the witness can be built.
|
||||||
|
if statedb.GetTrie().IsVerkle() {
|
||||||
|
statedb.AccessEvents().Merge(evm.AccessEvents)
|
||||||
|
}
|
||||||
|
|
||||||
|
return MakeReceipt(evm, result, statedb, blockNumber, blockHash, tx, *usedGas, root), entries, nil
|
||||||
|
}
|
||||||
|
|
@ -45,6 +45,17 @@ type journalEntry interface {
|
||||||
copy() journalEntry
|
copy() journalEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type JournalEntry interface {
|
||||||
|
// revert undoes the changes introduced by this journal entry.
|
||||||
|
revert(*StateDB)
|
||||||
|
|
||||||
|
// dirtied returns the Ethereum address modified by this journal entry.
|
||||||
|
dirtied() *common.Address
|
||||||
|
|
||||||
|
// copy returns a deep-copied journal entry.
|
||||||
|
copy() journalEntry
|
||||||
|
}
|
||||||
|
|
||||||
// journal contains the list of state modifications applied since the last state
|
// journal contains the list of state modifications applied since the last state
|
||||||
// commit. These are tracked to be able to be reverted in the case of an execution
|
// commit. These are tracked to be able to be reverted in the case of an execution
|
||||||
// exception or request for reversal.
|
// exception or request for reversal.
|
||||||
|
|
@ -148,6 +159,14 @@ func (j *journal) copy() *journal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (j *journal) copyEntries() []JournalEntry {
|
||||||
|
entries := make([]JournalEntry, j.length())
|
||||||
|
for i := range j.length() {
|
||||||
|
entries[i] = j.entries[i].copy()
|
||||||
|
}
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
func (j *journal) logChange(txHash common.Hash) {
|
func (j *journal) logChange(txHash common.Hash) {
|
||||||
j.append(addLogChange{txhash: txHash})
|
j.append(addLogChange{txhash: txHash})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -218,6 +218,7 @@ func (s *stateObject) SetState(key, value common.Hash) common.Hash {
|
||||||
}
|
}
|
||||||
// New value is different, update and journal the change
|
// New value is different, update and journal the change
|
||||||
s.db.journal.storageChange(s.address, key, prev, origin)
|
s.db.journal.storageChange(s.address, key, prev, origin)
|
||||||
|
s.db.updateJournal.storageChange(s.address, key, value, prev)
|
||||||
s.setState(key, value, origin)
|
s.setState(key, value, origin)
|
||||||
return prev
|
return prev
|
||||||
}
|
}
|
||||||
|
|
@ -470,6 +471,7 @@ func (s *stateObject) AddBalance(amount *uint256.Int) uint256.Int {
|
||||||
func (s *stateObject) SetBalance(amount *uint256.Int) uint256.Int {
|
func (s *stateObject) SetBalance(amount *uint256.Int) uint256.Int {
|
||||||
prev := *s.data.Balance
|
prev := *s.data.Balance
|
||||||
s.db.journal.balanceChange(s.address, s.data.Balance)
|
s.db.journal.balanceChange(s.address, s.data.Balance)
|
||||||
|
s.db.updateJournal.balanceChange(s.address, amount)
|
||||||
s.setBalance(amount)
|
s.setBalance(amount)
|
||||||
return prev
|
return prev
|
||||||
}
|
}
|
||||||
|
|
@ -551,6 +553,7 @@ func (s *stateObject) CodeSize() int {
|
||||||
func (s *stateObject) SetCode(codeHash common.Hash, code []byte) (prev []byte) {
|
func (s *stateObject) SetCode(codeHash common.Hash, code []byte) (prev []byte) {
|
||||||
prev = slices.Clone(s.code)
|
prev = slices.Clone(s.code)
|
||||||
s.db.journal.setCode(s.address, prev)
|
s.db.journal.setCode(s.address, prev)
|
||||||
|
s.db.updateJournal.setCode(s.address, code)
|
||||||
s.setCode(codeHash, code)
|
s.setCode(codeHash, code)
|
||||||
return prev
|
return prev
|
||||||
}
|
}
|
||||||
|
|
@ -563,6 +566,7 @@ func (s *stateObject) setCode(codeHash common.Hash, code []byte) {
|
||||||
|
|
||||||
func (s *stateObject) SetNonce(nonce uint64) {
|
func (s *stateObject) SetNonce(nonce uint64) {
|
||||||
s.db.journal.nonceChange(s.address, s.data.Nonce)
|
s.db.journal.nonceChange(s.address, s.data.Nonce)
|
||||||
|
s.db.updateJournal.nonceChange(s.address, nonce)
|
||||||
s.setNonce(nonce)
|
s.setNonce(nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,8 @@ type StateDB struct {
|
||||||
// Journal of state modifications. This is the backbone of
|
// Journal of state modifications. This is the backbone of
|
||||||
// Snapshot and RevertToSnapshot.
|
// Snapshot and RevertToSnapshot.
|
||||||
journal *journal
|
journal *journal
|
||||||
|
//
|
||||||
|
updateJournal *journal
|
||||||
|
|
||||||
// State witness if cross validation is needed
|
// State witness if cross validation is needed
|
||||||
witness *stateless.Witness
|
witness *stateless.Witness
|
||||||
|
|
@ -184,6 +186,7 @@ func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, erro
|
||||||
logs: make(map[common.Hash][]*types.Log),
|
logs: make(map[common.Hash][]*types.Log),
|
||||||
preimages: make(map[common.Hash][]byte),
|
preimages: make(map[common.Hash][]byte),
|
||||||
journal: newJournal(),
|
journal: newJournal(),
|
||||||
|
updateJournal: newJournal(),
|
||||||
accessList: newAccessList(),
|
accessList: newAccessList(),
|
||||||
transientStorage: newTransientStorage(),
|
transientStorage: newTransientStorage(),
|
||||||
}
|
}
|
||||||
|
|
@ -287,6 +290,42 @@ func (s *StateDB) AddRefund(gas uint64) {
|
||||||
s.refund += gas
|
s.refund += gas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) JournalEntriesCopy() []JournalEntry {
|
||||||
|
return s.updateJournal.copyEntries()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) MergeState(entries []JournalEntry) {
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch v := entry.(type) {
|
||||||
|
case createObjectChange:
|
||||||
|
s.CreateAccount(v.account)
|
||||||
|
case createContractChange:
|
||||||
|
s.CreateContract(v.account)
|
||||||
|
case selfDestructChange:
|
||||||
|
s.SelfDestruct(v.account)
|
||||||
|
case balanceChange:
|
||||||
|
{
|
||||||
|
s.SetBalance(v.account, v.prev, tracing.BalanceChangeUnspecified)
|
||||||
|
|
||||||
|
}
|
||||||
|
case nonceChange:
|
||||||
|
s.SetNonce(v.account, v.prev, tracing.NonceChangeUnspecified)
|
||||||
|
case storageChange:
|
||||||
|
{
|
||||||
|
obj := s.getOrNewStateObject(v.account)
|
||||||
|
obj.SetState(v.key, v.prevvalue)
|
||||||
|
}
|
||||||
|
case codeChange:
|
||||||
|
s.SetCode(v.account, v.prevCode)
|
||||||
|
default:
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SubRefund removes gas from the refund counter.
|
// SubRefund removes gas from the refund counter.
|
||||||
// This method will panic if the refund counter goes below zero
|
// This method will panic if the refund counter goes below zero
|
||||||
func (s *StateDB) SubRefund(gas uint64) {
|
func (s *StateDB) SubRefund(gas uint64) {
|
||||||
|
|
@ -516,6 +555,7 @@ func (s *StateDB) SelfDestruct(addr common.Address) uint256.Int {
|
||||||
// for journalling a second time.
|
// for journalling a second time.
|
||||||
if !stateObject.selfDestructed {
|
if !stateObject.selfDestructed {
|
||||||
s.journal.destruct(addr)
|
s.journal.destruct(addr)
|
||||||
|
s.updateJournal.destruct(addr)
|
||||||
stateObject.markSelfdestructed()
|
stateObject.markSelfdestructed()
|
||||||
}
|
}
|
||||||
return prevBalance
|
return prevBalance
|
||||||
|
|
@ -633,6 +673,7 @@ func (s *StateDB) getOrNewStateObject(addr common.Address) *stateObject {
|
||||||
func (s *StateDB) createObject(addr common.Address) *stateObject {
|
func (s *StateDB) createObject(addr common.Address) *stateObject {
|
||||||
obj := newObject(s, addr, nil)
|
obj := newObject(s, addr, nil)
|
||||||
s.journal.createObject(addr)
|
s.journal.createObject(addr)
|
||||||
|
s.updateJournal.createObject(addr)
|
||||||
s.setStateObject(obj)
|
s.setStateObject(obj)
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
@ -655,6 +696,7 @@ func (s *StateDB) CreateContract(addr common.Address) {
|
||||||
if !obj.newContract {
|
if !obj.newContract {
|
||||||
obj.newContract = true
|
obj.newContract = true
|
||||||
s.journal.createContract(addr)
|
s.journal.createContract(addr)
|
||||||
|
s.updateJournal.createContract(addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -687,6 +729,8 @@ func (s *StateDB) Copy() *StateDB {
|
||||||
accessList: s.accessList.Copy(),
|
accessList: s.accessList.Copy(),
|
||||||
transientStorage: s.transientStorage.Copy(),
|
transientStorage: s.transientStorage.Copy(),
|
||||||
journal: s.journal.copy(),
|
journal: s.journal.copy(),
|
||||||
|
// The update journal is not copies to avoid duplicated updates in later transactions.
|
||||||
|
updateJournal: newJournal(),
|
||||||
}
|
}
|
||||||
if s.witness != nil {
|
if s.witness != nil {
|
||||||
state.witness = s.witness.Copy()
|
state.witness = s.witness.Copy()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue