Skip to content

Commit

Permalink
feat: add replay blockResult API (ethereum#139)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
3 people authored Sep 20, 2022
1 parent edbb595 commit 15391ee
Show file tree
Hide file tree
Showing 19 changed files with 680 additions and 370 deletions.
1 change: 0 additions & 1 deletion cmd/geth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
125 changes: 37 additions & 88 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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.
//
Expand Down Expand Up @@ -134,22 +132,20 @@ 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
}

// 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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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")
Expand All @@ -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)
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
}
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down
10 changes: 3 additions & 7 deletions core/blockchain_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 }

Expand Down
7 changes: 3 additions & 4 deletions core/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion core/types/l2trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
18 changes: 0 additions & 18 deletions eth/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
4 changes: 4 additions & 0 deletions eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
8 changes: 0 additions & 8 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
}
)
Expand Down Expand Up @@ -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",
Expand Down
3 changes: 1 addition & 2 deletions eth/ethconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
29 changes: 0 additions & 29 deletions eth/filters/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 15391ee

Please sign in to comment.