Remove EmbeddedTx and related logic

This commit is contained in:
Aleksandr Bezobchuk 2018-08-24 08:29:10 -07:00
parent 8b642e3f46
commit cd593e3e37
8 changed files with 94 additions and 394 deletions

54
Gopkg.lock generated
View File

@ -3,19 +3,19 @@
[[projects]]
branch = "master"
digest = "1:fcdf62d2d7e43c2565d6f8707ab4eae54dac702ed4bafb194b85139f0508929f"
digest = "1:79b02529d2120af44eaad5f7fee9ff7e003739d469e2c2767008b797d290e9fd"
name = "github.com/aristanetworks/goarista"
packages = ["monotime"]
pruneopts = "T"
revision = "b2d71c282dc706f4b4f6c15b65810e1202ecd53f"
revision = "18b896026201d8e1758468d9c5d5a558c69c5e9b"
[[projects]]
branch = "master"
digest = "1:d4d66abd43dbb9b5f5e6a176c5ed279c289f8db734904c047d95113a04aa2e60"
digest = "1:8ad24ea05e770b745b5c286b4f94cf73d5be87005680e36b2d0dd1de0a2f9fbf"
name = "github.com/btcsuite/btcd"
packages = ["btcec"]
pruneopts = "T"
revision = "cf05f92c3f815bbd5091ed6c73eff51f7b1945e8"
revision = "d81d8877b8f327112e94e814937143a71d1692a7"
[[projects]]
digest = "1:d0d998526cfb68788229a31c16a557fdf1fbbb510654be6b3732c2758e06b533"
@ -25,7 +25,7 @@
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
[[projects]]
digest = "1:36773b598dec105de46a87978ae14e64c8d2c45aa556b8e0ddfc62d6abc7c47e"
digest = "1:bc28e755cf6a9fd8e65497514d20c4907973e7a6a6409d30ead3fd37bfeb19a9"
name = "github.com/cosmos/cosmos-sdk"
packages = [
"baseapp",
@ -36,16 +36,16 @@
"x/auth",
]
pruneopts = "T"
revision = "23e3d5ac12145c02fcb4b4767d7dfccad782aee5"
version = "v0.23.1"
revision = "1c38c70468ec721e3a555ba2f3bf5f9da31f0cc9"
version = "v0.24.2"
[[projects]]
digest = "1:52f195ad0e20a92d8604c1ba3cd246c61644c03eaa454b5acd41be89841e0d10"
digest = "1:9f42202ac457c462ad8bb9642806d275af9ab4850cf0b1960b9c6f083d4a309a"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = "T"
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.1"
[[projects]]
branch = "master"
@ -224,12 +224,12 @@
version = "v0.0.3"
[[projects]]
digest = "1:6de2f73eb31e80d74f84ce1c861e4c0c8f00ca5fb41a25901f987e63a0647c28"
digest = "1:9ba911fe3884995431690e7eb180cf848da0d637ba5f61711783b795d031793f"
name = "github.com/spf13/pflag"
packages = ["."]
pruneopts = "T"
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
version = "v1.0.1"
revision = "9a97c102cda95a86cec2345a6f09f55a939babf5"
version = "v1.0.2"
[[projects]]
digest = "1:e95496462101745805bd4e041a5b841e108c7cf761264d53648246308de2761e"
@ -245,7 +245,7 @@
[[projects]]
branch = "master"
digest = "1:7d44c4d11eb65cfdc78c76040f37ef305b16474c019c98a8a7cf188fece2d574"
digest = "1:ee395d0d8c1719b5a1407f34af93953b4763bacb19a8961aba5b6d312824da41"
name = "github.com/syndtr/goleveldb"
packages = [
"leveldb",
@ -262,7 +262,7 @@
"leveldb/util",
]
pruneopts = "T"
revision = "c4c61651e9e37fa117f53c5a906d3b63090d8445"
revision = "ae2bd5eed72d46b28834ec3f60db3a3ebedd8dbd"
[[projects]]
branch = "master"
@ -276,14 +276,6 @@
pruneopts = "T"
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057"
[[projects]]
branch = "master"
digest = "1:3a6bdd02e7f2585c860e368467c5989310740af6206a1ada85cfa19c712e5afd"
name = "github.com/tendermint/ethermint"
packages = ["version"]
pruneopts = "T"
revision = "c1e6ebf80a6cc9119bc178faee18ef13490d707a"
[[projects]]
digest = "1:0e2addab3f64ece97ca434b2bf2d4e8cb54a4509904a03be8c81da3fc2ddb245"
name = "github.com/tendermint/go-amino"
@ -301,7 +293,7 @@
version = "v0.9.2"
[[projects]]
digest = "1:9f6704ae2aedbadf616e5850375c504909d46b6ea57d4679de2b7cbc715f08e1"
digest = "1:5a60cb048b401c0263c227baf8778ecaf038be531707057607949540486874ef"
name = "github.com/tendermint/tendermint"
packages = [
"abci/server",
@ -321,16 +313,16 @@
"types",
]
pruneopts = "T"
revision = "d542d2c3945116697f60451e6a407082c41c3cc9"
version = "v0.22.8"
revision = "81df19e68ab1519399fccf0cab81cb75bf9d782e"
version = "v0.23.1-rc0"
[[projects]]
branch = "master"
digest = "1:2cbe8758697d867fcebf73bcc69dff8e8abaa7fd65e5704e0744e522ccff4e6a"
digest = "1:da29cbeb9d244918393b37243c008ab7128688fb017c966aaf876587c010bcdd"
name = "golang.org/x/crypto"
packages = ["ripemd160"]
pruneopts = "T"
revision = "f027049dab0ad238e394a753dba2d14753473a04"
revision = "614d502a4dac94afa3a6ce146bd1736da82514c6"
[[projects]]
digest = "1:5fdc7adede42f80d6201258355d478d856778e21d735f14972abd8ff793fdbf7"
@ -372,11 +364,12 @@
version = "v0.3.0"
[[projects]]
digest = "1:8cfa91d1b7f6b66fa9b1a738a4bc1325837b861e63fb9a2919931d68871bb770"
branch = "master"
digest = "1:960f1fa3f12667fe595c15c12523718ed8b1b5428c83d70da54bb014da9a4c1a"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
pruneopts = "T"
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200"
revision = "c66870c02cf823ceb633bcd05be3c7cda29976f4"
[[projects]]
digest = "1:adafc60b1d4688759f3fc8f9089e71dd17abd123f4729de6b913bf08c9143770"
@ -466,7 +459,6 @@
"github.com/pkg/errors",
"github.com/stretchr/testify/require",
"github.com/stretchr/testify/suite",
"github.com/tendermint/ethermint/version",
"github.com/tendermint/tendermint/libs/common",
"github.com/tendermint/tendermint/libs/db",
"github.com/tendermint/tendermint/libs/log",

View File

@ -4,7 +4,7 @@
[[constraint]]
name = "github.com/cosmos/cosmos-sdk"
version = "=0.23.1"
version = "=0.24.2"
[[constraint]]
name = "github.com/hashicorp/golang-lru"
@ -15,12 +15,12 @@
version = "~0.0.1"
[[override]]
name = "google.golang.org/genproto"
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200"
name = "github.com/tendermint/iavl"
version = "=v0.9.2"
[[override]]
name = "github.com/tendermint/tendermint"
version = "=v0.22.8"
version = "=v0.23.1-rc0"
[[constraint]]
name = "github.com/stretchr/testify"

View File

@ -26,11 +26,10 @@ type internalAnteHandler func(
sdkCtx sdk.Context, tx sdk.Tx, am auth.AccountMapper,
) (newCtx sdk.Context, res sdk.Result, abort bool)
// AnteHandler handles Ethereum transactions and passes SDK transactions to the
// embeddedAnteHandler if it's an Ethermint transaction. The ante handler gets
// invoked after the BaseApp performs the runTx. At this point, the transaction
// should be properly decoded via the TxDecoder and should be of a proper type,
// Transaction or EmbeddedTx.
// AnteHandler is responsible for attempting to route an Ethereum or SDK
// transaction to an internal ante handler for performing transaction-level
// processing (e.g. fee payment, signature verification) before being passed
// onto it's respective handler.
func AnteHandler(am auth.AccountMapper) sdk.AnteHandler {
return func(sdkCtx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) {
var (
@ -42,7 +41,7 @@ func AnteHandler(am auth.AccountMapper) sdk.AnteHandler {
case types.Transaction:
gasLimit = int64(tx.Data.GasLimit)
handler = handleEthTx
case types.EmbeddedTx:
case auth.StdTx:
gasLimit = tx.Fee.Gas
handler = handleEmbeddedTx
default:
@ -107,23 +106,23 @@ func handleEthTx(sdkCtx sdk.Context, tx sdk.Tx, am auth.AccountMapper) (sdk.Cont
// handleEmbeddedTx implements an ante handler for an SDK transaction. It
// validates the signature and if valid returns an OK result.
func handleEmbeddedTx(sdkCtx sdk.Context, tx sdk.Tx, am auth.AccountMapper) (sdk.Context, sdk.Result, bool) {
etx, ok := tx.(types.EmbeddedTx)
stdTx, ok := tx.(auth.StdTx)
if !ok {
return sdkCtx, sdk.ErrInternal(fmt.Sprintf("invalid transaction: %T", tx)).Result(), true
}
if err := validateEmbeddedTxBasic(etx); err != nil {
if err := validateStdTxBasic(stdTx); err != nil {
return sdkCtx, err.Result(), true
}
signerAddrs := etx.GetRequiredSigners()
signerAddrs := stdTx.GetSigners()
signerAccs := make([]auth.Account, len(signerAddrs))
// validate signatures
for i, sig := range etx.Signatures {
for i, sig := range stdTx.Signatures {
signer := ethcmn.BytesToAddress(signerAddrs[i].Bytes())
signerAcc, err := validateSignature(sdkCtx, etx, signer, sig, am)
signerAcc, err := validateSignature(sdkCtx, stdTx, signer, sig, am)
if err.Code() != sdk.CodeOK {
return sdkCtx, err.Result(), false
}
@ -136,18 +135,18 @@ func handleEmbeddedTx(sdkCtx sdk.Context, tx sdk.Tx, am auth.AccountMapper) (sdk
newCtx := auth.WithSigners(sdkCtx, signerAccs)
return newCtx, sdk.Result{GasWanted: etx.Fee.Gas}, false
return newCtx, sdk.Result{GasWanted: stdTx.Fee.Gas}, false
}
// validateEmbeddedTxBasic validates an EmbeddedTx based on things that don't
// validateStdTxBasic validates an auth.StdTx based on parameters that do not
// depend on the context.
func validateEmbeddedTxBasic(etx types.EmbeddedTx) (err sdk.Error) {
sigs := etx.Signatures
func validateStdTxBasic(stdTx auth.StdTx) (err sdk.Error) {
sigs := stdTx.Signatures
if len(sigs) == 0 {
return sdk.ErrUnauthorized("transaction missing signatures")
}
signerAddrs := etx.GetRequiredSigners()
signerAddrs := stdTx.GetSigners()
if len(sigs) != len(signerAddrs) {
return sdk.ErrUnauthorized("invalid number of transaction signers")
}
@ -156,8 +155,8 @@ func validateEmbeddedTxBasic(etx types.EmbeddedTx) (err sdk.Error) {
}
func validateSignature(
sdkCtx sdk.Context, etx types.EmbeddedTx, signer ethcmn.Address,
sig []byte, am auth.AccountMapper,
sdkCtx sdk.Context, stdTx auth.StdTx, signer ethcmn.Address,
sig auth.StdSignature, am auth.AccountMapper,
) (acc auth.Account, sdkErr sdk.Error) {
chainID := sdkCtx.ChainID()
@ -167,28 +166,29 @@ func validateSignature(
return nil, sdk.ErrUnknownAddress(fmt.Sprintf("no account with address %s found", signer))
}
signEtx := types.EmbeddedTxSign{
ChainID: chainID,
AccountNumber: acc.GetAccountNumber(),
Sequence: acc.GetSequence(),
Messages: etx.Messages,
Fee: etx.Fee,
accNum := acc.GetAccountNumber()
if accNum != sig.AccountNumber {
return nil, sdk.ErrInvalidSequence(
fmt.Sprintf("invalid account number; got %d, expected %d", sig.AccountNumber, accNum))
}
err := acc.SetSequence(signEtx.Sequence + 1)
accSeq := acc.GetSequence()
if accSeq != sig.Sequence {
return nil, sdk.ErrInvalidSequence(
fmt.Sprintf("invalid account sequence; got %d, expected %d", sig.Sequence, accSeq))
}
err := acc.SetSequence(accSeq + 1)
if err != nil {
return nil, sdk.ErrInternal(err.Error())
}
signBytes, err := signEtx.Bytes()
if err != nil {
return nil, sdk.ErrInternal(err.Error())
}
signBytes := types.GetStdTxSignBytes(chainID, accNum, accSeq, stdTx.Fee, stdTx.GetMsgs(), stdTx.Memo)
// consume gas for signature verification
sdkCtx.GasMeter().ConsumeGas(verifySigCost, "ante verify")
sdkCtx.GasMeter().ConsumeGas(verifySigCost, "ante signature verification")
if err := types.ValidateSigner(signBytes, sig, signer); err != nil {
if err := types.ValidateSigner(signBytes, sig.Signature, signer); err != nil {
return nil, sdk.ErrUnauthorized(err.Error())
}

View File

@ -1,17 +1,13 @@
package types
import (
"bytes"
"crypto/ecdsa"
"crypto/sha256"
"encoding/json"
"fmt"
"math/big"
"sync/atomic"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
ethcmn "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
@ -26,13 +22,9 @@ const (
TypeTxEthereum = "Ethereum"
)
// ----------------------------------------------------------------------------
// Ethereum transaction
// ----------------------------------------------------------------------------
type (
// Transaction implements the Ethereum transaction structure as an exact
// copy. It implements the Cosmos sdk.Tx interface. Due to the private
// replica. It implements the Cosmos sdk.Tx interface. Due to the private
// fields, it must be replicated here and cannot be embedded or used
// directly.
//
@ -47,9 +39,7 @@ type (
from atomic.Value
}
// TxData implements the Ethereum transaction data structure as an exact
// copy. It is used solely as intended in Ethereum abiding by the protocol
// except for the payload field which may embed a Cosmos SDK transaction.
// TxData defines internal Ethereum transaction information
TxData struct {
AccountNonce uint64 `json:"nonce"`
Price sdk.Int `json:"gasPrice"`
@ -204,6 +194,8 @@ func (tx Transaction) GetMsgs() []sdk.Msg {
// with the signature set. The signature if first recovered and then a new
// Transaction is created with that signature. If setting the signature fails,
// a panic will be triggered.
//
// TODO: To be removed in #470
func (tx Transaction) ConvertTx(chainID *big.Int) ethtypes.Transaction {
gethTx := ethtypes.NewTransaction(
tx.Data.AccountNonce, *tx.Data.Recipient, tx.Data.Amount.BigInt(),
@ -221,139 +213,16 @@ func (tx Transaction) ConvertTx(chainID *big.Int) ethtypes.Transaction {
return *gethTx
}
// HasEmbeddedTx returns a boolean reflecting if the transaction contains an
// SDK transaction or not based on the recipient address.
func (tx Transaction) HasEmbeddedTx(addr ethcmn.Address) bool {
return bytes.Equal(tx.Data.Recipient.Bytes(), addr.Bytes())
}
// GetEmbeddedTx returns the embedded SDK transaction from an Ethereum
// transaction. It returns an error if decoding the inner transaction fails.
//
// CONTRACT: The payload field of an Ethereum transaction must contain a valid
// encoded SDK transaction.
func (tx Transaction) GetEmbeddedTx(codec *wire.Codec) (EmbeddedTx, sdk.Error) {
etx := EmbeddedTx{}
err := codec.UnmarshalBinary(tx.Data.Payload, &etx)
if err != nil {
return EmbeddedTx{}, sdk.ErrTxDecode("failed to encode embedded tx")
}
return etx, nil
}
// ----------------------------------------------------------------------------
// embedded SDK transaction
// ----------------------------------------------------------------------------
type (
// EmbeddedTx implements an SDK transaction. It is to be encoded into the
// payload field of an Ethereum transaction in order to route and handle SDK
// transactions.
EmbeddedTx struct {
Messages []sdk.Msg `json:"messages"`
Fee auth.StdFee `json:"fee"`
Signatures [][]byte `json:"signatures"`
}
// embeddedSignDoc implements a simple SignDoc for a EmbeddedTx signer to
// sign over.
embeddedSignDoc struct {
ChainID string `json:"chainID"`
AccountNumber int64 `json:"accountNumber"`
Sequence int64 `json:"sequence"`
Messages []json.RawMessage `json:"messages"`
Fee json.RawMessage `json:"fee"`
}
// EmbeddedTxSign implements a structure for containing the information
// necessary for building and signing an EmbeddedTx.
EmbeddedTxSign struct {
ChainID string
AccountNumber int64
Sequence int64
Messages []sdk.Msg
Fee auth.StdFee
}
)
// GetMsgs implements the sdk.Tx interface. It returns all the SDK transaction
// messages.
func (etx EmbeddedTx) GetMsgs() []sdk.Msg {
return etx.Messages
}
// GetRequiredSigners returns all the required signers of an SDK transaction
// accumulated from messages. It returns them in a deterministic fashion given
// a list of messages.
func (etx EmbeddedTx) GetRequiredSigners() []sdk.AccAddress {
seen := map[string]bool{}
var signers []sdk.AccAddress
for _, msg := range etx.GetMsgs() {
for _, addr := range msg.GetSigners() {
if !seen[addr.String()] {
signers = append(signers, sdk.AccAddress(addr))
seen[addr.String()] = true
}
}
}
return signers
}
// Bytes returns the EmbeddedTxSign signature bytes for a signer to sign over.
func (ets EmbeddedTxSign) Bytes() ([]byte, error) {
sigBytes, err := EmbeddedSignBytes(ets.ChainID, ets.AccountNumber, ets.Sequence, ets.Messages, ets.Fee)
if err != nil {
return nil, err
}
hash := sha256.Sum256(sigBytes)
return hash[:], nil
}
// EmbeddedSignBytes creates signature bytes for a signer to sign an embedded
// transaction. The signature bytes require a chainID and an account number.
// The signature bytes are JSON encoded.
func EmbeddedSignBytes(chainID string, accnum, sequence int64, msgs []sdk.Msg, fee auth.StdFee) ([]byte, error) {
var msgsBytes []json.RawMessage
for _, msg := range msgs {
msgsBytes = append(msgsBytes, json.RawMessage(msg.GetSignBytes()))
}
signDoc := embeddedSignDoc{
ChainID: chainID,
AccountNumber: accnum,
Sequence: sequence,
Messages: msgsBytes,
Fee: json.RawMessage(fee.Bytes()),
}
bz, err := typesCodec.MarshalJSON(signDoc)
if err != nil {
errors.Wrap(err, "failed to JSON encode EmbeddedSignDoc")
}
return bz, nil
}
// ----------------------------------------------------------------------------
// Utilities
// ----------------------------------------------------------------------------
// TxDecoder returns an sdk.TxDecoder that given raw transaction bytes,
// attempts to decode them into a Transaction or an EmbeddedTx or returning an
// error if decoding fails.
func TxDecoder(codec *wire.Codec, sdkAddress ethcmn.Address) sdk.TxDecoder {
// attempts to decode them into a valid sdk.Tx.
func TxDecoder(codec *wire.Codec) sdk.TxDecoder {
return func(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx = Transaction{}
if len(txBytes) == 0 {
return nil, sdk.ErrTxDecode("txBytes are empty")
}
var tx sdk.Tx
// The given codec should have all the appropriate message types
// registered.
err := codec.UnmarshalBinary(txBytes, &tx)
@ -361,17 +230,6 @@ func TxDecoder(codec *wire.Codec, sdkAddress ethcmn.Address) sdk.TxDecoder {
return nil, sdk.ErrTxDecode("failed to decode tx").TraceSDK(err.Error())
}
// If the transaction is routed as an SDK transaction, decode and
// return the embedded transaction.
if tx.HasEmbeddedTx(sdkAddress) {
etx, err := tx.GetEmbeddedTx(codec)
if err != nil {
return nil, err
}
return etx, nil
}
return tx, nil
}
}

View File

@ -3,14 +3,12 @@ package types
import (
"crypto/ecdsa"
"fmt"
"math/big"
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
ethcmn "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
)
@ -23,64 +21,42 @@ var (
testAddr1 = PrivKeyToEthAddress(testPrivKey1)
testAddr2 = PrivKeyToEthAddress(testPrivKey2)
testSDKAddress = GenerateEthAddress()
)
func newTestCodec() *wire.Codec {
codec := wire.NewCodec()
RegisterWire(codec)
codec.RegisterConcrete(auth.StdTx{}, "test/StdTx", nil)
codec.RegisterConcrete(&sdk.TestMsg{}, "test/TestMsg", nil)
wire.RegisterCrypto(codec)
return codec
}
func newStdFee() auth.StdFee {
return auth.NewStdFee(5000, sdk.NewCoin("photon", 150))
return auth.NewStdFee(5000, sdk.NewCoin("photon", sdk.NewInt(150)))
}
func newTestEmbeddedTx(
func newTestStdTx(
chainID sdk.Int, msgs []sdk.Msg, pKeys []*ecdsa.PrivateKey,
accNums []int64, seqs []int64, fee auth.StdFee,
) sdk.Tx {
sigs := make([][]byte, len(pKeys))
sigs := make([]auth.StdSignature, len(pKeys))
for i, priv := range pKeys {
signEtx := EmbeddedTxSign{chainID.String(), accNums[i], seqs[i], msgs, newStdFee()}
signBytes, err := signEtx.Bytes()
if err != nil {
panic(err)
}
signBytes := GetStdTxSignBytes(chainID.String(), accNums[i], seqs[i], newStdFee(), msgs, "")
sig, err := ethcrypto.Sign(signBytes, priv)
if err != nil {
panic(err)
}
sigs[i] = sig
sigs[i] = auth.StdSignature{Signature: sig, AccountNumber: accNums[i], Sequence: seqs[i]}
}
return EmbeddedTx{msgs, fee, sigs}
}
func newTestGethTxs(chainID sdk.Int, pKeys []*ecdsa.PrivateKey, addrs []ethcmn.Address) []ethtypes.Transaction {
txs := make([]ethtypes.Transaction, len(pKeys))
for i, priv := range pKeys {
ethTx := ethtypes.NewTransaction(
uint64(i), addrs[i], big.NewInt(10), 100, big.NewInt(100), nil,
)
signer := ethtypes.NewEIP155Signer(chainID.BigInt())
ethTx, _ = ethtypes.SignTx(ethTx, signer, priv)
txs[i] = *ethTx
}
return txs
return auth.NewStdTx(msgs, fee, sigs, "")
}
func newTestEthTxs(chainID sdk.Int, pKeys []*ecdsa.PrivateKey, addrs []ethcmn.Address) []Transaction {
@ -99,63 +75,6 @@ func newTestEthTxs(chainID sdk.Int, pKeys []*ecdsa.PrivateKey, addrs []ethcmn.Ad
return txs
}
func newTestSDKTxs(
codec *wire.Codec, chainID sdk.Int, msgs []sdk.Msg, pKeys []*ecdsa.PrivateKey,
accNums []int64, seqs []int64, fee auth.StdFee,
) []Transaction {
txs := make([]Transaction, len(pKeys))
etx := newTestEmbeddedTx(chainID, msgs, pKeys, accNums, seqs, fee)
for i, priv := range pKeys {
payload := codec.MustMarshalBinary(etx)
emintTx := NewTransaction(
uint64(i), testSDKAddress, sdk.NewInt(10), 100,
sdk.NewInt(100), payload,
)
emintTx.Sign(testChainID, priv)
txs[i] = emintTx
}
return txs
}
func TestConvertTx(t *testing.T) {
gethTxs := newTestGethTxs(
testChainID,
[]*ecdsa.PrivateKey{testPrivKey1, testPrivKey2},
[]ethcmn.Address{testAddr1, testAddr2},
)
ethTxs := newTestEthTxs(
testChainID,
[]*ecdsa.PrivateKey{testPrivKey1, testPrivKey2},
[]ethcmn.Address{testAddr1, testAddr2},
)
testCases := []struct {
ethTx ethtypes.Transaction
emintTx Transaction
expectedEq bool
}{
{gethTxs[0], ethTxs[0], true},
{gethTxs[0], ethTxs[1], false},
{gethTxs[1], ethTxs[0], false},
}
for i, tc := range testCases {
convertedTx := tc.emintTx.ConvertTx(testChainID.BigInt())
if tc.expectedEq {
require.Equal(t, tc.ethTx, convertedTx, fmt.Sprintf("unexpected result: test case #%d", i))
} else {
require.NotEqual(t, tc.ethTx, convertedTx, fmt.Sprintf("unexpected result: test case #%d", i))
}
}
}
func TestValidation(t *testing.T) {
ethTxs := newTestEthTxs(
testChainID,
@ -193,47 +112,6 @@ func TestValidation(t *testing.T) {
}
}
func TestHasEmbeddedTx(t *testing.T) {
testCodec := newTestCodec()
msgs := []sdk.Msg{sdk.NewTestMsg(sdk.AccAddress(testAddr1.Bytes()))}
sdkTxs := newTestSDKTxs(
testCodec, testChainID, msgs, []*ecdsa.PrivateKey{testPrivKey1},
[]int64{0}, []int64{0}, newStdFee(),
)
require.True(t, sdkTxs[0].HasEmbeddedTx(testSDKAddress))
ethTxs := newTestEthTxs(
testChainID,
[]*ecdsa.PrivateKey{testPrivKey1},
[]ethcmn.Address{testAddr1},
)
require.False(t, ethTxs[0].HasEmbeddedTx(testSDKAddress))
}
func TestGetEmbeddedTx(t *testing.T) {
testCodec := newTestCodec()
msgs := []sdk.Msg{sdk.NewTestMsg(sdk.AccAddress(testAddr1.Bytes()))}
ethTxs := newTestEthTxs(
testChainID,
[]*ecdsa.PrivateKey{testPrivKey1},
[]ethcmn.Address{testAddr1},
)
sdkTxs := newTestSDKTxs(
testCodec, testChainID, msgs, []*ecdsa.PrivateKey{testPrivKey1},
[]int64{0}, []int64{0}, newStdFee(),
)
etx, err := sdkTxs[0].GetEmbeddedTx(testCodec)
require.NoError(t, err)
require.NotEmpty(t, etx.Messages)
etx, err = ethTxs[0].GetEmbeddedTx(testCodec)
require.Error(t, err)
require.Empty(t, etx.Messages)
}
func TestTransactionGetMsgs(t *testing.T) {
ethTxs := newTestEthTxs(
testChainID,
@ -246,7 +124,7 @@ func TestTransactionGetMsgs(t *testing.T) {
require.Equal(t, ethTxs[0], msgs[0])
expectedMsgs := []sdk.Msg{sdk.NewTestMsg(sdk.AccAddress(testAddr1.Bytes()))}
etx := newTestEmbeddedTx(
etx := newTestStdTx(
testChainID, expectedMsgs, []*ecdsa.PrivateKey{testPrivKey1},
[]int64{0}, []int64{0}, newStdFee(),
)
@ -256,21 +134,10 @@ func TestTransactionGetMsgs(t *testing.T) {
require.Equal(t, expectedMsgs, msgs)
}
func TestGetRequiredSigners(t *testing.T) {
msgs := []sdk.Msg{sdk.NewTestMsg(sdk.AccAddress(testAddr1.Bytes()))}
etx := newTestEmbeddedTx(
testChainID, msgs, []*ecdsa.PrivateKey{testPrivKey1},
[]int64{0}, []int64{0}, newStdFee(),
)
signers := etx.(EmbeddedTx).GetRequiredSigners()
require.Equal(t, []sdk.AccAddress{sdk.AccAddress(testAddr1.Bytes())}, signers)
}
func TestTxDecoder(t *testing.T) {
testCodec := newTestCodec()
txDecoder := TxDecoder(testCodec, testSDKAddress)
msgs := []sdk.Msg{sdk.NewTestMsg(sdk.AccAddress(testAddr1.Bytes()))}
txDecoder := TxDecoder(testCodec)
msgs := []sdk.Msg{sdk.NewTestMsg()}
// create a non-SDK Ethereum transaction
emintTx := NewTransaction(
@ -284,43 +151,20 @@ func TestTxDecoder(t *testing.T) {
require.NoError(t, err)
require.Equal(t, emintTx, tx)
// create embedded transaction and encode
etx := newTestEmbeddedTx(
// create a SDK (auth.StdTx) transaction and encode
stdTx := newTestStdTx(
testChainID, msgs, []*ecdsa.PrivateKey{testPrivKey1},
[]int64{0}, []int64{0}, newStdFee(),
)
payload := testCodec.MustMarshalBinary(etx)
expectedEtx := EmbeddedTx{}
testCodec.UnmarshalBinary(payload, &expectedEtx)
emintTx = NewTransaction(
uint64(0), testSDKAddress, sdk.NewInt(10), 100,
sdk.NewInt(100), payload,
)
emintTx.Sign(testChainID, testPrivKey1)
// require the transaction to properly decode into a Transaction
txBytes = testCodec.MustMarshalBinary(emintTx)
txBytes = testCodec.MustMarshalBinary(stdTx)
tx, err = txDecoder(txBytes)
require.NoError(t, err)
require.Equal(t, expectedEtx, tx)
require.Equal(t, stdTx, tx)
// require the decoding to fail when no transaction bytes are given
tx, err = txDecoder([]byte{})
require.Error(t, err)
require.Nil(t, tx)
// create a non-SDK Ethereum transaction with an SDK address and garbage payload
emintTx = NewTransaction(
uint64(0), testSDKAddress, sdk.NewInt(10), 100, sdk.NewInt(100), []byte("garbage"),
)
emintTx.Sign(testChainID, testPrivKey1)
// require the transaction to fail decoding as the payload is invalid
txBytes = testCodec.MustMarshalBinary(emintTx)
tx, err = txDecoder(txBytes)
require.Error(t, err)
require.Nil(t, tx)
}

View File

@ -2,8 +2,11 @@ package types
import (
"crypto/ecdsa"
"crypto/sha256"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
ethcmn "github.com/ethereum/go-ethereum/common"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
@ -39,3 +42,11 @@ func ValidateSigner(signBytes, sig []byte, signer ethcmn.Address) error {
return nil
}
// GetStdTxSignBytes returns the signature bytes for an auth.StdTx transaction
// that is compatible with Ethereum's signature mechanism.
func GetStdTxSignBytes(chainID string, accNum int64, seq int64, fee auth.StdFee, msgs []sdk.Msg, memo string) []byte {
signBytes := auth.StdSignBytes(chainID, accNum, seq, fee, msgs, "")
hash := sha256.Sum256(signBytes)
return hash[:]
}

View File

@ -12,12 +12,8 @@ import (
func TestValidateSigner(t *testing.T) {
msgs := []sdk.Msg{sdk.NewTestMsg(sdk.AccAddress(testAddr1.Bytes()))}
// create message signing structure
signEtx := EmbeddedTxSign{testChainID.String(), 0, 0, msgs, newStdFee()}
// create signing bytes and sign
signBytes, err := signEtx.Bytes()
require.NoError(t, err)
// create message signing structure and bytes
signBytes := GetStdTxSignBytes(testChainID.String(), 0, 0, newStdFee(), msgs, "")
// require signing not to fail
sig, err := ethcrypto.Sign(signBytes, testPrivKey1)

View File

@ -18,5 +18,4 @@ func RegisterWire(codec *wire.Codec) {
codec.RegisterConcrete(&EthSignature{}, "types/EthSignature", nil)
codec.RegisterConcrete(TxData{}, "types/TxData", nil)
codec.RegisterConcrete(Transaction{}, "types/Transaction", nil)
codec.RegisterConcrete(EmbeddedTx{}, "types/EmbeddedTx", nil)
}