Fixes in rpctest and rpcdaemon for debug_traceTransaction, and opcode tracer, tool for repairing broken receipts (#2284)

* Fixes in rpctest and rpcdaemon for debug_traceTransaction

* Fix for opcode tracer

* Tool to fix receipts

Co-authored-by: Alexey Sharp <alexeysharp@Alexeys-iMac.local>
This commit is contained in:
ledgerwatch 2021-07-03 14:34:23 +01:00 committed by GitHub
parent 95756c8857
commit f5e42b1e7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 282 additions and 107 deletions

View File

@ -21,7 +21,13 @@ import (
"github.com/RoaringBitmap/roaring/roaring64"
"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon/consensus/ethash"
"github.com/ledgerwatch/erigon/consensus/misc"
"github.com/ledgerwatch/erigon/core"
"github.com/ledgerwatch/erigon/ethdb/cbor"
kv2 "github.com/ledgerwatch/erigon/ethdb/kv"
"github.com/ledgerwatch/erigon/migrations"
"github.com/ledgerwatch/erigon/params"
"github.com/wcharczuk/go-chart"
"github.com/wcharczuk/go-chart/util"
@ -37,6 +43,7 @@ import (
"github.com/ledgerwatch/erigon/core/state"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/types/accounts"
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/eth/stagedsync"
"github.com/ledgerwatch/erigon/eth/stagedsync/stages"
"github.com/ledgerwatch/erigon/ethdb"
@ -2071,26 +2078,121 @@ func scanTxs(chaindata string) error {
func scanReceipts(chaindata string, block uint64) error {
dbdb := kv2.MustOpen(chaindata).RwKV()
defer dbdb.Close()
txtx, err := dbdb.BeginRw(context.Background())
tx, err := dbdb.BeginRw(context.Background())
if err != nil {
return err
}
defer txtx.Rollback()
var db ethdb.Database = kv2.WrapIntoTxDB(txtx)
var tx ethdb.Tx
if hasTx, ok := db.(ethdb.HasTx); ok {
tx = hasTx.Tx()
} else {
return fmt.Errorf("no transaction")
}
var key [8]byte
var v []byte
binary.BigEndian.PutUint64(key[:], block)
if v, err = tx.GetOne(dbutils.BlockReceiptsPrefix, key[:]); err != nil {
defer tx.Rollback()
genesisBlock, err := rawdb.ReadBlockByNumber(tx, 0)
if err != nil {
return err
}
fmt.Printf("blockNum = %d, receipt %x\n", block, v)
return nil
chainConfig, cerr := rawdb.ReadChainConfig(tx, genesisBlock.Hash())
if cerr != nil {
return cerr
}
vmConfig := vm.Config{}
noOpWriter := state.NewNoopWriter()
var buf bytes.Buffer
fixedCount := 0
logInterval := 30 * time.Second
logEvery := time.NewTicker(logInterval)
for blockNum := block; blockNum < block+100000; blockNum++ {
select {
default:
case <-logEvery.C:
log.Info("Commit", "block", blockNum, "fixed", fixedCount)
tx.Commit()
if tx, err = dbdb.BeginRw(context.Background()); err != nil {
return err
}
}
var hash common.Hash
if hash, err = rawdb.ReadCanonicalHash(tx, blockNum); err != nil {
return err
}
if hash == (common.Hash{}) {
break
}
var block *types.Block
var senders []common.Address
if block, senders, err = rawdb.ReadBlockWithSenders(tx, hash, blockNum); err != nil {
return err
}
receipts := rawdb.ReadReceipts(tx, block, senders)
for _, receipt := range receipts {
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
}
if chainConfig.IsByzantium(block.Number().Uint64()) {
receiptSha := types.DeriveSha(receipts)
if receiptSha == block.Header().ReceiptHash {
continue
}
}
dbstate := state.NewPlainKvState(tx, block.NumberU64()-1)
intraBlockState := state.New(dbstate)
getHeader := func(hash common.Hash, number uint64) *types.Header { return rawdb.ReadHeader(tx, hash, number) }
receipts1, err1 := runBlock(intraBlockState, noOpWriter, noOpWriter, chainConfig, getHeader, nil /* checkTEVM */, block, vmConfig)
if err1 != nil {
return err1
}
if chainConfig.IsByzantium(block.Number().Uint64()) {
receiptSha := types.DeriveSha(receipts1)
if receiptSha != block.Header().ReceiptHash {
fmt.Printf("(retrace) mismatched receipt headers for block %d: %x, %x\n", block.NumberU64(), receiptSha, block.Header().ReceiptHash)
} else {
// All good, we can fix receipt record
buf.Reset()
err := cbor.Marshal(&buf, receipts1)
if err != nil {
return fmt.Errorf("encode block receipts for block %d: %v", blockNum, err)
}
if err = tx.Put(dbutils.BlockReceiptsPrefix, dbutils.ReceiptsKey(blockNum), buf.Bytes()); err != nil {
return fmt.Errorf("writing receipts for block %d: %v", blockNum, err)
}
fixedCount++
}
}
}
return tx.Commit()
}
func runBlock(ibs *state.IntraBlockState, txnWriter state.StateWriter, blockWriter state.StateWriter,
chainConfig *params.ChainConfig, getHeader func(hash common.Hash, number uint64) *types.Header, checkTEVM func(common.Hash) (bool, error), block *types.Block, vmConfig vm.Config) (types.Receipts, error) {
header := block.Header()
vmConfig.TraceJumpDest = true
engine := ethash.NewFullFaker()
gp := new(core.GasPool).AddGas(block.GasLimit())
usedGas := new(uint64)
var receipts types.Receipts
if chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number()) == 0 {
misc.ApplyDAOHardFork(ibs)
}
for i, tx := range block.Transactions() {
ibs.Prepare(tx.Hash(), block.Hash(), i)
receipt, _, err := core.ApplyTransaction(chainConfig, getHeader, engine, nil, gp, ibs, txnWriter, header, tx, usedGas, vmConfig, checkTEVM)
if err != nil {
return nil, fmt.Errorf("could not apply tx %d [%x] failed: %v", i, tx.Hash(), err)
}
receipts = append(receipts, receipt)
//fmt.Printf("%d, cumulative gas: %d\n", i, receipt.CumulativeGasUsed)
}
if !vmConfig.ReadOnly {
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
if _, err := engine.FinalizeAndAssemble(chainConfig, header, ibs, block.Transactions(), block.Uncles(), receipts, nil); err != nil {
return nil, fmt.Errorf("finalize of block %d failed: %v", block.NumberU64(), err)
}
ctx := chainConfig.WithEIPsFlags(context.Background(), header.Number.Uint64())
if err := ibs.CommitBlock(ctx, blockWriter); err != nil {
return nil, fmt.Errorf("committing block %d failed: %v", block.NumberU64(), err)
}
}
return receipts, nil
}
var txParseTests = []struct {
@ -2139,6 +2241,43 @@ func testTxPool() error {
return nil
}
var receiptTests = []struct {
cborStr string
}{
{cborStr: "98a08400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000084004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400000840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000008400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400000840040000184004000018400400001840040000084004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000008400400001840040000184004000018400400001840040000184004000018400400000840040000184004000018400400001840040000184004000008400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001840040000184004000018400400001"},
{cborStr: "98a08400f60119e8948400f6011a00028ffb8400f6011a0003777d8400f6011a000a19e18400f6011a000e662c8400f6011a000eb8348400f6011a000f8c3a8400f6011a001040638400f6011a0011374c8400f6011a0011b0d18400f6011a00140bba8400f6011a00145dc28400f6011a00197eab8400f6011a0019d0b38400f6011a001a69898400f6011a001b60728400f6011a001da1118400f6011a001e92a58400f6011a001f5c478400f6011a001fae4f8400f6011a002000578400f6011a0020525f8400f6011a0020a4678400f6011a0020f66f8400f6011a002148778400f6011a00219a7f8400f6011a0021ec878400f6011a00223e8f8400f6011a002290978400f6011a0022e29f8400f6011a00235c248400f6011a0023ae2c8400f6011a002400348400f6011a0024523c8400f6011a0024cbc18400f6011a00251dc98400f6011a00256fd18400f6011a0025c1d98400f6011a0027505d8400f6011a0027a2658400f6011a00282a038400f6011a002be66d8400f6011a003536528400f6011a0035885a8400f6011a0035da628400f6011a0038f4e88400f6011a003946f08400f6011a003b664f8400f6011a003e02188400f6001a003e54208400f6011a004101348400f6011a004417598400f6011a0046439c8400f6011a00470f098400f6011a00479e928400f6011a0047f09a8400f6011a004842a28400f6011a0048f91a8400f6011a00494b228400f6011a00499d2a8400f6011a004a34c88400f6011a004a86d08400f6011a004ad8d88400f6011a004b2ae08400f6011a004d40c38400f6011a004e37ac8400f6011a005220de8400f6011a006680b08400f6011a0068b65f8400f6011a006cada28400f6011a006e77bf8400f6011a007117398400f6011a0071b02a8400f6011a007b8b518400f6011a007c4c088400f6011a007c98b78400f6011a007ceabf8400f6011a0080e8bf8400f6001a008175678400f6011a0081c76f8400f6011a00867d0d8400f6011a0087312a8400f6011a00881e0e8400f6011a008ad2058400f6011a008b86bd8400f6011a008bd8c58400f6011a008c2acd8400f6011a008c7cd58400f6011a008ccedd8400f6011a008d84808400f6011a008dd6888400f6011a008e28908400f6011a008eb8bb8400f6011a00902b2d8400f6011a0091b4508400f6001a0092b8098400f6011a00930a118400f6011a00935c198400f6011a0093ae218400f6011a009400298400f6011a00991f568400f6011a009a97b58400f6011a009ae9bd8400f6011a009b9ed78400f6011a009f69cf8400f6011a00a1364d8400f6011a00a188558400f6011a00a355a28400f6011a00a614ca8400f6011a00a6f05d8400f6011a00a7af5e8400f6011a00aaa96f8400f6011a00acd7898400f6011a00ae6c558400f6011a00b2a59f8400f6011a00b63dc68400f6011a00b8d7bb8400f6001a00b9591b8400f6011a00b9d0528400f6011a00ba22918400f6011a00ba74998400f6001a00baefa68400f6011a00bb41ae8400f6011a00bb93b68400f6011a00bc68e28400f6011a00bcbaea8400f6011a00bd0cf28400f6011a00bd5efa8400f6011a00bdb1028400f6011a00be030a8400f6011a00c1caed8400f6011a00c21cf58400f6011a00c26efd8400f6011a00c2c1058400f6001a00c33c008400f6011a00c3a63b8400f6011a00c585798400f6011a00c70f258400f6011a00c7612d8400f6011a00c98d4c8400f6011a00cb71988400f6001a00cbec998400f6011a00cfe49f8400f6011a00d1c6868400f6011a00d3a86d8400f6011a00d4671a8400f6001a00d4e2f08400f6011a00d6aca68400f6011a00d6feae8400f6011a00d750b68400f6011a00d80d078400f6011a00d85f0f8400f6011a00d915308400f6011a00dbc9d08400f6011a00ddcb408400f6011a00dfad278400f6011a00dfff2f8400f6011a00e272e38400f6011a00e4333c8400f6011a00e4c2c5"},
}
func testReceipts() error {
for i, tt := range receiptTests {
var cborBytes []byte
var err error
if cborBytes, err = hex.DecodeString(tt.cborStr); err != nil {
return err
}
var receipts types.Receipts
if err := cbor.Unmarshal(&receipts, bytes.NewReader(cborBytes)); err != nil {
log.Error("receipt unmarshal failed", "err", err)
return nil
}
fmt.Printf("%d: Number of receipts: %d\n", i, len(receipts))
for j, receipt := range receipts {
fmt.Printf("%d) type: %d, postState: %x, status: %d, cumulative gas: %d\n", j, receipt.Type, receipt.PostState, receipt.Status, receipt.CumulativeGasUsed)
}
var oldReceipts migrations.OldReceipts
reader := bytes.NewReader(cborBytes)
if err = cbor.Unmarshal(&oldReceipts, reader); err != nil {
fmt.Printf("Error parsing as old: %v\n", err)
} else {
fmt.Printf("Unread portion: %d\n", reader.Len())
for j, receipt := range oldReceipts {
fmt.Printf("old-%d) postState: %x, status: %d, cumulative gas: %d\n", j, receipt.PostState, receipt.Status, receipt.CumulativeGasUsed)
}
}
}
return nil
}
func main() {
flag.Parse()
@ -2301,6 +2440,9 @@ func main() {
case "testTxPool":
err = testTxPool()
case "testReceipts":
err = testReceipts()
}
if err != nil {

View File

@ -11,7 +11,6 @@ import (
"github.com/ledgerwatch/erigon/core/state"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/eth/tracers"
"github.com/ledgerwatch/erigon/ethdb"
"github.com/ledgerwatch/erigon/internal/ethapi"
"github.com/ledgerwatch/erigon/rpc"
"github.com/ledgerwatch/erigon/turbo/rpchelper"
@ -50,8 +49,7 @@ func (api *PrivateDebugAPIImpl) TraceTransaction(ctx context.Context, hash commo
getHeader := func(hash common.Hash, number uint64) *types.Header {
return rawdb.ReadHeader(tx, hash, number)
}
checkTEVM := ethdb.GetCheckTEVM(tx)
msg, blockCtx, txCtx, ibs, _, err := transactions.ComputeTxEnv(ctx, block, chainConfig, getHeader, checkTEVM, ethash.NewFaker(), tx, blockHash, txIndex)
msg, blockCtx, txCtx, ibs, _, err := transactions.ComputeTxEnv(ctx, block, chainConfig, getHeader, nil /* checkTEVM */, ethash.NewFaker(), tx, blockHash, txIndex)
if err != nil {
stream.WriteNil()
return err

View File

@ -134,18 +134,15 @@ func main() {
}
with(bench9Cmd, withErigonUrl, withGethUrl, withNeedCompare)
var bench10Cmd = &cobra.Command{
Use: "bench10",
var benchTraceTransactionCmd = &cobra.Command{
Use: "benchTraceTransaction",
Short: "",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
err := rpctest.Bench10(erigonURL, gethURL, blockFrom, blockTo, recordFile)
if err != nil {
log.Error("bench 10 err", "err", err)
}
rpctest.BenchTraceTransaction(erigonURL, gethURL, needCompare, blockFrom, blockTo, recordFile)
},
}
with(bench10Cmd, withGethUrl, withErigonUrl, withBlockNum, withRecord)
with(benchTraceTransactionCmd, withGethUrl, withErigonUrl, withNeedCompare, withBlockNum, withRecord)
var bench11Cmd = &cobra.Command{
Use: "bench11",
@ -248,7 +245,7 @@ func main() {
bench7Cmd,
bench8Cmd,
bench9Cmd,
bench10Cmd,
benchTraceTransactionCmd,
bench11Cmd,
bench12Cmd,
bench13Cmd,

View File

@ -1,63 +0,0 @@
package rpctest
import (
"fmt"
"net/http"
"time"
)
func Bench10(erigonUrl, gethUrl string, blockFrom uint64, blockTo uint64, recordFile string) error {
setRoutes(erigonUrl, gethUrl)
var client = &http.Client{
Timeout: time.Second * 600,
}
var res CallResult
reqGen := &RequestGenerator{
client: client,
}
reqGen.reqID++
for bn := blockFrom; bn < blockTo; bn++ {
var b EthBlockByNumber
res = reqGen.Erigon("eth_getBlockByNumber", reqGen.getBlockByNumber(bn), &b)
if res.Err != nil {
return fmt.Errorf("retrieve block (Erigon) %d: %v", blockFrom, res.Err)
}
if b.Error != nil {
return fmt.Errorf("retrieving block (Erigon): %d %s", b.Error.Code, b.Error.Message)
}
for _, tx := range b.Result.Transactions {
reqGen.reqID++
var trace EthTxTrace
res = reqGen.Erigon("debug_traceTransaction", reqGen.traceTransaction(tx.Hash), &trace)
if res.Err != nil {
fmt.Printf("Could not trace transaction (Erigon) %s: %v\n", tx.Hash, res.Err)
print(client, routes[Erigon], reqGen.traceTransaction(tx.Hash))
}
if trace.Error != nil {
fmt.Printf("Error tracing transaction (Erigon): %d %s\n", trace.Error.Code, trace.Error.Message)
}
var traceg EthTxTrace
res = reqGen.Geth("debug_traceTransaction", reqGen.traceTransaction(tx.Hash), &traceg)
if res.Err != nil {
print(client, routes[Geth], reqGen.traceTransaction(tx.Hash))
return fmt.Errorf("trace transaction (geth) %s: %v", tx.Hash, res.Err)
}
if traceg.Error != nil {
return fmt.Errorf("tracing transaction (geth): %d %s", traceg.Error.Code, traceg.Error.Message)
}
if res.Err == nil && trace.Error == nil {
if !compareTraces(&trace, &traceg) {
return fmt.Errorf("different traces block %d, tx %s", blockFrom, tx.Hash)
}
}
reqGen.reqID++
}
}
return nil
}

View File

@ -0,0 +1,87 @@
package rpctest
import (
"bufio"
"fmt"
"net/http"
"os"
"time"
)
func BenchTraceTransaction(erigonUrl, gethUrl string, needCompare bool, blockFrom uint64, blockTo uint64, recordFile string) {
setRoutes(erigonUrl, gethUrl)
var client = &http.Client{
Timeout: time.Second * 600,
}
var rec *bufio.Writer
if recordFile != "" {
f, err := os.Create(recordFile)
if err != nil {
fmt.Printf("Cannot create file %s for recording: %v\n", recordFile, err)
return
}
defer f.Close()
rec = bufio.NewWriter(f)
defer rec.Flush()
}
var res CallResult
reqGen := &RequestGenerator{
client: client,
}
reqGen.reqID++
for bn := blockFrom; bn < blockTo; bn++ {
var b EthBlockByNumber
res = reqGen.Erigon("eth_getBlockByNumber", reqGen.getBlockByNumber(bn), &b)
if res.Err != nil {
fmt.Printf("retrieve block (Erigon) %d: %v", blockFrom, res.Err)
return
}
if b.Error != nil {
fmt.Printf("retrieving block (Erigon): %d %s", b.Error.Code, b.Error.Message)
return
}
for _, tx := range b.Result.Transactions {
reqGen.reqID++
request := reqGen.traceTransaction(tx.Hash)
recording := rec != nil // This flag will be set to false if recording is not to be performed
res = reqGen.Erigon2("debug_traceTransaction", request)
if res.Err != nil {
fmt.Printf("Could not trace transaction (Erigon) %s: %v\n", tx.Hash, res.Err)
return
}
if errVal := res.Result.Get("error"); errVal != nil {
fmt.Printf("Error tracing transaction (Erigon): %d %s\n", errVal.GetInt("code"), errVal.GetStringBytes("message"))
return
}
if needCompare {
resg := reqGen.Geth2("debug_traceTransaction", request)
if resg.Err != nil {
fmt.Printf("Could not trace transaction (geth) %s: %v\n", tx.Hash, res.Err)
return
}
if errVal := resg.Result.Get("error"); errVal != nil {
fmt.Printf("Error tracing transaction (geth): %d %s\n", errVal.GetInt("code"), errVal.GetStringBytes("message"))
return
}
if resg.Err == nil && resg.Result.Get("error") == nil {
if err := compareResults(res.Result, resg.Result); err != nil {
fmt.Printf("Different traceTransaction block %d, tx %s: %v\n", bn, tx.Hash, err)
fmt.Printf("\n\nTG response=================================\n%s\n", res.Response)
fmt.Printf("\n\nG response=================================\n%s\n", resg.Response)
return
}
}
}
if recording {
fmt.Fprintf(rec, "%s\n%s\n\n", request, res.Response)
}
}
}
}

View File

@ -19,7 +19,6 @@ import (
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/eth/stagedsync/stages"
"github.com/ledgerwatch/erigon/ethdb"
kv2 "github.com/ledgerwatch/erigon/ethdb/kv"
"github.com/ledgerwatch/erigon/log"
"github.com/spf13/cobra"
@ -137,8 +136,7 @@ func CheckChangeSets(genesis *core.Genesis, blockNum uint64, chaindata string, h
}
getHeader := func(hash common.Hash, number uint64) *types.Header { return rawdb.ReadHeader(rwtx, hash, number) }
checkTEVM := ethdb.GetCheckTEVM(rwtx)
receipts, err1 := runBlock(intraBlockState, noOpWriter, blockWriter, chainConfig, getHeader, checkTEVM, block, vmConfig)
receipts, err1 := runBlock(intraBlockState, noOpWriter, blockWriter, chainConfig, getHeader, nil /* checkTEVM */, block, vmConfig)
if err1 != nil {
return err1
}

View File

@ -393,10 +393,10 @@ func OpcodeTracer(genesis *core.Genesis, blockNum uint64, chaindata string, numB
ot := NewOpcodeTracer(blockNum, saveOpcodes, saveBblocks)
chainDb := kv.MustOpen(chaindata)
chainDb := kv.MustOpen(chaindata).RwKV()
defer chainDb.Close()
historyDb := chainDb
historyTx, err1 := historyDb.Begin(context.Background(), ethdb.RO)
historyTx, err1 := historyDb.BeginRo(context.Background())
if err1 != nil {
return err1
}
@ -526,7 +526,7 @@ func OpcodeTracer(genesis *core.Genesis, blockNum uint64, chaindata string, numB
blockNumLastReport := blockNum
for !interrupt {
var block *types.Block
if err := chainDb.RwKV().View(context.Background(), func(tx ethdb.Tx) (err error) {
if err := chainDb.View(context.Background(), func(tx ethdb.Tx) (err error) {
block, err = rawdb.ReadBlockByNumber(tx, blockNum)
return err
}); err != nil {
@ -544,13 +544,12 @@ func OpcodeTracer(genesis *core.Genesis, blockNum uint64, chaindata string, numB
ot.fsumWriter = bufio.NewWriter(fsum)
}
dbstate := state.NewPlainDBState(historyTx, block.NumberU64()-1)
dbstate := state.NewPlainKvState(historyTx, block.NumberU64()-1)
intraBlockState := state.New(dbstate)
intraBlockState.SetTracer(ot)
getHeader := func(hash common.Hash, number uint64) *types.Header { return rawdb.ReadHeader(chainDb, hash, number) }
checkTEVM := ethdb.GetCheckTEVM(chainDb)
receipts, err1 := runBlock(intraBlockState, noOpWriter, noOpWriter, chainConfig, getHeader, checkTEVM, block, vmConfig)
getHeader := func(hash common.Hash, number uint64) *types.Header { return rawdb.ReadHeader(historyTx, hash, number) }
receipts, err1 := runBlock(intraBlockState, noOpWriter, noOpWriter, chainConfig, getHeader, nil /* checkTEVM */, block, vmConfig)
if err1 != nil {
return err1
}

View File

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"math/big"
"sort"
"time"
"github.com/holiman/uint256"
@ -157,8 +158,13 @@ func TraceTx(
stream.WriteObjectField("failed")
stream.WriteBool(result.Failed())
stream.WriteMore()
// If the result contains a revert reason, return it.
returnVal := fmt.Sprintf("%x", result.Return())
if len(result.Revert()) > 0 {
returnVal = fmt.Sprintf("%x", result.Revert())
}
stream.WriteObjectField("returnValue")
stream.WriteString(fmt.Sprintf("%x", result.Return()))
stream.WriteString(returnVal)
stream.WriteObjectEnd()
} else {
if r, err1 := tracer.(*tracers.Tracer).GetResult(); err1 == nil {
@ -181,10 +187,11 @@ type JsonStreamLogger struct {
stream *jsoniter.Stream
firstCapture bool
storage map[common.Address]vm.Storage
logs []vm.StructLog
output []byte //nolint
err error //nolint
locations common.Hashes // For sorting
storage map[common.Address]vm.Storage
logs []vm.StructLog
output []byte //nolint
err error //nolint
}
// NewStructLogger returns a new logger
@ -299,14 +306,24 @@ func (l *JsonStreamLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, ga
l.stream.WriteObjectField("storage")
l.stream.WriteObjectStart()
first := true
for i, storageValue := range l.storage[contract.Address()] {
// Sort storage by locations for easier comparison with geth
if l.locations != nil {
l.locations = l.locations[:0]
}
s := l.storage[contract.Address()]
for loc := range s {
l.locations = append(l.locations, loc)
}
sort.Sort(l.locations)
for _, loc := range l.locations {
value := s[loc]
if first {
first = false
} else {
l.stream.WriteMore()
}
l.stream.WriteObjectField(fmt.Sprintf("%x", i))
l.stream.WriteString(fmt.Sprintf("%x", storageValue))
l.stream.WriteObjectField(fmt.Sprintf("%x", loc))
l.stream.WriteString(fmt.Sprintf("%x", value))
}
l.stream.WriteObjectEnd()
}