From f5e42b1e7aa232548be389953969bc9f05aaad7f Mon Sep 17 00:00:00 2001 From: ledgerwatch Date: Sat, 3 Jul 2021 14:34:23 +0100 Subject: [PATCH] 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 --- cmd/hack/hack.go | 172 ++++++++++++++++-- cmd/rpcdaemon/commands/tracing.go | 4 +- cmd/rpctest/main.go | 13 +- cmd/rpctest/rpctest/bench10.go | 63 ------- cmd/rpctest/rpctest/bench_tracetransaction.go | 87 +++++++++ cmd/state/commands/check_change_sets.go | 4 +- cmd/state/commands/opcode_tracer.go | 13 +- turbo/transactions/tracing.go | 33 +++- 8 files changed, 282 insertions(+), 107 deletions(-) delete mode 100644 cmd/rpctest/rpctest/bench10.go create mode 100644 cmd/rpctest/rpctest/bench_tracetransaction.go diff --git a/cmd/hack/hack.go b/cmd/hack/hack.go index 10f7f0c52..ee5921ff2 100644 --- a/cmd/hack/hack.go +++ b/cmd/hack/hack.go @@ -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 { diff --git a/cmd/rpcdaemon/commands/tracing.go b/cmd/rpcdaemon/commands/tracing.go index 7bc99bee5..5d4634b86 100644 --- a/cmd/rpcdaemon/commands/tracing.go +++ b/cmd/rpcdaemon/commands/tracing.go @@ -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 diff --git a/cmd/rpctest/main.go b/cmd/rpctest/main.go index dcc16a57f..97c4f4e0d 100644 --- a/cmd/rpctest/main.go +++ b/cmd/rpctest/main.go @@ -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, diff --git a/cmd/rpctest/rpctest/bench10.go b/cmd/rpctest/rpctest/bench10.go deleted file mode 100644 index 991e87f2c..000000000 --- a/cmd/rpctest/rpctest/bench10.go +++ /dev/null @@ -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 -} diff --git a/cmd/rpctest/rpctest/bench_tracetransaction.go b/cmd/rpctest/rpctest/bench_tracetransaction.go new file mode 100644 index 000000000..0703911f7 --- /dev/null +++ b/cmd/rpctest/rpctest/bench_tracetransaction.go @@ -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) + } + } + } +} diff --git a/cmd/state/commands/check_change_sets.go b/cmd/state/commands/check_change_sets.go index bd2bac4b1..229fd9931 100644 --- a/cmd/state/commands/check_change_sets.go +++ b/cmd/state/commands/check_change_sets.go @@ -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 } diff --git a/cmd/state/commands/opcode_tracer.go b/cmd/state/commands/opcode_tracer.go index 484b14afc..6c121ad43 100644 --- a/cmd/state/commands/opcode_tracer.go +++ b/cmd/state/commands/opcode_tracer.go @@ -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 } diff --git a/turbo/transactions/tracing.go b/turbo/transactions/tracing.go index 1ccc0da59..de13b22c4 100644 --- a/turbo/transactions/tracing.go +++ b/turbo/transactions/tracing.go @@ -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() }