Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/scripts/run_rpc_tests_ethereum.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ DISABLED_TEST_LIST=(
# Failing after the PR https://github.com/erigontech/erigon/pull/13617 that fixed this incompatibility
# issues https://hive.pectra-devnet-5.ethpandaops.io/suite.html?suiteid=1738266984-51ae1a2f376e5de5e9ba68f034f80e32.json&suitename=rpc-compat
net_listening/test_1.json
# Erigon2 and Erigon3 never supported this api methods
trace_rawTransaction
# Temporary disable required block 24298763
debug_traceBlockByNumber/test_51.json
# to investigate
Expand Down
2 changes: 1 addition & 1 deletion cmd/rpcdaemon/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ The following table shows the current implementation status of Erigon's RPC daem
| | | |
| trace_call | Yes | |
| trace_callMany | Yes | |
| trace_rawTransaction | - | not yet implemented (come help!) |
| trace_rawTransaction | Yes | |
| trace_replayBlockTransactions | yes | stateDiff only (come help!) |
| trace_replayTransaction | yes | stateDiff only (come help!) |
| trace_block | Yes | |
Expand Down
155 changes: 152 additions & 3 deletions rpc/jsonrpc/trace_adhoc.go
Original file line number Diff line number Diff line change
Expand Up @@ -1734,7 +1734,156 @@ func (api *TraceAPIImpl) doCall(ctx context.Context, dbtx kv.Tx, stateReader sta
}

// RawTransaction implements trace_rawTransaction.
func (api *TraceAPIImpl) RawTransaction(ctx context.Context, txHash common.Hash, traceTypes []string) ([]any, error) {
var stub []any
return stub, fmt.Errorf(NotImplemented, "trace_rawTransaction")
func (api *TraceAPIImpl) RawTransaction(ctx context.Context, encodedTx hexutil.Bytes, traceTypes []string) (*TraceCallResult, error) {
txn, err := types.DecodeWrappedTransaction(encodedTx)
if err != nil {
return nil, err
}

dbtx, err := api.kv.BeginTemporalRo(ctx)
if err != nil {
return nil, err
}
defer dbtx.Rollback()

chainConfig, err := api.chainConfig(ctx, dbtx)
if err != nil {
return nil, err
}
engine := api.engine()

var num = rpc.LatestBlockNumber
blockNrOrHash := rpc.BlockNumberOrHash{BlockNumber: &num}

blockNumber, hash, latest, err := rpchelper.GetBlockNumber(ctx, blockNrOrHash, dbtx, api._blockReader, api.filters)
if err != nil {
return nil, err
}

err = api.BaseAPI.checkPruneHistory(ctx, dbtx, blockNumber)
if err != nil {
return nil, err
}

header, err := api.headerByNumber(ctx, rpc.BlockNumber(blockNumber), dbtx)
if err != nil {
return nil, err
}
if header == nil {
return nil, fmt.Errorf("block %d(%x) not found", blockNumber, hash)
}

err = rpchelper.CheckBlockExecuted(dbtx, blockNumber)
if err != nil {
return nil, err
}

stateReader, err := rpchelper.CreateStateReaderFromBlockNumber(ctx, dbtx, blockNumber, latest, 0, api.stateCache, api._txNumReader)
if err != nil {
return nil, err
}

ibs := state.New(stateReader)

var cancel context.CancelFunc
if api.evmCallTimeout > 0 {
ctx, cancel = context.WithTimeout(ctx, api.evmCallTimeout)
} else {
ctx, cancel = context.WithCancel(ctx)
}
defer cancel()

traceResult := &TraceCallResult{Trace: []*ParityTrace{}}
var traceTypeTrace, traceTypeStateDiff, traceTypeVmTrace bool
for _, traceType := range traceTypes {
switch traceType {
case TraceTypeTrace:
traceTypeTrace = true
case TraceTypeStateDiff:
traceTypeStateDiff = true
case TraceTypeVmTrace:
traceTypeVmTrace = true
default:
return nil, fmt.Errorf("unrecognized trace type: %s", traceType)
}
}
if traceTypeVmTrace {
traceResult.VmTrace = &VmTrace{Ops: []*VmTraceOp{}}
}

var ot OeTracer
ot.config, err = parseOeTracerConfig(nil)
if err != nil {
return nil, err
}
ot.compat = api.compatibility
if traceTypeTrace || traceTypeVmTrace || traceTypeStateDiff {
ot.r = traceResult
ot.traceAddr = []int{}
}

signer := types.MakeSigner(chainConfig, header.Number.Uint64(), header.Time)
blockCtx := transactions.NewEVMBlockContext(engine, header, blockNrOrHash.RequireCanonical, dbtx, api._blockReader, chainConfig)
rules := blockCtx.Rules(chainConfig)

msg, err := txn.AsMessage(*signer, header.BaseFee, rules)
if err != nil {
return nil, err
}
msg.SetCheckNonce(false)
msg.SetCheckTransaction(false)
msg.SetCheckGas(false)

txCtx := protocol.NewEVMTxContext(msg)

blockCtx.GasLimit = math.MaxUint64
blockCtx.MaxGasLimit = true

evm := vm.NewEVM(blockCtx, txCtx, ibs, chainConfig, vm.Config{Tracer: ot.Tracer().Hooks})

go func() {
<-ctx.Done()
evm.Cancel()
}()

gp := new(protocol.GasPool).AddGas(msg.Gas()).AddBlobGas(msg.BlobGas())
var execResult *evmtypes.ExecutionResult
ibs.SetTxContext(blockCtx.BlockNumber, 0)
ibs.SetHooks(ot.Tracer().Hooks)

if ot.Tracer() != nil && ot.Tracer().Hooks.OnTxStart != nil {
ot.Tracer().OnTxStart(evm.GetVMContext(), txn, msg.From())
}
execResult, err = protocol.ApplyMessage(evm, msg, gp, true /* refunds */, true /* gasBailout */, engine)
if err != nil {
if ot.Tracer() != nil && ot.Tracer().Hooks.OnTxEnd != nil {
ot.Tracer().OnTxEnd(nil, err)
}
return nil, err
}
if ot.Tracer() != nil && ot.Tracer().Hooks.OnTxEnd != nil {
ot.Tracer().OnTxEnd(&types.Receipt{GasUsed: execResult.ReceiptGasUsed}, nil)
}

traceResult.Output = common.Copy(execResult.ReturnData)
if traceTypeStateDiff {
sdMap := make(map[accounts.Address]*StateDiffAccount)
traceResult.StateDiff = sdMap
sd := &StateDiff{sdMap: sdMap}
if err = ibs.FinalizeTx(evm.ChainRules(), sd); err != nil {
return nil, err
}
initialIbs := state.New(stateReader)
sd.CompareStates(initialIbs, ibs)
}

if evm.Cancelled() {
return nil, fmt.Errorf("execution aborted (timeout = %v)", api.evmCallTimeout)
}

if !traceTypeTrace {
traceResult.Trace = []*ParityTrace{}
}

return traceResult, nil
}
29 changes: 29 additions & 0 deletions rpc/jsonrpc/trace_adhoc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package jsonrpc

import (
"bytes"
"context"
"encoding/json"
"os"
Expand Down Expand Up @@ -459,3 +460,31 @@ func TestOeTracer(t *testing.T) {
})
}
}

func TestRawTransaction(t *testing.T) {
m, _, _ := rpcdaemontest.CreateTestExecModule(t)
api := NewTraceAPI(newBaseApiForTest(m), m.DB, &httpcfg.HttpCfg{})

// Read a transaction from block 6 and re-encode it as raw bytes.
var encodedTx []byte
if err := m.DB.View(context.Background(), func(tx kv.Tx) error {
b, err := m.BlockReader.BlockByNumber(m.Ctx, tx, 6)
if err != nil {
return err
}
txn := b.Transactions()[0]
var buf bytes.Buffer
if err = txn.MarshalBinary(&buf); err != nil {
return err
}
encodedTx = buf.Bytes()
return nil
}); err != nil {
t.Fatal(err)
}

result, err := api.RawTransaction(context.Background(), encodedTx, []string{"trace"})
require.NoError(t, err)
require.NotNil(t, result)
require.NotEmpty(t, result.Trace)
}
2 changes: 1 addition & 1 deletion rpc/jsonrpc/trace_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type TraceAPI interface {
ReplayTransaction(ctx context.Context, txHash common.Hash, traceTypes []string, gasBailOut *bool, traceConfig *config.TraceConfig) (*TraceCallResult, error)
Call(ctx context.Context, call TraceCallParam, types []string, blockNr *rpc.BlockNumberOrHash, traceConfig *config.TraceConfig) (*TraceCallResult, error)
CallMany(ctx context.Context, calls json.RawMessage, blockNr *rpc.BlockNumberOrHash, traceConfig *config.TraceConfig) ([]*TraceCallResult, error)
RawTransaction(ctx context.Context, txHash common.Hash, traceTypes []string) ([]any, error)
RawTransaction(ctx context.Context, encodedTx hexutil.Bytes, traceTypes []string) (*TraceCallResult, error)

// Filtering (see ./trace_filtering.go)

Expand Down
Loading