Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(evm): add prestate tracer (fixes #3512) #3513

Merged
merged 2 commits into from
Oct 24, 2024
Merged
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
77 changes: 39 additions & 38 deletions packages/evm/jsonrpc/evmchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -677,8 +677,16 @@ func (e *EVMChain) isFakeTransaction(tx *types.Transaction) bool {
return false
}

// Trace allows the tracing of EVM transactions and considers "fake" evm transactions that are emitted when ISC internal requests are being made. (Transfer of funds from L1->L2EVM for example)
func (e *EVMChain) trace(config *tracers.TraceConfig, blockInfo *blocklog.BlockInfo, requestsInBlock []isc.Request, evmTxs types.Transactions, txIndex uint64, txHash common.Hash, blockHash common.Hash) (json.RawMessage, error) {
// traceTransaction allows the tracing of a single EVM transaction.
// "Fake" transactions that are emitted e.g. for L1 deposits return some mocked trace.
func (e *EVMChain) traceTransaction(
config *tracers.TraceConfig,
blockInfo *blocklog.BlockInfo,
requestsInBlock []isc.Request,
tx *types.Transaction,
txIndex uint64,
blockHash common.Hash,
) (json.RawMessage, error) {
tracerType := "callTracer"
if config.Tracer != nil {
tracerType = *config.Tracer
Expand All @@ -690,55 +698,29 @@ func (e *EVMChain) trace(config *tracers.TraceConfig, blockInfo *blocklog.BlockI
BlockHash: blockHash,
BlockNumber: new(big.Int).SetUint64(blockNumber),
TxIndex: int(txIndex),
TxHash: txHash,
TxHash: tx.Hash(),
}, config.TracerConfig)
if err != nil {
return nil, err
}

if e.isFakeTransaction(tx) {
return tracer.TraceFakeTx(tx)
}

err = e.backend.EVMTrace(
blockInfo.PreviousAliasOutput,
blockInfo.Timestamp,
requestsInBlock,
&txIndex,
&blockNumber,
tracer,
tracer.Tracer,
)
if err != nil {
return nil, err
}

result, err := tracer.GetResult()
if err != nil {
if !errors.Is(err, ErrIncorrectTopLevelCalls) {
return nil, err
}

tx, ok := lo.Find(evmTxs, func(tx *types.Transaction) bool { return slices.Equal(txHash.Bytes(), tx.Hash().Bytes()) })
if !ok {
return nil, fmt.Errorf("can not find transaction: %v", txHash.String())
}

if e.isFakeTransaction(tx) {
return json.Marshal(RPCMarshalTransactionTraceForFakeTX(tx, tx.GasPrice()))
}
}

return result, nil
}

func (e *EVMChain) traceTransaction(config *tracers.TraceConfig, txIndex uint64, txHash common.Hash, blockNumber uint64, blockHash common.Hash) (any, error) {
iscBlock, iscRequestsInBlock, err := e.iscRequestsInBlock(blockNumber)
if err != nil {
return nil, err
}

blockTxs, err := e.txsByBlockNumber(new(big.Int).SetUint64(blockNumber))
if err != nil {
return nil, err
}

return e.trace(config, iscBlock, iscRequestsInBlock, blockTxs, txIndex, txHash, blockHash)
return tracer.GetResult()
}

func (e *EVMChain) traceBlock(config *tracers.TraceConfig, block *types.Block) (any, error) {
Expand All @@ -754,7 +736,14 @@ func (e *EVMChain) traceBlock(config *tracers.TraceConfig, block *types.Block) (

results := make([]TxTraceResult, 0)
for i, tx := range blockTxs {
result, err := e.trace(config, iscBlock, iscRequestsInBlock, blockTxs, uint64(i), tx.Hash(), block.Hash())
result, err := e.traceTransaction(
config,
iscBlock,
iscRequestsInBlock,
tx,
uint64(i),
block.Hash(),
)

// Transactions which failed tracing will be omitted, so the rest of the block can be returned
if err == nil {
Expand All @@ -771,15 +760,27 @@ func (e *EVMChain) traceBlock(config *tracers.TraceConfig, block *types.Block) (
func (e *EVMChain) TraceTransaction(txHash common.Hash, config *tracers.TraceConfig) (any, error) {
e.log.Debugf("TraceTransaction(txHash=%v, config=?)", txHash)

_, blockHash, blockNumber, txIndex, err := e.TransactionByHash(txHash)
tx, blockHash, blockNumber, txIndex, err := e.TransactionByHash(txHash)
if err != nil {
return nil, err
}
if blockNumber == 0 {
return nil, errors.New("transaction not found")
}

return e.traceTransaction(config, txIndex, txHash, blockNumber, blockHash)
iscBlock, iscRequestsInBlock, err := e.iscRequestsInBlock(blockNumber)
if err != nil {
return nil, err
}

return e.traceTransaction(
config,
iscBlock,
iscRequestsInBlock,
tx,
txIndex,
blockHash,
)
}

func (e *EVMChain) TraceBlockByHash(blockHash common.Hash, config *tracers.TraceConfig) (any, error) {
Expand Down
68 changes: 68 additions & 0 deletions packages/evm/jsonrpc/jsonrpctest/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package jsonrpctest
import (
"context"
"crypto/ecdsa"
"encoding/json"
"errors"
"math"
"math/big"
Expand All @@ -18,6 +19,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -288,6 +290,72 @@ func (e *Env) getLogs(q ethereum.FilterQuery) []types.Log {
return logs
}

func (e *Env) traceTransactionWithCallTracer(txHash common.Hash) (jsonrpc.CallFrame, error) {
var res json.RawMessage
// we have to use the raw client, because the normal client does not support debug methods
tracer := "callTracer"
err := e.RawClient.CallContext(
context.Background(),
&res,
"debug_traceTransaction",
txHash,
tracers.TraceConfig{Tracer: &tracer},
)
if err != nil {
return jsonrpc.CallFrame{}, err
}
trace := jsonrpc.CallFrame{}
err = json.Unmarshal(res, &trace)
require.NoError(e.T, err)
return trace, nil
}

func (e *Env) traceTransactionWithPrestate(txHash common.Hash) (jsonrpc.PrestateAccountMap, error) {
var res json.RawMessage
// we have to use the raw client, because the normal client does not support debug methods
tracer := "prestateTracer"
err := e.RawClient.CallContext(
context.Background(),
&res,
"debug_traceTransaction",
txHash,
tracers.TraceConfig{
Tracer: &tracer,
TracerConfig: []byte(`{"diffMode": false}`),
},
)
if err != nil {
return nil, err
}
var ret jsonrpc.PrestateAccountMap
err = json.Unmarshal(res, &ret)
require.NoError(e.T, err)
return ret, nil
}

func (e *Env) traceTransactionWithPrestateDiff(txHash common.Hash) (jsonrpc.PrestateDiffResult, error) {
var res json.RawMessage
// we have to use the raw client, because the normal client does not support debug methods
tracer := "prestateTracer"
err := e.RawClient.CallContext(
context.Background(),
&res,
"debug_traceTransaction",
txHash,
tracers.TraceConfig{
Tracer: &tracer,
TracerConfig: []byte(`{"diffMode": true}`),
},
)
if err != nil {
return jsonrpc.PrestateDiffResult{}, err
}
var ret jsonrpc.PrestateDiffResult
err = json.Unmarshal(res, &ret)
require.NoError(e.T, err)
return ret, nil
}

func (e *Env) TestRPCGetLogs() {
creator, creatorAddress := e.NewAccountWithL2Funds()
contractABI, err := abi.JSON(strings.NewReader(evmtest.ERC20ContractABI))
Expand Down
Loading
Loading