mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 14:46:42 +00:00
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 <shenhaichen@gmail.com> * Update eth/tracers/api_blockResult.go Co-authored-by: Haichen Shen <shenhaichen@gmail.com> * Update eth/tracers/api_blockResult.go Co-authored-by: Haichen Shen <shenhaichen@gmail.com> * Update eth/tracers/api_blockResult.go Co-authored-by: Haichen Shen <shenhaichen@gmail.com> * 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 <shenhaichen@gmail.com> * Update ethclient/ethclient.go Co-authored-by: Haichen Shen <shenhaichen@gmail.com> * rm coinbase api in miner * fix comment * Update eth/tracers/api.go Co-authored-by: Haichen Shen <shenhaichen@gmail.com> * fix comment Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com> Co-authored-by: Haichen Shen <shenhaichen@gmail.com>
This commit is contained in:
parent
edbb595e66
commit
15391eeaf7
19 changed files with 680 additions and 370 deletions
|
|
@ -215,7 +215,6 @@ func dumpConfig(ctx *cli.Context) error {
|
||||||
|
|
||||||
func applyTraceConfig(ctx *cli.Context, cfg *ethconfig.Config) {
|
func applyTraceConfig(ctx *cli.Context, cfg *ethconfig.Config) {
|
||||||
subCfg := debug.ConfigTrace(ctx)
|
subCfg := debug.ConfigTrace(ctx)
|
||||||
cfg.TraceCacheLimit = subCfg.TraceCacheLimit
|
|
||||||
cfg.MPTWitness = subCfg.MPTWitness
|
cfg.MPTWitness = subCfg.MPTWitness
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ import (
|
||||||
lru "github.com/hashicorp/golang-lru"
|
lru "github.com/hashicorp/golang-lru"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"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/mclock"
|
||||||
"github.com/scroll-tech/go-ethereum/common/prque"
|
"github.com/scroll-tech/go-ethereum/common/prque"
|
||||||
"github.com/scroll-tech/go-ethereum/consensus"
|
"github.com/scroll-tech/go-ethereum/consensus"
|
||||||
|
|
@ -87,14 +86,13 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
bodyCacheLimit = 256
|
bodyCacheLimit = 256
|
||||||
blockCacheLimit = 256
|
blockCacheLimit = 256
|
||||||
receiptsCacheLimit = 32
|
receiptsCacheLimit = 32
|
||||||
txLookupCacheLimit = 1024
|
txLookupCacheLimit = 1024
|
||||||
maxFutureBlocks = 256
|
maxFutureBlocks = 256
|
||||||
maxTimeFutureBlocks = 30
|
maxTimeFutureBlocks = 30
|
||||||
TriesInMemory = 128
|
TriesInMemory = 128
|
||||||
blockResultCacheLimit = 128
|
|
||||||
|
|
||||||
// BlockChainVersion ensures that an incompatible database forces a resync from scratch.
|
// 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
|
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
|
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
|
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
|
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
|
// defaultCacheConfig are the default caching values if none are specified by the
|
||||||
// user (also used during testing).
|
// user (also used during testing).
|
||||||
var defaultCacheConfig = &CacheConfig{
|
var defaultCacheConfig = &CacheConfig{
|
||||||
TrieCleanLimit: 256,
|
TrieCleanLimit: 256,
|
||||||
TrieDirtyLimit: 256,
|
TrieDirtyLimit: 256,
|
||||||
TrieTimeLimit: 5 * time.Minute,
|
TrieTimeLimit: 5 * time.Minute,
|
||||||
SnapshotLimit: 256,
|
SnapshotLimit: 256,
|
||||||
SnapshotWait: true,
|
SnapshotWait: true,
|
||||||
TraceCacheLimit: 32,
|
MPTWitness: int(zkproof.MPTWitnessNothing),
|
||||||
MPTWitness: int(zkproof.MPTWitnessNothing),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockChain represents the canonical chain given a database with a genesis
|
// 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
|
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!)
|
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)
|
stateCache state.Database // State database to reuse between imports (contains state cache)
|
||||||
bodyCache *lru.Cache // Cache for the most recent block bodies
|
bodyCache *lru.Cache // Cache for the most recent block bodies
|
||||||
bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
|
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
|
receiptsCache *lru.Cache // Cache for the most recent receipts per block
|
||||||
blockCache *lru.Cache // Cache for the most recent entire blocks
|
blockCache *lru.Cache // Cache for the most recent entire blocks
|
||||||
txLookupCache *lru.Cache // Cache for the most recent transaction lookup data.
|
txLookupCache *lru.Cache // Cache for the most recent transaction lookup data.
|
||||||
futureBlocks *lru.Cache // future blocks are blocks added for later processing
|
futureBlocks *lru.Cache // future blocks are blocks added for later processing
|
||||||
blockResultCache *lru.Cache // Cache for the most recent block results.
|
|
||||||
|
|
||||||
wg sync.WaitGroup //
|
wg sync.WaitGroup //
|
||||||
quit chan struct{} // shutdown signal, closed in Stop.
|
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)
|
blockCache, _ := lru.New(blockCacheLimit)
|
||||||
txLookupCache, _ := lru.New(txLookupCacheLimit)
|
txLookupCache, _ := lru.New(txLookupCacheLimit)
|
||||||
futureBlocks, _ := lru.New(maxFutureBlocks)
|
futureBlocks, _ := lru.New(maxFutureBlocks)
|
||||||
blockResultCache, _ := lru.New(blockResultCacheLimit)
|
|
||||||
if cacheConfig.TraceCacheLimit != 0 {
|
|
||||||
blockResultCache, _ = lru.New(cacheConfig.TraceCacheLimit)
|
|
||||||
}
|
|
||||||
// override snapshot setting
|
// override snapshot setting
|
||||||
if chainConfig.Zktrie && cacheConfig.SnapshotLimit > 0 {
|
if chainConfig.Zktrie && cacheConfig.SnapshotLimit > 0 {
|
||||||
log.Warn("snapshot has been disabled by zktrie")
|
log.Warn("snapshot has been disabled by zktrie")
|
||||||
|
|
@ -256,18 +247,17 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
||||||
Preimages: cacheConfig.Preimages,
|
Preimages: cacheConfig.Preimages,
|
||||||
Zktrie: chainConfig.Zktrie,
|
Zktrie: chainConfig.Zktrie,
|
||||||
}),
|
}),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
chainmu: syncx.NewClosableMutex(),
|
chainmu: syncx.NewClosableMutex(),
|
||||||
shouldPreserve: shouldPreserve,
|
shouldPreserve: shouldPreserve,
|
||||||
bodyCache: bodyCache,
|
bodyCache: bodyCache,
|
||||||
bodyRLPCache: bodyRLPCache,
|
bodyRLPCache: bodyRLPCache,
|
||||||
receiptsCache: receiptsCache,
|
receiptsCache: receiptsCache,
|
||||||
blockCache: blockCache,
|
blockCache: blockCache,
|
||||||
txLookupCache: txLookupCache,
|
txLookupCache: txLookupCache,
|
||||||
futureBlocks: futureBlocks,
|
futureBlocks: futureBlocks,
|
||||||
blockResultCache: blockResultCache,
|
engine: engine,
|
||||||
engine: engine,
|
vmConfig: vmConfig,
|
||||||
vmConfig: vmConfig,
|
|
||||||
}
|
}
|
||||||
bc.validator = NewBlockValidator(chainConfig, bc, engine)
|
bc.validator = NewBlockValidator(chainConfig, bc, engine)
|
||||||
bc.prefetcher = newStatePrefetcher(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.
|
// 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() {
|
if !bc.chainmu.TryLock() {
|
||||||
return NonStatTy, errInsertionInterrupted
|
return NonStatTy, errInsertionInterrupted
|
||||||
}
|
}
|
||||||
defer bc.chainmu.Unlock()
|
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,
|
// writeBlockWithState writes the block and all associated state to the database,
|
||||||
// but is expects the chain mutex to be held.
|
// 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() {
|
if bc.insertStopped() {
|
||||||
return NonStatTy, errInsertionInterrupted
|
return NonStatTy, errInsertionInterrupted
|
||||||
}
|
}
|
||||||
|
|
@ -1333,15 +1323,8 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
||||||
}
|
}
|
||||||
bc.futureBlocks.Remove(block.Hash())
|
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 {
|
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 {
|
if len(logs) > 0 {
|
||||||
bc.logsFeed.Send(logs)
|
bc.logsFeed.Send(logs)
|
||||||
}
|
}
|
||||||
|
|
@ -1359,40 +1342,6 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
||||||
return status, nil
|
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
|
// 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
|
// accepted for future processing, and returns an error if the block is too far
|
||||||
// ahead and was not added.
|
// 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.
|
// Write the block to the chain and get the status.
|
||||||
substart = time.Now()
|
substart = time.Now()
|
||||||
// EvmTraces & StorageTrace being nil is safe because l2geth's p2p server is stoped and the code will not execute there.
|
// 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)
|
atomic.StoreUint32(&followupInterrupt, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it.index, err
|
return it.index, err
|
||||||
|
|
|
||||||
|
|
@ -158,13 +158,6 @@ func (bc *BlockChain) GetBlockByHash(hash common.Hash) *types.Block {
|
||||||
return bc.GetBlock(hash, *number)
|
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
|
// GetBlockByNumber retrieves a block from the database by number, caching it
|
||||||
// (associated with its hash) if found.
|
// (associated with its hash) if found.
|
||||||
func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block {
|
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.
|
// Config retrieves the chain's fork configuration.
|
||||||
func (bc *BlockChain) Config() *params.ChainConfig { return bc.chainConfig }
|
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.
|
// Engine retrieves the blockchain's consensus engine.
|
||||||
func (bc *BlockChain) Engine() consensus.Engine { return bc.engine }
|
func (bc *BlockChain) Engine() consensus.Engine { return bc.engine }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,9 @@ type NewMinedBlockEvent struct{ Block *types.Block }
|
||||||
type RemovedLogsEvent struct{ Logs []*types.Log }
|
type RemovedLogsEvent struct{ Logs []*types.Log }
|
||||||
|
|
||||||
type ChainEvent struct {
|
type ChainEvent struct {
|
||||||
Block *types.Block
|
Block *types.Block
|
||||||
Hash common.Hash
|
Hash common.Hash
|
||||||
Logs []*types.Log
|
Logs []*types.Log
|
||||||
BlockResult *types.BlockResult
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChainSideEvent struct {
|
type ChainSideEvent struct {
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ type StorageTrace struct {
|
||||||
type ExecutionResult struct {
|
type ExecutionResult struct {
|
||||||
Gas uint64 `json:"gas"`
|
Gas uint64 `json:"gas"`
|
||||||
Failed bool `json:"failed"`
|
Failed bool `json:"failed"`
|
||||||
ReturnValue string `json:"returnValue,omitempty"`
|
ReturnValue string `json:"returnValue"`
|
||||||
// Sender's account state (before Tx)
|
// Sender's account state (before Tx)
|
||||||
From *AccountWrapper `json:"from,omitempty"`
|
From *AccountWrapper `json:"from,omitempty"`
|
||||||
// Receiver's account state (before Tx)
|
// Receiver's account state (before Tx)
|
||||||
|
|
|
||||||
18
eth/api.go
18
eth/api.go
|
|
@ -607,21 +607,3 @@ func (api *PrivateDebugAPI) GetAccessibleState(from, to rpc.BlockNumber) (uint64
|
||||||
}
|
}
|
||||||
return 0, fmt.Errorf("No state found")
|
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")
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,10 @@ func (b *EthAPIBackend) ChainConfig() *params.ChainConfig {
|
||||||
return b.eth.blockchain.Config()
|
return b.eth.blockchain.Config()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *EthAPIBackend) CacheConfig() *core.CacheConfig {
|
||||||
|
return b.eth.blockchain.CacheConfig()
|
||||||
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) CurrentBlock() *types.Block {
|
func (b *EthAPIBackend) CurrentBlock() *types.Block {
|
||||||
return b.eth.blockchain.CurrentBlock()
|
return b.eth.blockchain.CurrentBlock()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -175,8 +175,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
var (
|
var (
|
||||||
vmConfig = vm.Config{
|
vmConfig = vm.Config{
|
||||||
EnablePreimageRecording: config.EnablePreimageRecording,
|
EnablePreimageRecording: config.EnablePreimageRecording,
|
||||||
Debug: true,
|
|
||||||
Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: false}),
|
|
||||||
}
|
}
|
||||||
cacheConfig = &core.CacheConfig{
|
cacheConfig = &core.CacheConfig{
|
||||||
TrieCleanLimit: config.TrieCleanCache,
|
TrieCleanLimit: config.TrieCleanCache,
|
||||||
|
|
@ -188,7 +186,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
TrieTimeLimit: config.TrieTimeout,
|
TrieTimeLimit: config.TrieTimeout,
|
||||||
SnapshotLimit: config.SnapshotCache,
|
SnapshotLimit: config.SnapshotCache,
|
||||||
Preimages: config.Preimages,
|
Preimages: config.Preimages,
|
||||||
TraceCacheLimit: config.TraceCacheLimit,
|
|
||||||
MPTWitness: config.MPTWitness,
|
MPTWitness: config.MPTWitness,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -318,11 +315,6 @@ func (s *Ethereum) APIs() []rpc.API {
|
||||||
Version: "1.0",
|
Version: "1.0",
|
||||||
Service: downloader.NewPublicDownloaderAPI(s.handler.downloader, s.eventMux),
|
Service: downloader.NewPublicDownloaderAPI(s.handler.downloader, s.eventMux),
|
||||||
Public: true,
|
Public: true,
|
||||||
}, {
|
|
||||||
Namespace: "eth",
|
|
||||||
Version: "1.0",
|
|
||||||
Service: NewPublicTraceAPI(s),
|
|
||||||
Public: true,
|
|
||||||
}, {
|
}, {
|
||||||
Namespace: "miner",
|
Namespace: "miner",
|
||||||
Version: "1.0",
|
Version: "1.0",
|
||||||
|
|
|
||||||
|
|
@ -206,8 +206,7 @@ type Config struct {
|
||||||
OverrideArrowGlacier *big.Int `toml:",omitempty"`
|
OverrideArrowGlacier *big.Int `toml:",omitempty"`
|
||||||
|
|
||||||
// Trace option
|
// Trace option
|
||||||
TraceCacheLimit int
|
MPTWitness int
|
||||||
MPTWitness int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateConsensusEngine creates a consensus engine for the given chain configuration.
|
// CreateConsensusEngine creates a consensus engine for the given chain configuration.
|
||||||
|
|
|
||||||
|
|
@ -278,35 +278,6 @@ func (api *PublicFilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc
|
||||||
return rpcSub, nil
|
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.
|
// FilterCriteria represents a request to create a new filter.
|
||||||
// Same as ethereum.FilterQuery but with UnmarshalJSON() method.
|
// Same as ethereum.FilterQuery but with UnmarshalJSON() method.
|
||||||
type FilterCriteria ethereum.FilterQuery
|
type FilterCriteria ethereum.FilterQuery
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,6 @@ const (
|
||||||
PendingTransactionsSubscription
|
PendingTransactionsSubscription
|
||||||
// BlocksSubscription queries hashes for blocks that are imported
|
// BlocksSubscription queries hashes for blocks that are imported
|
||||||
BlocksSubscription
|
BlocksSubscription
|
||||||
// BlockResultsSubscription queries for block execution traces
|
|
||||||
BlockResultsSubscription
|
|
||||||
// LastSubscription keeps track of the last index
|
// LastSubscription keeps track of the last index
|
||||||
LastIndexSubscription
|
LastIndexSubscription
|
||||||
)
|
)
|
||||||
|
|
@ -71,16 +69,15 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type subscription struct {
|
type subscription struct {
|
||||||
id rpc.ID
|
id rpc.ID
|
||||||
typ Type
|
typ Type
|
||||||
created time.Time
|
created time.Time
|
||||||
logsCrit ethereum.FilterQuery
|
logsCrit ethereum.FilterQuery
|
||||||
logs chan []*types.Log
|
logs chan []*types.Log
|
||||||
hashes chan []common.Hash
|
hashes chan []common.Hash
|
||||||
headers chan *types.Header
|
headers chan *types.Header
|
||||||
blockResults chan *types.BlockResult
|
installed chan struct{} // closed when the filter is installed
|
||||||
installed chan struct{} // closed when the filter is installed
|
err chan error // closed when the filter is uninstalled
|
||||||
err chan error // closed when the filter is uninstalled
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EventSystem creates subscriptions, processes events and broadcasts them to the
|
// 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)
|
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
|
// SubscribePendingTxs creates a subscription that writes transaction hashes for
|
||||||
// transactions that enter the transaction pool.
|
// transactions that enter the transaction pool.
|
||||||
func (es *EventSystem) SubscribePendingTxs(hashes chan []common.Hash) *Subscription {
|
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] {
|
for _, f := range filters[BlocksSubscription] {
|
||||||
f.headers <- ev.Block.Header()
|
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 {
|
if es.lightMode && len(filters[LogsSubscription]) > 0 {
|
||||||
es.lightFilterNewHead(ev.Block.Header(), func(header *types.Header, remove bool) {
|
es.lightFilterNewHead(ev.Block.Header(), func(header *types.Header, remove bool) {
|
||||||
for _, f := range filters[LogsSubscription] {
|
for _, f := range filters[LogsSubscription] {
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ type Backend interface {
|
||||||
GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
|
GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
|
||||||
RPCGasCap() uint64
|
RPCGasCap() uint64
|
||||||
ChainConfig() *params.ChainConfig
|
ChainConfig() *params.ChainConfig
|
||||||
|
CacheConfig() *core.CacheConfig
|
||||||
Engine() consensus.Engine
|
Engine() consensus.Engine
|
||||||
ChainDb() ethdb.Database
|
ChainDb() ethdb.Database
|
||||||
// StateAtBlock returns the state corresponding to the stateroot of the block.
|
// StateAtBlock returns the state corresponding to the stateroot of the block.
|
||||||
|
|
@ -937,5 +938,11 @@ func APIs(backend Backend) []rpc.API {
|
||||||
Service: NewAPI(backend),
|
Service: NewAPI(backend),
|
||||||
Public: false,
|
Public: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Namespace: "scroll",
|
||||||
|
Version: "1.0",
|
||||||
|
Service: TraceBlock(NewAPI(backend)),
|
||||||
|
Public: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
364
eth/tracers/api_blockresult.go
Normal file
364
eth/tracers/api_blockresult.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
216
eth/tracers/api_blockresult_test.go
Normal file
216
eth/tracers/api_blockresult_test.go
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -130,6 +130,10 @@ func (b *testBackend) ChainConfig() *params.ChainConfig {
|
||||||
return b.chainConfig
|
return b.chainConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *testBackend) CacheConfig() *core.CacheConfig {
|
||||||
|
return b.chain.CacheConfig()
|
||||||
|
}
|
||||||
|
|
||||||
func (b *testBackend) Engine() consensus.Engine {
|
func (b *testBackend) Engine() consensus.Engine {
|
||||||
return b.engine
|
return b.engine
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -325,13 +325,16 @@ func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header)
|
||||||
return ec.c.EthSubscribe(ctx, ch, "newHeads")
|
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) {
|
func (ec *Client) GetBlockResultByHash(ctx context.Context, blockHash common.Hash) (*types.BlockResult, error) {
|
||||||
var blockResult types.BlockResult
|
blockResult := &types.BlockResult{}
|
||||||
if err := ec.c.CallContext(ctx, &blockResult, "eth_getBlockResultByHash", blockHash); err != nil {
|
return blockResult, ec.c.CallContext(ctx, &blockResult, "scroll_getBlockResultByNumberOrHash", blockHash)
|
||||||
return nil, err
|
}
|
||||||
}
|
|
||||||
return &blockResult, nil
|
// 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.
|
// SubscribeNewBlockResult subscribes to block execution trace when a new block is created.
|
||||||
|
|
|
||||||
|
|
@ -91,12 +91,6 @@ var (
|
||||||
Name: "trace",
|
Name: "trace",
|
||||||
Usage: "Write execution trace to the given file",
|
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
|
// mpt witness settings
|
||||||
mptWitnessFlag = cli.IntFlag{
|
mptWitnessFlag = cli.IntFlag{
|
||||||
Name: "trace.mptwitness",
|
Name: "trace.mptwitness",
|
||||||
|
|
@ -119,7 +113,6 @@ var Flags = []cli.Flag{
|
||||||
blockprofilerateFlag,
|
blockprofilerateFlag,
|
||||||
cpuprofileFlag,
|
cpuprofileFlag,
|
||||||
traceFlag,
|
traceFlag,
|
||||||
traceCacheLimitFlag,
|
|
||||||
mptWitnessFlag,
|
mptWitnessFlag,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,14 +128,12 @@ func init() {
|
||||||
type TraceConfig struct {
|
type TraceConfig struct {
|
||||||
TracePath string
|
TracePath string
|
||||||
// Trace option
|
// Trace option
|
||||||
TraceCacheLimit int
|
MPTWitness int
|
||||||
MPTWitness int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConfigTrace(ctx *cli.Context) *TraceConfig {
|
func ConfigTrace(ctx *cli.Context) *TraceConfig {
|
||||||
cfg := new(TraceConfig)
|
cfg := new(TraceConfig)
|
||||||
cfg.TracePath = ctx.GlobalString(traceFlag.Name)
|
cfg.TracePath = ctx.GlobalString(traceFlag.Name)
|
||||||
cfg.TraceCacheLimit = ctx.GlobalInt(traceCacheLimitFlag.Name)
|
|
||||||
cfg.MPTWitness = ctx.GlobalInt(mptWitnessFlag.Name)
|
cfg.MPTWitness = ctx.GlobalInt(mptWitnessFlag.Name)
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,10 @@ func (b *LesApiBackend) ChainConfig() *params.ChainConfig {
|
||||||
return b.eth.chainConfig
|
return b.eth.chainConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *LesApiBackend) CacheConfig() *core.CacheConfig {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (b *LesApiBackend) CurrentBlock() *types.Block {
|
func (b *LesApiBackend) CurrentBlock() *types.Block {
|
||||||
return types.NewBlockWithHeader(b.eth.BlockChain().CurrentHeader())
|
return types.NewBlockWithHeader(b.eth.BlockChain().CurrentHeader())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
183
miner/worker.go
183
miner/worker.go
|
|
@ -19,7 +19,6 @@ package miner
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
@ -28,13 +27,11 @@ import (
|
||||||
mapset "github.com/deckarep/golang-set"
|
mapset "github.com/deckarep/golang-set"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"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"
|
||||||
"github.com/scroll-tech/go-ethereum/consensus/misc"
|
"github.com/scroll-tech/go-ethereum/consensus/misc"
|
||||||
"github.com/scroll-tech/go-ethereum/core"
|
"github.com/scroll-tech/go-ethereum/core"
|
||||||
"github.com/scroll-tech/go-ethereum/core/state"
|
"github.com/scroll-tech/go-ethereum/core/state"
|
||||||
"github.com/scroll-tech/go-ethereum/core/types"
|
"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/event"
|
||||||
"github.com/scroll-tech/go-ethereum/log"
|
"github.com/scroll-tech/go-ethereum/log"
|
||||||
"github.com/scroll-tech/go-ethereum/params"
|
"github.com/scroll-tech/go-ethereum/params"
|
||||||
|
|
@ -92,22 +89,17 @@ type environment struct {
|
||||||
tcount int // tx count in cycle
|
tcount int // tx count in cycle
|
||||||
gasPool *core.GasPool // available gas used to pack transactions
|
gasPool *core.GasPool // available gas used to pack transactions
|
||||||
|
|
||||||
header *types.Header
|
header *types.Header
|
||||||
txs []*types.Transaction
|
txs []*types.Transaction
|
||||||
receipts []*types.Receipt
|
receipts []*types.Receipt
|
||||||
executionResults []*types.ExecutionResult
|
|
||||||
proofs map[string][]hexutil.Bytes
|
|
||||||
storageProofs map[string]map[string][]hexutil.Bytes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// task contains all information for consensus engine sealing and result submitting.
|
// task contains all information for consensus engine sealing and result submitting.
|
||||||
type task struct {
|
type task struct {
|
||||||
receipts []*types.Receipt
|
receipts []*types.Receipt
|
||||||
executionResults []*types.ExecutionResult
|
state *state.StateDB
|
||||||
storageResults *types.StorageTrace
|
block *types.Block
|
||||||
state *state.StateDB
|
createdAt time.Time
|
||||||
block *types.Block
|
|
||||||
createdAt time.Time
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -642,21 +634,14 @@ func (w *worker) resultLoop() {
|
||||||
}
|
}
|
||||||
// Different block could share same sealhash, deep copy here to prevent write-write conflict.
|
// Different block could share same sealhash, deep copy here to prevent write-write conflict.
|
||||||
var (
|
var (
|
||||||
receipts = make([]*types.Receipt, len(task.receipts))
|
receipts = make([]*types.Receipt, len(task.receipts))
|
||||||
evmTraces = make([]*types.ExecutionResult, len(task.executionResults))
|
logs []*types.Log
|
||||||
logs []*types.Log
|
|
||||||
storageTrace = new(types.StorageTrace)
|
|
||||||
)
|
)
|
||||||
for i, taskReceipt := range task.receipts {
|
for i, taskReceipt := range task.receipts {
|
||||||
receipt := new(types.Receipt)
|
receipt := new(types.Receipt)
|
||||||
receipts[i] = receipt
|
receipts[i] = receipt
|
||||||
*receipt = *taskReceipt
|
*receipt = *taskReceipt
|
||||||
|
|
||||||
evmTrace := new(types.ExecutionResult)
|
|
||||||
evmTraces[i] = evmTrace
|
|
||||||
*evmTrace = *task.executionResults[i]
|
|
||||||
*storageTrace = *task.storageResults
|
|
||||||
|
|
||||||
// add block location fields
|
// add block location fields
|
||||||
receipt.BlockHash = hash
|
receipt.BlockHash = hash
|
||||||
receipt.BlockNumber = block.Number()
|
receipt.BlockNumber = block.Number()
|
||||||
|
|
@ -674,7 +659,7 @@ func (w *worker) resultLoop() {
|
||||||
logs = append(logs, receipt.Logs...)
|
logs = append(logs, receipt.Logs...)
|
||||||
}
|
}
|
||||||
// Commit block and state to database.
|
// 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 {
|
if err != nil {
|
||||||
log.Error("Failed writing block to chain", "err", err)
|
log.Error("Failed writing block to chain", "err", err)
|
||||||
continue
|
continue
|
||||||
|
|
@ -704,37 +689,13 @@ func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error {
|
||||||
}
|
}
|
||||||
state.StartPrefetcher("miner")
|
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{
|
env := &environment{
|
||||||
signer: types.MakeSigner(w.chainConfig, header.Number),
|
signer: types.MakeSigner(w.chainConfig, header.Number),
|
||||||
state: state,
|
state: state,
|
||||||
ancestors: mapset.NewSet(),
|
ancestors: mapset.NewSet(),
|
||||||
family: mapset.NewSet(),
|
family: mapset.NewSet(),
|
||||||
uncles: mapset.NewSet(),
|
uncles: mapset.NewSet(),
|
||||||
header: header,
|
header: header,
|
||||||
proofs: proofs,
|
|
||||||
storageProofs: make(map[string]map[string][]hexutil.Bytes),
|
|
||||||
}
|
}
|
||||||
// when 08 is processed ancestors contain 07 (quick block)
|
// when 08 is processed ancestors contain 07 (quick block)
|
||||||
for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) {
|
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) {
|
func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) {
|
||||||
snap := w.current.state.Snapshot()
|
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())
|
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 {
|
if err != nil {
|
||||||
w.current.state.RevertToSnapshot(snap)
|
w.current.state.RevertToSnapshot(snap)
|
||||||
return nil, err
|
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.txs = append(w.current.txs, tx)
|
||||||
w.current.receipts = append(w.current.receipts, receipt)
|
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
|
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.
|
// Deep copy receipts here to avoid interaction between different tasks.
|
||||||
receipts := copyReceipts(w.current.receipts)
|
receipts := copyReceipts(w.current.receipts)
|
||||||
s := w.current.state.Copy()
|
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)
|
block, err := w.engine.FinalizeAndAssemble(w.chain, w.current.header, s, w.current.txs, uncles, receipts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
storage.RootAfter = block.Header().Root
|
|
||||||
|
|
||||||
if w.isRunning() {
|
if w.isRunning() {
|
||||||
if interval != nil {
|
if interval != nil {
|
||||||
interval()
|
interval()
|
||||||
}
|
}
|
||||||
select {
|
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)
|
w.unconfirmed.Shift(block.NumberU64() - 1)
|
||||||
log.Info("Commit new mining work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()),
|
log.Info("Commit new mining work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()),
|
||||||
"uncles", len(uncles), "txs", w.current.tcount,
|
"uncles", len(uncles), "txs", w.current.tcount,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue