Skip to content

Commit

Permalink
Finish fake-evm transaction calls
Browse files Browse the repository at this point in the history
  • Loading branch information
lmoe committed Oct 21, 2024
1 parent 29b551c commit af5ccee
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 18 deletions.
58 changes: 40 additions & 18 deletions packages/evm/jsonrpc/evmchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"math"
"math/big"
"path"
"slices"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -665,12 +666,26 @@ func (e *EVMChain) iscRequestsInBlock(evmBlockNumber uint64) (*blocklog.BlockInf
return blocklog.GetRequestsInBlock(blocklogStatePartition, iscBlockIndex)
}

func (e *EVMChain) trace(config *tracers.TraceConfig, blockInfo *blocklog.BlockInfo, requestsInBlock []isc.Request, txIndex uint64, txHash common.Hash, blockNumber uint64, blockHash common.Hash) (json.RawMessage, error) {
func (e *EVMChain) isFakeTransaction(tx *types.Transaction) bool {
sender, err := evmutil.GetSender(tx)

// the error will fire when the transaction is invalid. This is most of the time a fake evm tx we use for internal calls, therefore it's fine to assume both.
if slices.Equal(sender.Bytes(), common.Address{}.Bytes()) || err != nil {
return true
}

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) {
tracerType := "callTracer"
if config.Tracer != nil {
tracerType = *config.Tracer
}

blockNumber := uint64(blockInfo.BlockIndex())

tracer, err := newTracer(tracerType, &tracers.Context{
BlockHash: blockHash,
BlockNumber: new(big.Int).SetUint64(blockNumber),
Expand All @@ -689,11 +704,24 @@ func (e *EVMChain) trace(config *tracers.TraceConfig, blockInfo *blocklog.BlockI
&blockNumber,
tracer,
)

result, err := tracer.GetResult()
if err != nil {
return nil, err
if err != nil && !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(RPCMarshalTransactionForFakeTX(tx, tx.GasPrice()))
}
}

return tracer.GetResult()
return result, nil
}

func (e *EVMChain) traceTransaction(config *tracers.TraceConfig, txIndex uint64, txHash common.Hash, blockNumber uint64, blockHash common.Hash) (any, error) {
Expand All @@ -702,42 +730,36 @@ func (e *EVMChain) traceTransaction(config *tracers.TraceConfig, txIndex uint64,
return nil, err
}

result, err := e.trace(config, iscBlock, iscRequestsInBlock, txIndex, txHash, blockNumber, blockHash)
blockTxs, err := e.txsByBlockNumber(new(big.Int).SetUint64(blockNumber))
if err != nil {
return nil, err
}

return result, nil
return e.trace(config, iscBlock, iscRequestsInBlock, blockTxs, txIndex, txHash, blockHash)
}

func (e *EVMChain) traceBlock(config *tracers.TraceConfig, blockNumber uint64, blockHash common.Hash) (any, error) {
iscBlock, iscRequestsInBlock, err := e.iscRequestsInBlock(blockNumber)
func (e *EVMChain) traceBlock(config *tracers.TraceConfig, block *types.Block) (any, error) {
iscBlock, iscRequestsInBlock, err := e.iscRequestsInBlock(block.NumberU64())
if err != nil {
return nil, err
}

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

results := make([]TxTraceResult, 0)

for i, tx := range blockTxs {
result, err := e.trace(config, iscBlock, iscRequestsInBlock, uint64(i), tx.Hash(), blockNumber, blockHash)
result, err := e.trace(config, iscBlock, iscRequestsInBlock, blockTxs, uint64(i), tx.Hash(), block.Hash())

// Transactions which failed tracing will be omitted, so the rest of the block can be returned
if err == nil {
results = append(results, TxTraceResult{
TxHash: tx.Hash(),
Result: result,
})
}

if err != nil && !errors.Is(err, ErrIncorrectTopLevelCalls) {
return nil, err
}

// Continue the loop for next TXs
}

return results, nil
Expand Down Expand Up @@ -765,7 +787,7 @@ func (e *EVMChain) TraceBlockByHash(blockHash common.Hash, config *tracers.Trace
return nil, fmt.Errorf("block not found: %s", blockHash.String())
}

return e.traceBlock(config, block.Number().Uint64(), blockHash)
return e.traceBlock(config, block)
}

func (e *EVMChain) TraceBlockByNumber(blockNumber uint64, config *tracers.TraceConfig) (any, error) {
Expand All @@ -776,7 +798,7 @@ func (e *EVMChain) TraceBlockByNumber(blockNumber uint64, config *tracers.TraceC
return nil, fmt.Errorf("block not found: %d", blockNumber)
}

return e.traceBlock(config, blockNumber, block.Hash())
return e.traceBlock(config, block)
}

func (e *EVMChain) GetRawBlock(blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
Expand Down
44 changes: 44 additions & 0 deletions packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package jsonrpctest
import (
"context"
"encoding/json"
"fmt"
"math/big"
"slices"
"strings"
Expand Down Expand Up @@ -607,6 +608,49 @@ func TestRPCTraceTx(t *testing.T) {
require.Contains(t, trace2.GasUsed.String(), "0x")
}

// Transfer calls produce "fake" Transactions to simulate EVM behavior.
// They are not real in the sense of being persisted to the blockchain, therefore requires additional checks.
func TestRPCTraceEvmDeposit(t *testing.T) {
env := newSoloTestEnv(t)
wallet, _ := env.solo.NewKeyPairWithFunds()
_, evmAddr := env.soloChain.NewEthereumAccountWithL2Funds()

err := env.soloChain.TransferAllowanceTo(
isc.NewAssetsBaseTokens(1000),
isc.NewEthereumAddressAgentID(env.soloChain.ChainID, evmAddr),
wallet)

block := env.BlockByNumber(nil)
require.NoError(t, err)
txs := block.Transactions()
tx := txs[0]

require.Equal(t, evmAddr, *tx.To())

rc, err := env.TxReceipt(txs[0].Hash())
require.NoError(t, err)
require.EqualValues(t, types.ReceiptStatusSuccessful, rc.Status)

var res1 json.RawMessage
err = env.RawClient.CallContext(
context.Background(),
&res1,
"debug_traceTransaction",
tx.Hash().Hex(),
tracers.TraceConfig{TracerConfig: []byte(`{"tracer": "callTracer"}`)},
)
require.NoError(t, err)

var trace1 jsonrpc.SendTxArgs
err = json.Unmarshal(res1, &trace1)
require.NoError(t, err)

fmt.Print(hexutil.EncodeUint64(isc.NewAssetsBaseTokens(1000).BaseTokens))

require.Equal(t, evmAddr.String(), trace1.To.String())
require.Equal(t, hexutil.EncodeUint64(isc.NewAssetsBaseTokens(1000).BaseTokens*1e12), trace1.Value.String())
}

func TestRPCTraceBlock(t *testing.T) {
env := newSoloTestEnv(t)
creator, creatorAddress := env.soloChain.NewEthereumAccountWithL2Funds()
Expand Down
13 changes: 13 additions & 0 deletions packages/evm/jsonrpc/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/rpc"

iotago "github.com/iotaledger/iota.go/v3"
Expand Down Expand Up @@ -158,6 +159,18 @@ func parseBlockNumber(bn rpc.BlockNumber) *big.Int {
return big.NewInt(n)
}

func RPCMarshalTransactionForFakeTX(tx *types.Transaction, effectiveGasPrice *big.Int) map[string]interface{} {
return map[string]interface{}{
"from": evmutil.MustGetSenderIfTxSigned(tx),
"gas": hexutil.Uint64(tx.Gas()),
"gasUsed": hexutil.Uint64(tx.Gas()),
"to": tx.To(),
"input": hexutil.Bytes(tx.Data()),
"type": vm.OpCode(tx.Type()).String(),
"value": hexutil.Big(*tx.Value()),
}
}

func RPCMarshalReceipt(r *types.Receipt, tx *types.Transaction, effectiveGasPrice *big.Int) map[string]interface{} {
// fix for an already fixed bug where some old failed receipts contain non-empty logs
if r.Status != types.ReceiptStatusSuccessful {
Expand Down

0 comments on commit af5ccee

Please sign in to comment.