mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-10 14:03:46 +00:00
Merge pull request #819 from cffls/jc/block-stm/state_processor_interrupt
State processor interrupt
This commit is contained in:
commit
d76fc157aa
7 changed files with 71 additions and 18 deletions
|
|
@ -1803,7 +1803,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool)
|
||||||
|
|
||||||
// Process block using the parent state as reference point
|
// Process block using the parent state as reference point
|
||||||
substart := time.Now()
|
substart := time.Now()
|
||||||
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig)
|
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bc.reportBlock(block, receipts, err)
|
bc.reportBlock(block, receipts, err)
|
||||||
atomic.StoreUint32(&followupInterrupt, 1)
|
atomic.StoreUint32(&followupInterrupt, 1)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
@ -123,9 +124,11 @@ func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, compara
|
||||||
if full {
|
if full {
|
||||||
cur := blockchain.CurrentBlock()
|
cur := blockchain.CurrentBlock()
|
||||||
tdPre = blockchain.GetTd(cur.Hash(), cur.NumberU64())
|
tdPre = blockchain.GetTd(cur.Hash(), cur.NumberU64())
|
||||||
if err := testBlockChainImport(blockChainB, blockchain); err != nil {
|
|
||||||
|
if err := testBlockChainImport(blockChainB, blockchain, nil); err != nil {
|
||||||
t.Fatalf("failed to import forked block chain: %v", err)
|
t.Fatalf("failed to import forked block chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
last := blockChainB[len(blockChainB)-1]
|
last := blockChainB[len(blockChainB)-1]
|
||||||
tdPost = blockchain.GetTd(last.Hash(), last.NumberU64())
|
tdPost = blockchain.GetTd(last.Hash(), last.NumberU64())
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -143,7 +146,7 @@ func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, compara
|
||||||
|
|
||||||
// testBlockChainImport tries to process a chain of blocks, writing them into
|
// testBlockChainImport tries to process a chain of blocks, writing them into
|
||||||
// the database if successful.
|
// the database if successful.
|
||||||
func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
func testBlockChainImport(chain types.Blocks, blockchain *BlockChain, ctx context.Context) error {
|
||||||
for _, block := range chain {
|
for _, block := range chain {
|
||||||
// Try and process the block
|
// Try and process the block
|
||||||
err := blockchain.engine.VerifyHeader(blockchain, block.Header(), true)
|
err := blockchain.engine.VerifyHeader(blockchain, block.Header(), true)
|
||||||
|
|
@ -156,11 +159,14 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), blockchain.stateCache, nil)
|
statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), blockchain.stateCache, nil)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
receipts, _, usedGas, err := blockchain.processor.Process(block, statedb, vm.Config{})
|
|
||||||
|
receipts, _, usedGas, err := blockchain.processor.Process(block, statedb, vm.Config{}, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
blockchain.reportBlock(block, receipts, err)
|
blockchain.reportBlock(block, receipts, err)
|
||||||
return err
|
return err
|
||||||
|
|
@ -180,6 +186,26 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBlockChainImportInterrupt(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
db, blockchain, err := newCanonical(ethash.NewFaker(), 10, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to make new canonical chain: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer blockchain.Stop()
|
||||||
|
|
||||||
|
blockChainB := makeFakeNonEmptyBlockChain(blockchain.CurrentBlock(), 5, ethash.NewFaker(), db, forkSeed, 5)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
if err := testBlockChainImport(blockChainB, blockchain, ctx); err != context.Canceled {
|
||||||
|
t.Errorf("block chain import is not cancelled correctly, got %v, want %v", err, context.Canceled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// testHeaderChainImport tries to process a chain of header, writing them into
|
// testHeaderChainImport tries to process a chain of header, writing them into
|
||||||
// the database if successful.
|
// the database if successful.
|
||||||
func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error {
|
func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error {
|
||||||
|
|
@ -476,7 +502,7 @@ func testBrokenChain(t *testing.T, full bool) {
|
||||||
// Create a forked chain, and try to insert with a missing link
|
// Create a forked chain, and try to insert with a missing link
|
||||||
if full {
|
if full {
|
||||||
chain := makeBlockChain(blockchain.CurrentBlock(), 5, ethash.NewFaker(), db, forkSeed)[1:]
|
chain := makeBlockChain(blockchain.CurrentBlock(), 5, ethash.NewFaker(), db, forkSeed)[1:]
|
||||||
if err := testBlockChainImport(chain, blockchain); err == nil {
|
if err := testBlockChainImport(chain, blockchain, nil); err == nil {
|
||||||
t.Errorf("broken block chain not reported")
|
t.Errorf("broken block chain not reported")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -335,6 +335,19 @@ func makeBlockChain(parent *types.Block, n int, engine consensus.Engine, db ethd
|
||||||
return blocks
|
return blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// makeBlockChain creates a deterministic chain of blocks rooted at parent with fake invalid transactions.
|
||||||
|
func makeFakeNonEmptyBlockChain(parent *types.Block, n int, engine consensus.Engine, db ethdb.Database, seed int, numTx int) []*types.Block {
|
||||||
|
blocks, _ := GenerateChain(params.TestChainConfig, parent, engine, db, n, func(i int, b *BlockGen) {
|
||||||
|
addr := common.Address{0: byte(seed), 19: byte(i)}
|
||||||
|
b.SetCoinbase(addr)
|
||||||
|
for j := 0; j < numTx; j++ {
|
||||||
|
b.txs = append(b.txs, types.NewTransaction(0, addr, big.NewInt(1000), params.TxGas, nil, nil))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return blocks
|
||||||
|
}
|
||||||
|
|
||||||
type fakeChainReader struct {
|
type fakeChainReader struct {
|
||||||
config *params.ChainConfig
|
config *params.ChainConfig
|
||||||
stateSyncData []*types.StateSyncData
|
stateSyncData []*types.StateSyncData
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -273,7 +274,7 @@ var parallelizabilityTimer = metrics.NewRegisteredTimer("block/parallelizability
|
||||||
// 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.
|
||||||
// nolint:gocognit
|
// nolint:gocognit
|
||||||
func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
|
func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config, interruptCtx context.Context) (types.Receipts, []*types.Log, uint64, error) {
|
||||||
blockstm.SetProcs(cfg.ParallelSpeculativeProcesses)
|
blockstm.SetProcs(cfg.ParallelSpeculativeProcesses)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
|
@ -56,7 +57,7 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen
|
||||||
// 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.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
|
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config, interruptCtx context.Context) (types.Receipts, []*types.Log, uint64, error) {
|
||||||
var (
|
var (
|
||||||
receipts types.Receipts
|
receipts types.Receipts
|
||||||
usedGas = new(uint64)
|
usedGas = new(uint64)
|
||||||
|
|
@ -74,6 +75,14 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
|
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
|
||||||
// Iterate over and process the individual transactions
|
// Iterate over and process the individual transactions
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
|
if interruptCtx != nil {
|
||||||
|
select {
|
||||||
|
case <-interruptCtx.Done():
|
||||||
|
return nil, nil, 0, interruptCtx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), header.BaseFee)
|
msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), header.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
|
@ -47,5 +49,5 @@ 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.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error)
|
Process(block *types.Block, statedb *state.StateDB, cfg vm.Config, interruptCtx context.Context) (types.Receipts, []*types.Log, uint64, error)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,15 +36,15 @@ import (
|
||||||
// base layer statedb can be passed then it's regarded as the statedb of the
|
// base layer statedb can be passed then it's regarded as the statedb of the
|
||||||
// parent block.
|
// parent block.
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - block: The block for which we want the state (== state at the stateRoot of the parent)
|
// - block: The block for which we want the state (== state at the stateRoot of the parent)
|
||||||
// - reexec: The maximum number of blocks to reprocess trying to obtain the desired state
|
// - reexec: The maximum number of blocks to reprocess trying to obtain the desired state
|
||||||
// - base: If the caller is tracing multiple blocks, the caller can provide the parent state
|
// - base: If the caller is tracing multiple blocks, the caller can provide the parent state
|
||||||
// continuously from the callsite.
|
// continuously from the callsite.
|
||||||
// - checklive: if true, then the live 'blockchain' state database is used. If the caller want to
|
// - checklive: if true, then the live 'blockchain' state database is used. If the caller want to
|
||||||
// perform Commit or other 'save-to-disk' changes, this should be set to false to avoid
|
// perform Commit or other 'save-to-disk' changes, this should be set to false to avoid
|
||||||
// storing trash persistently
|
// storing trash persistently
|
||||||
// - preferDisk: this arg can be used by the caller to signal that even though the 'base' is provided,
|
// - preferDisk: this arg can be used by the caller to signal that even though the 'base' is provided,
|
||||||
// it would be preferrable to start from a fresh state, if we have it on disk.
|
// it would be preferrable to start from a fresh state, if we have it on disk.
|
||||||
func (eth *Ethereum) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) {
|
func (eth *Ethereum) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) {
|
||||||
var (
|
var (
|
||||||
current *types.Block
|
current *types.Block
|
||||||
|
|
@ -131,7 +131,9 @@ func (eth *Ethereum) StateAtBlock(block *types.Block, reexec uint64, base *state
|
||||||
if current = eth.blockchain.GetBlockByNumber(next); current == nil {
|
if current = eth.blockchain.GetBlockByNumber(next); current == nil {
|
||||||
return nil, fmt.Errorf("block #%d not found", next)
|
return nil, fmt.Errorf("block #%d not found", next)
|
||||||
}
|
}
|
||||||
_, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{})
|
|
||||||
|
_, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{}, nil)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err)
|
return nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue