mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 14:46:42 +00:00
Extract execution trace from EVM execution and provide subscription API (#19)
* Create go.yml * Merge from zkrollup and fix conflict * go mod tidy * fix worker_test test case * fix worker_test test case * Delete go.yml * add log content, enable memory trace * add tracer pool handler * Add comments and format code * add evmTrace subscribe api * Move the evmTrace struct. * Fix miner bug. * upgrade evmTrace api * fix bug about evmTracesByHash api * Fix the bug about block.timestamp and remove unnecessary copy. * Update eth/filters/api.go Co-authored-by: Haichen Shen <shenhaichen@gmail.com> * Upgrade comments. * Delete useless code in test file * Update miner/worker.go Co-authored-by: Haichen Shen <shenhaichen@gmail.com> * Change the return result to BlockResult. * Change return type. * Change blockResult to blockResults. * Add ReturnValue. * Update core/rawdb/l2trace.go Co-authored-by: Haichen Shen <shenhaichen@gmail.com> * Update core/rawdb/l2trace.go Co-authored-by: Haichen Shen <shenhaichen@gmail.com> * Update core/types/l2trace.go Co-authored-by: Haichen Shen <shenhaichen@gmail.com> * Add indent to the comment and rm json encoding flag. * Rm json encoding flag. * Update core/rawdb/l2trace.go Co-authored-by: Haichen Shen <shenhaichen@gmail.com> * Rm json encoding flag. * Update ethclient/ethclient.go Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com> * Update eth/filters/api.go Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com> * Use as the blockResult prefix flag. * Update eth/filters/filter_system.go Co-authored-by: Haichen Shen <shenhaichen@gmail.com> * Update eth/filters/filter_system.go Co-authored-by: Haichen Shen <shenhaichen@gmail.com> * Update ethclient/ethclient.go Co-authored-by: Haichen Shen <shenhaichen@gmail.com> * Update eth/filters/api.go Co-authored-by: Haichen Shen <shenhaichen@gmail.com> Co-authored-by: Haichen Shen <shenhaichen@gmail.com> Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
This commit is contained in:
parent
b4d60884d2
commit
7745fd5840
21 changed files with 770 additions and 108 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -47,3 +47,4 @@ profile.cov
|
|||
/dashboard/assets/package-lock.json
|
||||
|
||||
**/yarn-error.log
|
||||
|
||||
|
|
|
|||
|
|
@ -1182,17 +1182,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, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
|
||||
func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, blockResult *types.BlockResult, 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, state, emitHeadEvent)
|
||||
return bc.writeBlockWithState(block, receipts, logs, blockResult, 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, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
|
||||
func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, blockResult *types.BlockResult, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
|
||||
if bc.insertStopped() {
|
||||
return NonStatTy, errInsertionInterrupted
|
||||
}
|
||||
|
|
@ -1215,6 +1215,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
rawdb.WriteTd(blockBatch, block.Hash(), block.NumberU64(), externTd)
|
||||
rawdb.WriteBlock(blockBatch, block)
|
||||
rawdb.WriteReceipts(blockBatch, block.Hash(), block.NumberU64(), receipts)
|
||||
rawdb.WriteBlockResult(blockBatch, block.Hash(), blockResult)
|
||||
rawdb.WritePreimages(blockBatch, state.Preimages())
|
||||
if err := blockBatch.Write(); err != nil {
|
||||
log.Crit("Failed to write block into disk", "err", err)
|
||||
|
|
@ -1314,7 +1315,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
bc.futureBlocks.Remove(block.Hash())
|
||||
|
||||
if status == CanonStatTy {
|
||||
bc.chainFeed.Send(ChainEvent{Block: block, Hash: block.Hash(), Logs: logs})
|
||||
bc.chainFeed.Send(ChainEvent{Block: block, Hash: block.Hash(), Logs: logs, BlockResult: blockResult})
|
||||
if len(logs) > 0 {
|
||||
bc.logsFeed.Send(logs)
|
||||
}
|
||||
|
|
@ -1644,7 +1645,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
|
|||
|
||||
// Write the block to the chain and get the status.
|
||||
substart = time.Now()
|
||||
status, err := bc.writeBlockWithState(block, receipts, logs, statedb, false)
|
||||
// EvmTraces is 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, statedb, false)
|
||||
atomic.StoreUint32(&followupInterrupt, 1)
|
||||
if err != nil {
|
||||
return it.index, err
|
||||
|
|
|
|||
|
|
@ -31,9 +31,10 @@ type NewMinedBlockEvent struct{ Block *types.Block }
|
|||
type RemovedLogsEvent struct{ Logs []*types.Log }
|
||||
|
||||
type ChainEvent struct {
|
||||
Block *types.Block
|
||||
Hash common.Hash
|
||||
Logs []*types.Log
|
||||
Block *types.Block
|
||||
Hash common.Hash
|
||||
Logs []*types.Log
|
||||
BlockResult *types.BlockResult
|
||||
}
|
||||
|
||||
type ChainSideEvent struct {
|
||||
|
|
|
|||
39
core/rawdb/l2trace.go
Normal file
39
core/rawdb/l2trace.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package rawdb
|
||||
|
||||
import (
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/scroll-tech/go-ethereum/ethdb"
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
"github.com/scroll-tech/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// ReadBlockResult retrieves all data required by roller.
|
||||
func ReadBlockResult(db ethdb.Reader, hash common.Hash) *types.BlockResult {
|
||||
data, _ := db.Get(blockResultKey(hash))
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
var blockResult types.BlockResult
|
||||
if err := rlp.DecodeBytes(data, &blockResult); err != nil {
|
||||
log.Error("Failed to decode BlockResult", "err", err)
|
||||
return nil
|
||||
}
|
||||
return &blockResult
|
||||
}
|
||||
|
||||
// WriteBlockResult stores blockResult into leveldb.
|
||||
func WriteBlockResult(db ethdb.KeyValueWriter, hash common.Hash, blockResult *types.BlockResult) {
|
||||
bytes, err := rlp.EncodeToBytes(blockResult)
|
||||
if err != nil {
|
||||
log.Crit("Failed to RLP encode BlockResult", "err", err)
|
||||
}
|
||||
db.Put(blockResultKey(hash), bytes)
|
||||
}
|
||||
|
||||
// DeleteBlockResult removes blockResult with a block hash.
|
||||
func DeleteBlockResult(db ethdb.KeyValueWriter, hash common.Hash) {
|
||||
if err := db.Delete(blockResultKey(hash)); err != nil {
|
||||
log.Crit("Failed to delete BlockResult", "err", err)
|
||||
}
|
||||
}
|
||||
355
core/rawdb/l2trace_test.go
Normal file
355
core/rawdb/l2trace_test.go
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
package rawdb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/scroll-tech/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
func TestBlockEvmTracesStorage(t *testing.T) {
|
||||
db := NewMemoryDatabase()
|
||||
|
||||
data1 := []byte(`{
|
||||
"gas": 1,
|
||||
"failed": false,
|
||||
"returnValue": "",
|
||||
"structLogs": [
|
||||
{
|
||||
"pc": 0,
|
||||
"op": "PUSH1",
|
||||
"gas": 1000000,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [],
|
||||
"memory": []
|
||||
},
|
||||
{
|
||||
"pc": 2,
|
||||
"op": "SLOAD",
|
||||
"gas": 999997,
|
||||
"gasCost": 2100,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x1"
|
||||
],
|
||||
"memory": [],
|
||||
"storage": {
|
||||
"0000000000000000000000000000000000000000000000000000000000000001": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pc": 3,
|
||||
"op": "POP",
|
||||
"gas": 997897,
|
||||
"gasCost": 2,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x0"
|
||||
],
|
||||
"memory": []
|
||||
},
|
||||
{
|
||||
"pc": 4,
|
||||
"op": "PUSH1",
|
||||
"gas": 997895,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [],
|
||||
"memory": []
|
||||
},
|
||||
{
|
||||
"pc": 6,
|
||||
"op": "PUSH1",
|
||||
"gas": 997892,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x11"
|
||||
],
|
||||
"memory": []
|
||||
},
|
||||
{
|
||||
"pc": 8,
|
||||
"op": "SSTORE",
|
||||
"gas": 997889,
|
||||
"gasCost": 20000,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x11",
|
||||
"0x1"
|
||||
],
|
||||
"memory": [],
|
||||
"storage": {
|
||||
"0000000000000000000000000000000000000000000000000000000000000001": "0000000000000000000000000000000000000000000000000000000000000011"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pc": 9,
|
||||
"op": "PUSH1",
|
||||
"gas": 977889,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [],
|
||||
"memory": []
|
||||
},
|
||||
{
|
||||
"pc": 11,
|
||||
"op": "PUSH1",
|
||||
"gas": 977886,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x11"
|
||||
],
|
||||
"memory": []
|
||||
},
|
||||
{
|
||||
"pc": 13,
|
||||
"op": "SSTORE",
|
||||
"gas": 977883,
|
||||
"gasCost": 22100,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x11",
|
||||
"0x2"
|
||||
],
|
||||
"memory": [],
|
||||
"storage": {
|
||||
"0000000000000000000000000000000000000000000000000000000000000001": "0000000000000000000000000000000000000000000000000000000000000011",
|
||||
"0000000000000000000000000000000000000000000000000000000000000002": "0000000000000000000000000000000000000000000000000000000000000011"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pc": 14,
|
||||
"op": "PUSH1",
|
||||
"gas": 955783,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [],
|
||||
"memory": []
|
||||
},
|
||||
{
|
||||
"pc": 16,
|
||||
"op": "PUSH1",
|
||||
"gas": 955780,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x11"
|
||||
],
|
||||
"memory": []
|
||||
},
|
||||
{
|
||||
"pc": 18,
|
||||
"op": "SSTORE",
|
||||
"gas": 955777,
|
||||
"gasCost": 100,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x11",
|
||||
"0x2"
|
||||
],
|
||||
"memory": [],
|
||||
"storage": {
|
||||
"0000000000000000000000000000000000000000000000000000000000000001": "0000000000000000000000000000000000000000000000000000000000000011",
|
||||
"0000000000000000000000000000000000000000000000000000000000000002": "0000000000000000000000000000000000000000000000000000000000000011"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pc": 19,
|
||||
"op": "PUSH1",
|
||||
"gas": 955677,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [],
|
||||
"memory": []
|
||||
},
|
||||
{
|
||||
"pc": 21,
|
||||
"op": "SLOAD",
|
||||
"gas": 955674,
|
||||
"gasCost": 100,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x2"
|
||||
],
|
||||
"memory": [],
|
||||
"storage": {
|
||||
"0000000000000000000000000000000000000000000000000000000000000001": "0000000000000000000000000000000000000000000000000000000000000011",
|
||||
"0000000000000000000000000000000000000000000000000000000000000002": "0000000000000000000000000000000000000000000000000000000000000011"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pc": 22,
|
||||
"op": "PUSH1",
|
||||
"gas": 955574,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x11"
|
||||
],
|
||||
"memory": []
|
||||
},
|
||||
{
|
||||
"pc": 24,
|
||||
"op": "SLOAD",
|
||||
"gas": 955571,
|
||||
"gasCost": 100,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x11",
|
||||
"0x1"
|
||||
],
|
||||
"memory": [],
|
||||
"storage": {
|
||||
"0000000000000000000000000000000000000000000000000000000000000001": "0000000000000000000000000000000000000000000000000000000000000011",
|
||||
"0000000000000000000000000000000000000000000000000000000000000002": "0000000000000000000000000000000000000000000000000000000000000011"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pc": 25,
|
||||
"op": "STOP",
|
||||
"gas": 955471,
|
||||
"gasCost": 0,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x11",
|
||||
"0x11"
|
||||
],
|
||||
"memory": []
|
||||
}
|
||||
]
|
||||
}`)
|
||||
evmTrace1 := &types.ExecutionResult{ReturnValue: "0xaaa"}
|
||||
if err := json.Unmarshal(data1, evmTrace1); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
data2 := []byte(`{
|
||||
"gas": 1,
|
||||
"failed": false,
|
||||
"returnValue": "000000000000000000000000000000000000000000000000000000000000000a",
|
||||
"structLogs": [
|
||||
{
|
||||
"pc": 0,
|
||||
"op": "PUSH1",
|
||||
"gas": 1000000,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [],
|
||||
"memory": []
|
||||
},
|
||||
{
|
||||
"pc": 2,
|
||||
"op": "PUSH1",
|
||||
"gas": 999997,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0xa"
|
||||
],
|
||||
"memory": []
|
||||
},
|
||||
{
|
||||
"pc": 4,
|
||||
"op": "MSTORE",
|
||||
"gas": 999994,
|
||||
"gasCost": 6,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0xa",
|
||||
"0x0"
|
||||
],
|
||||
"memory": [
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 5,
|
||||
"op": "PUSH1",
|
||||
"gas": 999988,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [],
|
||||
"memory": [
|
||||
"000000000000000000000000000000000000000000000000000000000000000a"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 7,
|
||||
"op": "PUSH1",
|
||||
"gas": 999985,
|
||||
"gasCost": 3,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x20"
|
||||
],
|
||||
"memory": [
|
||||
"000000000000000000000000000000000000000000000000000000000000000a"
|
||||
]
|
||||
},
|
||||
{
|
||||
"pc": 9,
|
||||
"op": "RETURN",
|
||||
"gas": 999982,
|
||||
"gasCost": 0,
|
||||
"depth": 1,
|
||||
"stack": [
|
||||
"0x20",
|
||||
"0x0"
|
||||
],
|
||||
"memory": [
|
||||
"000000000000000000000000000000000000000000000000000000000000000a"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`)
|
||||
evmTrace2 := &types.ExecutionResult{ReturnValue: "0xbbb"}
|
||||
if err := json.Unmarshal(data2, evmTrace2); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
evmTraces := []*types.ExecutionResult{evmTrace1, evmTrace2}
|
||||
hash := common.BytesToHash([]byte{0x03, 0x04})
|
||||
// Insert the blockResult into the database and check presence.
|
||||
WriteBlockResult(db, hash, &types.BlockResult{ExecutionResults: evmTraces})
|
||||
// Read blockResult from db.
|
||||
if blockResult := ReadBlockResult(db, hash); len(blockResult.ExecutionResults) == 0 {
|
||||
t.Fatalf("No evmTraces returned")
|
||||
} else {
|
||||
if err := checkEvmTracesRLP(blockResult.ExecutionResults, evmTraces); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
}
|
||||
// Delete blockResult by blockHash.
|
||||
DeleteBlockResult(db, hash)
|
||||
if blockResult := ReadBlockResult(db, hash); blockResult != nil && len(blockResult.ExecutionResults) != 0 {
|
||||
t.Fatalf("The evmTrace list should be empty.")
|
||||
}
|
||||
}
|
||||
|
||||
func checkEvmTracesRLP(have, want []*types.ExecutionResult) error {
|
||||
if len(have) != len(want) {
|
||||
return fmt.Errorf("evmTraces sizes mismatch: have: %d, want: %d", len(have), len(want))
|
||||
}
|
||||
for i := 0; i < len(want); i++ {
|
||||
rlpHave, err := rlp.EncodeToBytes(have[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rlpWant, err := rlp.EncodeToBytes(want[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !bytes.Equal(rlpHave, rlpWant) {
|
||||
return fmt.Errorf("evmTrace #%d: evmTrace mismatch: have %s, want %s", i, hex.EncodeToString(rlpHave), hex.EncodeToString(rlpWant))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -89,6 +89,7 @@ var (
|
|||
SnapshotAccountPrefix = []byte("a") // SnapshotAccountPrefix + account hash -> account trie value
|
||||
SnapshotStoragePrefix = []byte("o") // SnapshotStoragePrefix + account hash + storage hash -> storage trie value
|
||||
CodePrefix = []byte("c") // CodePrefix + code hash -> account code
|
||||
blockResultPrefix = []byte("T") // blockResultPrefix + hash -> blockResult
|
||||
|
||||
PreimagePrefix = []byte("secure-key-") // PreimagePrefix + hash -> preimage
|
||||
configPrefix = []byte("ethereum-config-") // config prefix for the db
|
||||
|
|
@ -230,3 +231,8 @@ func IsCodeKey(key []byte) (bool, []byte) {
|
|||
func configKey(hash common.Hash) []byte {
|
||||
return append(configPrefix, hash.Bytes()...)
|
||||
}
|
||||
|
||||
// blockResultKey = blockResultPrefix + hash
|
||||
func blockResultKey(hash common.Hash) []byte {
|
||||
return append(blockResultPrefix, hash.Bytes()...)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,9 +112,14 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon
|
|||
}
|
||||
*usedGas += result.UsedGas
|
||||
|
||||
// If the result contains a revert reason, return it.
|
||||
returnVal := result.Return()
|
||||
if len(result.Revert()) > 0 {
|
||||
returnVal = result.Revert()
|
||||
}
|
||||
// Create a new receipt for the transaction, storing the intermediate root and gas used
|
||||
// by the tx.
|
||||
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas}
|
||||
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas, ReturnValue: returnVal}
|
||||
if result.Failed() {
|
||||
receipt.Status = types.ReceiptStatusFailed
|
||||
} else {
|
||||
|
|
|
|||
165
core/types/l2trace.go
Normal file
165
core/types/l2trace.go
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// BlockResult contains block execution traces and results required for rollers.
|
||||
type BlockResult struct {
|
||||
ExecutionResults []*ExecutionResult `json:"executionResults"`
|
||||
}
|
||||
|
||||
type rlpBlockResult struct {
|
||||
ExecutionResults []*ExecutionResult
|
||||
}
|
||||
|
||||
func (b *BlockResult) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, &rlpBlockResult{
|
||||
ExecutionResults: b.ExecutionResults,
|
||||
})
|
||||
}
|
||||
|
||||
func (b *BlockResult) DecodeRLP(s *rlp.Stream) error {
|
||||
var dec rlpBlockResult
|
||||
err := s.Decode(&dec)
|
||||
if err == nil {
|
||||
b.ExecutionResults = dec.ExecutionResults
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecutionResult groups all structured logs emitted by the EVM
|
||||
// while replaying a transaction in debug mode as well as transaction
|
||||
// execution status, the amount of gas used and the return value
|
||||
type ExecutionResult struct {
|
||||
Gas uint64 `json:"gas"`
|
||||
Failed bool `json:"failed"`
|
||||
ReturnValue string `json:"returnValue,omitempty"`
|
||||
StructLogs []StructLogRes `json:"structLogs"`
|
||||
}
|
||||
|
||||
type rlpExecutionResult struct {
|
||||
Gas uint64
|
||||
Failed bool
|
||||
ReturnValue string
|
||||
StructLogs []StructLogRes
|
||||
}
|
||||
|
||||
func (e *ExecutionResult) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, rlpExecutionResult{
|
||||
Gas: e.Gas,
|
||||
Failed: e.Failed,
|
||||
ReturnValue: e.ReturnValue,
|
||||
StructLogs: e.StructLogs,
|
||||
})
|
||||
}
|
||||
|
||||
func (e *ExecutionResult) DecodeRLP(s *rlp.Stream) error {
|
||||
var dec rlpExecutionResult
|
||||
err := s.Decode(&dec)
|
||||
if err == nil {
|
||||
e.Gas, e.Failed, e.ReturnValue, e.StructLogs = dec.Gas, dec.Failed, dec.ReturnValue, dec.StructLogs
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// StructLogRes stores a structured log emitted by the EVM while replaying a
|
||||
// transaction in debug mode
|
||||
type StructLogRes struct {
|
||||
Pc uint64 `json:"pc"`
|
||||
Op string `json:"op"`
|
||||
Gas uint64 `json:"gas"`
|
||||
GasCost uint64 `json:"gasCost"`
|
||||
Depth int `json:"depth"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Stack *[]string `json:"stack,omitempty"`
|
||||
Memory *[]string `json:"memory,omitempty"`
|
||||
Storage *map[string]string `json:"storage,omitempty"`
|
||||
}
|
||||
|
||||
type rlpStructLogRes struct {
|
||||
Pc uint64
|
||||
Op string
|
||||
Gas uint64
|
||||
GasCost uint64
|
||||
Depth uint
|
||||
Error string
|
||||
Stack []string
|
||||
Memory []string
|
||||
Storage []string
|
||||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
func (r *StructLogRes) EncodeRLP(w io.Writer) error {
|
||||
data := rlpStructLogRes{
|
||||
Pc: r.Pc,
|
||||
Op: r.Op,
|
||||
Gas: r.Gas,
|
||||
GasCost: r.GasCost,
|
||||
Depth: uint(r.Depth),
|
||||
Error: r.Error,
|
||||
}
|
||||
if r.Stack != nil {
|
||||
data.Stack = make([]string, len(*r.Stack))
|
||||
for i, val := range *r.Stack {
|
||||
data.Stack[i] = val
|
||||
}
|
||||
}
|
||||
if r.Memory != nil {
|
||||
data.Memory = make([]string, len(*r.Memory))
|
||||
for i, val := range *r.Memory {
|
||||
data.Memory[i] = val
|
||||
}
|
||||
}
|
||||
if r.Storage != nil {
|
||||
keys := make([]string, 0, len(*r.Storage))
|
||||
for key := range *r.Storage {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return strings.Compare(keys[i], keys[j]) >= 0
|
||||
})
|
||||
data.Storage = make([]string, 0, len(*r.Storage)*2)
|
||||
for _, key := range keys {
|
||||
data.Storage = append(data.Storage, []string{key, (*r.Storage)[key]}...)
|
||||
}
|
||||
}
|
||||
return rlp.Encode(w, data)
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder.
|
||||
func (r *StructLogRes) DecodeRLP(s *rlp.Stream) error {
|
||||
var dec rlpStructLogRes
|
||||
err := s.Decode(&dec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Pc, r.Op, r.Gas, r.GasCost, r.Depth, r.Error = dec.Pc, dec.Op, dec.Gas, dec.GasCost, int(dec.Depth), dec.Error
|
||||
if len(dec.Stack) != 0 {
|
||||
stack := make([]string, len(dec.Stack))
|
||||
for i, val := range dec.Stack {
|
||||
stack[i] = val
|
||||
}
|
||||
r.Stack = &stack
|
||||
}
|
||||
if len(dec.Memory) != 0 {
|
||||
memory := make([]string, len(dec.Memory))
|
||||
for i, val := range dec.Memory {
|
||||
memory[i] = val
|
||||
}
|
||||
r.Memory = &memory
|
||||
}
|
||||
if len(dec.Storage) != 0 {
|
||||
storage := make(map[string]string, len(dec.Storage)*2)
|
||||
for i := 0; i < len(dec.Storage); i += 2 {
|
||||
key, val := dec.Storage[i], dec.Storage[i+1]
|
||||
storage[key] = val
|
||||
}
|
||||
r.Storage = &storage
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -70,6 +70,9 @@ type Receipt struct {
|
|||
BlockHash common.Hash `json:"blockHash,omitempty"`
|
||||
BlockNumber *big.Int `json:"blockNumber,omitempty"`
|
||||
TransactionIndex uint `json:"transactionIndex"`
|
||||
|
||||
// The value of evm execution result.
|
||||
ReturnValue []byte `json:"returnValue,omitempty"`
|
||||
}
|
||||
|
||||
type receiptMarshaling struct {
|
||||
|
|
|
|||
|
|
@ -358,3 +358,40 @@ func (t *mdLogger) CaptureEnter(typ OpCode, from common.Address, to common.Addre
|
|||
}
|
||||
|
||||
func (t *mdLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
|
||||
|
||||
// FormatLogs formats EVM returned structured logs for json output
|
||||
func FormatLogs(logs []StructLog) []types.StructLogRes {
|
||||
formatted := make([]types.StructLogRes, len(logs))
|
||||
for index, trace := range logs {
|
||||
formatted[index] = types.StructLogRes{
|
||||
Pc: trace.Pc,
|
||||
Op: trace.Op.String(),
|
||||
Gas: trace.Gas,
|
||||
GasCost: trace.GasCost,
|
||||
Depth: trace.Depth,
|
||||
Error: trace.ErrorString(),
|
||||
}
|
||||
if trace.Stack != nil {
|
||||
stack := make([]string, len(trace.Stack))
|
||||
for i, stackValue := range trace.Stack {
|
||||
stack[i] = stackValue.Hex()
|
||||
}
|
||||
formatted[index].Stack = &stack
|
||||
}
|
||||
if trace.Memory != nil {
|
||||
memory := make([]string, 0, (len(trace.Memory)+31)/32)
|
||||
for i := 0; i+32 <= len(trace.Memory); i += 32 {
|
||||
memory = append(memory, fmt.Sprintf("%x", trace.Memory[i:i+32]))
|
||||
}
|
||||
formatted[index].Memory = &memory
|
||||
}
|
||||
if trace.Storage != nil {
|
||||
storage := make(map[string]string)
|
||||
for i, storageValue := range trace.Storage {
|
||||
storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue)
|
||||
}
|
||||
formatted[index].Storage = &storage
|
||||
}
|
||||
}
|
||||
return formatted
|
||||
}
|
||||
|
|
|
|||
15
eth/api.go
15
eth/api.go
|
|
@ -607,3 +607,18 @@ 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}
|
||||
}
|
||||
|
||||
// BlockResultByHash returns the blockResult by blockHash.
|
||||
func (api *PublicTraceAPI) BlockResultByHash(blockHash common.Hash) (*types.BlockResult, error) {
|
||||
return rawdb.ReadBlockResult(api.e.chainDb, blockHash), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,6 +175,8 @@ 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: true}),
|
||||
}
|
||||
cacheConfig = &core.CacheConfig{
|
||||
TrieCleanLimit: config.TrieCleanCache,
|
||||
|
|
@ -314,6 +316,11 @@ 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",
|
||||
|
|
|
|||
|
|
@ -278,6 +278,35 @@ 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,6 +52,8 @@ 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
|
||||
)
|
||||
|
|
@ -69,15 +71,16 @@ 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
|
||||
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
|
||||
blockResults chan *types.BlockResult
|
||||
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
|
||||
|
|
@ -290,6 +293,19 @@ 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 {
|
||||
|
|
@ -355,6 +371,9 @@ func (es *EventSystem) handleChainEvent(filters filterIndex, ev core.ChainEvent)
|
|||
for _, f := range filters[BlocksSubscription] {
|
||||
f.headers <- ev.Block.Header()
|
||||
}
|
||||
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] {
|
||||
|
|
|
|||
|
|
@ -912,11 +912,11 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex
|
|||
if len(result.Revert()) > 0 {
|
||||
returnVal = fmt.Sprintf("%x", result.Revert())
|
||||
}
|
||||
return ðapi.ExecutionResult{
|
||||
return &types.ExecutionResult{
|
||||
Gas: result.UsedGas,
|
||||
Failed: result.Failed(),
|
||||
ReturnValue: returnVal,
|
||||
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
|
||||
StructLogs: vm.FormatLogs(tracer.StructLogs()),
|
||||
}, nil
|
||||
|
||||
case Tracer:
|
||||
|
|
|
|||
|
|
@ -213,11 +213,11 @@ func TestTraceCall(t *testing.T) {
|
|||
},
|
||||
config: nil,
|
||||
expectErr: nil,
|
||||
expect: ðapi.ExecutionResult{
|
||||
expect: &types.ExecutionResult{
|
||||
Gas: params.TxGas,
|
||||
Failed: false,
|
||||
ReturnValue: "",
|
||||
StructLogs: []ethapi.StructLogRes{},
|
||||
StructLogs: []types.StructLogRes{},
|
||||
},
|
||||
},
|
||||
// Standard JSON trace upon the head, plain transfer.
|
||||
|
|
@ -230,11 +230,11 @@ func TestTraceCall(t *testing.T) {
|
|||
},
|
||||
config: nil,
|
||||
expectErr: nil,
|
||||
expect: ðapi.ExecutionResult{
|
||||
expect: &types.ExecutionResult{
|
||||
Gas: params.TxGas,
|
||||
Failed: false,
|
||||
ReturnValue: "",
|
||||
StructLogs: []ethapi.StructLogRes{},
|
||||
StructLogs: []types.StructLogRes{},
|
||||
},
|
||||
},
|
||||
// Standard JSON trace upon the non-existent block, error expects
|
||||
|
|
@ -259,11 +259,11 @@ func TestTraceCall(t *testing.T) {
|
|||
},
|
||||
config: nil,
|
||||
expectErr: nil,
|
||||
expect: ðapi.ExecutionResult{
|
||||
expect: &types.ExecutionResult{
|
||||
Gas: params.TxGas,
|
||||
Failed: false,
|
||||
ReturnValue: "",
|
||||
StructLogs: []ethapi.StructLogRes{},
|
||||
StructLogs: []types.StructLogRes{},
|
||||
},
|
||||
},
|
||||
// Standard JSON trace upon the pending block
|
||||
|
|
@ -276,11 +276,11 @@ func TestTraceCall(t *testing.T) {
|
|||
},
|
||||
config: nil,
|
||||
expectErr: nil,
|
||||
expect: ðapi.ExecutionResult{
|
||||
expect: &types.ExecutionResult{
|
||||
Gas: params.TxGas,
|
||||
Failed: false,
|
||||
ReturnValue: "",
|
||||
StructLogs: []ethapi.StructLogRes{},
|
||||
StructLogs: []types.StructLogRes{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -329,11 +329,11 @@ func TestTraceTransaction(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("Failed to trace transaction %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(result, ðapi.ExecutionResult{
|
||||
if !reflect.DeepEqual(result, &types.ExecutionResult{
|
||||
Gas: params.TxGas,
|
||||
Failed: false,
|
||||
ReturnValue: "",
|
||||
StructLogs: []ethapi.StructLogRes{},
|
||||
StructLogs: []types.StructLogRes{},
|
||||
}) {
|
||||
t.Error("Transaction tracing result is different")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -325,6 +325,20 @@ func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header)
|
|||
return ec.c.EthSubscribe(ctx, ch, "newHeads")
|
||||
}
|
||||
|
||||
// BlockResultByHash returns the blockResult.
|
||||
func (ec *Client) BlockResultByHash(ctx context.Context, blockHash common.Hash) (*types.BlockResult, error) {
|
||||
var blockResult types.BlockResult
|
||||
if err := ec.c.CallContext(ctx, &blockResult, "eth_blockResultByHash", blockHash); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &blockResult, nil
|
||||
}
|
||||
|
||||
// SubscribeNewBlockResult subscribes to block execution trace when a new block is created.
|
||||
func (ec *Client) SubscribeNewBlockResult(ctx context.Context, ch chan<- *types.BlockResult) (ethereum.Subscription, error) {
|
||||
return ec.c.EthSubscribe(ctx, ch, "newBlockResult")
|
||||
}
|
||||
|
||||
// State Access
|
||||
|
||||
// NetworkID returns the network ID (also known as the chain ID) for this chain.
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -52,6 +52,7 @@ require (
|
|||
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/tsdb v0.7.1
|
||||
github.com/rjeczalik/notify v0.9.1
|
||||
github.com/rs/cors v1.7.0
|
||||
|
|
|
|||
|
|
@ -1113,67 +1113,6 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args TransactionA
|
|||
return DoEstimateGas(ctx, s.b, args, bNrOrHash, s.b.RPCGasCap())
|
||||
}
|
||||
|
||||
// ExecutionResult groups all structured logs emitted by the EVM
|
||||
// while replaying a transaction in debug mode as well as transaction
|
||||
// execution status, the amount of gas used and the return value
|
||||
type ExecutionResult struct {
|
||||
Gas uint64 `json:"gas"`
|
||||
Failed bool `json:"failed"`
|
||||
ReturnValue string `json:"returnValue"`
|
||||
StructLogs []StructLogRes `json:"structLogs"`
|
||||
}
|
||||
|
||||
// StructLogRes stores a structured log emitted by the EVM while replaying a
|
||||
// transaction in debug mode
|
||||
type StructLogRes struct {
|
||||
Pc uint64 `json:"pc"`
|
||||
Op string `json:"op"`
|
||||
Gas uint64 `json:"gas"`
|
||||
GasCost uint64 `json:"gasCost"`
|
||||
Depth int `json:"depth"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Stack *[]string `json:"stack,omitempty"`
|
||||
Memory *[]string `json:"memory,omitempty"`
|
||||
Storage *map[string]string `json:"storage,omitempty"`
|
||||
}
|
||||
|
||||
// FormatLogs formats EVM returned structured logs for json output
|
||||
func FormatLogs(logs []vm.StructLog) []StructLogRes {
|
||||
formatted := make([]StructLogRes, len(logs))
|
||||
for index, trace := range logs {
|
||||
formatted[index] = StructLogRes{
|
||||
Pc: trace.Pc,
|
||||
Op: trace.Op.String(),
|
||||
Gas: trace.Gas,
|
||||
GasCost: trace.GasCost,
|
||||
Depth: trace.Depth,
|
||||
Error: trace.ErrorString(),
|
||||
}
|
||||
if trace.Stack != nil {
|
||||
stack := make([]string, len(trace.Stack))
|
||||
for i, stackValue := range trace.Stack {
|
||||
stack[i] = stackValue.Hex()
|
||||
}
|
||||
formatted[index].Stack = &stack
|
||||
}
|
||||
if trace.Memory != nil {
|
||||
memory := make([]string, 0, (len(trace.Memory)+31)/32)
|
||||
for i := 0; i+32 <= len(trace.Memory); i += 32 {
|
||||
memory = append(memory, fmt.Sprintf("%x", trace.Memory[i:i+32]))
|
||||
}
|
||||
formatted[index].Memory = &memory
|
||||
}
|
||||
if trace.Storage != nil {
|
||||
storage := make(map[string]string)
|
||||
for i, storageValue := range trace.Storage {
|
||||
storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue)
|
||||
}
|
||||
formatted[index].Storage = &storage
|
||||
}
|
||||
}
|
||||
return formatted
|
||||
}
|
||||
|
||||
// RPCMarshalHeader converts the given header to the RPC output .
|
||||
func RPCMarshalHeader(head *types.Header) map[string]interface{} {
|
||||
result := map[string]interface{}{
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package miner
|
|||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
|
@ -31,6 +32,7 @@ import (
|
|||
"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"
|
||||
|
|
@ -88,17 +90,19 @@ 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
|
||||
header *types.Header
|
||||
txs []*types.Transaction
|
||||
receipts []*types.Receipt
|
||||
executionResults []*types.ExecutionResult
|
||||
}
|
||||
|
||||
// task contains all information for consensus engine sealing and result submitting.
|
||||
type task struct {
|
||||
receipts []*types.Receipt
|
||||
state *state.StateDB
|
||||
block *types.Block
|
||||
createdAt time.Time
|
||||
receipts []*types.Receipt
|
||||
executionResults []*types.ExecutionResult
|
||||
state *state.StateDB
|
||||
block *types.Block
|
||||
createdAt time.Time
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
@ -393,10 +397,10 @@ func (w *worker) newWorkLoop(recommit time.Duration) {
|
|||
timestamp = time.Now().Unix()
|
||||
commit(false, commitInterruptNewHead)
|
||||
|
||||
/*case head := <-w.chainHeadCh:
|
||||
clearPending(head.Block.NumberU64())
|
||||
timestamp = time.Now().Unix()
|
||||
commit(false, commitInterruptNewHead)*/
|
||||
case head := <-w.chainHeadCh:
|
||||
clearPending(head.Block.NumberU64())
|
||||
timestamp = time.Now().Unix()
|
||||
// commit(false, commitInterruptNewHead)
|
||||
|
||||
case <-timer.C:
|
||||
// If mining is running resubmit a new work cycle periodically to pull in
|
||||
|
|
@ -462,6 +466,7 @@ func (w *worker) mainLoop() {
|
|||
select {
|
||||
case req := <-w.newWorkCh:
|
||||
w.commitNewWork(req.interrupt, req.noempty, req.timestamp)
|
||||
// new block created.
|
||||
|
||||
case ev := <-w.chainSideCh:
|
||||
// Short circuit for duplicate side blocks
|
||||
|
|
@ -632,14 +637,19 @@ 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))
|
||||
logs []*types.Log
|
||||
receipts = make([]*types.Receipt, len(task.receipts))
|
||||
evmTraces = make([]*types.ExecutionResult, len(task.executionResults))
|
||||
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]
|
||||
|
||||
// add block location fields
|
||||
receipt.BlockHash = hash
|
||||
receipt.BlockNumber = block.Number()
|
||||
|
|
@ -657,7 +667,7 @@ func (w *worker) resultLoop() {
|
|||
logs = append(logs, receipt.Logs...)
|
||||
}
|
||||
// Commit block and state to database.
|
||||
_, err := w.chain.WriteBlockWithState(block, receipts, logs, task.state, true)
|
||||
_, err := w.chain.WriteBlockWithState(block, receipts, logs, &types.BlockResult{ExecutionResults: evmTraces}, task.state, true)
|
||||
if err != nil {
|
||||
log.Error("Failed writing block to chain", "err", err)
|
||||
continue
|
||||
|
|
@ -771,6 +781,10 @@ 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()
|
||||
|
||||
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)
|
||||
|
|
@ -778,6 +792,12 @@ func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Addres
|
|||
}
|
||||
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,
|
||||
Failed: receipt.Status == types.ReceiptStatusSuccessful,
|
||||
ReturnValue: fmt.Sprintf("%x", receipt.ReturnValue),
|
||||
StructLogs: vm.FormatLogs(tracer.StructLogs()),
|
||||
})
|
||||
|
||||
return receipt.Logs, nil
|
||||
}
|
||||
|
|
@ -1042,7 +1062,7 @@ func (w *worker) commit(uncles []*types.Header, interval func(), update bool, st
|
|||
interval()
|
||||
}
|
||||
select {
|
||||
case w.taskCh <- &task{receipts: receipts, state: s, block: block, createdAt: time.Now()}:
|
||||
case w.taskCh <- &task{receipts: receipts, executionResults: w.current.executionResults, 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,
|
||||
|
|
|
|||
|
|
@ -135,7 +135,9 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine
|
|||
}
|
||||
genesis := gspec.MustCommit(db)
|
||||
|
||||
chain, _ := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec.Config, engine, vm.Config{}, nil, nil)
|
||||
chain, _ := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec.Config, engine, vm.Config{
|
||||
Debug: true,
|
||||
Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: true})}, nil, nil)
|
||||
txpool := core.NewTxPool(testTxPoolConfig, chainConfig, chain)
|
||||
|
||||
// Generate a small n-block chain and an uncle block for it
|
||||
|
|
@ -232,7 +234,9 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool) {
|
|||
// This test chain imports the mined blocks.
|
||||
db2 := rawdb.NewMemoryDatabase()
|
||||
b.genesis.MustCommit(db2)
|
||||
chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{}, nil, nil)
|
||||
chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{
|
||||
Debug: true,
|
||||
Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: true})}, nil, nil)
|
||||
defer chain.Stop()
|
||||
|
||||
// Ignore empty commit here for less noise.
|
||||
|
|
|
|||
Loading…
Reference in a new issue