Skip to content

Commit 40aeaa6

Browse files
committed
impl trace_rawTransaction
1 parent 5ff1079 commit 40aeaa6

File tree

5 files changed

+183
-7
lines changed

5 files changed

+183
-7
lines changed

.github/workflows/scripts/run_rpc_tests_ethereum.sh

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ DISABLED_TEST_LIST=(
1111
# Failing after the PR https://github.com/erigontech/erigon/pull/13617 that fixed this incompatibility
1212
# issues https://hive.pectra-devnet-5.ethpandaops.io/suite.html?suiteid=1738266984-51ae1a2f376e5de5e9ba68f034f80e32.json&suitename=rpc-compat
1313
net_listening/test_1.json
14-
# Erigon2 and Erigon3 never supported this api methods
15-
trace_rawTransaction
1614
# Temporary disable required block 24298763
1715
debug_traceBlockByNumber/test_51.json
1816
# to investigate

cmd/rpcdaemon/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ The following table shows the current implementation status of Erigon's RPC daem
355355
| | | |
356356
| trace_call | Yes | |
357357
| trace_callMany | Yes | |
358-
| trace_rawTransaction | - | not yet implemented (come help!) |
358+
| trace_rawTransaction | Yes | |
359359
| trace_replayBlockTransactions | yes | stateDiff only (come help!) |
360360
| trace_replayTransaction | yes | stateDiff only (come help!) |
361361
| trace_block | Yes | |

rpc/jsonrpc/trace_adhoc.go

Lines changed: 152 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,7 +1734,156 @@ func (api *TraceAPIImpl) doCall(ctx context.Context, dbtx kv.Tx, stateReader sta
17341734
}
17351735

17361736
// RawTransaction implements trace_rawTransaction.
1737-
func (api *TraceAPIImpl) RawTransaction(ctx context.Context, txHash common.Hash, traceTypes []string) ([]any, error) {
1738-
var stub []any
1739-
return stub, fmt.Errorf(NotImplemented, "trace_rawTransaction")
1737+
func (api *TraceAPIImpl) RawTransaction(ctx context.Context, encodedTx hexutil.Bytes, traceTypes []string) (*TraceCallResult, error) {
1738+
txn, err := types.DecodeWrappedTransaction(encodedTx)
1739+
if err != nil {
1740+
return nil, err
1741+
}
1742+
1743+
dbtx, err := api.kv.BeginTemporalRo(ctx)
1744+
if err != nil {
1745+
return nil, err
1746+
}
1747+
defer dbtx.Rollback()
1748+
1749+
chainConfig, err := api.chainConfig(ctx, dbtx)
1750+
if err != nil {
1751+
return nil, err
1752+
}
1753+
engine := api.engine()
1754+
1755+
var num = rpc.LatestBlockNumber
1756+
blockNrOrHash := rpc.BlockNumberOrHash{BlockNumber: &num}
1757+
1758+
blockNumber, hash, latest, err := rpchelper.GetBlockNumber(ctx, blockNrOrHash, dbtx, api._blockReader, api.filters)
1759+
if err != nil {
1760+
return nil, err
1761+
}
1762+
1763+
err = api.BaseAPI.checkPruneHistory(ctx, dbtx, blockNumber)
1764+
if err != nil {
1765+
return nil, err
1766+
}
1767+
1768+
header, err := api.headerByNumber(ctx, rpc.BlockNumber(blockNumber), dbtx)
1769+
if err != nil {
1770+
return nil, err
1771+
}
1772+
if header == nil {
1773+
return nil, fmt.Errorf("block %d(%x) not found", blockNumber, hash)
1774+
}
1775+
1776+
err = rpchelper.CheckBlockExecuted(dbtx, blockNumber)
1777+
if err != nil {
1778+
return nil, err
1779+
}
1780+
1781+
stateReader, err := rpchelper.CreateStateReaderFromBlockNumber(ctx, dbtx, blockNumber, latest, 0, api.stateCache, api._txNumReader)
1782+
if err != nil {
1783+
return nil, err
1784+
}
1785+
1786+
ibs := state.New(stateReader)
1787+
1788+
var cancel context.CancelFunc
1789+
if api.evmCallTimeout > 0 {
1790+
ctx, cancel = context.WithTimeout(ctx, api.evmCallTimeout)
1791+
} else {
1792+
ctx, cancel = context.WithCancel(ctx)
1793+
}
1794+
defer cancel()
1795+
1796+
traceResult := &TraceCallResult{Trace: []*ParityTrace{}}
1797+
var traceTypeTrace, traceTypeStateDiff, traceTypeVmTrace bool
1798+
for _, traceType := range traceTypes {
1799+
switch traceType {
1800+
case TraceTypeTrace:
1801+
traceTypeTrace = true
1802+
case TraceTypeStateDiff:
1803+
traceTypeStateDiff = true
1804+
case TraceTypeVmTrace:
1805+
traceTypeVmTrace = true
1806+
default:
1807+
return nil, fmt.Errorf("unrecognized trace type: %s", traceType)
1808+
}
1809+
}
1810+
if traceTypeVmTrace {
1811+
traceResult.VmTrace = &VmTrace{Ops: []*VmTraceOp{}}
1812+
}
1813+
1814+
var ot OeTracer
1815+
ot.config, err = parseOeTracerConfig(nil)
1816+
if err != nil {
1817+
return nil, err
1818+
}
1819+
ot.compat = api.compatibility
1820+
if traceTypeTrace || traceTypeVmTrace || traceTypeStateDiff {
1821+
ot.r = traceResult
1822+
ot.traceAddr = []int{}
1823+
}
1824+
1825+
signer := types.MakeSigner(chainConfig, header.Number.Uint64(), header.Time)
1826+
blockCtx := transactions.NewEVMBlockContext(engine, header, blockNrOrHash.RequireCanonical, dbtx, api._blockReader, chainConfig)
1827+
rules := blockCtx.Rules(chainConfig)
1828+
1829+
msg, err := txn.AsMessage(*signer, header.BaseFee, rules)
1830+
if err != nil {
1831+
return nil, err
1832+
}
1833+
msg.SetCheckNonce(false)
1834+
msg.SetCheckTransaction(false)
1835+
msg.SetCheckGas(false)
1836+
1837+
txCtx := protocol.NewEVMTxContext(msg)
1838+
1839+
blockCtx.GasLimit = math.MaxUint64
1840+
blockCtx.MaxGasLimit = true
1841+
1842+
evm := vm.NewEVM(blockCtx, txCtx, ibs, chainConfig, vm.Config{Tracer: ot.Tracer().Hooks})
1843+
1844+
go func() {
1845+
<-ctx.Done()
1846+
evm.Cancel()
1847+
}()
1848+
1849+
gp := new(protocol.GasPool).AddGas(msg.Gas()).AddBlobGas(msg.BlobGas())
1850+
var execResult *evmtypes.ExecutionResult
1851+
ibs.SetTxContext(blockCtx.BlockNumber, 0)
1852+
ibs.SetHooks(ot.Tracer().Hooks)
1853+
1854+
if ot.Tracer() != nil && ot.Tracer().Hooks.OnTxStart != nil {
1855+
ot.Tracer().OnTxStart(evm.GetVMContext(), txn, msg.From())
1856+
}
1857+
execResult, err = protocol.ApplyMessage(evm, msg, gp, true /* refunds */, true /* gasBailout */, engine)
1858+
if err != nil {
1859+
if ot.Tracer() != nil && ot.Tracer().Hooks.OnTxEnd != nil {
1860+
ot.Tracer().OnTxEnd(nil, err)
1861+
}
1862+
return nil, err
1863+
}
1864+
if ot.Tracer() != nil && ot.Tracer().Hooks.OnTxEnd != nil {
1865+
ot.Tracer().OnTxEnd(&types.Receipt{GasUsed: execResult.ReceiptGasUsed}, nil)
1866+
}
1867+
1868+
traceResult.Output = common.Copy(execResult.ReturnData)
1869+
if traceTypeStateDiff {
1870+
sdMap := make(map[accounts.Address]*StateDiffAccount)
1871+
traceResult.StateDiff = sdMap
1872+
sd := &StateDiff{sdMap: sdMap}
1873+
if err = ibs.FinalizeTx(evm.ChainRules(), sd); err != nil {
1874+
return nil, err
1875+
}
1876+
initialIbs := state.New(stateReader)
1877+
sd.CompareStates(initialIbs, ibs)
1878+
}
1879+
1880+
if evm.Cancelled() {
1881+
return nil, fmt.Errorf("execution aborted (timeout = %v)", api.evmCallTimeout)
1882+
}
1883+
1884+
if !traceTypeTrace {
1885+
traceResult.Trace = []*ParityTrace{}
1886+
}
1887+
1888+
return traceResult, nil
17401889
}

rpc/jsonrpc/trace_adhoc_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package jsonrpc
1818

1919
import (
20+
"bytes"
2021
"context"
2122
"encoding/json"
2223
"os"
@@ -459,3 +460,31 @@ func TestOeTracer(t *testing.T) {
459460
})
460461
}
461462
}
463+
464+
func TestRawTransaction(t *testing.T) {
465+
m, _, _ := rpcdaemontest.CreateTestExecModule(t)
466+
api := NewTraceAPI(newBaseApiForTest(m), m.DB, &httpcfg.HttpCfg{})
467+
468+
// Read a transaction from block 6 and re-encode it as raw bytes.
469+
var encodedTx []byte
470+
if err := m.DB.View(context.Background(), func(tx kv.Tx) error {
471+
b, err := m.BlockReader.BlockByNumber(m.Ctx, tx, 6)
472+
if err != nil {
473+
return err
474+
}
475+
txn := b.Transactions()[0]
476+
var buf bytes.Buffer
477+
if err = txn.MarshalBinary(&buf); err != nil {
478+
return err
479+
}
480+
encodedTx = buf.Bytes()
481+
return nil
482+
}); err != nil {
483+
t.Fatal(err)
484+
}
485+
486+
result, err := api.RawTransaction(context.Background(), encodedTx, []string{"trace"})
487+
require.NoError(t, err)
488+
require.NotNil(t, result)
489+
require.NotEmpty(t, result.Trace)
490+
}

rpc/jsonrpc/trace_api.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type TraceAPI interface {
3737
ReplayTransaction(ctx context.Context, txHash common.Hash, traceTypes []string, gasBailOut *bool, traceConfig *config.TraceConfig) (*TraceCallResult, error)
3838
Call(ctx context.Context, call TraceCallParam, types []string, blockNr *rpc.BlockNumberOrHash, traceConfig *config.TraceConfig) (*TraceCallResult, error)
3939
CallMany(ctx context.Context, calls json.RawMessage, blockNr *rpc.BlockNumberOrHash, traceConfig *config.TraceConfig) ([]*TraceCallResult, error)
40-
RawTransaction(ctx context.Context, txHash common.Hash, traceTypes []string) ([]any, error)
40+
RawTransaction(ctx context.Context, encodedTx hexutil.Bytes, traceTypes []string) (*TraceCallResult, error)
4141

4242
// Filtering (see ./trace_filtering.go)
4343

0 commit comments

Comments
 (0)