From 15391eeaf7b43642c840a930b4e08cbc3725ed20 Mon Sep 17 00:00:00 2001 From: maskpp Date: Tue, 20 Sep 2022 14:30:57 +0800 Subject: [PATCH] feat: add replay blockResult API (#139) * replay trace * replay trace * add sdk * fix trace test case * fix bug * Update eth/tracers/api_blockResult.go Co-authored-by: Haichen Shen * Update eth/tracers/api_blockResult.go Co-authored-by: Haichen Shen * Update eth/tracers/api_blockResult.go Co-authored-by: Haichen Shen * Update eth/tracers/api_blockResult.go Co-authored-by: Haichen Shen * add comments * fail the rpc call if get error * adjust channel length * fix bug * fix bug * remove redundant codes in worker * add test case * fix bug * fix goimports * Update eth/tracers/api_blockResult.go Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com> * fix comments * Update ethclient/ethclient.go Co-authored-by: Haichen Shen * Update ethclient/ethclient.go Co-authored-by: Haichen Shen * rm coinbase api in miner * fix comment * Update eth/tracers/api.go Co-authored-by: Haichen Shen * fix comment Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com> Co-authored-by: Haichen Shen --- cmd/geth/config.go | 1 - core/blockchain.go | 125 +++------- core/blockchain_reader.go | 10 +- core/events.go | 7 +- core/types/l2trace.go | 2 +- eth/api.go | 18 -- eth/api_backend.go | 4 + eth/backend.go | 8 - eth/ethconfig/config.go | 3 +- eth/filters/api.go | 29 --- eth/filters/filter_system.go | 39 +-- eth/tracers/api.go | 7 + eth/tracers/api_blockresult.go | 364 ++++++++++++++++++++++++++++ eth/tracers/api_blockresult_test.go | 216 +++++++++++++++++ eth/tracers/api_test.go | 4 + ethclient/ethclient.go | 15 +- internal/debug/flags.go | 11 +- les/api_backend.go | 4 + miner/worker.go | 183 ++------------ 19 files changed, 680 insertions(+), 370 deletions(-) create mode 100644 eth/tracers/api_blockresult.go create mode 100644 eth/tracers/api_blockresult_test.go diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 85de4030c8..e33d57eef2 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -215,7 +215,6 @@ func dumpConfig(ctx *cli.Context) error { func applyTraceConfig(ctx *cli.Context, cfg *ethconfig.Config) { subCfg := debug.ConfigTrace(ctx) - cfg.TraceCacheLimit = subCfg.TraceCacheLimit cfg.MPTWitness = subCfg.MPTWitness } diff --git a/core/blockchain.go b/core/blockchain.go index d62a7f33a5..6bacdefd04 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -31,7 +31,6 @@ import ( lru "github.com/hashicorp/golang-lru" "github.com/scroll-tech/go-ethereum/common" - "github.com/scroll-tech/go-ethereum/common/hexutil" "github.com/scroll-tech/go-ethereum/common/mclock" "github.com/scroll-tech/go-ethereum/common/prque" "github.com/scroll-tech/go-ethereum/consensus" @@ -87,14 +86,13 @@ var ( ) const ( - bodyCacheLimit = 256 - blockCacheLimit = 256 - receiptsCacheLimit = 32 - txLookupCacheLimit = 1024 - maxFutureBlocks = 256 - maxTimeFutureBlocks = 30 - TriesInMemory = 128 - blockResultCacheLimit = 128 + bodyCacheLimit = 256 + blockCacheLimit = 256 + receiptsCacheLimit = 32 + txLookupCacheLimit = 1024 + maxFutureBlocks = 256 + maxTimeFutureBlocks = 30 + TriesInMemory = 128 // BlockChainVersion ensures that an incompatible database forces a resync from scratch. // @@ -134,8 +132,7 @@ type CacheConfig struct { TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory Preimages bool // Whether to store preimage of trie key to the disk - TraceCacheLimit int - MPTWitness int // How to generate witness data for mpt circuit, 0: nothing, 1: natural + MPTWitness int // How to generate witness data for mpt circuit, 0: nothing, 1: natural SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it } @@ -143,13 +140,12 @@ type CacheConfig struct { // defaultCacheConfig are the default caching values if none are specified by the // user (also used during testing). var defaultCacheConfig = &CacheConfig{ - TrieCleanLimit: 256, - TrieDirtyLimit: 256, - TrieTimeLimit: 5 * time.Minute, - SnapshotLimit: 256, - SnapshotWait: true, - TraceCacheLimit: 32, - MPTWitness: int(zkproof.MPTWitnessNothing), + TrieCleanLimit: 256, + TrieDirtyLimit: 256, + TrieTimeLimit: 5 * time.Minute, + SnapshotLimit: 256, + SnapshotWait: true, + MPTWitness: int(zkproof.MPTWitnessNothing), } // BlockChain represents the canonical chain given a database with a genesis @@ -199,14 +195,13 @@ type BlockChain struct { currentBlock atomic.Value // Current head of the block chain currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!) - stateCache state.Database // State database to reuse between imports (contains state cache) - bodyCache *lru.Cache // Cache for the most recent block bodies - bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format - receiptsCache *lru.Cache // Cache for the most recent receipts per block - blockCache *lru.Cache // Cache for the most recent entire blocks - txLookupCache *lru.Cache // Cache for the most recent transaction lookup data. - futureBlocks *lru.Cache // future blocks are blocks added for later processing - blockResultCache *lru.Cache // Cache for the most recent block results. + stateCache state.Database // State database to reuse between imports (contains state cache) + bodyCache *lru.Cache // Cache for the most recent block bodies + bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format + receiptsCache *lru.Cache // Cache for the most recent receipts per block + blockCache *lru.Cache // Cache for the most recent entire blocks + txLookupCache *lru.Cache // Cache for the most recent transaction lookup data. + futureBlocks *lru.Cache // future blocks are blocks added for later processing wg sync.WaitGroup // quit chan struct{} // shutdown signal, closed in Stop. @@ -235,10 +230,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par blockCache, _ := lru.New(blockCacheLimit) txLookupCache, _ := lru.New(txLookupCacheLimit) futureBlocks, _ := lru.New(maxFutureBlocks) - blockResultCache, _ := lru.New(blockResultCacheLimit) - if cacheConfig.TraceCacheLimit != 0 { - blockResultCache, _ = lru.New(cacheConfig.TraceCacheLimit) - } // override snapshot setting if chainConfig.Zktrie && cacheConfig.SnapshotLimit > 0 { log.Warn("snapshot has been disabled by zktrie") @@ -256,18 +247,17 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par Preimages: cacheConfig.Preimages, Zktrie: chainConfig.Zktrie, }), - quit: make(chan struct{}), - chainmu: syncx.NewClosableMutex(), - shouldPreserve: shouldPreserve, - bodyCache: bodyCache, - bodyRLPCache: bodyRLPCache, - receiptsCache: receiptsCache, - blockCache: blockCache, - txLookupCache: txLookupCache, - futureBlocks: futureBlocks, - blockResultCache: blockResultCache, - engine: engine, - vmConfig: vmConfig, + quit: make(chan struct{}), + chainmu: syncx.NewClosableMutex(), + shouldPreserve: shouldPreserve, + bodyCache: bodyCache, + bodyRLPCache: bodyRLPCache, + receiptsCache: receiptsCache, + blockCache: blockCache, + txLookupCache: txLookupCache, + futureBlocks: futureBlocks, + engine: engine, + vmConfig: vmConfig, } bc.validator = NewBlockValidator(chainConfig, bc, engine) bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine) @@ -1202,17 +1192,17 @@ func (bc *BlockChain) writeKnownBlock(block *types.Block) error { } // WriteBlockWithState writes the block and all associated state to the database. -func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, evmTraces []*types.ExecutionResult, storageTrace *types.StorageTrace, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { +func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { if !bc.chainmu.TryLock() { return NonStatTy, errInsertionInterrupted } defer bc.chainmu.Unlock() - return bc.writeBlockWithState(block, receipts, logs, evmTraces, storageTrace, state, emitHeadEvent) + return bc.writeBlockWithState(block, receipts, logs, state, emitHeadEvent) } // writeBlockWithState writes the block and all associated state to the database, // but is expects the chain mutex to be held. -func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, evmTraces []*types.ExecutionResult, storageTrace *types.StorageTrace, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { +func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { if bc.insertStopped() { return NonStatTy, errInsertionInterrupted } @@ -1333,15 +1323,8 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. } bc.futureBlocks.Remove(block.Hash()) - // Fill blockResult content - var blockResult *types.BlockResult - if evmTraces != nil { - blockResult = bc.writeBlockResult(state, block, evmTraces, storageTrace) - bc.blockResultCache.Add(block.Hash(), blockResult) - } - if status == CanonStatTy { - bc.chainFeed.Send(ChainEvent{Block: block, Hash: block.Hash(), Logs: logs, BlockResult: blockResult}) + bc.chainFeed.Send(ChainEvent{Block: block, Hash: block.Hash(), Logs: logs}) if len(logs) > 0 { bc.logsFeed.Send(logs) } @@ -1359,40 +1342,6 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. return status, nil } -// Fill blockResult content -func (bc *BlockChain) writeBlockResult(state *state.StateDB, block *types.Block, evmTraces []*types.ExecutionResult, storageTrace *types.StorageTrace) *types.BlockResult { - blockResult := &types.BlockResult{ - ExecutionResults: evmTraces, - StorageTrace: storageTrace, - } - coinbase := types.AccountWrapper{ - Address: block.Coinbase(), - Nonce: state.GetNonce(block.Coinbase()), - Balance: (*hexutil.Big)(state.GetBalance(block.Coinbase())), - CodeHash: state.GetCodeHash(block.Coinbase()), - } - - blockResult.BlockTrace = types.NewTraceBlock(bc.chainConfig, block, &coinbase) - for i, tx := range block.Transactions() { - evmTrace := blockResult.ExecutionResults[i] - // Contract is called - if len(tx.Data()) != 0 && tx.To() != nil { - evmTrace.ByteCode = hexutil.Encode(state.GetCode(*tx.To())) - // Get tx.to address's code hash. - codeHash := state.GetCodeHash(*tx.To()) - evmTrace.CodeHash = &codeHash - } else if tx.To() == nil { // Contract is created. - evmTrace.ByteCode = hexutil.Encode(tx.Data()) - } - } - - if err := zkproof.FillBlockResultForMPTWitness(zkproof.MPTWitnessType(bc.cacheConfig.MPTWitness), blockResult); err != nil { - log.Error("fill mpt witness fail", "error", err) - } - - return blockResult -} - // addFutureBlock checks if the block is within the max allowed window to get // accepted for future processing, and returns an error if the block is too far // ahead and was not added. @@ -1706,7 +1655,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er // Write the block to the chain and get the status. substart = time.Now() // EvmTraces & StorageTrace being nil is safe because l2geth's p2p server is stoped and the code will not execute there. - status, err := bc.writeBlockWithState(block, receipts, logs, nil, nil, statedb, false) + status, err := bc.writeBlockWithState(block, receipts, logs, statedb, false) atomic.StoreUint32(&followupInterrupt, 1) if err != nil { return it.index, err diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index a25b9ce425..ffa8db3f0e 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -158,13 +158,6 @@ func (bc *BlockChain) GetBlockByHash(hash common.Hash) *types.Block { return bc.GetBlock(hash, *number) } -func (bc *BlockChain) GetBlockResultByHash(blockHash common.Hash) *types.BlockResult { - if blockResult, ok := bc.blockResultCache.Get(blockHash); ok { - return blockResult.(*types.BlockResult) - } - return nil -} - // GetBlockByNumber retrieves a block from the database by number, caching it // (associated with its hash) if found. func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block { @@ -312,6 +305,9 @@ func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) { // Config retrieves the chain's fork configuration. func (bc *BlockChain) Config() *params.ChainConfig { return bc.chainConfig } +// CacheConfig retrieves the chain's cacheConfig. +func (bc *BlockChain) CacheConfig() *CacheConfig { return bc.cacheConfig } + // Engine retrieves the blockchain's consensus engine. func (bc *BlockChain) Engine() consensus.Engine { return bc.engine } diff --git a/core/events.go b/core/events.go index cce09dffd8..398bac1ba0 100644 --- a/core/events.go +++ b/core/events.go @@ -31,10 +31,9 @@ type NewMinedBlockEvent struct{ Block *types.Block } type RemovedLogsEvent struct{ Logs []*types.Log } type ChainEvent struct { - Block *types.Block - Hash common.Hash - Logs []*types.Log - BlockResult *types.BlockResult + Block *types.Block + Hash common.Hash + Logs []*types.Log } type ChainSideEvent struct { diff --git a/core/types/l2trace.go b/core/types/l2trace.go index 05e823df29..8ecb5e65db 100644 --- a/core/types/l2trace.go +++ b/core/types/l2trace.go @@ -49,7 +49,7 @@ type StorageTrace struct { type ExecutionResult struct { Gas uint64 `json:"gas"` Failed bool `json:"failed"` - ReturnValue string `json:"returnValue,omitempty"` + ReturnValue string `json:"returnValue"` // Sender's account state (before Tx) From *AccountWrapper `json:"from,omitempty"` // Receiver's account state (before Tx) diff --git a/eth/api.go b/eth/api.go index 5dfedf943a..2e9b912460 100644 --- a/eth/api.go +++ b/eth/api.go @@ -607,21 +607,3 @@ func (api *PrivateDebugAPI) GetAccessibleState(from, to rpc.BlockNumber) (uint64 } return 0, fmt.Errorf("No state found") } - -// PublicTraceAPI provides an API to get evmTrace, mpt proof. -type PublicTraceAPI struct { - e *Ethereum -} - -// NewPublicTraceAPI creates a new Ethereum trace API. -func NewPublicTraceAPI(eth *Ethereum) *PublicTraceAPI { - return &PublicTraceAPI{eth} -} - -// GetBlockResultByHash returns the blockResult by blockHash. -func (api *PublicTraceAPI) GetBlockResultByHash(blockHash common.Hash) (*types.BlockResult, error) { - if blockResult := api.e.blockchain.GetBlockResultByHash(blockHash); blockResult != nil { - return blockResult, nil - } - return nil, fmt.Errorf("No block result found") -} diff --git a/eth/api_backend.go b/eth/api_backend.go index d6ea967f0b..cfabc499a1 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -53,6 +53,10 @@ func (b *EthAPIBackend) ChainConfig() *params.ChainConfig { return b.eth.blockchain.Config() } +func (b *EthAPIBackend) CacheConfig() *core.CacheConfig { + return b.eth.blockchain.CacheConfig() +} + func (b *EthAPIBackend) CurrentBlock() *types.Block { return b.eth.blockchain.CurrentBlock() } diff --git a/eth/backend.go b/eth/backend.go index 1a53f8710c..9b4ee254ab 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -175,8 +175,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { var ( vmConfig = vm.Config{ EnablePreimageRecording: config.EnablePreimageRecording, - Debug: true, - Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: false}), } cacheConfig = &core.CacheConfig{ TrieCleanLimit: config.TrieCleanCache, @@ -188,7 +186,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { TrieTimeLimit: config.TrieTimeout, SnapshotLimit: config.SnapshotCache, Preimages: config.Preimages, - TraceCacheLimit: config.TraceCacheLimit, MPTWitness: config.MPTWitness, } ) @@ -318,11 +315,6 @@ func (s *Ethereum) APIs() []rpc.API { Version: "1.0", Service: downloader.NewPublicDownloaderAPI(s.handler.downloader, s.eventMux), Public: true, - }, { - Namespace: "eth", - Version: "1.0", - Service: NewPublicTraceAPI(s), - Public: true, }, { Namespace: "miner", Version: "1.0", diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 84deca9122..75ec0e90ce 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -206,8 +206,7 @@ type Config struct { OverrideArrowGlacier *big.Int `toml:",omitempty"` // Trace option - TraceCacheLimit int - MPTWitness int + MPTWitness int } // CreateConsensusEngine creates a consensus engine for the given chain configuration. diff --git a/eth/filters/api.go b/eth/filters/api.go index 52dbcca9d3..66d62a86de 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -278,35 +278,6 @@ func (api *PublicFilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc return rpcSub, nil } -// NewBlockResult sends the block execution result when a new block is created. -func (api *PublicFilterAPI) NewBlockResult(ctx context.Context) (*rpc.Subscription, error) { - notifier, supported := rpc.NotifierFromContext(ctx) - if !supported { - return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported - } - - rpcSub := notifier.CreateSubscription() - - go func() { - blockResults := make(chan *types.BlockResult) - blockResultsSub := api.events.SubscribeBlockResult(blockResults) - - for { - select { - case blockResult := <-blockResults: - notifier.Notify(rpcSub.ID, blockResult) - case <-rpcSub.Err(): - blockResultsSub.Unsubscribe() - return - case <-notifier.Closed(): - blockResultsSub.Unsubscribe() - return - } - } - }() - return rpcSub, nil -} - // FilterCriteria represents a request to create a new filter. // Same as ethereum.FilterQuery but with UnmarshalJSON() method. type FilterCriteria ethereum.FilterQuery diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index 8099206ec5..6275a57bb8 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -52,8 +52,6 @@ const ( PendingTransactionsSubscription // BlocksSubscription queries hashes for blocks that are imported BlocksSubscription - // BlockResultsSubscription queries for block execution traces - BlockResultsSubscription // LastSubscription keeps track of the last index LastIndexSubscription ) @@ -71,16 +69,15 @@ const ( ) type subscription struct { - id rpc.ID - typ Type - created time.Time - logsCrit ethereum.FilterQuery - logs chan []*types.Log - hashes chan []common.Hash - headers chan *types.Header - blockResults chan *types.BlockResult - installed chan struct{} // closed when the filter is installed - err chan error // closed when the filter is uninstalled + id rpc.ID + typ Type + created time.Time + logsCrit ethereum.FilterQuery + logs chan []*types.Log + hashes chan []common.Hash + headers chan *types.Header + installed chan struct{} // closed when the filter is installed + err chan error // closed when the filter is uninstalled } // EventSystem creates subscriptions, processes events and broadcasts them to the @@ -293,19 +290,6 @@ func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscripti return es.subscribe(sub) } -// SubscribeBlockResult creates a subscription that writes the block trace when a new block is created. -func (es *EventSystem) SubscribeBlockResult(blockResult chan *types.BlockResult) *Subscription { - sub := &subscription{ - id: rpc.NewID(), - typ: BlockResultsSubscription, - created: time.Now(), - blockResults: blockResult, - installed: make(chan struct{}), - err: make(chan error), - } - return es.subscribe(sub) -} - // SubscribePendingTxs creates a subscription that writes transaction hashes for // transactions that enter the transaction pool. func (es *EventSystem) SubscribePendingTxs(hashes chan []common.Hash) *Subscription { @@ -371,11 +355,6 @@ func (es *EventSystem) handleChainEvent(filters filterIndex, ev core.ChainEvent) for _, f := range filters[BlocksSubscription] { f.headers <- ev.Block.Header() } - if ev.BlockResult != nil { - for _, f := range filters[BlockResultsSubscription] { - f.blockResults <- ev.BlockResult - } - } if es.lightMode && len(filters[LogsSubscription]) > 0 { es.lightFilterNewHead(ev.Block.Header(), func(header *types.Header, remove bool) { for _, f := range filters[LogsSubscription] { diff --git a/eth/tracers/api.go b/eth/tracers/api.go index cf209d7f72..578f103885 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -72,6 +72,7 @@ type Backend interface { GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) RPCGasCap() uint64 ChainConfig() *params.ChainConfig + CacheConfig() *core.CacheConfig Engine() consensus.Engine ChainDb() ethdb.Database // StateAtBlock returns the state corresponding to the stateroot of the block. @@ -937,5 +938,11 @@ func APIs(backend Backend) []rpc.API { Service: NewAPI(backend), Public: false, }, + { + Namespace: "scroll", + Version: "1.0", + Service: TraceBlock(NewAPI(backend)), + Public: true, + }, } } diff --git a/eth/tracers/api_blockresult.go b/eth/tracers/api_blockresult.go new file mode 100644 index 0000000000..72d0ab63a3 --- /dev/null +++ b/eth/tracers/api_blockresult.go @@ -0,0 +1,364 @@ +package tracers + +import ( + "context" + "errors" + "fmt" + "runtime" + "sync" + + "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/common/hexutil" + "github.com/scroll-tech/go-ethereum/core" + "github.com/scroll-tech/go-ethereum/core/state" + "github.com/scroll-tech/go-ethereum/core/types" + "github.com/scroll-tech/go-ethereum/core/vm" + "github.com/scroll-tech/go-ethereum/log" + "github.com/scroll-tech/go-ethereum/rpc" + "github.com/scroll-tech/go-ethereum/trie/zkproof" +) + +type TraceBlock interface { + GetBlockResultByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (trace *types.BlockResult, err error) +} + +type traceEnv struct { + config *TraceConfig + + coinbase common.Address + + // rMu lock is used to protect txs executed in parallel. + signer types.Signer + state *state.StateDB + blockCtx vm.BlockContext + + // pMu lock is used to protect Proofs' read and write mutual exclusion, + // since txs are executed in parallel, so this lock is required. + pMu sync.Mutex + // sMu is required because of txs are executed in parallel, + // this lock is used to protect StorageTrace's read and write mutual exclusion. + sMu sync.Mutex + *types.StorageTrace + executionResults []*types.ExecutionResult +} + +// GetBlockResultByNumberOrHash replays the block and returns the structured BlockResult by hash or number. +func (api *API) GetBlockResultByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (trace *types.BlockResult, err error) { + var block *types.Block + if number, ok := blockNrOrHash.Number(); ok { + block, err = api.blockByNumber(ctx, number) + } + if hash, ok := blockNrOrHash.Hash(); ok { + block, err = api.blockByHash(ctx, hash) + } + if err != nil { + return nil, err + } + if block.NumberU64() == 0 { + return nil, errors.New("genesis is not traceable") + } + if config == nil { + config = &TraceConfig{ + LogConfig: &vm.LogConfig{ + EnableMemory: false, + EnableReturnData: true, + }, + } + } else if config.Tracer != nil { + config.Tracer = nil + log.Warn("Tracer params is unsupported") + } + + // create current execution environment. + env, err := api.createTraceEnv(ctx, config, block) + if err != nil { + return nil, err + } + + return api.getBlockResult(block, env) +} + +// Make trace environment for current block. +func (api *API) createTraceEnv(ctx context.Context, config *TraceConfig, block *types.Block) (*traceEnv, error) { + parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash()) + if err != nil { + return nil, err + } + reexec := defaultTraceReexec + if config != nil && config.Reexec != nil { + reexec = *config.Reexec + } + statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, true) + if err != nil { + return nil, err + } + + // get coinbase + coinbase, err := api.backend.Engine().Author(block.Header()) + if err != nil { + return nil, err + } + + env := &traceEnv{ + config: config, + coinbase: coinbase, + signer: types.MakeSigner(api.backend.ChainConfig(), block.Number()), + state: statedb, + blockCtx: core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil), + StorageTrace: &types.StorageTrace{ + RootBefore: parent.Root(), + RootAfter: block.Root(), + Proofs: make(map[string][]hexutil.Bytes), + StorageProofs: make(map[string]map[string][]hexutil.Bytes), + }, + executionResults: make([]*types.ExecutionResult, block.Transactions().Len()), + } + + key := coinbase.String() + if _, exist := env.Proofs[key]; !exist { + proof, err := env.state.GetProof(coinbase) + if err != nil { + log.Error("Proof for coinbase not available", "coinbase", coinbase, "error", err) + // but we still mark the proofs map with nil array + } + wrappedProof := make([]hexutil.Bytes, len(proof)) + for i, bt := range proof { + wrappedProof[i] = bt + } + env.Proofs[key] = wrappedProof + } + return env, nil +} + +func (api *API) getBlockResult(block *types.Block, env *traceEnv) (*types.BlockResult, error) { + // Execute all the transaction contained within the block concurrently + var ( + txs = block.Transactions() + pend = new(sync.WaitGroup) + jobs = make(chan *txTraceTask, len(txs)) + errCh = make(chan error, 1) + ) + threads := runtime.NumCPU() + if threads > len(txs) { + threads = len(txs) + } + for th := 0; th < threads; th++ { + pend.Add(1) + go func() { + defer pend.Done() + // Fetch and execute the next transaction trace tasks + for task := range jobs { + if err := api.getTxResult(env, task.statedb, task.index, block); err != nil { + select { + case errCh <- err: + default: + } + log.Error("failed to trace tx", "txHash", txs[task.index].Hash().String()) + } + } + }() + } + + // Feed the transactions into the tracers and return + var failed error + for i, tx := range txs { + // Send the trace task over for execution + jobs <- &txTraceTask{statedb: env.state.Copy(), index: i} + + // Generate the next state snapshot fast without tracing + msg, _ := tx.AsMessage(env.signer, block.BaseFee()) + env.state.Prepare(tx.Hash(), i) + vmenv := vm.NewEVM(env.blockCtx, core.NewEVMTxContext(msg), env.state, api.backend.ChainConfig(), vm.Config{}) + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { + failed = err + break + } + // Finalize the state so any modifications are written to the trie + // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect + env.state.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) + } + close(jobs) + pend.Wait() + + // If execution failed in between, abort + select { + case err := <-errCh: + return nil, err + default: + if failed != nil { + return nil, failed + } + } + + return api.fillBlockResult(env, block) +} + +func (api *API) getTxResult(env *traceEnv, state *state.StateDB, index int, block *types.Block) error { + tx := block.Transactions()[index] + msg, _ := tx.AsMessage(env.signer, block.BaseFee()) + from, _ := types.Sender(env.signer, tx) + to := tx.To() + + txctx := &Context{ + BlockHash: block.TxHash(), + TxIndex: index, + TxHash: tx.Hash(), + } + + sender := &types.AccountWrapper{ + Address: from, + Nonce: state.GetNonce(from), + Balance: (*hexutil.Big)(state.GetBalance(from)), + CodeHash: state.GetCodeHash(from), + } + var receiver *types.AccountWrapper + if to != nil { + receiver = &types.AccountWrapper{ + Address: *to, + Nonce: state.GetNonce(*to), + Balance: (*hexutil.Big)(state.GetBalance(*to)), + CodeHash: state.GetCodeHash(*to), + } + } + + tracer := vm.NewStructLogger(env.config.LogConfig) + // Run the transaction with tracing enabled. + vmenv := vm.NewEVM(env.blockCtx, core.NewEVMTxContext(msg), state, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) + + // Call Prepare to clear out the statedb access list + state.Prepare(txctx.TxHash, txctx.TxIndex) + + // Computes the new state by applying the given message. + result, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())) + if err != nil { + return fmt.Errorf("tracing failed: %w", err) + } + // If the result contains a revert reason, return it. + returnVal := result.Return() + if len(result.Revert()) > 0 { + returnVal = result.Revert() + } + + createdAcc := tracer.CreatedAccount() + var after []*types.AccountWrapper + if to == nil { + if createdAcc == nil { + return errors.New("unexpected tx: address for created contract unavailable") + } + to = &createdAcc.Address + } + // collect affected account after tx being applied + for _, acc := range []common.Address{from, *to, env.coinbase} { + after = append(after, &types.AccountWrapper{ + Address: acc, + Nonce: state.GetNonce(acc), + Balance: (*hexutil.Big)(state.GetBalance(acc)), + CodeHash: state.GetCodeHash(acc), + }) + } + + // merge required proof data + proofAccounts := tracer.UpdatedAccounts() + for addr := range proofAccounts { + addrStr := addr.String() + + env.pMu.Lock() + _, existed := env.Proofs[addrStr] + env.pMu.Unlock() + if existed { + continue + } + proof, err := state.GetProof(addr) + if err != nil { + log.Error("Proof not available", "address", addrStr, "error", err) + // but we still mark the proofs map with nil array + } + wrappedProof := make([]hexutil.Bytes, len(proof)) + for i, bt := range proof { + wrappedProof[i] = bt + } + env.pMu.Lock() + env.Proofs[addrStr] = wrappedProof + env.pMu.Unlock() + } + + proofStorages := tracer.UpdatedStorages() + for addr, keys := range proofStorages { + for key := range keys { + addrStr := addr.String() + keyStr := key.String() + + env.sMu.Lock() + m, existed := env.StorageProofs[addrStr] + if !existed { + m = make(map[string][]hexutil.Bytes) + env.StorageProofs[addrStr] = m + } else if _, existed := m[keyStr]; existed { + env.sMu.Unlock() + continue + } + env.sMu.Unlock() + + proof, err := state.GetStorageTrieProof(addr, key) + if err != nil { + log.Error("Storage proof not available", "error", err, "address", addrStr, "key", keyStr) + // but we still mark the proofs map with nil array + } + wrappedProof := make([]hexutil.Bytes, len(proof)) + for i, bt := range proof { + wrappedProof[i] = bt + } + env.sMu.Lock() + m[keyStr] = wrappedProof + env.sMu.Unlock() + } + } + + env.executionResults[index] = &types.ExecutionResult{ + From: sender, + To: receiver, + AccountCreated: createdAcc, + AccountsAfter: after, + Gas: result.UsedGas, + Failed: result.Failed(), + ReturnValue: fmt.Sprintf("%x", returnVal), + StructLogs: vm.FormatLogs(tracer.StructLogs()), + } + + return nil +} + +// Fill blockResult content after all the txs are finished running. +func (api *API) fillBlockResult(env *traceEnv, block *types.Block) (*types.BlockResult, error) { + statedb := env.state + txs := block.Transactions() + coinbase := types.AccountWrapper{ + Address: env.coinbase, + Nonce: statedb.GetNonce(env.coinbase), + Balance: (*hexutil.Big)(statedb.GetBalance(env.coinbase)), + CodeHash: statedb.GetCodeHash(env.coinbase), + } + blockResult := &types.BlockResult{ + BlockTrace: types.NewTraceBlock(api.backend.ChainConfig(), block, &coinbase), + StorageTrace: env.StorageTrace, + ExecutionResults: env.executionResults, + } + for i, tx := range txs { + evmTrace := env.executionResults[i] + // probably a Contract Call + if len(tx.Data()) != 0 && tx.To() != nil { + evmTrace.ByteCode = hexutil.Encode(statedb.GetCode(*tx.To())) + // Get tx.to address's code hash. + codeHash := statedb.GetCodeHash(*tx.To()) + evmTrace.CodeHash = &codeHash + } else if tx.To() == nil { // Contract is created. + evmTrace.ByteCode = hexutil.Encode(tx.Data()) + } + } + + if err := zkproof.FillBlockResultForMPTWitness(zkproof.MPTWitnessType(api.backend.CacheConfig().MPTWitness), blockResult); err != nil { + log.Error("fill mpt witness fail", "error", err) + } + + return blockResult, nil +} diff --git a/eth/tracers/api_blockresult_test.go b/eth/tracers/api_blockresult_test.go new file mode 100644 index 0000000000..cbd30c87f7 --- /dev/null +++ b/eth/tracers/api_blockresult_test.go @@ -0,0 +1,216 @@ +package tracers + +import ( + "context" + "encoding/json" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/scroll-tech/go-ethereum/accounts/abi/bind" + "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/common/hexutil" + "github.com/scroll-tech/go-ethereum/core" + "github.com/scroll-tech/go-ethereum/core/types" + "github.com/scroll-tech/go-ethereum/params" + "github.com/scroll-tech/go-ethereum/rpc" +) + +// erc20MetaData contains all meta data concerning the ERC20 contract. +var erc20MetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"pauser\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"symbol\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"decimal\",\"type\":\"uint8\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"new_operator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"new_pauser\",\"type\":\"address\"}],\"name\":\"changeUser\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Sigs: map[string]string{ + "dd62ed3e": "allowance(address,address)", + "095ea7b3": "approve(address,uint256)", + "70a08231": "balanceOf(address)", + "9dc29fac": "burn(address,uint256)", + "8e50817a": "changeUser(address,address)", + "313ce567": "decimals()", + "a457c2d7": "decreaseAllowance(address,uint256)", + "39509351": "increaseAllowance(address,uint256)", + "40c10f19": "mint(address,uint256)", + "06fdde03": "name()", + "8456cb59": "pause()", + "5c975abb": "paused()", + "95d89b41": "symbol()", + "18160ddd": "totalSupply()", + "a9059cbb": "transfer(address,uint256)", + "23b872dd": "transferFrom(address,address,uint256)", + "3f4ba83a": "unpause()", + }, + Bin: "0x60806040523480156200001157600080fd5b50604051620014b2380380620014b2833981810160405260a08110156200003757600080fd5b815160208301516040808501805191519395929483019291846401000000008211156200006357600080fd5b9083019060208201858111156200007957600080fd5b82516401000000008111828201881017156200009457600080fd5b82525081516020918201929091019080838360005b83811015620000c3578181015183820152602001620000a9565b50505050905090810190601f168015620000f15780820380516001836020036101000a031916815260200191505b50604052602001805160405193929190846401000000008211156200011557600080fd5b9083019060208201858111156200012b57600080fd5b82516401000000008111828201881017156200014657600080fd5b82525081516020918201929091019080838360005b83811015620001755781810151838201526020016200015b565b50505050905090810190601f168015620001a35780820380516001836020036101000a031916815260200191505b5060405260209081015185519093508592508491620001c8916003918501906200026b565b508051620001de9060049060208401906200026b565b50506005805461ff001960ff1990911660121716905550600680546001600160a01b038088166001600160a01b0319928316179092556007805492871692909116919091179055620002308162000255565b50506005805462010000600160b01b0319163362010000021790555062000307915050565b6005805460ff191660ff92909216919091179055565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620002ae57805160ff1916838001178555620002de565b82800160010185558215620002de579182015b82811115620002de578251825591602001919060010190620002c1565b50620002ec929150620002f0565b5090565b5b80821115620002ec5760008155600101620002f1565b61119b80620003176000396000f3fe608060405234801561001057600080fd5b506004361061010b5760003560e01c80635c975abb116100a257806395d89b411161007157806395d89b41146103015780639dc29fac14610309578063a457c2d714610335578063a9059cbb14610361578063dd62ed3e1461038d5761010b565b80635c975abb1461029d57806370a08231146102a55780638456cb59146102cb5780638e50817a146102d35761010b565b8063313ce567116100de578063313ce5671461021d578063395093511461023b5780633f4ba83a1461026757806340c10f19146102715761010b565b806306fdde0314610110578063095ea7b31461018d57806318160ddd146101cd57806323b872dd146101e7575b600080fd5b6101186103bb565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561015257818101518382015260200161013a565b50505050905090810190601f16801561017f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101b9600480360360408110156101a357600080fd5b506001600160a01b038135169060200135610451565b604080519115158252519081900360200190f35b6101d561046e565b60408051918252519081900360200190f35b6101b9600480360360608110156101fd57600080fd5b506001600160a01b03813581169160208101359091169060400135610474565b6102256104fb565b6040805160ff9092168252519081900360200190f35b6101b96004803603604081101561025157600080fd5b506001600160a01b038135169060200135610504565b61026f610552565b005b61026f6004803603604081101561028757600080fd5b506001600160a01b0381351690602001356105a9565b6101b9610654565b6101d5600480360360208110156102bb57600080fd5b50356001600160a01b0316610662565b61026f61067d565b61026f600480360360408110156102e957600080fd5b506001600160a01b03813581169160200135166106d2565b610118610757565b61026f6004803603604081101561031f57600080fd5b506001600160a01b0381351690602001356107b8565b6101b96004803603604081101561034b57600080fd5b506001600160a01b03813516906020013561085f565b6101b96004803603604081101561037757600080fd5b506001600160a01b0381351690602001356108c7565b6101d5600480360360408110156103a357600080fd5b506001600160a01b03813581169160200135166108db565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156104475780601f1061041c57610100808354040283529160200191610447565b820191906000526020600020905b81548152906001019060200180831161042a57829003601f168201915b5050505050905090565b600061046561045e610906565b848461090a565b50600192915050565b60025490565b60006104818484846109f6565b6104f18461048d610906565b6104ec85604051806060016040528060288152602001611085602891396001600160a01b038a166000908152600160205260408120906104cb610906565b6001600160a01b031681526020810191909152604001600020549190610b51565b61090a565b5060019392505050565b60055460ff1690565b6000610465610511610906565b846104ec8560016000610522610906565b6001600160a01b03908116825260208083019390935260409182016000908120918c168152925290205490610be8565b6007546001600160a01b0316331461059f576040805162461bcd60e51b815260206004820152600b60248201526a1b9bdd08185b1b1bddd95960aa1b604482015290519081900360640190fd5b6105a7610c49565b565b600554610100900460ff16156105f9576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b6006546001600160a01b03163314610646576040805162461bcd60e51b815260206004820152600b60248201526a1b9bdd08185b1b1bddd95960aa1b604482015290519081900360640190fd5b6106508282610ced565b5050565b600554610100900460ff1690565b6001600160a01b031660009081526020819052604090205490565b6007546001600160a01b031633146106ca576040805162461bcd60e51b815260206004820152600b60248201526a1b9bdd08185b1b1bddd95960aa1b604482015290519081900360640190fd5b6105a7610ddd565b6005546201000090046001600160a01b03163314610726576040805162461bcd60e51b815260206004820152600c60248201526b6f6e6c7920466163746f727960a01b604482015290519081900360640190fd5b600780546001600160a01b039283166001600160a01b03199182161790915560068054939092169216919091179055565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156104475780601f1061041c57610100808354040283529160200191610447565b600554610100900460ff1615610808576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b6006546001600160a01b03163314610855576040805162461bcd60e51b815260206004820152600b60248201526a1b9bdd08185b1b1bddd95960aa1b604482015290519081900360640190fd5b6106508282610e65565b600061046561086c610906565b846104ec856040518060600160405280602581526020016111176025913960016000610896610906565b6001600160a01b03908116825260208083019390935260409182016000908120918d16815292529020549190610b51565b60006104656108d4610906565b84846109f6565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b3390565b6001600160a01b03831661094f5760405162461bcd60e51b81526004018080602001828103825260248152602001806110f36024913960400191505060405180910390fd5b6001600160a01b0382166109945760405162461bcd60e51b815260040180806020018281038252602281526020018061103d6022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b038316610a3b5760405162461bcd60e51b81526004018080602001828103825260258152602001806110ce6025913960400191505060405180910390fd5b6001600160a01b038216610a805760405162461bcd60e51b8152600401808060200182810382526023815260200180610ff86023913960400191505060405180910390fd5b610a8b838383610f61565b610ac88160405180606001604052806026815260200161105f602691396001600160a01b0386166000908152602081905260409020549190610b51565b6001600160a01b038085166000908152602081905260408082209390935590841681522054610af79082610be8565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b60008184841115610be05760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610ba5578181015183820152602001610b8d565b50505050905090810190601f168015610bd25780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600082820183811015610c42576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b600554610100900460ff16610c9c576040805162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604482015290519081900360640190fd5b6005805461ff00191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa610cd0610906565b604080516001600160a01b039092168252519081900360200190a1565b6001600160a01b038216610d48576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b610d5460008383610f61565b600254610d619082610be8565b6002556001600160a01b038216600090815260208190526040902054610d879082610be8565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b600554610100900460ff1615610e2d576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b6005805461ff0019166101001790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258610cd0610906565b6001600160a01b038216610eaa5760405162461bcd60e51b81526004018080602001828103825260218152602001806110ad6021913960400191505060405180910390fd5b610eb682600083610f61565b610ef38160405180606001604052806022815260200161101b602291396001600160a01b0385166000908152602081905260409020549190610b51565b6001600160a01b038316600090815260208190526040902055600254610f199082610fb5565b6002556040805182815290516000916001600160a01b038516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a35050565b610f6c838383610fb0565b610f74610654565b15610fb05760405162461bcd60e51b815260040180806020018281038252602a81526020018061113c602a913960400191505060405180910390fd5b505050565b6000610c4283836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250610b5156fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a206275726e20616d6f756e7420657863656564732062616c616e636545524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a206275726e2066726f6d20746865207a65726f206164647265737345524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726f45524332305061757361626c653a20746f6b656e207472616e73666572207768696c6520706175736564a2646970667358221220e96342bec8f6c2bf72815a39998973b64c3bed57770f402e9a7b7eeda0265d4c64736f6c634300060c0033", +} + +func TestAPI_GetBlockResultByNumberOrHash(t *testing.T) { + t.Parallel() + erc20Abi, err := erc20MetaData.GetAbi() + assert.NoError(t, err) + + var ( + accounts = createAccounts(5) + genesis = &core.Genesis{Alloc: core.GenesisAlloc{ + accounts[0].From: {Balance: big.NewInt(params.Ether)}, + accounts[1].From: {Balance: big.NewInt(params.Ether)}, + accounts[2].From: {Balance: big.NewInt(params.Ether)}, + accounts[3].From: {Balance: big.NewInt(params.Ether)}, + accounts[4].From: {Balance: big.NewInt(params.Ether)}, + }} + + txs = make([]*types.Transaction, 0, 6) + ) + + // deploy erc20 contract + backend := newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) { + // deploy erc20 contract + tx, err := createTx(b, accounts[0], nil, nil, common.FromHex(erc20MetaData.Bin)) + assert.NoError(t, err) + txs = append(txs, tx) + }) + parent, err := backend.BlockByNumber(context.Background(), 1) + assert.NoError(t, err) + + // mint erc20 balance + blocks, _ := core.GenerateChain(backend.chainConfig, parent, backend.engine, backend.chaindb, 1, func(i int, gen *core.BlockGen) { + // mint erc20 tokens + for _, acc := range accounts { + to := common.BigToAddress(big.NewInt(int64(i))) + data, err := erc20Abi.Pack("mint", acc.From, big.NewInt(1e18)) + assert.NoError(t, err) + tx, err := createTx(gen, acc, &to, nil, data) + assert.NoError(t, err) + txs = append(txs, tx) + } + }) + _, err = backend.chain.InsertChain(blocks) + assert.NoError(t, err) + + block, err := backend.BlockByNumber(context.Background(), 2) + assert.NoError(t, err) + + api := NewAPI(backend) + // get trace + hash := block.Hash() + blockResult, err := api.GetBlockResultByNumberOrHash(context.Background(), rpc.BlockNumberOrHash{ + BlockHash: &hash, + }, nil) + assert.NoError(t, err) + + // check chain status + checkChainAndProof(t, backend, parent, block, blockResult) + + // check txs. + checkTxs(t, block.Transactions(), blockResult.BlockTrace.Transactions) + + txTraces, err := api.TraceBlockByNumber(context.Background(), 2, nil) + assert.NoError(t, err) + + // check executionResults + checkStructLogs(t, txTraces, blockResult.ExecutionResults) + + // check coinbase + checkCoinbase(t, backend, blockResult.BlockTrace.Coinbase) +} + +func verifyProof(t *testing.T, expect [][]byte, actual []hexutil.Bytes) { + assert.Equal(t, true, actual != nil) + assert.Equal(t, len(expect), len(actual)) + for i, val := range expect { + assert.Equal(t, hexutil.Encode(val), actual[i].String()) + } +} + +func checkChainAndProof(t *testing.T, b *testBackend, parent *types.Block, block *types.Block, blockResult *types.BlockResult) { + assert.Equal(t, parent.Hash(), block.ParentHash()) + storgeTrace := blockResult.StorageTrace + assert.Equal(t, parent.Root().String(), storgeTrace.RootBefore.String()) + assert.Equal(t, block.Root().String(), storgeTrace.RootAfter.String()) + + statedb, err := b.chain.StateAt(parent.Root()) + assert.NoError(t, err) + + storageProof := blockResult.StorageTrace.StorageProofs + for _, tx := range blockResult.BlockTrace.Transactions { + for _, addr := range []common.Address{tx.From, *tx.To} { + // verify proofs + if data2, ok := storgeTrace.Proofs[addr.String()]; ok { + data1, err := statedb.GetProof(addr) + assert.NoError(t, err) + verifyProof(t, data1, data2) + } + // verify storage proofs + for addr, wrappedProof := range storageProof { + for keyStr, data2 := range wrappedProof { + data1, err := statedb.GetStorageProof(common.HexToAddress(addr), common.HexToHash(keyStr)) + assert.NoError(t, err) + verifyProof(t, data1, data2) + } + } + } + } +} + +func checkTxs(t *testing.T, expect []*types.Transaction, actual []*types.TransactionTrace) { + assert.Equal(t, len(expect), len(actual)) + for i := range expect { + eTx, aTx := expect[i], actual[i] + assert.Equal(t, eTx.Hash().String(), aTx.TxHash) + assert.Equal(t, eTx.Gas(), aTx.Gas) + } +} + +func checkStructLogs(t *testing.T, expect []*txTraceResult, actual []*types.ExecutionResult) { + // assert executionResults + assert.Equal(t, len(expect), len(actual)) + for i, val := range expect { + trace1, trace2 := val.Result.(*types.ExecutionResult), actual[i] + assert.Equal(t, trace1.Failed, trace2.Failed) + assert.Equal(t, trace1.Gas, trace2.Gas) + assert.Equal(t, trace1.ReturnValue, trace2.ReturnValue) + assert.Equal(t, len(trace1.StructLogs), len(trace2.StructLogs)) + + for i, opcode := range trace1.StructLogs { + data1, err := json.Marshal(opcode) + assert.NoError(t, err) + data2, err := json.Marshal(trace2.StructLogs[i]) + assert.NoError(t, err) + assert.Equal(t, string(data1), string(data2)) + } + } +} + +func checkCoinbase(t *testing.T, b *testBackend, wrapper *types.AccountWrapper) { + header, err := b.HeaderByNumber(context.Background(), 1) + assert.NoError(t, err) + coinbase, err := b.engine.Author(header) + assert.NoError(t, err) + assert.Equal(t, true, coinbase.String() == wrapper.Address.String()) +} + +func createAccounts(n int) (auths []*bind.TransactOpts) { + accounts := newAccounts(5) + for _, acc := range accounts { + auth, _ := bind.NewKeyedTransactorWithChainID(acc.key, big.NewInt(1)) + auth.Nonce = big.NewInt(0) + auths = append(auths, auth) + } + return +} + +func createTx(b *core.BlockGen, auth *bind.TransactOpts, to *common.Address, value *big.Int, data []byte) (*types.Transaction, error) { + nonce := auth.Nonce.Uint64() + tx := types.NewTx(&types.LegacyTx{ + Nonce: nonce, + To: to, + Value: value, + Gas: 200000, + GasPrice: b.BaseFee(), + Data: data, + }) + signedTx, err := auth.Signer(auth.From, tx) + if err == nil { + auth.Nonce = big.NewInt(int64(nonce + 1)) + b.AddTx(signedTx) + } + return signedTx, err +} diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 1cb5ad7859..fe97805907 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -130,6 +130,10 @@ func (b *testBackend) ChainConfig() *params.ChainConfig { return b.chainConfig } +func (b *testBackend) CacheConfig() *core.CacheConfig { + return b.chain.CacheConfig() +} + func (b *testBackend) Engine() consensus.Engine { return b.engine } diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 1c93412ff5..02de047686 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -325,13 +325,16 @@ func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) return ec.c.EthSubscribe(ctx, ch, "newHeads") } -// GetBlockResultByHash returns the blockResult. +// GetBlockResultByHash returns the BlockResult given the block hash. func (ec *Client) GetBlockResultByHash(ctx context.Context, blockHash common.Hash) (*types.BlockResult, error) { - var blockResult types.BlockResult - if err := ec.c.CallContext(ctx, &blockResult, "eth_getBlockResultByHash", blockHash); err != nil { - return nil, err - } - return &blockResult, nil + blockResult := &types.BlockResult{} + return blockResult, ec.c.CallContext(ctx, &blockResult, "scroll_getBlockResultByNumberOrHash", blockHash) +} + +// GetBlockResultByNumber returns the BlockResult given the block number. +func (ec *Client) GetBlockResultByNumber(ctx context.Context, number *big.Int) (*types.BlockResult, error) { + blockResult := &types.BlockResult{} + return blockResult, ec.c.CallContext(ctx, &blockResult, "scroll_getBlockResultByNumberOrHash", toBlockNumArg(number)) } // SubscribeNewBlockResult subscribes to block execution trace when a new block is created. diff --git a/internal/debug/flags.go b/internal/debug/flags.go index 6db66706a5..dc99094712 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -91,12 +91,6 @@ var ( Name: "trace", Usage: "Write execution trace to the given file", } - // NoTrace settings - traceCacheLimitFlag = cli.IntFlag{ - Name: "trace.limit", - Usage: "Handle the latest several blockResults", - Value: 32, - } // mpt witness settings mptWitnessFlag = cli.IntFlag{ Name: "trace.mptwitness", @@ -119,7 +113,6 @@ var Flags = []cli.Flag{ blockprofilerateFlag, cpuprofileFlag, traceFlag, - traceCacheLimitFlag, mptWitnessFlag, } @@ -135,14 +128,12 @@ func init() { type TraceConfig struct { TracePath string // Trace option - TraceCacheLimit int - MPTWitness int + MPTWitness int } func ConfigTrace(ctx *cli.Context) *TraceConfig { cfg := new(TraceConfig) cfg.TracePath = ctx.GlobalString(traceFlag.Name) - cfg.TraceCacheLimit = ctx.GlobalInt(traceCacheLimitFlag.Name) cfg.MPTWitness = ctx.GlobalInt(mptWitnessFlag.Name) return cfg diff --git a/les/api_backend.go b/les/api_backend.go index fa1d17f0f2..886117feb8 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -51,6 +51,10 @@ func (b *LesApiBackend) ChainConfig() *params.ChainConfig { return b.eth.chainConfig } +func (b *LesApiBackend) CacheConfig() *core.CacheConfig { + return nil +} + func (b *LesApiBackend) CurrentBlock() *types.Block { return types.NewBlockWithHeader(b.eth.BlockChain().CurrentHeader()) } diff --git a/miner/worker.go b/miner/worker.go index 8401d815d7..3140f38938 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -19,7 +19,6 @@ package miner import ( "bytes" "errors" - "fmt" "math/big" "sync" "sync/atomic" @@ -28,13 +27,11 @@ import ( mapset "github.com/deckarep/golang-set" "github.com/scroll-tech/go-ethereum/common" - "github.com/scroll-tech/go-ethereum/common/hexutil" "github.com/scroll-tech/go-ethereum/consensus" "github.com/scroll-tech/go-ethereum/consensus/misc" "github.com/scroll-tech/go-ethereum/core" "github.com/scroll-tech/go-ethereum/core/state" "github.com/scroll-tech/go-ethereum/core/types" - "github.com/scroll-tech/go-ethereum/core/vm" "github.com/scroll-tech/go-ethereum/event" "github.com/scroll-tech/go-ethereum/log" "github.com/scroll-tech/go-ethereum/params" @@ -92,22 +89,17 @@ type environment struct { tcount int // tx count in cycle gasPool *core.GasPool // available gas used to pack transactions - header *types.Header - txs []*types.Transaction - receipts []*types.Receipt - executionResults []*types.ExecutionResult - proofs map[string][]hexutil.Bytes - storageProofs map[string]map[string][]hexutil.Bytes + header *types.Header + txs []*types.Transaction + receipts []*types.Receipt } // task contains all information for consensus engine sealing and result submitting. type task struct { - receipts []*types.Receipt - executionResults []*types.ExecutionResult - storageResults *types.StorageTrace - state *state.StateDB - block *types.Block - createdAt time.Time + receipts []*types.Receipt + state *state.StateDB + block *types.Block + createdAt time.Time } const ( @@ -642,21 +634,14 @@ func (w *worker) resultLoop() { } // Different block could share same sealhash, deep copy here to prevent write-write conflict. var ( - receipts = make([]*types.Receipt, len(task.receipts)) - evmTraces = make([]*types.ExecutionResult, len(task.executionResults)) - logs []*types.Log - storageTrace = new(types.StorageTrace) + receipts = make([]*types.Receipt, len(task.receipts)) + logs []*types.Log ) for i, taskReceipt := range task.receipts { receipt := new(types.Receipt) receipts[i] = receipt *receipt = *taskReceipt - evmTrace := new(types.ExecutionResult) - evmTraces[i] = evmTrace - *evmTrace = *task.executionResults[i] - *storageTrace = *task.storageResults - // add block location fields receipt.BlockHash = hash receipt.BlockNumber = block.Number() @@ -674,7 +659,7 @@ func (w *worker) resultLoop() { logs = append(logs, receipt.Logs...) } // Commit block and state to database. - _, err := w.chain.WriteBlockWithState(block, receipts, logs, evmTraces, storageTrace, task.state, true) + _, err := w.chain.WriteBlockWithState(block, receipts, logs, task.state, true) if err != nil { log.Error("Failed writing block to chain", "err", err) continue @@ -704,37 +689,13 @@ func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error { } state.StartPrefetcher("miner") - proofs := make(map[string][]hexutil.Bytes) - // init proof with coinbase's proof - for _, coinbase := range []common.Address{w.coinbase, header.Coinbase} { - if coinbase == (common.Address{}) { - continue - } - - key := coinbase.String() - if _, exist := proofs[key]; !exist { - proof, err := state.GetProof(coinbase) - if err != nil { - log.Error("Proof for coinbase not available", "coinbase", coinbase, "error", err) - // but we still mark the proofs map with nil array - } - wrappedProof := make([]hexutil.Bytes, len(proof)) - for i, bt := range proof { - wrappedProof[i] = bt - } - proofs[key] = wrappedProof - } - } - env := &environment{ - signer: types.MakeSigner(w.chainConfig, header.Number), - state: state, - ancestors: mapset.NewSet(), - family: mapset.NewSet(), - uncles: mapset.NewSet(), - header: header, - proofs: proofs, - storageProofs: make(map[string]map[string][]hexutil.Bytes), + signer: types.MakeSigner(w.chainConfig, header.Number), + state: state, + ancestors: mapset.NewSet(), + family: mapset.NewSet(), + uncles: mapset.NewSet(), + header: header, } // when 08 is processed ancestors contain 07 (quick block) for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) { @@ -812,114 +773,14 @@ func (w *worker) updateSnapshot() { func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) { snap := w.current.state.Snapshot() - // reset tracer. - tracer := w.chain.GetVMConfig().Tracer.(*vm.StructLogger) - tracer.Reset() - // Get sender's address. - from, _ := types.Sender(w.current.signer, tx) - sender := &types.AccountWrapper{ - Address: from, - Nonce: w.current.state.GetNonce(from), - Balance: (*hexutil.Big)(w.current.state.GetBalance(from)), - CodeHash: w.current.state.GetCodeHash(from), - } - // Get receiver's address. - var receiver *types.AccountWrapper - if tx.To() != nil { - to := *tx.To() - receiver = &types.AccountWrapper{ - Address: to, - Nonce: w.current.state.GetNonce(to), - Balance: (*hexutil.Big)(w.current.state.GetBalance(to)), - CodeHash: w.current.state.GetCodeHash(to), - } - } - receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, *w.chain.GetVMConfig()) if err != nil { w.current.state.RevertToSnapshot(snap) return nil, err } - - createdAcc := tracer.CreatedAccount() - var after []*types.AccountWrapper - to := tx.To() - - if to == nil { - if createdAcc == nil { - panic("unexpected tx: address for created contract unavialable") - } - to = &createdAcc.Address - } - - // collect affected account after tx being applied - for acc := range map[common.Address]bool{from: true, (*to): true, w.coinbase: true} { - after = append(after, &types.AccountWrapper{ - Address: acc, - Nonce: w.current.state.GetNonce(acc), - Balance: (*hexutil.Big)(w.current.state.GetBalance(acc)), - CodeHash: w.current.state.GetCodeHash(acc), - }) - } - - // merge required proof data - proofAccounts := tracer.UpdatedAccounts() - for addr := range proofAccounts { - addrStr := addr.String() - if _, existed := w.current.proofs[addrStr]; !existed { - proof, err := w.current.state.GetProof(addr) - if err != nil { - log.Error("Proof not available", "address", addrStr, "error", err) - // but we still mark the proofs map with nil array - } - wrappedProof := make([]hexutil.Bytes, len(proof)) - for i, bt := range proof { - wrappedProof[i] = bt - } - w.current.proofs[addrStr] = wrappedProof - } - } - - proofStorages := tracer.UpdatedStorages() - for addr, keys := range proofStorages { - for key := range keys { - addrStr := addr.String() - m, existed := w.current.storageProofs[addrStr] - if !existed { - m = make(map[string][]hexutil.Bytes) - w.current.storageProofs[addrStr] = m - } - - keyStr := key.String() - if _, existed := m[keyStr]; !existed { - proof, err := w.current.state.GetStorageTrieProof(addr, key) - if err != nil { - log.Error("Storage proof not available", "error", err, "address", addrStr, "key", keyStr) - // but we still mark the proofs map with nil array - } - wrappedProof := make([]hexutil.Bytes, len(proof)) - for i, bt := range proof { - wrappedProof[i] = bt - } - m[keyStr] = wrappedProof - } - } - } - w.current.txs = append(w.current.txs, tx) w.current.receipts = append(w.current.receipts, receipt) - w.current.executionResults = append(w.current.executionResults, &types.ExecutionResult{ - Gas: receipt.GasUsed, - From: sender, - To: receiver, - AccountCreated: createdAcc, - AccountsAfter: after, - Failed: receipt.Status != types.ReceiptStatusSuccessful, - ReturnValue: fmt.Sprintf("%x", receipt.ReturnValue), - StructLogs: vm.FormatLogs(tracer.StructLogs()), - }) - return receipt.Logs, nil } @@ -1174,26 +1035,16 @@ func (w *worker) commit(uncles []*types.Header, interval func(), update bool, st // Deep copy receipts here to avoid interaction between different tasks. receipts := copyReceipts(w.current.receipts) s := w.current.state.Copy() - // when block is not mined by myself, we just omit - // complete storage before Finalize state (only RootAfter left unknown) - storage := &types.StorageTrace{ - RootBefore: s.GetRootHash(), - Proofs: w.current.proofs, - StorageProofs: w.current.storageProofs, - } - block, err := w.engine.FinalizeAndAssemble(w.chain, w.current.header, s, w.current.txs, uncles, receipts) if err != nil { return err } - storage.RootAfter = block.Header().Root - if w.isRunning() { if interval != nil { interval() } select { - case w.taskCh <- &task{receipts: receipts, executionResults: w.current.executionResults, storageResults: storage, state: s, block: block, createdAt: time.Now()}: + case w.taskCh <- &task{receipts: receipts, state: s, block: block, createdAt: time.Now()}: w.unconfirmed.Shift(block.NumberU64() - 1) log.Info("Commit new mining work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()), "uncles", len(uncles), "txs", w.current.tcount,