rpc, evm: secure tx signing (#20)
* rpc, evm: secure signing * evm, ante: test signer * tests
This commit is contained in:
parent
decd0748ac
commit
65453e4aa0
@ -63,7 +63,7 @@ func NewAnteHandler(
|
|||||||
opts := txWithExtensions.GetExtensionOptions()
|
opts := txWithExtensions.GetExtensionOptions()
|
||||||
if len(opts) > 0 {
|
if len(opts) > 0 {
|
||||||
switch typeURL := opts[0].GetTypeUrl(); typeURL {
|
switch typeURL := opts[0].GetTypeUrl(); typeURL {
|
||||||
case "/ethermint.evm.v1beta1.ExtensionOptionsEthereumTx":
|
case "/ethermint.evm.v1alpha1.ExtensionOptionsEthereumTx":
|
||||||
// handle as *evmtypes.MsgEthereumTx
|
// handle as *evmtypes.MsgEthereumTx
|
||||||
|
|
||||||
anteHandler = sdk.ChainAnteDecorators(
|
anteHandler = sdk.ChainAnteDecorators(
|
||||||
@ -79,7 +79,7 @@ func NewAnteHandler(
|
|||||||
NewEthIncrementSenderSequenceDecorator(ak), // innermost AnteDecorator.
|
NewEthIncrementSenderSequenceDecorator(ak), // innermost AnteDecorator.
|
||||||
)
|
)
|
||||||
|
|
||||||
case "/ethermint.evm.v1beta1.ExtensionOptionsWeb3Tx":
|
case "/ethermint.evm.v1alpha1.ExtensionOptionsWeb3Tx":
|
||||||
// handle as normal Cosmos SDK tx, except signature is checked for EIP712 representation
|
// handle as normal Cosmos SDK tx, except signature is checked for EIP712 representation
|
||||||
|
|
||||||
switch tx.(type) {
|
switch tx.(type) {
|
||||||
|
@ -290,7 +290,7 @@ func (avd EthAccountVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx
|
|||||||
if balance.Amount.BigInt().Cmp(msgEthTx.Cost()) < 0 {
|
if balance.Amount.BigInt().Cmp(msgEthTx.Cost()) < 0 {
|
||||||
return ctx, sdkerrors.Wrapf(
|
return ctx, sdkerrors.Wrapf(
|
||||||
sdkerrors.ErrInsufficientFunds,
|
sdkerrors.ErrInsufficientFunds,
|
||||||
"sender balance < tx gas cost (%s%s < %s%s)", balance.String(), evmDenom, msgEthTx.Cost().String(), evmDenom,
|
"sender balance < tx gas cost (%s < %s%s)", balance.String(), msgEthTx.Cost().String(), evmDenom,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,9 +17,11 @@ import (
|
|||||||
"github.com/cosmos/ethermint/app"
|
"github.com/cosmos/ethermint/app"
|
||||||
ante "github.com/cosmos/ethermint/app/ante"
|
ante "github.com/cosmos/ethermint/app/ante"
|
||||||
"github.com/cosmos/ethermint/crypto/ethsecp256k1"
|
"github.com/cosmos/ethermint/crypto/ethsecp256k1"
|
||||||
|
"github.com/cosmos/ethermint/tests"
|
||||||
ethermint "github.com/cosmos/ethermint/types"
|
ethermint "github.com/cosmos/ethermint/types"
|
||||||
evmtypes "github.com/cosmos/ethermint/x/evm/types"
|
evmtypes "github.com/cosmos/ethermint/x/evm/types"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
||||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||||
@ -102,7 +104,10 @@ func newTestSDKTx(
|
|||||||
func (suite *AnteTestSuite) newTestEthTx(msg *evmtypes.MsgEthereumTx, priv cryptotypes.PrivKey) (sdk.Tx, error) {
|
func (suite *AnteTestSuite) newTestEthTx(msg *evmtypes.MsgEthereumTx, priv cryptotypes.PrivKey) (sdk.Tx, error) {
|
||||||
privkey := ðsecp256k1.PrivKey{Key: priv.Bytes()}
|
privkey := ðsecp256k1.PrivKey{Key: priv.Bytes()}
|
||||||
|
|
||||||
if err := msg.Sign(suite.chainID, privkey.ToECDSA()); err != nil {
|
signer := tests.NewSigner(privkey)
|
||||||
|
msg.From = common.BytesToAddress(privkey.PubKey().Address().Bytes()).String()
|
||||||
|
|
||||||
|
if err := msg.Sign(suite.chainID, signer); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||||
@ -105,10 +104,14 @@ func (privKey *PrivKey) UnmarshalAminoJSON(bz []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sign creates a recoverable ECDSA signature on the secp256k1 curve over the
|
// Sign creates a recoverable ECDSA signature on the secp256k1 curve over the
|
||||||
// Keccak256 hash of the provided message. The produced signature is 65 bytes
|
// provided hash of the message. The produced signature is 65 bytes
|
||||||
// where the last byte contains the recovery ID.
|
// where the last byte contains the recovery ID.
|
||||||
func (privKey PrivKey) Sign(msg []byte) ([]byte, error) {
|
func (privKey PrivKey) Sign(digestBz []byte) ([]byte, error) {
|
||||||
return ethcrypto.Sign(ethcrypto.Keccak256Hash(msg).Bytes(), privKey.ToECDSA())
|
if len(digestBz) != ethcrypto.DigestLength {
|
||||||
|
digestBz = ethcrypto.Keccak256Hash(digestBz).Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ethcrypto.Sign(digestBz, privKey.ToECDSA())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToECDSA returns the ECDSA private key as a reference to ecdsa.PrivateKey type.
|
// ToECDSA returns the ECDSA private key as a reference to ecdsa.PrivateKey type.
|
||||||
@ -190,12 +193,14 @@ func (pubKey *PubKey) UnmarshalAminoJSON(bz []byte) error {
|
|||||||
// VerifySignature verifies that the ECDSA public key created a given signature over
|
// VerifySignature verifies that the ECDSA public key created a given signature over
|
||||||
// the provided message. It will calculate the Keccak256 hash of the message
|
// the provided message. It will calculate the Keccak256 hash of the message
|
||||||
// prior to verification.
|
// prior to verification.
|
||||||
|
//
|
||||||
|
// CONTRACT: The signature should be in [R || S] format.
|
||||||
func (pubKey PubKey) VerifySignature(msg []byte, sig []byte) bool {
|
func (pubKey PubKey) VerifySignature(msg []byte, sig []byte) bool {
|
||||||
if len(sig) == 65 {
|
if len(sig) == ethcrypto.SignatureLength {
|
||||||
// remove recovery ID if contained in the signature
|
// remove recovery ID (V) if contained in the signature
|
||||||
sig = sig[:len(sig)-1]
|
sig = sig[:len(sig)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// the signature needs to be in [R || S] format when provided to VerifySignature
|
// the signature needs to be in [R || S] format when provided to VerifySignature
|
||||||
return secp256k1.VerifySignature(pubKey.Key, ethcrypto.Keccak256Hash(msg).Bytes(), sig)
|
return ethcrypto.VerifySignature(pubKey.Key, ethcrypto.Keccak256Hash(msg).Bytes(), sig)
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -23,6 +24,7 @@ import (
|
|||||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
@ -288,13 +290,96 @@ func (e *PublicEthAPI) GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log,
|
|||||||
// Sign signs the provided data using the private key of address via Geth's signature standard.
|
// Sign signs the provided data using the private key of address via Geth's signature standard.
|
||||||
func (e *PublicEthAPI) Sign(address common.Address, data hexutil.Bytes) (hexutil.Bytes, error) {
|
func (e *PublicEthAPI) Sign(address common.Address, data hexutil.Bytes) (hexutil.Bytes, error) {
|
||||||
e.logger.Debugln("eth_sign", "address", address.Hex(), "data", common.Bytes2Hex(data))
|
e.logger.Debugln("eth_sign", "address", address.Hex(), "data", common.Bytes2Hex(data))
|
||||||
return nil, errors.New("eth_sign not supported")
|
|
||||||
|
from := sdk.AccAddress(address.Bytes())
|
||||||
|
|
||||||
|
_, err := e.clientCtx.Keyring.KeyByAddress(from)
|
||||||
|
if err != nil {
|
||||||
|
e.logger.Errorln("failed to find key in keyring", "address", address.String())
|
||||||
|
return nil, fmt.Errorf("%s; %s", keystore.ErrNoMatch, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign the requested hash with the wallet
|
||||||
|
signature, _, err := e.clientCtx.Keyring.SignByAddress(from, data)
|
||||||
|
if err != nil {
|
||||||
|
e.logger.Panicln("keyring.SignByAddress failed")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
|
||||||
|
return signature, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendTransaction sends an Ethereum transaction.
|
// SendTransaction sends an Ethereum transaction.
|
||||||
func (e *PublicEthAPI) SendTransaction(args types.SendTxArgs) (common.Hash, error) {
|
func (e *PublicEthAPI) SendTransaction(args types.SendTxArgs) (common.Hash, error) {
|
||||||
e.logger.Debugln("eth_sendTransaction", "args", args)
|
e.logger.Debugln("eth_sendTransaction", "args", args)
|
||||||
return common.Hash{}, errors.New("eth_sendTransaction not supported")
|
|
||||||
|
// Look up the wallet containing the requested signer
|
||||||
|
_, err := e.clientCtx.Keyring.KeyByAddress(sdk.AccAddress(args.From.Bytes()))
|
||||||
|
if err != nil {
|
||||||
|
e.logger.WithError(err).Errorln("failed to find key in keyring", "address", args.From)
|
||||||
|
return common.Hash{}, fmt.Errorf("%s; %s", keystore.ErrNoMatch, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err = e.setTxDefaults(args)
|
||||||
|
if err != nil {
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := args.ToTransaction()
|
||||||
|
|
||||||
|
// Assemble transaction from fields
|
||||||
|
builder, ok := e.clientCtx.TxConfig.NewTxBuilder().(authtx.ExtensionOptionsTxBuilder)
|
||||||
|
if !ok {
|
||||||
|
e.logger.WithError(err).Panicln("clientCtx.TxConfig.NewTxBuilder returns unsupported builder")
|
||||||
|
}
|
||||||
|
|
||||||
|
option, err := codectypes.NewAnyWithValue(&evmtypes.ExtensionOptionsEthereumTx{})
|
||||||
|
if err != nil {
|
||||||
|
e.logger.WithError(err).Panicln("codectypes.NewAnyWithValue failed to pack an obvious value")
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.SetExtensionOptions(option)
|
||||||
|
err = builder.SetMsgs(tx.GetMsgs()...)
|
||||||
|
if err != nil {
|
||||||
|
e.logger.WithError(err).Panicln("builder.SetMsgs failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
fees := sdk.NewCoins(ethermint.NewPhotonCoin(sdk.NewIntFromBigInt(tx.Fee())))
|
||||||
|
builder.SetFeeAmount(fees)
|
||||||
|
builder.SetGasLimit(tx.GetGas())
|
||||||
|
|
||||||
|
if err := tx.ValidateBasic(); err != nil {
|
||||||
|
e.logger.Debugln("tx failed basic validation", "error", err)
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign transaction
|
||||||
|
if err := tx.Sign(e.chainIDEpoch, e.clientCtx.Keyring); err != nil {
|
||||||
|
e.logger.Debugln("failed to sign tx", "error", err)
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode transaction by default Tx encoder
|
||||||
|
txEncoder := e.clientCtx.TxConfig.TxEncoder()
|
||||||
|
txBytes, err := txEncoder(tx)
|
||||||
|
if err != nil {
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
txHash := common.BytesToHash(txBytes)
|
||||||
|
|
||||||
|
// Broadcast transaction in sync mode (default)
|
||||||
|
// NOTE: If error is encountered on the node, the broadcast will not return an error
|
||||||
|
asyncCtx := e.clientCtx.WithBroadcastMode(flags.BroadcastAsync)
|
||||||
|
if _, err := asyncCtx.BroadcastTx(txBytes); err != nil {
|
||||||
|
e.logger.WithError(err).Errorln("failed to broadcast Eth tx")
|
||||||
|
return txHash, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return transaction hash
|
||||||
|
return txHash, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendRawTransaction send a raw Ethereum transaction.
|
// SendRawTransaction send a raw Ethereum transaction.
|
||||||
@ -317,13 +402,13 @@ func (e *PublicEthAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, erro
|
|||||||
|
|
||||||
option, err := codectypes.NewAnyWithValue(&evmtypes.ExtensionOptionsEthereumTx{})
|
option, err := codectypes.NewAnyWithValue(&evmtypes.ExtensionOptionsEthereumTx{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.logger.Panicln("codectypes.NewAnyWithValue failed to pack an obvious value")
|
e.logger.WithError(err).Panicln("codectypes.NewAnyWithValue failed to pack an obvious value")
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.SetExtensionOptions(option)
|
builder.SetExtensionOptions(option)
|
||||||
err = builder.SetMsgs(ethereumTx.GetMsgs()...)
|
err = builder.SetMsgs(ethereumTx.GetMsgs()...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.logger.Panicln("builder.SetMsgs failed")
|
e.logger.WithError(err).Panicln("builder.SetMsgs failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
fees := sdk.NewCoins(ethermint.NewPhotonCoin(sdk.NewIntFromBigInt(ethereumTx.Fee())))
|
fees := sdk.NewCoins(ethermint.NewPhotonCoin(sdk.NewIntFromBigInt(ethereumTx.Fee())))
|
||||||
@ -354,8 +439,11 @@ func (e *PublicEthAPI) Call(args types.CallArgs, blockNr types.BlockNumber, _ *t
|
|||||||
simRes, err := e.doCall(args, blockNr, big.NewInt(ethermint.DefaultRPCGasLimit))
|
simRes, err := e.doCall(args, blockNr, big.NewInt(ethermint.DefaultRPCGasLimit))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{}, err
|
return []byte{}, err
|
||||||
} else if len(simRes.Result.Log) > 0 {
|
}
|
||||||
|
|
||||||
|
if len(simRes.Result.Log) > 0 {
|
||||||
var logs []types.SDKTxLogs
|
var logs []types.SDKTxLogs
|
||||||
|
|
||||||
if err := json.Unmarshal([]byte(simRes.Result.Log), &logs); err != nil {
|
if err := json.Unmarshal([]byte(simRes.Result.Log), &logs); err != nil {
|
||||||
e.logger.WithError(err).Errorln("failed to unmarshal simRes.Result.Log")
|
e.logger.WithError(err).Errorln("failed to unmarshal simRes.Result.Log")
|
||||||
}
|
}
|
||||||
@ -366,6 +454,7 @@ func (e *PublicEthAPI) Call(args types.CallArgs, blockNr types.BlockNumber, _ *t
|
|||||||
e.logger.WithError(err).Warningln("call result decoding failed")
|
e.logger.WithError(err).Warningln("call result decoding failed")
|
||||||
return []byte{}, err
|
return []byte{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return []byte{}, types.ErrRevertedWith(data.Ret)
|
return []byte{}, types.ErrRevertedWith(data.Ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -771,3 +860,74 @@ func (e *PublicEthAPI) GetProof(address common.Address, storageKeys []string, bl
|
|||||||
StorageProof: storageProofs,
|
StorageProof: storageProofs,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setTxDefaults populates tx message with default values in case they are not
|
||||||
|
// provided on the args
|
||||||
|
func (e *PublicEthAPI) setTxDefaults(args rpctypes.SendTxArgs) (rpctypes.SendTxArgs, error) {
|
||||||
|
|
||||||
|
// Get nonce (sequence) from sender account
|
||||||
|
from := sdk.AccAddress(args.From.Bytes())
|
||||||
|
|
||||||
|
if args.GasPrice == nil {
|
||||||
|
// TODO: Change to either:
|
||||||
|
// - min gas price from context once available through server/daemon, or
|
||||||
|
// - suggest a gas price based on the previous included txs
|
||||||
|
args.GasPrice = (*hexutil.Big)(big.NewInt(ethermint.DefaultGasPrice))
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.Nonce != nil {
|
||||||
|
// get the nonce from the account retriever
|
||||||
|
// ignore error in case tge account doesn't exist yet
|
||||||
|
_, nonce, _ := e.clientCtx.AccountRetriever.GetAccountNumberSequence(e.clientCtx, from)
|
||||||
|
args.Nonce = (*hexutil.Uint64)(&nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) {
|
||||||
|
return args, errors.New("both 'data' and 'input' are set and not equal. Please use 'input' to pass transaction call data")
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.To == nil {
|
||||||
|
// Contract creation
|
||||||
|
var input []byte
|
||||||
|
if args.Data != nil {
|
||||||
|
input = *args.Data
|
||||||
|
} else if args.Input != nil {
|
||||||
|
input = *args.Input
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(input) == 0 {
|
||||||
|
return args, errors.New(`contract creation without any data provided`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.Gas == nil {
|
||||||
|
// For backwards-compatibility reason, we try both input and data
|
||||||
|
// but input is preferred.
|
||||||
|
input := args.Input
|
||||||
|
if input == nil {
|
||||||
|
input = args.Data
|
||||||
|
}
|
||||||
|
|
||||||
|
callArgs := rpctypes.CallArgs{
|
||||||
|
From: &args.From, // From shouldn't be nil
|
||||||
|
To: args.To,
|
||||||
|
Gas: args.Gas,
|
||||||
|
GasPrice: args.GasPrice,
|
||||||
|
Value: args.Value,
|
||||||
|
Data: input,
|
||||||
|
AccessList: args.AccessList,
|
||||||
|
}
|
||||||
|
estimated, err := e.EstimateGas(callArgs)
|
||||||
|
if err != nil {
|
||||||
|
return args, err
|
||||||
|
}
|
||||||
|
args.Gas = &estimated
|
||||||
|
e.logger.Debugln("estimate gas usage automatically", "gas", args.Gas)
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.ChainID == nil {
|
||||||
|
args.ChainID = (*hexutil.Big)(e.chainIDEpoch)
|
||||||
|
}
|
||||||
|
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
evmtypes "github.com/cosmos/ethermint/x/evm/types"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
@ -67,6 +68,33 @@ type SendTxArgs struct {
|
|||||||
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToTransaction converts the arguments to an ethereum transaction.
|
||||||
|
// This assumes that setTxDefaults has been called.
|
||||||
|
func (args *SendTxArgs) ToTransaction() *evmtypes.MsgEthereumTx {
|
||||||
|
var input []byte
|
||||||
|
if args.Input != nil {
|
||||||
|
input = *args.Input
|
||||||
|
} else if args.Data != nil {
|
||||||
|
input = *args.Data
|
||||||
|
}
|
||||||
|
|
||||||
|
data := &evmtypes.TxData{
|
||||||
|
To: args.To.Bytes(),
|
||||||
|
ChainID: args.ChainID.ToInt().Bytes(),
|
||||||
|
Nonce: uint64(*args.Nonce),
|
||||||
|
GasLimit: uint64(*args.Gas),
|
||||||
|
GasPrice: args.GasPrice.ToInt().Bytes(),
|
||||||
|
Amount: args.Value.ToInt().Bytes(),
|
||||||
|
Input: input,
|
||||||
|
Accesses: evmtypes.NewAccessList(args.AccessList),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &evmtypes.MsgEthereumTx{
|
||||||
|
Data: data,
|
||||||
|
From: args.From.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CallArgs represents the arguments for a call.
|
// CallArgs represents the arguments for a call.
|
||||||
type CallArgs struct {
|
type CallArgs struct {
|
||||||
From *common.Address `json:"from"`
|
From *common.Address `json:"from"`
|
||||||
|
4
go.mod
4
go.mod
@ -43,9 +43,9 @@ require (
|
|||||||
github.com/tyler-smith/go-bip39 v1.1.0
|
github.com/tyler-smith/go-bip39 v1.1.0
|
||||||
github.com/xlab/closer v0.0.0-20190328110542-03326addb7c2
|
github.com/xlab/closer v0.0.0-20190328110542-03326addb7c2
|
||||||
github.com/xlab/suplog v1.3.0
|
github.com/xlab/suplog v1.3.0
|
||||||
golang.org/x/crypto v0.0.0-20210505212654-3497b51f5e64
|
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf
|
||||||
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 // indirect
|
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20210505142820-a42aa055cf76
|
google.golang.org/genproto v0.0.0-20210510173355-fb37daa5cd7a
|
||||||
google.golang.org/grpc v1.37.1
|
google.golang.org/grpc v1.37.1
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||||
|
8
go.sum
8
go.sum
@ -916,8 +916,8 @@ golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210505212654-3497b51f5e64 h1:QuAh/1Gwc0d+u9walMU1NqzhRemNegsv5esp2ALQIY4=
|
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o=
|
||||||
golang.org/x/crypto v0.0.0-20210505212654-3497b51f5e64/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
@ -1161,8 +1161,8 @@ google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6D
|
|||||||
google.golang.org/genproto v0.0.0-20201111145450-ac7456db90a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20201111145450-ac7456db90a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20210505142820-a42aa055cf76 h1:0pBp6vCQyvmttnWa4c74n/y2U7bAQeIUVyVvZpb7Fyo=
|
google.golang.org/genproto v0.0.0-20210510173355-fb37daa5cd7a h1:tzkHckzMzgPr8SC4taTC3AldLr4+oJivSoq1xf/nhsc=
|
||||||
google.golang.org/genproto v0.0.0-20210505142820-a42aa055cf76/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
google.golang.org/genproto v0.0.0-20210510173355-fb37daa5cd7a/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
52
tests/signer.go
Normal file
52
tests/signer.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||||
|
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
|
"github.com/cosmos/ethermint/crypto/ethsecp256k1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ keyring.Signer = &Signer{}
|
||||||
|
|
||||||
|
// Signer defines a type that is used on testing for signing MsgEthereumTx
|
||||||
|
type Signer struct {
|
||||||
|
privKey cryptotypes.PrivKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSigner(sk cryptotypes.PrivKey) keyring.Signer {
|
||||||
|
return &Signer{
|
||||||
|
privKey: sk,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign signs the message using the underlying private key
|
||||||
|
func (s Signer) Sign(_ string, msg []byte) ([]byte, cryptotypes.PubKey, error) {
|
||||||
|
if s.privKey.Type() != ethsecp256k1.KeyType {
|
||||||
|
return nil, nil, fmt.Errorf(
|
||||||
|
"invalid private key type for signing ethereum tx; expected %s, got %s",
|
||||||
|
ethsecp256k1.KeyType,
|
||||||
|
s.privKey.Type(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := s.privKey.Sign(msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sig, s.privKey.PubKey(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignByAddress sign byte messages with a user key providing the address.
|
||||||
|
func (s Signer) SignByAddress(address sdk.Address, msg []byte) ([]byte, cryptotypes.PubKey, error) {
|
||||||
|
signer := sdk.AccAddress(s.privKey.PubKey().Address())
|
||||||
|
if !signer.Equals(address) {
|
||||||
|
return nil, nil, fmt.Errorf("address mismatch: signer %s ≠ given address %s", signer, address)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Sign("", msg)
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package evm_test
|
package evm_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
@ -14,13 +13,14 @@ import (
|
|||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
"github.com/cosmos/ethermint/app"
|
"github.com/cosmos/ethermint/app"
|
||||||
"github.com/cosmos/ethermint/crypto/ethsecp256k1"
|
"github.com/cosmos/ethermint/crypto/ethsecp256k1"
|
||||||
|
"github.com/cosmos/ethermint/tests"
|
||||||
ethermint "github.com/cosmos/ethermint/types"
|
ethermint "github.com/cosmos/ethermint/types"
|
||||||
"github.com/cosmos/ethermint/x/evm"
|
"github.com/cosmos/ethermint/x/evm"
|
||||||
"github.com/cosmos/ethermint/x/evm/types"
|
"github.com/cosmos/ethermint/x/evm/types"
|
||||||
@ -37,7 +37,7 @@ type EvmTestSuite struct {
|
|||||||
codec codec.BinaryMarshaler
|
codec codec.BinaryMarshaler
|
||||||
chainID *big.Int
|
chainID *big.Int
|
||||||
|
|
||||||
privKey *ethsecp256k1.PrivKey
|
signer keyring.Signer
|
||||||
from ethcmn.Address
|
from ethcmn.Address
|
||||||
to sdk.AccAddress
|
to sdk.AccAddress
|
||||||
}
|
}
|
||||||
@ -49,16 +49,17 @@ func (suite *EvmTestSuite) SetupTest() {
|
|||||||
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, ChainID: "ethermint-888", Time: time.Now().UTC()})
|
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, ChainID: "ethermint-888", Time: time.Now().UTC()})
|
||||||
suite.handler = evm.NewHandler(suite.app.EvmKeeper)
|
suite.handler = evm.NewHandler(suite.app.EvmKeeper)
|
||||||
suite.codec = suite.app.AppCodec()
|
suite.codec = suite.app.AppCodec()
|
||||||
suite.chainID = big.NewInt(888)
|
suite.chainID = suite.chainID
|
||||||
|
|
||||||
privKey, err := ethsecp256k1.GenerateKey()
|
privKey, err := ethsecp256k1.GenerateKey()
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
suite.to = sdk.AccAddress(privKey.PubKey().Address())
|
suite.to = sdk.AccAddress(privKey.PubKey().Address())
|
||||||
|
|
||||||
suite.privKey, err = ethsecp256k1.GenerateKey()
|
privKey, err = ethsecp256k1.GenerateKey()
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
suite.signer = tests.NewSigner(privKey)
|
||||||
suite.from = ethcmn.BytesToAddress(privKey.PubKey().Address().Bytes())
|
suite.from = ethcmn.BytesToAddress(privKey.PubKey().Address().Bytes())
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -80,14 +81,16 @@ func (suite *EvmTestSuite) TestHandleMsgEthereumTx() {
|
|||||||
"passed",
|
"passed",
|
||||||
func() {
|
func() {
|
||||||
suite.app.EvmKeeper.SetBalance(suite.ctx, suite.from, big.NewInt(100))
|
suite.app.EvmKeeper.SetBalance(suite.ctx, suite.from, big.NewInt(100))
|
||||||
tx = types.NewMsgEthereumTx(suite.chainID, 0, &suite.from, big.NewInt(100), 0, big.NewInt(10000), nil, nil)
|
to := ethcmn.BytesToAddress(suite.to)
|
||||||
|
tx = types.NewMsgEthereumTx(suite.chainID, 0, &to, big.NewInt(100), 0, big.NewInt(10000), nil, nil)
|
||||||
|
tx.From = suite.from.String()
|
||||||
|
|
||||||
// parse context chain ID to big.Int
|
// parse context chain ID to big.Int
|
||||||
chainID, err := ethermint.ParseChainID(suite.ctx.ChainID())
|
chainID, err := ethermint.ParseChainID(suite.ctx.ChainID())
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
// sign transaction
|
// sign transaction
|
||||||
err = tx.Sign(chainID, suite.privKey.ToECDSA())
|
err = tx.Sign(chainID, suite.signer)
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
@ -102,7 +105,7 @@ func (suite *EvmTestSuite) TestHandleMsgEthereumTx() {
|
|||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
// sign transaction
|
// sign transaction
|
||||||
err = tx.Sign(chainID, suite.privKey.ToECDSA())
|
err = tx.Sign(chainID, suite.signer)
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
@ -173,12 +176,11 @@ func (suite *EvmTestSuite) TestHandlerLogs() {
|
|||||||
gasLimit := uint64(100000)
|
gasLimit := uint64(100000)
|
||||||
gasPrice := big.NewInt(1000000)
|
gasPrice := big.NewInt(1000000)
|
||||||
|
|
||||||
priv, err := ethsecp256k1.GenerateKey()
|
|
||||||
suite.Require().NoError(err, "failed to create key")
|
|
||||||
|
|
||||||
bytecode := common.FromHex("0x6080604052348015600f57600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a2603580604b6000396000f3fe6080604052600080fdfea165627a7a723058206cab665f0f557620554bb45adf266708d2bd349b8a4314bdff205ee8440e3c240029")
|
bytecode := common.FromHex("0x6080604052348015600f57600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a2603580604b6000396000f3fe6080604052600080fdfea165627a7a723058206cab665f0f557620554bb45adf266708d2bd349b8a4314bdff205ee8440e3c240029")
|
||||||
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
||||||
err = tx.Sign(big.NewInt(888), priv.ToECDSA())
|
tx.From = suite.from.String()
|
||||||
|
|
||||||
|
err := tx.Sign(suite.chainID, suite.signer)
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
result, err := suite.handler(suite.ctx, tx)
|
result, err := suite.handler(suite.ctx, tx)
|
||||||
@ -204,13 +206,12 @@ func (suite *EvmTestSuite) TestQueryTxLogs() {
|
|||||||
gasLimit := uint64(100000)
|
gasLimit := uint64(100000)
|
||||||
gasPrice := big.NewInt(1000000)
|
gasPrice := big.NewInt(1000000)
|
||||||
|
|
||||||
priv, err := ethsecp256k1.GenerateKey()
|
|
||||||
suite.Require().NoError(err, "failed to create key")
|
|
||||||
|
|
||||||
// send contract deployment transaction with an event in the constructor
|
// send contract deployment transaction with an event in the constructor
|
||||||
bytecode := common.FromHex("0x6080604052348015600f57600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a2603580604b6000396000f3fe6080604052600080fdfea165627a7a723058206cab665f0f557620554bb45adf266708d2bd349b8a4314bdff205ee8440e3c240029")
|
bytecode := common.FromHex("0x6080604052348015600f57600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a2603580604b6000396000f3fe6080604052600080fdfea165627a7a723058206cab665f0f557620554bb45adf266708d2bd349b8a4314bdff205ee8440e3c240029")
|
||||||
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
||||||
err = tx.Sign(big.NewInt(888), priv.ToECDSA())
|
tx.From = suite.from.String()
|
||||||
|
|
||||||
|
err := tx.Sign(suite.chainID, suite.signer)
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
result, err := suite.handler(suite.ctx, tx)
|
result, err := suite.handler(suite.ctx, tx)
|
||||||
@ -291,12 +292,11 @@ func (suite *EvmTestSuite) TestDeployAndCallContract() {
|
|||||||
gasLimit := uint64(100000000)
|
gasLimit := uint64(100000000)
|
||||||
gasPrice := big.NewInt(10000)
|
gasPrice := big.NewInt(10000)
|
||||||
|
|
||||||
priv, err := ethsecp256k1.GenerateKey()
|
|
||||||
suite.Require().NoError(err, "failed to create key")
|
|
||||||
|
|
||||||
bytecode := common.FromHex("0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a36102c4806100dc6000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c010000000000000000000000000000000000000000000000000000000090048063893d20e814610058578063a6f9dae1146100a2575b600080fd5b6100606100e6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100e4600480360360208110156100b857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061010f565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146101d1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f43616c6c6572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72315820f397f2733a89198bc7fed0764083694c5b828791f39ebcbc9e414bccef14b48064736f6c63430005100032")
|
bytecode := common.FromHex("0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a36102c4806100dc6000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c010000000000000000000000000000000000000000000000000000000090048063893d20e814610058578063a6f9dae1146100a2575b600080fd5b6100606100e6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100e4600480360360208110156100b857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061010f565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146101d1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f43616c6c6572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72315820f397f2733a89198bc7fed0764083694c5b828791f39ebcbc9e414bccef14b48064736f6c63430005100032")
|
||||||
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
||||||
tx.Sign(big.NewInt(888), priv.ToECDSA())
|
tx.From = suite.from.String()
|
||||||
|
|
||||||
|
err := tx.Sign(suite.chainID, suite.signer)
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
result, err := suite.handler(suite.ctx, tx)
|
result, err := suite.handler(suite.ctx, tx)
|
||||||
@ -313,7 +313,9 @@ func (suite *EvmTestSuite) TestDeployAndCallContract() {
|
|||||||
storeAddr := "0xa6f9dae10000000000000000000000006a82e4a67715c8412a9114fbd2cbaefbc8181424"
|
storeAddr := "0xa6f9dae10000000000000000000000006a82e4a67715c8412a9114fbd2cbaefbc8181424"
|
||||||
bytecode = common.FromHex(storeAddr)
|
bytecode = common.FromHex(storeAddr)
|
||||||
tx = types.NewMsgEthereumTx(suite.chainID, 2, &receiver, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
tx = types.NewMsgEthereumTx(suite.chainID, 2, &receiver, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
||||||
tx.Sign(big.NewInt(888), priv.ToECDSA())
|
tx.From = suite.from.String()
|
||||||
|
|
||||||
|
err = tx.Sign(suite.chainID, suite.signer)
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
result, err = suite.handler(suite.ctx, tx)
|
result, err = suite.handler(suite.ctx, tx)
|
||||||
@ -325,7 +327,8 @@ func (suite *EvmTestSuite) TestDeployAndCallContract() {
|
|||||||
// query - getOwner
|
// query - getOwner
|
||||||
bytecode = common.FromHex("0x893d20e8")
|
bytecode = common.FromHex("0x893d20e8")
|
||||||
tx = types.NewMsgEthereumTx(suite.chainID, 2, &receiver, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
tx = types.NewMsgEthereumTx(suite.chainID, 2, &receiver, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
||||||
tx.Sign(big.NewInt(888), priv.ToECDSA())
|
tx.From = suite.from.String()
|
||||||
|
err = tx.Sign(suite.chainID, suite.signer)
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
result, err = suite.handler(suite.ctx, tx)
|
result, err = suite.handler(suite.ctx, tx)
|
||||||
@ -342,15 +345,12 @@ func (suite *EvmTestSuite) TestSendTransaction() {
|
|||||||
gasLimit := uint64(21000)
|
gasLimit := uint64(21000)
|
||||||
gasPrice := big.NewInt(0x55ae82600)
|
gasPrice := big.NewInt(0x55ae82600)
|
||||||
|
|
||||||
priv, err := ethsecp256k1.GenerateKey()
|
suite.app.EvmKeeper.SetBalance(suite.ctx, suite.from, big.NewInt(100))
|
||||||
suite.Require().NoError(err, "failed to create key")
|
|
||||||
pub := priv.ToECDSA().Public().(*ecdsa.PublicKey)
|
|
||||||
|
|
||||||
suite.app.EvmKeeper.SetBalance(suite.ctx, ethcrypto.PubkeyToAddress(*pub), big.NewInt(100))
|
|
||||||
|
|
||||||
// send simple value transfer with gasLimit=21000
|
// send simple value transfer with gasLimit=21000
|
||||||
tx := types.NewMsgEthereumTx(suite.chainID, 1, ðcmn.Address{0x1}, big.NewInt(1), gasLimit, gasPrice, nil, nil)
|
tx := types.NewMsgEthereumTx(suite.chainID, 1, ðcmn.Address{0x1}, big.NewInt(1), gasLimit, gasPrice, nil, nil)
|
||||||
err = tx.Sign(big.NewInt(888), priv.ToECDSA())
|
tx.From = suite.from.String()
|
||||||
|
err := tx.Sign(suite.chainID, suite.signer)
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
result, err := suite.handler(suite.ctx, tx)
|
result, err := suite.handler(suite.ctx, tx)
|
||||||
@ -418,12 +418,11 @@ func (suite *EvmTestSuite) TestOutOfGasWhenDeployContract() {
|
|||||||
suite.ctx = suite.ctx.WithGasMeter(sdk.NewGasMeter(gasLimit))
|
suite.ctx = suite.ctx.WithGasMeter(sdk.NewGasMeter(gasLimit))
|
||||||
gasPrice := big.NewInt(10000)
|
gasPrice := big.NewInt(10000)
|
||||||
|
|
||||||
priv, err := ethsecp256k1.GenerateKey()
|
|
||||||
suite.Require().NoError(err, "failed to create key")
|
|
||||||
|
|
||||||
bytecode := common.FromHex("0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a36102c4806100dc6000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c010000000000000000000000000000000000000000000000000000000090048063893d20e814610058578063a6f9dae1146100a2575b600080fd5b6100606100e6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100e4600480360360208110156100b857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061010f565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146101d1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f43616c6c6572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72315820f397f2733a89198bc7fed0764083694c5b828791f39ebcbc9e414bccef14b48064736f6c63430005100032")
|
bytecode := common.FromHex("0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a36102c4806100dc6000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c010000000000000000000000000000000000000000000000000000000090048063893d20e814610058578063a6f9dae1146100a2575b600080fd5b6100606100e6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100e4600480360360208110156100b857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061010f565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146101d1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f43616c6c6572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72315820f397f2733a89198bc7fed0764083694c5b828791f39ebcbc9e414bccef14b48064736f6c63430005100032")
|
||||||
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
||||||
tx.Sign(big.NewInt(888), priv.ToECDSA())
|
tx.From = suite.from.String()
|
||||||
|
|
||||||
|
err := tx.Sign(suite.chainID, suite.signer)
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
snapshotCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper.CommitStateDB)
|
snapshotCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper.CommitStateDB)
|
||||||
@ -447,13 +446,12 @@ func (suite *EvmTestSuite) TestErrorWhenDeployContract() {
|
|||||||
gasLimit := uint64(1000000)
|
gasLimit := uint64(1000000)
|
||||||
gasPrice := big.NewInt(10000)
|
gasPrice := big.NewInt(10000)
|
||||||
|
|
||||||
priv, err := ethsecp256k1.GenerateKey()
|
|
||||||
suite.Require().NoError(err, "failed to create key")
|
|
||||||
|
|
||||||
bytecode := common.FromHex("0xa6f9dae10000000000000000000000006a82e4a67715c8412a9114fbd2cbaefbc8181424")
|
bytecode := common.FromHex("0xa6f9dae10000000000000000000000006a82e4a67715c8412a9114fbd2cbaefbc8181424")
|
||||||
|
|
||||||
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
||||||
tx.Sign(big.NewInt(888), priv.ToECDSA())
|
tx.From = suite.from.String()
|
||||||
|
|
||||||
|
err := tx.Sign(suite.chainID, suite.signer)
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
snapshotCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper.CommitStateDB)
|
snapshotCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper.CommitStateDB)
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||||
|
|
||||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -150,13 +150,17 @@ func (msg MsgEthereumTx) GetSignBytes() []byte {
|
|||||||
// RLPSignBytes returns the RLP hash of an Ethereum transaction message with a
|
// RLPSignBytes returns the RLP hash of an Ethereum transaction message with a
|
||||||
// given chainID used for signing.
|
// given chainID used for signing.
|
||||||
func (msg MsgEthereumTx) RLPSignBytes(chainID *big.Int) ethcmn.Hash {
|
func (msg MsgEthereumTx) RLPSignBytes(chainID *big.Int) ethcmn.Hash {
|
||||||
|
if msg.Data.ChainID != nil {
|
||||||
|
chainID = new(big.Int).SetBytes(msg.Data.ChainID)
|
||||||
|
}
|
||||||
|
|
||||||
var accessList *ethtypes.AccessList
|
var accessList *ethtypes.AccessList
|
||||||
if msg.Data.Accesses != nil {
|
if msg.Data.Accesses != nil {
|
||||||
accessList = msg.Data.Accesses.ToEthAccessList()
|
accessList = msg.Data.Accesses.ToEthAccessList()
|
||||||
}
|
}
|
||||||
|
|
||||||
return rlpHash([]interface{}{
|
return rlpHash([]interface{}{
|
||||||
new(big.Int).SetBytes(msg.Data.ChainID),
|
chainID,
|
||||||
msg.Data.Nonce,
|
msg.Data.Nonce,
|
||||||
new(big.Int).SetBytes(msg.Data.GasPrice),
|
new(big.Int).SetBytes(msg.Data.GasPrice),
|
||||||
msg.Data.GasLimit,
|
msg.Data.GasLimit,
|
||||||
@ -167,19 +171,6 @@ func (msg MsgEthereumTx) RLPSignBytes(chainID *big.Int) ethcmn.Hash {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// RLPSignHomesteadBytes returns the RLP hash of an Ethereum transaction message with a
|
|
||||||
// a Homestead layout without chainID.
|
|
||||||
func (msg MsgEthereumTx) RLPSignHomesteadBytes() ethcmn.Hash {
|
|
||||||
return rlpHash([]interface{}{
|
|
||||||
msg.Data.Nonce,
|
|
||||||
msg.Data.GasPrice,
|
|
||||||
msg.Data.GasLimit,
|
|
||||||
msg.To(),
|
|
||||||
msg.Data.Amount,
|
|
||||||
msg.Data.Input,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeRLP implements the rlp.Encoder interface.
|
// EncodeRLP implements the rlp.Encoder interface.
|
||||||
func (msg *MsgEthereumTx) EncodeRLP(w io.Writer) error {
|
func (msg *MsgEthereumTx) EncodeRLP(w io.Writer) error {
|
||||||
return rlp.Encode(w, &msg.Data)
|
return rlp.Encode(w, &msg.Data)
|
||||||
@ -202,19 +193,31 @@ func (msg *MsgEthereumTx) DecodeRLP(s *rlp.Stream) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sign calculates a secp256k1 ECDSA signature and signs the transaction. It
|
// Sign calculates a secp256k1 ECDSA signature and signs the transaction. It
|
||||||
// takes a private key and chainID to sign an Ethereum transaction according to
|
// takes a keyring signer and the chainID to sign an Ethereum transaction according to
|
||||||
// EIP155 standard. It mutates the transaction as it populates the V, R, S
|
// EIP155 standard.
|
||||||
|
// This method mutates the transaction as it populates the V, R, S
|
||||||
// fields of the Transaction's Signature.
|
// fields of the Transaction's Signature.
|
||||||
func (msg *MsgEthereumTx) Sign(chainID *big.Int, priv *ecdsa.PrivateKey) error {
|
// The function will fail if the sender address is not defined for the msg or if
|
||||||
|
// the sender is not registered on the keyring
|
||||||
|
func (msg *MsgEthereumTx) Sign(chainID *big.Int, signer keyring.Signer) error {
|
||||||
|
from := msg.GetFrom()
|
||||||
|
if from == nil {
|
||||||
|
return fmt.Errorf("sender address not defined for message")
|
||||||
|
}
|
||||||
|
|
||||||
txHash := msg.RLPSignBytes(chainID)
|
txHash := msg.RLPSignBytes(chainID)
|
||||||
|
|
||||||
sig, err := ethcrypto.Sign(txHash[:], priv)
|
sig, _, err := signer.SignByAddress(from, txHash[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(sig) != 65 {
|
if len(sig) != crypto.SignatureLength {
|
||||||
return fmt.Errorf("wrong size for signature: got %d, want 65", len(sig))
|
return fmt.Errorf(
|
||||||
|
"wrong size for signature: got %d, want %d",
|
||||||
|
len(sig),
|
||||||
|
crypto.SignatureLength,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
r := new(big.Int).SetBytes(sig[:32])
|
r := new(big.Int).SetBytes(sig[:32])
|
||||||
@ -279,10 +282,13 @@ func (msg *MsgEthereumTx) GetFrom() sdk.AccAddress {
|
|||||||
return ethcmn.HexToAddress(msg.From).Bytes()
|
return ethcmn.HexToAddress(msg.From).Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AsTransaction creates an Ethereum Transaction type from the msg fields
|
||||||
func (msg MsgEthereumTx) AsTransaction() *ethtypes.Transaction {
|
func (msg MsgEthereumTx) AsTransaction() *ethtypes.Transaction {
|
||||||
return ethtypes.NewTx(msg.Data.AsEthereumData())
|
return ethtypes.NewTx(msg.Data.AsEthereumData())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AsMessage creates an Ethereum core.Message from the msg fields. This method
|
||||||
|
// fails if the sender address is not defined
|
||||||
func (msg MsgEthereumTx) AsMessage() (core.Message, error) {
|
func (msg MsgEthereumTx) AsMessage() (core.Message, error) {
|
||||||
if msg.From == "" {
|
if msg.From == "" {
|
||||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "'from' address cannot be empty")
|
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "'from' address cannot be empty")
|
||||||
|
@ -2,38 +2,66 @@ package types
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/ethermint/crypto/ethsecp256k1"
|
||||||
|
"github.com/cosmos/ethermint/tests"
|
||||||
|
|
||||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||||
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMsgEthereumTx(t *testing.T) {
|
type MsgsTestSuite struct {
|
||||||
addr := GenerateEthAddress()
|
suite.Suite
|
||||||
|
|
||||||
msg := NewMsgEthereumTx(nil, 0, &addr, nil, 100000, nil, []byte("test"), nil)
|
signer keyring.Signer
|
||||||
require.NotNil(t, msg)
|
from ethcmn.Address
|
||||||
require.EqualValues(t, msg.Data.To, addr.Bytes())
|
to ethcmn.Address
|
||||||
require.Equal(t, msg.Route(), RouterKey)
|
chainID *big.Int
|
||||||
require.Equal(t, msg.Type(), TypeMsgEthereumTx)
|
|
||||||
require.NotNil(t, msg.To())
|
|
||||||
require.Equal(t, msg.GetMsgs(), []sdk.Msg{msg})
|
|
||||||
require.Panics(t, func() { msg.GetSigners() })
|
|
||||||
require.Panics(t, func() { msg.GetSignBytes() })
|
|
||||||
|
|
||||||
msg = NewMsgEthereumTxContract(nil, 0, nil, 100000, nil, []byte("test"), nil)
|
|
||||||
require.NotNil(t, msg)
|
|
||||||
require.Nil(t, msg.Data.To)
|
|
||||||
require.Nil(t, msg.To())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMsgEthereumTxValidation(t *testing.T) {
|
func TestMsgsTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(MsgsTestSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *MsgsTestSuite) SetupTest() {
|
||||||
|
privFrom, err := ethsecp256k1.GenerateKey()
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
privTo, err := ethsecp256k1.GenerateKey()
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
suite.signer = tests.NewSigner(privFrom)
|
||||||
|
suite.from = crypto.PubkeyToAddress(privFrom.ToECDSA().PublicKey)
|
||||||
|
suite.to = crypto.PubkeyToAddress(privTo.ToECDSA().PublicKey)
|
||||||
|
suite.chainID = big.NewInt(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *MsgsTestSuite) TestMsgEthereumTx_Constructor() {
|
||||||
|
msg := NewMsgEthereumTx(nil, 0, &suite.to, nil, 100000, nil, []byte("test"), nil)
|
||||||
|
|
||||||
|
suite.Require().Equal(msg.Data.To, suite.to.Bytes())
|
||||||
|
suite.Require().Equal(msg.Route(), RouterKey)
|
||||||
|
suite.Require().Equal(msg.Type(), TypeMsgEthereumTx)
|
||||||
|
suite.Require().NotNil(msg.To())
|
||||||
|
suite.Require().Equal(msg.GetMsgs(), []sdk.Msg{msg})
|
||||||
|
suite.Require().Panics(func() { msg.GetSigners() })
|
||||||
|
suite.Require().Panics(func() { msg.GetSignBytes() })
|
||||||
|
|
||||||
|
msg = NewMsgEthereumTxContract(nil, 0, nil, 100000, nil, []byte("test"), nil)
|
||||||
|
suite.Require().NotNil(msg)
|
||||||
|
suite.Require().Nil(msg.Data.To)
|
||||||
|
suite.Require().Nil(msg.To())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
msg string
|
msg string
|
||||||
amount *big.Int
|
amount *big.Int
|
||||||
@ -41,68 +69,81 @@ func TestMsgEthereumTxValidation(t *testing.T) {
|
|||||||
expectPass bool
|
expectPass bool
|
||||||
}{
|
}{
|
||||||
{msg: "pass", amount: big.NewInt(100), gasPrice: big.NewInt(100000), expectPass: true},
|
{msg: "pass", amount: big.NewInt(100), gasPrice: big.NewInt(100000), expectPass: true},
|
||||||
{msg: "invalid amount", amount: big.NewInt(-1), gasPrice: big.NewInt(100000), expectPass: false},
|
// NOTE: these can't be effectively tested because the SetBytes function from big.Int only sets
|
||||||
{msg: "invalid gas price", amount: big.NewInt(100), gasPrice: big.NewInt(-1), expectPass: false},
|
// the absolute value
|
||||||
{msg: "invalid gas price", amount: big.NewInt(100), gasPrice: big.NewInt(0), expectPass: false},
|
{msg: "negative amount", amount: big.NewInt(-1), gasPrice: big.NewInt(1000), expectPass: true},
|
||||||
|
{msg: "negative gas price", amount: big.NewInt(100), gasPrice: big.NewInt(-1), expectPass: true},
|
||||||
|
{msg: "zero gas price", amount: big.NewInt(100), gasPrice: big.NewInt(0), expectPass: true},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range testCases {
|
for i, tc := range testCases {
|
||||||
msg := NewMsgEthereumTx(nil, 0, nil, tc.amount, 0, tc.gasPrice, nil, nil)
|
msg := NewMsgEthereumTx(suite.chainID, 0, nil, tc.amount, 0, tc.gasPrice, nil, nil)
|
||||||
|
err := msg.ValidateBasic()
|
||||||
|
|
||||||
if tc.expectPass {
|
if tc.expectPass {
|
||||||
require.Nil(t, msg.ValidateBasic(), "valid test %d failed: %s", i, tc.msg)
|
suite.Require().NoError(err, "valid test %d failed: %s", i, tc.msg)
|
||||||
} else {
|
} else {
|
||||||
require.NotNil(t, msg.ValidateBasic(), "invalid test %d passed: %s", i, tc.msg)
|
suite.Require().Error(err, "invalid test %d passed: %s", i, tc.msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMsgEthereumTxRLPSignBytes(t *testing.T) {
|
func (suite *MsgsTestSuite) TestMsgEthereumTx_EncodeRLP() {
|
||||||
addr := ethcmn.BytesToAddress([]byte("test_address"))
|
expMsg := NewMsgEthereumTx(suite.chainID, 0, &suite.to, nil, 100000, nil, []byte("test"), nil)
|
||||||
chainID := big.NewInt(3)
|
|
||||||
|
|
||||||
msg := NewMsgEthereumTx(chainID, 0, &addr, nil, 100000, nil, []byte("test"), nil)
|
|
||||||
hash := msg.RLPSignBytes(chainID)
|
|
||||||
require.Equal(t, "5BD30E35AD27449390B14C91E6BCFDCAADF8FE44EF33680E3BC200FC0DC083C7", fmt.Sprintf("%X", hash))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMsgEthereumTxRLPEncode(t *testing.T) {
|
|
||||||
addr := ethcmn.BytesToAddress([]byte("test_address"))
|
|
||||||
expMsg := NewMsgEthereumTx(big.NewInt(1), 0, &addr, nil, 100000, nil, []byte("test"), nil)
|
|
||||||
|
|
||||||
raw, err := rlp.EncodeToBytes(&expMsg)
|
raw, err := rlp.EncodeToBytes(&expMsg)
|
||||||
require.NoError(t, err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
msg := &MsgEthereumTx{}
|
msg := &MsgEthereumTx{}
|
||||||
err = rlp.Decode(bytes.NewReader(raw), &msg)
|
err = rlp.Decode(bytes.NewReader(raw), &msg)
|
||||||
require.NoError(t, err)
|
suite.Require().NoError(err)
|
||||||
require.Equal(t, expMsg.Data, msg.Data)
|
suite.Require().Equal(expMsg.Data, msg.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// func TestMsgEthereumTxSig(t *testing.T) {
|
func (suite *MsgsTestSuite) TestMsgEthereumTx_RLPSignBytes() {
|
||||||
// chainID := big.NewInt(3)
|
msg := NewMsgEthereumTx(suite.chainID, 0, &suite.to, nil, 100000, nil, []byte("test"), nil)
|
||||||
|
suite.NotPanics(func() { _ = msg.RLPSignBytes(suite.chainID) })
|
||||||
|
}
|
||||||
|
|
||||||
// priv1, _ := ethsecp256k1.GenerateKey()
|
func (suite *MsgsTestSuite) TestMsgEthereumTx_Sign() {
|
||||||
// priv2, _ := ethsecp256k1.GenerateKey()
|
msg := NewMsgEthereumTx(suite.chainID, 0, &suite.to, nil, 100000, nil, []byte("test"), nil)
|
||||||
// addr1 := ethcmn.BytesToAddress(priv1.PubKey().Address().Bytes())
|
|
||||||
// addr2 := ethcmn.BytesToAddress(priv2.PubKey().Address().Bytes())
|
|
||||||
|
|
||||||
// // require valid signature passes validation
|
testCases := []struct {
|
||||||
// msg := NewMsgEthereumTx(nil, 0, &addr1, nil, 100000, nil, []byte("test"), nil)
|
msg string
|
||||||
// err := msg.Sign(chainID, priv1.ToECDSA())
|
malleate func()
|
||||||
// require.Nil(t, err)
|
expectPass bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"pass",
|
||||||
|
func() { msg.From = suite.from.Hex() },
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no from address ",
|
||||||
|
func() { msg.From = "" },
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from address ≠ signer address",
|
||||||
|
func() { msg.From = suite.to.Hex() },
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// signer, err := msg.VerifySig(chainID)
|
for i, tc := range testCases {
|
||||||
// require.NoError(t, err)
|
tc.malleate()
|
||||||
// require.Equal(t, addr1, signer)
|
err := msg.Sign(suite.chainID, suite.signer)
|
||||||
// require.NotEqual(t, addr2, signer)
|
if tc.expectPass {
|
||||||
|
suite.Require().NoError(err, "valid test %d failed: %s", i, tc.msg)
|
||||||
|
|
||||||
// // require invalid chain ID fail validation
|
tx := msg.AsTransaction()
|
||||||
// msg = NewMsgEthereumTx(nil, 0, &addr1, nil, 100000, nil, []byte("test"), nil)
|
signer := ethtypes.NewEIP2930Signer(suite.chainID)
|
||||||
// err = msg.Sign(chainID, priv1.ToECDSA())
|
|
||||||
// require.Nil(t, err)
|
|
||||||
|
|
||||||
// signer, err = msg.VerifySig(big.NewInt(4))
|
sender, err := ethtypes.Sender(signer, tx)
|
||||||
// require.Error(t, err)
|
suite.Require().NoError(err, tc.msg)
|
||||||
// require.Equal(t, ethcmn.Address{}, signer)
|
suite.Require().Equal(msg.From, sender.Hex(), tc.msg)
|
||||||
// }
|
} else {
|
||||||
|
suite.Require().Error(err, "invalid test %d passed: %s", i, tc.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,8 +2,6 @@ package types
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
log "github.com/xlab/suplog"
|
log "github.com/xlab/suplog"
|
||||||
|
|
||||||
@ -13,25 +11,9 @@ import (
|
|||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ValidateSigner attempts to validate a signer for a given slice of bytes over
|
|
||||||
// which a signature and signer is given. An error is returned if address
|
|
||||||
// derived from the signature and bytes signed does not match the given signer.
|
|
||||||
func ValidateSigner(signBytes, sig []byte, signer ethcmn.Address) error {
|
|
||||||
pk, err := ethcrypto.SigToPub(signBytes, sig)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to derive public key from signature")
|
|
||||||
} else if ethcrypto.PubkeyToAddress(*pk) != signer {
|
|
||||||
return fmt.Errorf("invalid signature for signer: %s", signer)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func rlpHash(x interface{}) (hash ethcmn.Hash) {
|
func rlpHash(x interface{}) (hash ethcmn.Hash) {
|
||||||
hasher := sha3.NewLegacyKeccak256()
|
hasher := sha3.NewLegacyKeccak256()
|
||||||
_ = rlp.Encode(hasher, x)
|
_ = rlp.Encode(hasher, x)
|
||||||
@ -88,45 +70,6 @@ func DecodeTransactionLogs(data []byte) (TransactionLogs, error) {
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Auxiliary
|
// Auxiliary
|
||||||
|
|
||||||
// recoverEthSig recovers a signature according to the Ethereum specification and
|
|
||||||
// returns the sender or an error.
|
|
||||||
//
|
|
||||||
// Ref: Ethereum Yellow Paper (BYZANTIUM VERSION 69351d5) Appendix F
|
|
||||||
// nolint: gocritic
|
|
||||||
func recoverEthSig(R, S, Vb *big.Int, sigHash ethcmn.Hash) (ethcmn.Address, error) {
|
|
||||||
if Vb.BitLen() > 8 {
|
|
||||||
return ethcmn.Address{}, errors.New("invalid signature")
|
|
||||||
}
|
|
||||||
|
|
||||||
V := byte(Vb.Uint64() - 27)
|
|
||||||
if !ethcrypto.ValidateSignatureValues(V, R, S, true) {
|
|
||||||
return ethcmn.Address{}, errors.New("invalid signature")
|
|
||||||
}
|
|
||||||
|
|
||||||
// encode the signature in uncompressed format
|
|
||||||
r, s := R.Bytes(), S.Bytes()
|
|
||||||
sig := make([]byte, 65)
|
|
||||||
|
|
||||||
copy(sig[32-len(r):32], r)
|
|
||||||
copy(sig[64-len(s):64], s)
|
|
||||||
sig[64] = V
|
|
||||||
|
|
||||||
// recover the public key from the signature
|
|
||||||
pub, err := ethcrypto.Ecrecover(sigHash[:], sig)
|
|
||||||
if err != nil {
|
|
||||||
return ethcmn.Address{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(pub) == 0 || pub[0] != 4 {
|
|
||||||
return ethcmn.Address{}, errors.New("invalid public key")
|
|
||||||
}
|
|
||||||
|
|
||||||
var addr ethcmn.Address
|
|
||||||
copy(addr[:], ethcrypto.Keccak256(pub[1:])[12:])
|
|
||||||
|
|
||||||
return addr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmptyHash returns true if the hash corresponds to an empty ethereum hex hash.
|
// IsEmptyHash returns true if the hash corresponds to an empty ethereum hex hash.
|
||||||
func IsEmptyHash(hash string) bool {
|
func IsEmptyHash(hash string) bool {
|
||||||
return bytes.Equal(ethcmn.HexToHash(hash).Bytes(), ethcmn.Hash{}.Bytes())
|
return bytes.Equal(ethcmn.HexToHash(hash).Bytes(), ethcmn.Hash{}.Bytes())
|
||||||
|
Loading…
Reference in New Issue
Block a user