mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 22:56:43 +00:00
feat: add more data into the execution trace (#20)
This commit is contained in:
parent
8b16b4cefa
commit
69c291cf7a
25 changed files with 457 additions and 580 deletions
|
|
@ -30,6 +30,7 @@ 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"
|
||||
|
|
@ -91,6 +92,7 @@ const (
|
|||
maxFutureBlocks = 256
|
||||
maxTimeFutureBlocks = 30
|
||||
TriesInMemory = 128
|
||||
blockResultCacheLimit = 128
|
||||
|
||||
// BlockChainVersion ensures that an incompatible database forces a resync from scratch.
|
||||
//
|
||||
|
|
@ -198,6 +200,7 @@ type BlockChain struct {
|
|||
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.
|
||||
|
||||
wg sync.WaitGroup //
|
||||
quit chan struct{} // shutdown signal, closed in Stop.
|
||||
|
|
@ -226,6 +229,7 @@ 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)
|
||||
|
||||
bc := &BlockChain{
|
||||
chainConfig: chainConfig,
|
||||
|
|
@ -246,6 +250,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
|||
blockCache: blockCache,
|
||||
txLookupCache: txLookupCache,
|
||||
futureBlocks: futureBlocks,
|
||||
blockResultCache: blockResultCache,
|
||||
engine: engine,
|
||||
vmConfig: vmConfig,
|
||||
}
|
||||
|
|
@ -1215,7 +1220,6 @@ 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,6 +1318,12 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
}
|
||||
bc.futureBlocks.Remove(block.Hash())
|
||||
|
||||
// Fill blockResult content
|
||||
if blockResult != nil {
|
||||
bc.writeBlockResult(state, block, blockResult)
|
||||
bc.blockResultCache.Add(block.Hash(), blockResult)
|
||||
}
|
||||
|
||||
if status == CanonStatTy {
|
||||
bc.chainFeed.Send(ChainEvent{Block: block, Hash: block.Hash(), Logs: logs, BlockResult: blockResult})
|
||||
if len(logs) > 0 {
|
||||
|
|
@ -1333,6 +1343,35 @@ 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, blockResult *types.BlockResult) {
|
||||
blockResult.BlockTrace = types.NewTraceBlock(bc.chainConfig, block)
|
||||
for i, tx := range block.Transactions() {
|
||||
evmTrace := blockResult.ExecutionResults[i]
|
||||
// Get the sender's address.
|
||||
from, _ := types.Sender(types.MakeSigner(bc.chainConfig, block.Number()), tx)
|
||||
// Get account's proof.
|
||||
proof, err := state.GetProof(from)
|
||||
if err != nil {
|
||||
log.Error("Failed to get proof", "blockNumber", block.NumberU64(), "address", from.String(), "err", err)
|
||||
} else {
|
||||
evmTrace.Proof = make([]string, len(proof))
|
||||
for i := range proof {
|
||||
evmTrace.Proof[i] = hexutil.Encode(proof[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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
|
|
|||
|
|
@ -158,6 +158,13 @@ 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 {
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
|
|||
func NewEVMTxContext(msg Message) vm.TxContext {
|
||||
return vm.TxContext{
|
||||
Origin: msg.From(),
|
||||
To: msg.To(),
|
||||
GasPrice: new(big.Int).Set(msg.GasPrice()),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,355 +0,0 @@
|
|||
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,7 +89,6 @@ 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
|
||||
|
|
@ -231,8 +230,3 @@ 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()...)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,37 +1,15 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/rlp"
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
)
|
||||
|
||||
// BlockResult contains block execution traces and results required for rollers.
|
||||
type BlockResult struct {
|
||||
BlockTrace *BlockTrace `json:"blockTrace"`
|
||||
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
|
||||
|
|
@ -39,34 +17,15 @@ type ExecutionResult struct {
|
|||
Gas uint64 `json:"gas"`
|
||||
Failed bool `json:"failed"`
|
||||
ReturnValue string `json:"returnValue,omitempty"`
|
||||
// It's exist only when tx is a contract call.
|
||||
CodeHash *common.Hash `json:"codeHash,omitempty"`
|
||||
// If it is a contract call, the contract code is returned.
|
||||
ByteCode string `json:"byteCode,omitempty"`
|
||||
// The account's proof.
|
||||
Proof []string `json:"proof,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 {
|
||||
|
|
@ -79,87 +38,43 @@ type StructLogRes struct {
|
|||
Stack *[]string `json:"stack,omitempty"`
|
||||
Memory *[]string `json:"memory,omitempty"`
|
||||
Storage *map[string]string `json:"storage,omitempty"`
|
||||
ExtraData *ExtraData `json:"extraData,omitempty"`
|
||||
}
|
||||
|
||||
type rlpStructLogRes struct {
|
||||
Pc uint64
|
||||
Op string
|
||||
Gas uint64
|
||||
GasCost uint64
|
||||
Depth uint
|
||||
Error string
|
||||
Stack []string
|
||||
Memory []string
|
||||
Storage []string
|
||||
type ExtraData struct {
|
||||
// CREATE | CREATE2: sender address
|
||||
From *common.Address `json:"from,omitempty"`
|
||||
// CREATE: sender nonce
|
||||
Nonce *uint64 `json:"nonce,omitempty"`
|
||||
// CALL | CALLCODE | DELEGATECALL | STATICCALL: [tx.to address’s code_hash, stack.nth_last(1) address’s code_hash]
|
||||
CodeHashList []common.Hash `json:"codeHashList,omitempty"`
|
||||
// SSTORE | SLOAD: [storageProof]
|
||||
// SELFDESTRUCT: [contract address’s accountProof, stack.nth_last(0) address’s accountProof]
|
||||
// SELFBALANCE: [contract address’s accountProof]
|
||||
// BALANCE | EXTCODEHASH: [stack.nth_last(0) address’s accountProof]
|
||||
// CREATE | CREATE2: [created contract address’s accountProof]
|
||||
// CALL | CALLCODE: [caller contract address’s accountProof, stack.nth_last(1) address’s accountProof]
|
||||
ProofList [][]string `json:"proofList,omitempty"`
|
||||
}
|
||||
|
||||
// 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,
|
||||
// NewExtraData create, init and return ExtraData
|
||||
func NewExtraData() *ExtraData {
|
||||
return &ExtraData{
|
||||
CodeHashList: make([]common.Hash, 0),
|
||||
ProofList: make([][]string, 0),
|
||||
}
|
||||
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
|
||||
// SealExtraData doesn't show empty fields.
|
||||
func (e *ExtraData) SealExtraData() *ExtraData {
|
||||
if len(e.CodeHashList) == 0 {
|
||||
e.CodeHashList = nil
|
||||
}
|
||||
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
|
||||
if len(e.ProofList) == 0 {
|
||||
e.ProofList = nil
|
||||
}
|
||||
if e.From == nil && e.Nonce == nil && e.CodeHashList == nil && e.ProofList == nil {
|
||||
return nil
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
|
|
|||
78
core/types/l2trace_block.go
Normal file
78
core/types/l2trace_block.go
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||
"github.com/scroll-tech/go-ethereum/params"
|
||||
)
|
||||
|
||||
type BlockTrace struct {
|
||||
Number *big.Int `json:"number"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
GasLimit uint64 `json:"gasLimit"`
|
||||
Difficulty *big.Int `json:"difficulty"`
|
||||
BaseFee *big.Int `json:"baseFee"`
|
||||
Coinbase common.Address `json:"coinbase"`
|
||||
Time uint64 `json:"time"`
|
||||
Transaction []*TransactionTrace `json:"transaction"`
|
||||
}
|
||||
|
||||
type TransactionTrace struct {
|
||||
Type uint8 `json:"type"`
|
||||
Nonce uint64 `json:"nonce"`
|
||||
Gas uint64 `json:"gas"`
|
||||
GasPrice *big.Int `json:"gasPrice"`
|
||||
From common.Address `json:"from"`
|
||||
To *common.Address `json:"to"`
|
||||
ChainId *big.Int `json:"chainId"`
|
||||
Value *big.Int `json:"value"`
|
||||
Data string `json:"data"`
|
||||
IsCreate bool `json:"isCreate"`
|
||||
V *big.Int `json:"v"`
|
||||
R *big.Int `json:"r"`
|
||||
S *big.Int `json:"s"`
|
||||
}
|
||||
|
||||
// NewTraceBlock supports necessary fields for roller.
|
||||
func NewTraceBlock(config *params.ChainConfig, block *Block) *BlockTrace {
|
||||
txs := make([]*TransactionTrace, block.Transactions().Len())
|
||||
for i, tx := range block.Transactions() {
|
||||
txs[i] = newTraceTransaction(tx, block.NumberU64(), config)
|
||||
}
|
||||
return &BlockTrace{
|
||||
Number: block.Number(),
|
||||
Hash: block.Hash(),
|
||||
GasLimit: block.GasLimit(),
|
||||
Difficulty: block.Difficulty(),
|
||||
BaseFee: block.BaseFee(),
|
||||
Coinbase: block.Coinbase(),
|
||||
Time: block.Time(),
|
||||
Transaction: txs,
|
||||
}
|
||||
}
|
||||
|
||||
// newTraceTransaction returns a transaction that will serialize to the trace
|
||||
// representation, with the given location metadata set (if available).
|
||||
func newTraceTransaction(tx *Transaction, blockNumber uint64, config *params.ChainConfig) *TransactionTrace {
|
||||
signer := MakeSigner(config, big.NewInt(0).SetUint64(blockNumber))
|
||||
from, _ := Sender(signer, tx)
|
||||
v, r, s := tx.RawSignatureValues()
|
||||
result := &TransactionTrace{
|
||||
Type: tx.Type(),
|
||||
Nonce: tx.Nonce(),
|
||||
ChainId: tx.ChainId(),
|
||||
From: from,
|
||||
Gas: tx.Gas(),
|
||||
GasPrice: tx.GasPrice(),
|
||||
To: tx.To(),
|
||||
Value: tx.Value(),
|
||||
Data: hexutil.Encode(tx.Data()),
|
||||
IsCreate: tx.To() == nil,
|
||||
V: v,
|
||||
R: r,
|
||||
S: s,
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
@ -161,6 +161,10 @@ func (a *AccessListTracer) CaptureState(pc uint64, op OpCode, gas, cost uint64,
|
|||
}
|
||||
}
|
||||
|
||||
// CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change.
|
||||
func (*AccessListTracer) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
|
||||
}
|
||||
|
||||
func (*AccessListTracer) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ type BlockContext struct {
|
|||
type TxContext struct {
|
||||
// Message information
|
||||
Origin common.Address // Provides information for ORIGIN
|
||||
To *common.Address // Provides information for trace
|
||||
GasPrice *big.Int // Provides information for GASPRICE
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,10 @@ type StateDB interface {
|
|||
GetState(common.Address, common.Hash) common.Hash
|
||||
SetState(common.Address, common.Hash, common.Hash)
|
||||
|
||||
GetProof(addr common.Address) ([][]byte, error)
|
||||
GetProofByHash(addrHash common.Hash) ([][]byte, error)
|
||||
GetStorageProof(a common.Address, key common.Hash) ([][]byte, error)
|
||||
|
||||
Suicide(common.Address) bool
|
||||
HasSuicided(common.Address) bool
|
||||
|
||||
|
|
|
|||
|
|
@ -265,6 +265,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
in.returnData = res
|
||||
}
|
||||
|
||||
if in.cfg.Debug {
|
||||
in.cfg.Tracer.CaptureStateAfter(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||
"github.com/scroll-tech/go-ethereum/common/math"
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
"github.com/scroll-tech/go-ethereum/params"
|
||||
)
|
||||
|
||||
|
|
@ -72,6 +73,7 @@ type StructLog struct {
|
|||
Storage map[common.Hash]common.Hash `json:"-"`
|
||||
Depth int `json:"depth"`
|
||||
RefundCounter uint64 `json:"refund"`
|
||||
ExtraData *types.ExtraData `json:"extraData"`
|
||||
Err error `json:"-"`
|
||||
}
|
||||
|
||||
|
|
@ -106,6 +108,7 @@ func (s *StructLog) ErrorString() string {
|
|||
type EVMLogger interface {
|
||||
CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
|
||||
CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
|
||||
CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
|
||||
CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
|
||||
CaptureExit(output []byte, gasUsed uint64, err error)
|
||||
CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
|
||||
|
|
@ -177,7 +180,10 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop
|
|||
}
|
||||
}
|
||||
// Copy a snapshot of the current storage to a new container
|
||||
var storage Storage
|
||||
var (
|
||||
storage Storage
|
||||
extraData *types.ExtraData
|
||||
)
|
||||
if !l.cfg.DisableStorage && (op == SLOAD || op == SSTORE) {
|
||||
// initialise new changed values storage container for this contract
|
||||
// if not present.
|
||||
|
|
@ -200,6 +206,11 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop
|
|||
)
|
||||
l.storage[contract.Address()][address] = value
|
||||
storage = l.storage[contract.Address()].Copy()
|
||||
|
||||
extraData = types.NewExtraData()
|
||||
if err := traceStorageProof(l, scope, extraData); err != nil {
|
||||
log.Warn("Failed to get proof", "contract address", contract.Address().String(), "key", address.String(), "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
var rdata []byte
|
||||
|
|
@ -207,8 +218,32 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop
|
|||
rdata = make([]byte, len(rData))
|
||||
copy(rdata, rData)
|
||||
}
|
||||
|
||||
// create a new snapshot of the EVM.
|
||||
log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), err}
|
||||
log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), extraData, err}
|
||||
l.logs = append(l.logs, log)
|
||||
}
|
||||
|
||||
// CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change.
|
||||
func (l *StructLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
|
||||
// check if already accumulated the specified number of logs
|
||||
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
|
||||
return
|
||||
}
|
||||
|
||||
execFuncList, ok := OpcodeExecs[op]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
extraData := types.NewExtraData()
|
||||
// execute trace func list.
|
||||
for _, exec := range execFuncList {
|
||||
if err = exec(l, scope, extraData); err != nil {
|
||||
log.Error("Failed to trace data", "opcode", op.String(), "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
log := StructLog{pc, op, gas, cost, nil, scope.Memory.Len(), nil, nil, nil, depth, l.env.StateDB.GetRefund(), extraData, err}
|
||||
l.logs = append(l.logs, log)
|
||||
}
|
||||
|
||||
|
|
@ -345,6 +380,10 @@ func (t *mdLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *S
|
|||
}
|
||||
}
|
||||
|
||||
// CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change.
|
||||
func (t *mdLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
|
||||
}
|
||||
|
||||
func (t *mdLogger) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
|
||||
fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
|
||||
}
|
||||
|
|
@ -371,27 +410,31 @@ func FormatLogs(logs []StructLog) []types.StructLogRes {
|
|||
Depth: trace.Depth,
|
||||
Error: trace.ErrorString(),
|
||||
}
|
||||
if trace.Stack != nil {
|
||||
if len(trace.Stack) != 0 {
|
||||
stack := make([]string, len(trace.Stack))
|
||||
for i, stackValue := range trace.Stack {
|
||||
stack[i] = stackValue.Hex()
|
||||
}
|
||||
formatted[index].Stack = &stack
|
||||
}
|
||||
if trace.Memory != nil {
|
||||
if len(trace.Memory) != 0 {
|
||||
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]))
|
||||
bytes := new(big.Int).SetBytes(trace.Memory[i : i+32]).Bytes()
|
||||
memory = append(memory, hexutil.Encode(bytes))
|
||||
}
|
||||
formatted[index].Memory = &memory
|
||||
}
|
||||
if trace.Storage != nil {
|
||||
if len(trace.Storage) != 0 {
|
||||
storage := make(map[string]string)
|
||||
for i, storageValue := range trace.Storage {
|
||||
storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue)
|
||||
}
|
||||
formatted[index].Storage = &storage
|
||||
}
|
||||
if trace.ExtraData != nil {
|
||||
formatted[index].ExtraData = trace.ExtraData.SealExtraData()
|
||||
}
|
||||
}
|
||||
return formatted
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,6 +75,10 @@ func (l *JSONLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope
|
|||
l.encoder.Encode(log)
|
||||
}
|
||||
|
||||
// CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change.
|
||||
func (l *JSONLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
|
||||
}
|
||||
|
||||
// CaptureEnd is triggered at end of execution.
|
||||
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
|
||||
type endLog struct {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/holiman/uint256"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/core/rawdb"
|
||||
"github.com/scroll-tech/go-ethereum/core/state"
|
||||
"github.com/scroll-tech/go-ethereum/params"
|
||||
)
|
||||
|
|
@ -42,6 +44,14 @@ func (d *dummyContractRef) SetBalance(*big.Int) {}
|
|||
func (d *dummyContractRef) SetNonce(uint64) {}
|
||||
func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) }
|
||||
|
||||
// makeTestState create a sample test state to test node-wise reconstruction.
|
||||
func makeTestState() *state.StateDB {
|
||||
// Create an empty state
|
||||
db := state.NewDatabase(rawdb.NewMemoryDatabase())
|
||||
stateDb, _ := state.New(common.Hash{}, db, nil)
|
||||
return stateDb
|
||||
}
|
||||
|
||||
type dummyStatedb struct {
|
||||
state.StateDB
|
||||
}
|
||||
|
|
@ -50,7 +60,7 @@ func (*dummyStatedb) GetRefund() uint64 { return 1337 }
|
|||
|
||||
func TestStoreCapture(t *testing.T) {
|
||||
var (
|
||||
env = NewEVM(BlockContext{}, TxContext{}, &dummyStatedb{}, params.TestChainConfig, Config{})
|
||||
env = NewEVM(BlockContext{}, TxContext{}, makeTestState(), params.TestChainConfig, Config{})
|
||||
logger = NewStructLogger(nil)
|
||||
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0)
|
||||
scope = &ScopeContext{
|
||||
|
|
|
|||
146
core/vm/logger_trace.go
Normal file
146
core/vm/logger_trace.go
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
type traceFunc func(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error
|
||||
|
||||
var (
|
||||
// OpcodeExecs the map to load opcodes' trace funcs.
|
||||
OpcodeExecs = map[OpCode][]traceFunc{
|
||||
CALL: {traceToAddressCodeHash, traceLastNAddressCodeHash(1), traceCallerProof, traceLastNAddressProof(1)},
|
||||
CALLCODE: {traceToAddressCodeHash, traceLastNAddressCodeHash(1), traceCallerProof, traceLastNAddressProof(1)},
|
||||
DELEGATECALL: {traceToAddressCodeHash, traceLastNAddressCodeHash(1)},
|
||||
STATICCALL: {traceToAddressCodeHash, traceLastNAddressCodeHash(1)},
|
||||
CREATE: {traceSenderAddress, traceCreatedContractProof, traceNonce},
|
||||
CREATE2: {traceSenderAddress, traceCreatedContractProof},
|
||||
SSTORE: {traceStorageProof},
|
||||
SLOAD: {traceStorageProof},
|
||||
SELFDESTRUCT: {traceContractProof, traceLastNAddressProof(0)},
|
||||
SELFBALANCE: {traceContractProof},
|
||||
BALANCE: {traceLastNAddressProof(0)},
|
||||
EXTCODEHASH: {traceLastNAddressProof(0)},
|
||||
}
|
||||
)
|
||||
|
||||
// traceToAddressCodeHash gets tx.to address’s code_hash
|
||||
func traceToAddressCodeHash(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||
if l.env.To == nil {
|
||||
return nil
|
||||
}
|
||||
codeHash := l.env.StateDB.GetCodeHash(*l.env.To)
|
||||
extraData.CodeHashList = append(extraData.CodeHashList, codeHash)
|
||||
return nil
|
||||
}
|
||||
|
||||
// traceLastNAddressCodeHash
|
||||
func traceLastNAddressCodeHash(n int) traceFunc {
|
||||
return func(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||
stack := scope.Stack
|
||||
if stack.len() <= n {
|
||||
return nil
|
||||
}
|
||||
address := common.Address(stack.data[stack.len()-1-n].Bytes20())
|
||||
codeHash := l.env.StateDB.GetCodeHash(address)
|
||||
extraData.CodeHashList = append(extraData.CodeHashList, codeHash)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// traceSenderAddress gets sender address
|
||||
func traceSenderAddress(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||
extraData.From = &l.env.Origin
|
||||
return nil
|
||||
}
|
||||
|
||||
// traceNonce gets sender nonce
|
||||
func traceNonce(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||
nonce := l.env.StateDB.GetNonce(l.env.Origin)
|
||||
extraData.Nonce = &nonce
|
||||
return nil
|
||||
}
|
||||
|
||||
// traceStorageProof get contract's storage proof at storage_address
|
||||
func traceStorageProof(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||
if scope.Stack.len() == 0 {
|
||||
return nil
|
||||
}
|
||||
address := common.Hash(scope.Stack.peek().Bytes32())
|
||||
contract := scope.Contract
|
||||
// Get storage proof.
|
||||
storageProof, err := l.env.StateDB.GetStorageProof(contract.Address(), address)
|
||||
if err == nil {
|
||||
extraData.ProofList = append(extraData.ProofList, encodeProof(storageProof))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// traceContractProof gets the contract's account proof
|
||||
func traceContractProof(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||
// Get account proof.
|
||||
proof, err := l.env.StateDB.GetProof(scope.Contract.Address())
|
||||
if err == nil {
|
||||
extraData.ProofList = append(extraData.ProofList, encodeProof(proof))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
/// traceCreatedContractProof get created contract address’s accountProof
|
||||
func traceCreatedContractProof(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||
stack := scope.Stack
|
||||
if stack.len() < 1 {
|
||||
return nil
|
||||
}
|
||||
stackvalue := stack.peek()
|
||||
if stackvalue.IsZero() {
|
||||
return errors.New("can't get created contract address from stack")
|
||||
}
|
||||
address := common.BytesToAddress(stackvalue.Bytes())
|
||||
proof, err := l.env.StateDB.GetProof(address)
|
||||
if err == nil {
|
||||
extraData.ProofList = append(extraData.ProofList, encodeProof(proof))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// traceLastNAddressProof returns func about the last N's address proof.
|
||||
func traceLastNAddressProof(n int) traceFunc {
|
||||
return func(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||
stack := scope.Stack
|
||||
if stack.len() <= n {
|
||||
return nil
|
||||
}
|
||||
|
||||
address := common.Address(stack.data[stack.len()-1-n].Bytes20())
|
||||
proof, err := l.env.StateDB.GetProof(address)
|
||||
if err == nil {
|
||||
extraData.ProofList = append(extraData.ProofList, encodeProof(proof))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// traceCallerProof gets caller address's proof.
|
||||
func traceCallerProof(l *StructLogger, scope *ScopeContext, extraData *types.ExtraData) error {
|
||||
address := scope.Contract.CallerAddress
|
||||
proof, err := l.env.StateDB.GetProof(address)
|
||||
if err == nil {
|
||||
extraData.ProofList = append(extraData.ProofList, encodeProof(proof))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func encodeProof(proof [][]byte) (res []string) {
|
||||
if len(proof) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, node := range proof {
|
||||
res = append(res, hexutil.Encode(node))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -618,7 +618,10 @@ 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
|
||||
// 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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -371,9 +371,11 @@ 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] {
|
||||
|
|
|
|||
|
|
@ -746,6 +746,10 @@ func (jst *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco
|
|||
}
|
||||
}
|
||||
|
||||
// CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change.
|
||||
func (jst *jsTracer) CaptureStateAfter(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
}
|
||||
|
||||
// CaptureFault implements the Tracer interface to trace an execution fault
|
||||
func (jst *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
||||
if jst.err != nil {
|
||||
|
|
|
|||
|
|
@ -96,6 +96,10 @@ func (t *fourByteTracer) CaptureStart(env *vm.EVM, from common.Address, to commo
|
|||
func (t *fourByteTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
}
|
||||
|
||||
// CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change.
|
||||
func (t *fourByteTracer) CaptureStateAfter(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
}
|
||||
|
||||
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
||||
func (t *fourByteTracer) CaptureEnter(op vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
// Skip if tracing was interrupted
|
||||
|
|
|
|||
|
|
@ -96,6 +96,10 @@ func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration,
|
|||
func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
}
|
||||
|
||||
// CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change.
|
||||
func (t *callTracer) CaptureStateAfter(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
}
|
||||
|
||||
// CaptureFault implements the EVMLogger interface to trace an execution fault.
|
||||
func (t *callTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,10 @@ func (t *noopTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration,
|
|||
func (t *noopTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
}
|
||||
|
||||
// CaptureStateAfter for special needs, tracks SSTORE ops and records the storage change.
|
||||
func (t *noopTracer) CaptureStateAfter(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
}
|
||||
|
||||
// CaptureFault implements the EVMLogger interface to trace an execution fault.
|
||||
func (t *noopTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -325,10 +325,10 @@ 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) {
|
||||
// GetBlockResultByHash returns the blockResult.
|
||||
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_blockResultByHash", blockHash); err != nil {
|
||||
if err := ec.c.CallContext(ctx, &blockResult, "eth_getBlockResultByHash", blockHash); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &blockResult, nil
|
||||
|
|
|
|||
|
|
@ -794,7 +794,7 @@ func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Addres
|
|||
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,
|
||||
Failed: receipt.Status != types.ReceiptStatusSuccessful,
|
||||
ReturnValue: fmt.Sprintf("%x", receipt.ReturnValue),
|
||||
StructLogs: vm.FormatLogs(tracer.StructLogs()),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool) {
|
|||
b.genesis.MustCommit(db2)
|
||||
chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{
|
||||
Debug: true,
|
||||
Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: true})}, nil, nil)
|
||||
Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: true, EnableReturnData: true})}, nil, nil)
|
||||
defer chain.Stop()
|
||||
|
||||
// Ignore empty commit here for less noise.
|
||||
|
|
|
|||
Loading…
Reference in a new issue