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) {
|
||||
subCfg := debug.ConfigTrace(ctx)
|
||||
cfg.TraceCacheLimit = subCfg.TraceCacheLimit
|
||||
cfg.MPTWitness = subCfg.MPTWitness
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
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")
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) CacheConfig() *core.CacheConfig {
|
||||
return b.eth.blockchain.CacheConfig()
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) CurrentBlock() *types.Block {
|
||||
return b.eth.blockchain.CurrentBlock()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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] {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
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
|
||||
}
|
||||
|
||||
func (b *testBackend) CacheConfig() *core.CacheConfig {
|
||||
return b.chain.CacheConfig()
|
||||
}
|
||||
|
||||
func (b *testBackend) Engine() consensus.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")
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
|
|
|
|||
183
miner/worker.go
183
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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue