Skip to content

Commit

Permalink
fix: handle rpc transaction with tx type
Browse files Browse the repository at this point in the history
  • Loading branch information
djm07073 committed Oct 17, 2024
1 parent e8282dc commit 5c90e76
Show file tree
Hide file tree
Showing 4 changed files with 241 additions and 46 deletions.
74 changes: 54 additions & 20 deletions jsonrpc/types/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,6 @@ func NewRPCTransaction(tx *coretypes.Transaction, blockHash common.Hash, blockNu
from, _ := coretypes.Sender(signer, tx)
v, r, s := tx.RawSignatureValues()
al := tx.AccessList()
var yparity *hexutil.Uint64 = new(hexutil.Uint64)
switch tx.Type() {
case coretypes.LegacyTxType:
yparity = nil
default: // Dynamic and Access List use yParity
*yparity = hexutil.Uint64(v.Sign())
}

result := &RPCTransaction{
Type: hexutil.Uint64(tx.Type()),
From: from,
Expand All @@ -64,27 +56,69 @@ func NewRPCTransaction(tx *coretypes.Transaction, blockHash common.Hash, blockNu
S: (*hexutil.Big)(s),
ChainID: (*hexutil.Big)(chainID),
Accesses: &al,
YParity: yparity,
}

if blockHash != (common.Hash{}) {
result.BlockHash = &blockHash
result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
result.TransactionIndex = (*hexutil.Uint64)(&index)
}

switch tx.Type() {
case coretypes.LegacyTxType: // Legacy type returns nil on yparity
default: // Dynamic and Access List type returns yParity
yParityValue := (v.Uint64())
result.YParity = (*hexutil.Uint64)(&yParityValue)
}

return result
}

func (rpcTx RPCTransaction) ToTransaction() *coretypes.Transaction {
return coretypes.NewTx(&coretypes.LegacyTx{
Nonce: uint64(rpcTx.Nonce),
GasPrice: rpcTx.GasPrice.ToInt(),
Gas: uint64(rpcTx.Gas),
To: rpcTx.To,
Value: rpcTx.Value.ToInt(),
Data: rpcTx.Input,
V: rpcTx.V.ToInt(),
R: rpcTx.R.ToInt(),
S: rpcTx.S.ToInt(),
})

switch rpcTx.Type {
case coretypes.LegacyTxType:
return coretypes.NewTx(&coretypes.LegacyTx{
Nonce: uint64(rpcTx.Nonce),
GasPrice: rpcTx.GasPrice.ToInt(),
Gas: uint64(rpcTx.Gas),
To: rpcTx.To,
Value: rpcTx.Value.ToInt(),
Data: rpcTx.Input,
V: rpcTx.V.ToInt(),
R: rpcTx.R.ToInt(),
S: rpcTx.S.ToInt(),
})
case coretypes.AccessListTxType:
return coretypes.NewTx(&coretypes.AccessListTx{
ChainID: rpcTx.ChainID.ToInt(),
Nonce: uint64(rpcTx.Nonce),
GasPrice: rpcTx.GasPrice.ToInt(),
Gas: uint64(rpcTx.Gas),
To: rpcTx.To,
Value: rpcTx.Value.ToInt(),
Data: rpcTx.Input,
AccessList: *rpcTx.Accesses,
V: rpcTx.V.ToInt(),
R: rpcTx.R.ToInt(),
S: rpcTx.S.ToInt(),
})
case coretypes.DynamicFeeTxType:
return coretypes.NewTx((&coretypes.DynamicFeeTx{
ChainID: rpcTx.ChainID.ToInt(),
Nonce: uint64(rpcTx.Nonce),
GasTipCap: rpcTx.GasTipCap.ToInt(),
GasFeeCap: rpcTx.GasFeeCap.ToInt(),
Gas: uint64(rpcTx.Gas),
To: rpcTx.To,
Value: rpcTx.Value.ToInt(),
Data: rpcTx.Input,
AccessList: *rpcTx.Accesses,
V: rpcTx.V.ToInt(),
R: rpcTx.R.ToInt(),
S: rpcTx.S.ToInt(),
}))
default:
return nil

Check warning on line 122 in jsonrpc/types/tx.go

View check run for this annotation

Codecov / codecov/patch

jsonrpc/types/tx.go#L121-L122

Added lines #L121 - L122 were not covered by tests
}
}
171 changes: 171 additions & 0 deletions jsonrpc/types/tx_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package types_test

import (
"fmt"
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
coretypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
types "github.com/initia-labs/minievm/jsonrpc/types"
"github.com/stretchr/testify/require"
)

func TestLegacyTxTypeRPCTransaction(t *testing.T) {
privateKey, err := crypto.GenerateKey()
if err != nil {
t.Fatalf("Failed to generate private key: %v", err)
}

toAddress := crypto.PubkeyToAddress(privateKey.PublicKey)
chainID := big.NewInt(1)
tx := coretypes.NewTx(&coretypes.LegacyTx{
Nonce: 0,
GasPrice: big.NewInt(1000),
Gas: 1000,
To: &toAddress,
Value: big.NewInt(100),
Data: []byte{},
V: nil,
R: nil,
S: nil,
})

signedTx, err := coretypes.SignTx(tx, coretypes.NewCancunSigner(chainID), privateKey)
if err != nil {
t.Fatalf("Failed to sign transaction: %v", err)
}

rpcTx := types.NewRPCTransaction(signedTx, common.Hash{}, 0, 0, chainID)
ethTx := rpcTx.ToTransaction()

err = matchTx(signedTx, ethTx)
require.NoError(t, err)

}

func TestAccessListTypeRPCTransaction(t *testing.T) {
privateKey, err := crypto.GenerateKey()
if err != nil {
t.Fatalf("Failed to generate private key: %v", err)
}

toAddress := crypto.PubkeyToAddress(privateKey.PublicKey)
chainID := big.NewInt(1)
tx := coretypes.NewTx(&coretypes.AccessListTx{
ChainID: chainID,
Nonce: 0,
GasPrice: big.NewInt(1000),
Gas: 1000,
To: &toAddress,
Value: big.NewInt(100),
Data: []byte{},
AccessList: []coretypes.AccessTuple{},
V: nil,
R: nil,
S: nil,
})

signedTx, err := coretypes.SignTx(tx, coretypes.NewCancunSigner(chainID), privateKey)
if err != nil {
t.Fatalf("Failed to sign transaction: %v", err)
}
rpcTx := types.NewRPCTransaction(signedTx, common.Hash{}, 0, 0, chainID)
ethTx := rpcTx.ToTransaction()
ethTx.Hash()
err = matchTx(signedTx, ethTx)
require.NoError(t, err)
}

func TestDynamicFeeTxTypeRPCTransaction(t *testing.T) {
privateKey, err := crypto.GenerateKey()
if err != nil {
t.Fatalf("Failed to generate private key: %v", err)
}

toAddress := crypto.PubkeyToAddress(privateKey.PublicKey)
chainID := big.NewInt(1)
tx := coretypes.NewTx(&coretypes.DynamicFeeTx{
ChainID: chainID,
Nonce: 0,
GasTipCap: big.NewInt(20),
GasFeeCap: big.NewInt(100),
Gas: 21000,
To: &toAddress,
Value: big.NewInt(1000),
Data: []byte{},
AccessList: []coretypes.AccessTuple{},
V: nil,
R: nil,
S: nil,
})

signedTx, err := coretypes.SignTx(tx, coretypes.NewCancunSigner(chainID), privateKey)
if err != nil {
t.Fatalf("Failed to sign transaction: %v", err)
}
rpcTx := types.NewRPCTransaction(signedTx, common.Hash{}, 0, 0, chainID)
ethTx := rpcTx.ToTransaction()

err = matchTx(signedTx, ethTx)
require.NoError(t, err)
}

func matchTx(signedTx *coretypes.Transaction, ethTx *coretypes.Transaction) error {
if signedTx.Type() != ethTx.Type() {
return fmt.Errorf("Expected transaction type %v, got %v", signedTx.Type(), ethTx.Type())
}

if signedTx.Hash() != ethTx.Hash() {
return fmt.Errorf("Expected hash %v, got %v", signedTx.Hash(), ethTx.Hash())
}

if signedTx.Nonce() != ethTx.Nonce() {
return fmt.Errorf("Expected nonce %v, got %v", signedTx.Nonce(), ethTx.Nonce())
}

if signedTx.Gas() != ethTx.Gas() {
return fmt.Errorf("Expected gas %v, got %v", signedTx.Gas(), ethTx.Gas())
}

if signedTx.GasFeeCapCmp(ethTx) != 0 {
return fmt.Errorf("Expected gas price %v, got %v", signedTx.GasPrice(), ethTx.GasPrice())
}

if signedTx.GasFeeCapCmp(ethTx) != 0 {
return fmt.Errorf("Expected gas fee cap %v, got %v", signedTx.GasFeeCap(), ethTx.GasFeeCap())
}

if signedTx.GasTipCapCmp(ethTx) != 0 {
return fmt.Errorf("Expected gas tip cap %v, got %v", signedTx.GasTipCap(), ethTx.GasTipCap())
}

if signedTx.Value().Cmp(ethTx.Value()) != 0 {
return fmt.Errorf("Expected value %v, got %v", signedTx.Value(), ethTx.Value())
}

if signedTx.To() == nil || ethTx.To() == nil || *signedTx.To() != *ethTx.To() {
return fmt.Errorf("Expected to address %v, got %v", signedTx.To(), ethTx.To())
}
signedTxAccesList := signedTx.AccessList()
ethTxAccessList := ethTx.AccessList()
if len(signedTxAccesList) != len(ethTxAccessList) {
return fmt.Errorf("Expected access list length %v, got %v", len(signedTxAccesList), len(ethTxAccessList))
}
for i := range signedTxAccesList {
if signedTxAccesList[i].Address != ethTxAccessList[i].Address || len(signedTxAccesList[i].StorageKeys) != len(ethTxAccessList[i].StorageKeys) {
return fmt.Errorf("Expected access list %v, got %v", signedTx.AccessList(), ethTxAccessList)
}
for j := range signedTxAccesList[i].StorageKeys {
if signedTxAccesList[i].StorageKeys[j] != ethTxAccessList[i].StorageKeys[j] {
return fmt.Errorf("Expected access list %v, got %v", signedTx.AccessList(), ethTxAccessList)
}
}
}
if string(signedTx.Data()) != string(ethTx.Data()) {
return fmt.Errorf("Expected data %v, got %v", signedTx.Data(), ethTx.Data())
}

return nil
}
38 changes: 14 additions & 24 deletions x/evm/keeper/txutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,20 @@ func (u *TxUtils) ConvertEthereumTxToCosmosTx(ctx context.Context, ethTx *corety
if err != nil {
return nil, err
}
var accessList []types.AccessTuple

var accessList []types.AccessTuple = nil
if len(ethTx.AccessList()) > 0 {
accessList = make([]types.AccessTuple, len(ethTx.AccessList()))
}
for i, al := range ethTx.AccessList() {
storageKeys := make([]string, len(al.StorageKeys))
for j, s := range al.StorageKeys {
storageKeys[j] = s.String()
}
accessList[i] = types.AccessTuple{
Address: al.Address.String(),
StorageKeys: storageKeys,
}
}
// sig bytes
v, r, s := ethTx.RawSignatureValues()
sigBytes := make([]byte, 65)
Expand All @@ -101,30 +113,8 @@ func (u *TxUtils) ConvertEthereumTxToCosmosTx(ctx context.Context, ethTx *corety
sigBytes[64] = byte(new(big.Int).Sub(v, new(big.Int).Add(new(big.Int).Add(ethChainID, ethChainID), big.NewInt(35))).Uint64())
case coretypes.AccessListTxType:
sigBytes[64] = byte(v.Uint64())
accessList = make([]types.AccessTuple, len(ethTx.AccessList()))
for i, al := range ethTx.AccessList() {
storageKeys := make([]string, len(al.StorageKeys))
for j, s := range al.StorageKeys {
storageKeys[j] = s.String()
}
accessList[i] = types.AccessTuple{
Address: al.Address.String(),
StorageKeys: storageKeys,
}
}
case coretypes.DynamicFeeTxType:
sigBytes[64] = byte(v.Uint64())
accessList = make([]types.AccessTuple, len(ethTx.AccessList()))
for i, al := range ethTx.AccessList() {
storageKeys := make([]string, len(al.StorageKeys))
for j, s := range al.StorageKeys {
storageKeys[j] = s.String()
}
accessList[i] = types.AccessTuple{
Address: al.Address.String(),
StorageKeys: storageKeys,
}
}
default:
return nil, sdkerrors.ErrorInvalidSigner.Wrapf("unsupported tx type: %d", ethTx.Type())
}
Expand Down
4 changes: 2 additions & 2 deletions x/evm/keeper/txutils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ func Test_AccessTxConversion(t *testing.T) {
To: &ethFactoryAddr,
Data: inputBz,
Value: value,
AccessList: coretypes.AccessList{},
AccessList: nil,
})

signer := coretypes.LatestSignerForChainID(ethChainID)
Expand Down Expand Up @@ -209,7 +209,7 @@ func Test_AccessTxConversion(t *testing.T) {
ContractAddr: ethFactoryAddr.Hex(),
Input: hexutil.Encode(inputBz),
Value: math.NewInt(100),
AccessList: []types.AccessTuple{},
AccessList: nil,
})

authTx := sdkTx.(authsigning.Tx)
Expand Down

0 comments on commit 5c90e76

Please sign in to comment.