Skip to content

Commit

Permalink
feat(evm): add prestate tracer (fixes #3512)
Browse files Browse the repository at this point in the history
  • Loading branch information
dessaya committed Oct 23, 2024
1 parent b4ee16a commit 1b00d8b
Show file tree
Hide file tree
Showing 9 changed files with 659 additions and 179 deletions.
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
49 changes: 48 additions & 1 deletion packages/evm/jsonrpc/jsonrpctest/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,12 +293,13 @@ func (e *Env) getLogs(q ethereum.FilterQuery) []types.Log {
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{TracerConfig: []byte(`{"tracer": "callTracer"}`)},
tracers.TraceConfig{Tracer: &tracer},
)
if err != nil {
return jsonrpc.CallFrame{}, err
Expand All @@ -309,6 +310,52 @@ func (e *Env) traceTransactionWithCallTracer(txHash common.Hash) (jsonrpc.CallFr
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

0 comments on commit 1b00d8b

Please sign in to comment.