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

fix: ethereum json-rpc calls #3511

Merged
merged 15 commits into from
Oct 22, 2024
Prev Previous commit
Next Next commit
Finish fake-evm transaction calls
lmoe committed Oct 21, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit af5ccee34c4500bfa6d4110a2843d22b06c7ba11
58 changes: 40 additions & 18 deletions packages/evm/jsonrpc/evmchain.go
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@
"math"
"math/big"
"path"
"slices"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
@@ -665,12 +666,26 @@
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),
@@ -681,7 +696,7 @@
return nil, err
}

err = e.backend.EVMTrace(

Check failure on line 699 in packages/evm/jsonrpc/evmchain.go

GitHub Actions / Lint

ineffectual assignment to err (ineffassign)
blockInfo.PreviousAliasOutput,
blockInfo.Timestamp,
requestsInBlock,
@@ -689,11 +704,24 @@
&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) {
@@ -702,42 +730,36 @@
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
@@ -765,7 +787,7 @@
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) {
@@ -776,7 +798,7 @@
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) {
44 changes: 44 additions & 0 deletions packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ package jsonrpctest
import (
"context"
"encoding/json"
"fmt"
"math/big"
"slices"
"strings"
@@ -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()
13 changes: 13 additions & 0 deletions packages/evm/jsonrpc/types.go
Original file line number Diff line number Diff line change
@@ -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"
@@ -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 {