Merge pull request #475 from cosmos/bez/474-refactor-tx-impl
R4R: Remove EmbeddedTx and Related Logic
This commit is contained in:
commit
50e0602923
54
Gopkg.lock
generated
54
Gopkg.lock
generated
@ -3,19 +3,19 @@
|
|||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:fcdf62d2d7e43c2565d6f8707ab4eae54dac702ed4bafb194b85139f0508929f"
|
digest = "1:79b02529d2120af44eaad5f7fee9ff7e003739d469e2c2767008b797d290e9fd"
|
||||||
name = "github.com/aristanetworks/goarista"
|
name = "github.com/aristanetworks/goarista"
|
||||||
packages = ["monotime"]
|
packages = ["monotime"]
|
||||||
pruneopts = "T"
|
pruneopts = "T"
|
||||||
revision = "b2d71c282dc706f4b4f6c15b65810e1202ecd53f"
|
revision = "18b896026201d8e1758468d9c5d5a558c69c5e9b"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:d4d66abd43dbb9b5f5e6a176c5ed279c289f8db734904c047d95113a04aa2e60"
|
digest = "1:8ad24ea05e770b745b5c286b4f94cf73d5be87005680e36b2d0dd1de0a2f9fbf"
|
||||||
name = "github.com/btcsuite/btcd"
|
name = "github.com/btcsuite/btcd"
|
||||||
packages = ["btcec"]
|
packages = ["btcec"]
|
||||||
pruneopts = "T"
|
pruneopts = "T"
|
||||||
revision = "cf05f92c3f815bbd5091ed6c73eff51f7b1945e8"
|
revision = "d81d8877b8f327112e94e814937143a71d1692a7"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:d0d998526cfb68788229a31c16a557fdf1fbbb510654be6b3732c2758e06b533"
|
digest = "1:d0d998526cfb68788229a31c16a557fdf1fbbb510654be6b3732c2758e06b533"
|
||||||
@ -25,7 +25,7 @@
|
|||||||
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
|
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:36773b598dec105de46a87978ae14e64c8d2c45aa556b8e0ddfc62d6abc7c47e"
|
digest = "1:bc28e755cf6a9fd8e65497514d20c4907973e7a6a6409d30ead3fd37bfeb19a9"
|
||||||
name = "github.com/cosmos/cosmos-sdk"
|
name = "github.com/cosmos/cosmos-sdk"
|
||||||
packages = [
|
packages = [
|
||||||
"baseapp",
|
"baseapp",
|
||||||
@ -36,16 +36,16 @@
|
|||||||
"x/auth",
|
"x/auth",
|
||||||
]
|
]
|
||||||
pruneopts = "T"
|
pruneopts = "T"
|
||||||
revision = "23e3d5ac12145c02fcb4b4767d7dfccad782aee5"
|
revision = "1c38c70468ec721e3a555ba2f3bf5f9da31f0cc9"
|
||||||
version = "v0.23.1"
|
version = "v0.24.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:52f195ad0e20a92d8604c1ba3cd246c61644c03eaa454b5acd41be89841e0d10"
|
digest = "1:9f42202ac457c462ad8bb9642806d275af9ab4850cf0b1960b9c6f083d4a309a"
|
||||||
name = "github.com/davecgh/go-spew"
|
name = "github.com/davecgh/go-spew"
|
||||||
packages = ["spew"]
|
packages = ["spew"]
|
||||||
pruneopts = "T"
|
pruneopts = "T"
|
||||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
|
||||||
version = "v1.1.0"
|
version = "v1.1.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
@ -224,12 +224,12 @@
|
|||||||
version = "v0.0.3"
|
version = "v0.0.3"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:6de2f73eb31e80d74f84ce1c861e4c0c8f00ca5fb41a25901f987e63a0647c28"
|
digest = "1:9ba911fe3884995431690e7eb180cf848da0d637ba5f61711783b795d031793f"
|
||||||
name = "github.com/spf13/pflag"
|
name = "github.com/spf13/pflag"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "T"
|
pruneopts = "T"
|
||||||
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
|
revision = "9a97c102cda95a86cec2345a6f09f55a939babf5"
|
||||||
version = "v1.0.1"
|
version = "v1.0.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:e95496462101745805bd4e041a5b841e108c7cf761264d53648246308de2761e"
|
digest = "1:e95496462101745805bd4e041a5b841e108c7cf761264d53648246308de2761e"
|
||||||
@ -245,7 +245,7 @@
|
|||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:7d44c4d11eb65cfdc78c76040f37ef305b16474c019c98a8a7cf188fece2d574"
|
digest = "1:ee395d0d8c1719b5a1407f34af93953b4763bacb19a8961aba5b6d312824da41"
|
||||||
name = "github.com/syndtr/goleveldb"
|
name = "github.com/syndtr/goleveldb"
|
||||||
packages = [
|
packages = [
|
||||||
"leveldb",
|
"leveldb",
|
||||||
@ -262,7 +262,7 @@
|
|||||||
"leveldb/util",
|
"leveldb/util",
|
||||||
]
|
]
|
||||||
pruneopts = "T"
|
pruneopts = "T"
|
||||||
revision = "c4c61651e9e37fa117f53c5a906d3b63090d8445"
|
revision = "ae2bd5eed72d46b28834ec3f60db3a3ebedd8dbd"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
@ -276,14 +276,6 @@
|
|||||||
pruneopts = "T"
|
pruneopts = "T"
|
||||||
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057"
|
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:3a6bdd02e7f2585c860e368467c5989310740af6206a1ada85cfa19c712e5afd"
|
|
||||||
name = "github.com/tendermint/ethermint"
|
|
||||||
packages = ["version"]
|
|
||||||
pruneopts = "T"
|
|
||||||
revision = "c1e6ebf80a6cc9119bc178faee18ef13490d707a"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:0e2addab3f64ece97ca434b2bf2d4e8cb54a4509904a03be8c81da3fc2ddb245"
|
digest = "1:0e2addab3f64ece97ca434b2bf2d4e8cb54a4509904a03be8c81da3fc2ddb245"
|
||||||
name = "github.com/tendermint/go-amino"
|
name = "github.com/tendermint/go-amino"
|
||||||
@ -301,7 +293,7 @@
|
|||||||
version = "v0.9.2"
|
version = "v0.9.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:9f6704ae2aedbadf616e5850375c504909d46b6ea57d4679de2b7cbc715f08e1"
|
digest = "1:5a60cb048b401c0263c227baf8778ecaf038be531707057607949540486874ef"
|
||||||
name = "github.com/tendermint/tendermint"
|
name = "github.com/tendermint/tendermint"
|
||||||
packages = [
|
packages = [
|
||||||
"abci/server",
|
"abci/server",
|
||||||
@ -321,16 +313,16 @@
|
|||||||
"types",
|
"types",
|
||||||
]
|
]
|
||||||
pruneopts = "T"
|
pruneopts = "T"
|
||||||
revision = "d542d2c3945116697f60451e6a407082c41c3cc9"
|
revision = "81df19e68ab1519399fccf0cab81cb75bf9d782e"
|
||||||
version = "v0.22.8"
|
version = "v0.23.1-rc0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:2cbe8758697d867fcebf73bcc69dff8e8abaa7fd65e5704e0744e522ccff4e6a"
|
digest = "1:da29cbeb9d244918393b37243c008ab7128688fb017c966aaf876587c010bcdd"
|
||||||
name = "golang.org/x/crypto"
|
name = "golang.org/x/crypto"
|
||||||
packages = ["ripemd160"]
|
packages = ["ripemd160"]
|
||||||
pruneopts = "T"
|
pruneopts = "T"
|
||||||
revision = "f027049dab0ad238e394a753dba2d14753473a04"
|
revision = "614d502a4dac94afa3a6ce146bd1736da82514c6"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:5fdc7adede42f80d6201258355d478d856778e21d735f14972abd8ff793fdbf7"
|
digest = "1:5fdc7adede42f80d6201258355d478d856778e21d735f14972abd8ff793fdbf7"
|
||||||
@ -372,11 +364,12 @@
|
|||||||
version = "v0.3.0"
|
version = "v0.3.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:8cfa91d1b7f6b66fa9b1a738a4bc1325837b861e63fb9a2919931d68871bb770"
|
branch = "master"
|
||||||
|
digest = "1:960f1fa3f12667fe595c15c12523718ed8b1b5428c83d70da54bb014da9a4c1a"
|
||||||
name = "google.golang.org/genproto"
|
name = "google.golang.org/genproto"
|
||||||
packages = ["googleapis/rpc/status"]
|
packages = ["googleapis/rpc/status"]
|
||||||
pruneopts = "T"
|
pruneopts = "T"
|
||||||
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200"
|
revision = "c66870c02cf823ceb633bcd05be3c7cda29976f4"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:adafc60b1d4688759f3fc8f9089e71dd17abd123f4729de6b913bf08c9143770"
|
digest = "1:adafc60b1d4688759f3fc8f9089e71dd17abd123f4729de6b913bf08c9143770"
|
||||||
@ -466,7 +459,6 @@
|
|||||||
"github.com/pkg/errors",
|
"github.com/pkg/errors",
|
||||||
"github.com/stretchr/testify/require",
|
"github.com/stretchr/testify/require",
|
||||||
"github.com/stretchr/testify/suite",
|
"github.com/stretchr/testify/suite",
|
||||||
"github.com/tendermint/ethermint/version",
|
|
||||||
"github.com/tendermint/tendermint/libs/common",
|
"github.com/tendermint/tendermint/libs/common",
|
||||||
"github.com/tendermint/tendermint/libs/db",
|
"github.com/tendermint/tendermint/libs/db",
|
||||||
"github.com/tendermint/tendermint/libs/log",
|
"github.com/tendermint/tendermint/libs/log",
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/cosmos/cosmos-sdk"
|
name = "github.com/cosmos/cosmos-sdk"
|
||||||
version = "=0.23.1"
|
version = "=0.24.2"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/hashicorp/golang-lru"
|
name = "github.com/hashicorp/golang-lru"
|
||||||
@ -15,12 +15,12 @@
|
|||||||
version = "~0.0.1"
|
version = "~0.0.1"
|
||||||
|
|
||||||
[[override]]
|
[[override]]
|
||||||
name = "google.golang.org/genproto"
|
name = "github.com/tendermint/iavl"
|
||||||
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200"
|
version = "=v0.9.2"
|
||||||
|
|
||||||
[[override]]
|
[[override]]
|
||||||
name = "github.com/tendermint/tendermint"
|
name = "github.com/tendermint/tendermint"
|
||||||
version = "=v0.22.8"
|
version = "=v0.23.1-rc0"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/stretchr/testify"
|
name = "github.com/stretchr/testify"
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/cosmos/ethermint/handlers"
|
"github.com/cosmos/ethermint/handlers"
|
||||||
"github.com/cosmos/ethermint/types"
|
"github.com/cosmos/ethermint/types"
|
||||||
|
|
||||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
|
||||||
ethparams "github.com/ethereum/go-ethereum/params"
|
ethparams "github.com/ethereum/go-ethereum/params"
|
||||||
|
|
||||||
tmcmn "github.com/tendermint/tendermint/libs/common"
|
tmcmn "github.com/tendermint/tendermint/libs/common"
|
||||||
@ -43,20 +42,17 @@ type (
|
|||||||
|
|
||||||
// NewEthermintApp returns a reference to a new initialized Ethermint
|
// NewEthermintApp returns a reference to a new initialized Ethermint
|
||||||
// application.
|
// application.
|
||||||
func NewEthermintApp(
|
func NewEthermintApp(logger tmlog.Logger, db dbm.DB, ethChainCfg *ethparams.ChainConfig, opts ...Options,
|
||||||
logger tmlog.Logger, db dbm.DB, ethChainCfg *ethparams.ChainConfig,
|
|
||||||
sdkAddr ethcmn.Address, opts ...Options,
|
|
||||||
) *EthermintApp {
|
) *EthermintApp {
|
||||||
|
|
||||||
codec := CreateCodec()
|
codec := CreateCodec()
|
||||||
app := &EthermintApp{
|
app := &EthermintApp{
|
||||||
BaseApp: bam.NewBaseApp(appName, codec, logger, db),
|
BaseApp: bam.NewBaseApp(appName, logger, db, types.TxDecoder(codec)),
|
||||||
codec: codec,
|
codec: codec,
|
||||||
accountKey: sdk.NewKVStoreKey("accounts"),
|
accountKey: sdk.NewKVStoreKey("accounts"),
|
||||||
}
|
}
|
||||||
app.accountMapper = auth.NewAccountMapper(codec, app.accountKey, auth.ProtoBaseAccount)
|
app.accountMapper = auth.NewAccountMapper(codec, app.accountKey, auth.ProtoBaseAccount)
|
||||||
|
|
||||||
app.SetTxDecoder(types.TxDecoder(codec, sdkAddr))
|
|
||||||
app.SetAnteHandler(handlers.AnteHandler(app.accountMapper))
|
app.SetAnteHandler(handlers.AnteHandler(app.accountMapper))
|
||||||
app.MountStoresIAVL(app.accountKey)
|
app.MountStoresIAVL(app.accountKey)
|
||||||
|
|
||||||
|
@ -6,34 +6,20 @@ and subject to change.
|
|||||||
## Routing
|
## Routing
|
||||||
|
|
||||||
Ethermint needs to parse and handle transactions routed for both the EVM and for
|
Ethermint needs to parse and handle transactions routed for both the EVM and for
|
||||||
the Cosmos hub. We attempt to achieve this by mimicking [Geth's](https://github.com/ethereum/go-ethereum) `Transaction` structure and utilizing
|
the Cosmos hub. We attempt to achieve this by mimicking [Geth's](https://github.com/ethereum/go-ethereum) `Transaction` structure to handle
|
||||||
the `Payload` as the potential encoding of a Cosmos-routed transaction. What
|
Ethereum transactions and utilizing the SDK's `auth.StdTx` for Cosmos
|
||||||
designates this encoding, and ultimately routing, is the `Transaction.Recipient`
|
transactions. Both of these structures are registered with an [Amino](https://github.com/tendermint/go-amino) codec, so the `TxDecoder` that in invoked
|
||||||
address -- if this address matches some global unique predefined and configured
|
during the `BaseApp#runTx`, will be able to decode raw transaction bytes into the
|
||||||
address, we regard it as a transaction meant for Cosmos, otherwise, the transaction
|
appropriate transaction type which will then be passed onto handlers downstream.
|
||||||
is a pure Ethereum transaction and will be executed in the EVM.
|
|
||||||
|
|
||||||
For Cosmos routed transactions, the `Transaction.Payload` will contain an
|
__Note__: Our goal is to utilize Geth as a library, at least as much as possible,
|
||||||
embedded encoded type: `EmbeddedTx`. This structure is analogous to the Cosmos
|
so it should be expected that these types and the operations you may perform on
|
||||||
SDK `sdk.StdTx`. If a client wishes to send an `EmbeddedTx`, it must first encode
|
them will keep in line with Ethereum (e.g. signature algorithms and gas/fees).
|
||||||
the embedded transaction, and then encode the embedding `Transaction`.
|
|
||||||
|
|
||||||
__Note__: The `Transaction` and `EmbeddedTx` types utilize the [Amino](https://github.com/tendermint/go-amino) object serialization protocol and as such,
|
|
||||||
the `Transaction` is not an exact replica of what will be found in Ethereum. Our
|
|
||||||
goal is to utilize Geth as a library, at least as much as possible, so it should
|
|
||||||
be expected that these types and the operations you may perform on them will keep
|
|
||||||
in line with Ethereum.
|
|
||||||
|
|
||||||
Being that Ethermint implements the ABCI application interface, as transactions
|
|
||||||
are sent they are passed through a series of handlers. Once such handler, `runTx`,
|
|
||||||
is responsible for invoking the `TxDecoder` which performs the business logic of
|
|
||||||
properly deserializing raw transaction bytes into either an Ethereum transaction
|
|
||||||
or a Cosmos transaction.
|
|
||||||
|
|
||||||
## Transactions & Messages
|
## Transactions & Messages
|
||||||
|
|
||||||
The SDK distinguishes between transactions (`sdk.Tx`) and messages (`sdk.Msg`).
|
The SDK distinguishes between transactions (`sdk.Tx`) and messages (`sdk.Msg`).
|
||||||
A `sdk.Tx` is a list of `sdk.Msg`s wrapped with authentication and fee data. Users
|
A `sdk.Tx` is a list of `sdk.Msg` wrapped with authentication and fee data. Users
|
||||||
can create messages containing arbitrary information by implementing the `sdk.Msg`
|
can create messages containing arbitrary information by implementing the `sdk.Msg`
|
||||||
interface.
|
interface.
|
||||||
|
|
||||||
@ -42,25 +28,21 @@ It addition, it implements the Cosmos SDK `sdk.Msg` interface for the sole purpo
|
|||||||
of being to perform basic validation checks in the `BaseApp`. It, however, has
|
of being to perform basic validation checks in the `BaseApp`. It, however, has
|
||||||
no distinction between transactions and messages.
|
no distinction between transactions and messages.
|
||||||
|
|
||||||
The `EmbeddedTx`, being analogous to the Cosmos SDK `sdk.StdTx`, implements the
|
|
||||||
Cosmos SDK `sdk.Tx` interface.
|
|
||||||
|
|
||||||
## Signatures
|
## Signatures
|
||||||
|
|
||||||
Ethermint supports [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md)
|
Ethermint supports [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md)
|
||||||
signatures. A `Transaction` is expected to have a single signature for Ethereum
|
signatures. A `Transaction` is expected to have a single signature for Ethereum
|
||||||
routed transactions. However, just as in Cosmos, Ethermint will support multiple
|
routed transactions. However, just as in Cosmos, Ethermint will support multiple
|
||||||
signers for `EmbeddedTx` Cosmos routed transactions. Signatures over the
|
signers for `auth.StdTx` Cosmos routed transactions. Signatures over the
|
||||||
`Transaction` type are identical to Ethereum. However, the `EmbeddedTx` contains
|
`Transaction` type are identical to Ethereum. However, the `auth.StdTx` contains
|
||||||
a canonical signature structure that contains the signature itself and other
|
a canonical signature structure that contains the signature itself and other
|
||||||
information such as an account's sequence number. The sequence number is expected
|
information such as an account's sequence number. This, in addition to the chainID,
|
||||||
to increment every time a message is signed by a given account. This, in addition
|
helps prevent "replay attacks", where the same message could be executed over and
|
||||||
to the chain ID, prevents "replay attacks", where the same message could be
|
over again.
|
||||||
executed over and over again.
|
|
||||||
|
|
||||||
An `EmbeddedTx` list of signatures must much the unique list of addresses returned
|
An `auth.StdTx` list of signatures must much the unique list of addresses returned
|
||||||
by each message's `GetSigners` call. In addition, the address of first signer of
|
by each message's `GetSigners` call. In addition, the address of first signer of
|
||||||
the `EmbeddedTx` is responsible for paying the fees.
|
the `auth.StdTx` is responsible for paying the fees.
|
||||||
|
|
||||||
## Gas & Fees
|
## Gas & Fees
|
||||||
|
|
||||||
|
@ -26,11 +26,10 @@ type internalAnteHandler func(
|
|||||||
sdkCtx sdk.Context, tx sdk.Tx, am auth.AccountMapper,
|
sdkCtx sdk.Context, tx sdk.Tx, am auth.AccountMapper,
|
||||||
) (newCtx sdk.Context, res sdk.Result, abort bool)
|
) (newCtx sdk.Context, res sdk.Result, abort bool)
|
||||||
|
|
||||||
// AnteHandler handles Ethereum transactions and passes SDK transactions to the
|
// AnteHandler is responsible for attempting to route an Ethereum or SDK
|
||||||
// embeddedAnteHandler if it's an Ethermint transaction. The ante handler gets
|
// transaction to an internal ante handler for performing transaction-level
|
||||||
// invoked after the BaseApp performs the runTx. At this point, the transaction
|
// processing (e.g. fee payment, signature verification) before being passed
|
||||||
// should be properly decoded via the TxDecoder and should be of a proper type,
|
// onto it's respective handler.
|
||||||
// Transaction or EmbeddedTx.
|
|
||||||
func AnteHandler(am auth.AccountMapper) sdk.AnteHandler {
|
func AnteHandler(am auth.AccountMapper) sdk.AnteHandler {
|
||||||
return func(sdkCtx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) {
|
return func(sdkCtx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) {
|
||||||
var (
|
var (
|
||||||
@ -42,7 +41,7 @@ func AnteHandler(am auth.AccountMapper) sdk.AnteHandler {
|
|||||||
case types.Transaction:
|
case types.Transaction:
|
||||||
gasLimit = int64(tx.Data.GasLimit)
|
gasLimit = int64(tx.Data.GasLimit)
|
||||||
handler = handleEthTx
|
handler = handleEthTx
|
||||||
case types.EmbeddedTx:
|
case auth.StdTx:
|
||||||
gasLimit = tx.Fee.Gas
|
gasLimit = tx.Fee.Gas
|
||||||
handler = handleEmbeddedTx
|
handler = handleEmbeddedTx
|
||||||
default:
|
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
|
// handleEmbeddedTx implements an ante handler for an SDK transaction. It
|
||||||
// validates the signature and if valid returns an OK result.
|
// 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) {
|
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 {
|
if !ok {
|
||||||
return sdkCtx, sdk.ErrInternal(fmt.Sprintf("invalid transaction: %T", tx)).Result(), true
|
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
|
return sdkCtx, err.Result(), true
|
||||||
}
|
}
|
||||||
|
|
||||||
signerAddrs := etx.GetRequiredSigners()
|
signerAddrs := stdTx.GetSigners()
|
||||||
signerAccs := make([]auth.Account, len(signerAddrs))
|
signerAccs := make([]auth.Account, len(signerAddrs))
|
||||||
|
|
||||||
// validate signatures
|
// validate signatures
|
||||||
for i, sig := range etx.Signatures {
|
for i, sig := range stdTx.Signatures {
|
||||||
signer := ethcmn.BytesToAddress(signerAddrs[i].Bytes())
|
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 {
|
if err.Code() != sdk.CodeOK {
|
||||||
return sdkCtx, err.Result(), false
|
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)
|
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.
|
// depend on the context.
|
||||||
func validateEmbeddedTxBasic(etx types.EmbeddedTx) (err sdk.Error) {
|
func validateStdTxBasic(stdTx auth.StdTx) (err sdk.Error) {
|
||||||
sigs := etx.Signatures
|
sigs := stdTx.Signatures
|
||||||
if len(sigs) == 0 {
|
if len(sigs) == 0 {
|
||||||
return sdk.ErrUnauthorized("transaction missing signatures")
|
return sdk.ErrUnauthorized("transaction missing signatures")
|
||||||
}
|
}
|
||||||
|
|
||||||
signerAddrs := etx.GetRequiredSigners()
|
signerAddrs := stdTx.GetSigners()
|
||||||
if len(sigs) != len(signerAddrs) {
|
if len(sigs) != len(signerAddrs) {
|
||||||
return sdk.ErrUnauthorized("invalid number of transaction signers")
|
return sdk.ErrUnauthorized("invalid number of transaction signers")
|
||||||
}
|
}
|
||||||
@ -156,8 +155,8 @@ func validateEmbeddedTxBasic(etx types.EmbeddedTx) (err sdk.Error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func validateSignature(
|
func validateSignature(
|
||||||
sdkCtx sdk.Context, etx types.EmbeddedTx, signer ethcmn.Address,
|
sdkCtx sdk.Context, stdTx auth.StdTx, signer ethcmn.Address,
|
||||||
sig []byte, am auth.AccountMapper,
|
sig auth.StdSignature, am auth.AccountMapper,
|
||||||
) (acc auth.Account, sdkErr sdk.Error) {
|
) (acc auth.Account, sdkErr sdk.Error) {
|
||||||
|
|
||||||
chainID := sdkCtx.ChainID()
|
chainID := sdkCtx.ChainID()
|
||||||
@ -167,28 +166,29 @@ func validateSignature(
|
|||||||
return nil, sdk.ErrUnknownAddress(fmt.Sprintf("no account with address %s found", signer))
|
return nil, sdk.ErrUnknownAddress(fmt.Sprintf("no account with address %s found", signer))
|
||||||
}
|
}
|
||||||
|
|
||||||
signEtx := types.EmbeddedTxSign{
|
accNum := acc.GetAccountNumber()
|
||||||
ChainID: chainID,
|
if accNum != sig.AccountNumber {
|
||||||
AccountNumber: acc.GetAccountNumber(),
|
return nil, sdk.ErrInvalidSequence(
|
||||||
Sequence: acc.GetSequence(),
|
fmt.Sprintf("invalid account number; got %d, expected %d", sig.AccountNumber, accNum))
|
||||||
Messages: etx.Messages,
|
|
||||||
Fee: etx.Fee,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
if err != nil {
|
||||||
return nil, sdk.ErrInternal(err.Error())
|
return nil, sdk.ErrInternal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
signBytes, err := signEtx.Bytes()
|
signBytes := types.GetStdTxSignBytes(chainID, accNum, accSeq, stdTx.Fee, stdTx.GetMsgs(), stdTx.Memo)
|
||||||
if err != nil {
|
|
||||||
return nil, sdk.ErrInternal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// consume gas for signature verification
|
// 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())
|
return nil, sdk.ErrUnauthorized(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
158
types/tx.go
158
types/tx.go
@ -1,17 +1,13 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||||
@ -26,13 +22,9 @@ const (
|
|||||||
TypeTxEthereum = "Ethereum"
|
TypeTxEthereum = "Ethereum"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Ethereum transaction
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// Transaction implements the Ethereum transaction structure as an exact
|
// 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
|
// fields, it must be replicated here and cannot be embedded or used
|
||||||
// directly.
|
// directly.
|
||||||
//
|
//
|
||||||
@ -47,9 +39,7 @@ type (
|
|||||||
from atomic.Value
|
from atomic.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
// TxData implements the Ethereum transaction data structure as an exact
|
// TxData defines internal Ethereum transaction information
|
||||||
// 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 struct {
|
TxData struct {
|
||||||
AccountNonce uint64 `json:"nonce"`
|
AccountNonce uint64 `json:"nonce"`
|
||||||
Price sdk.Int `json:"gasPrice"`
|
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
|
// with the signature set. The signature if first recovered and then a new
|
||||||
// Transaction is created with that signature. If setting the signature fails,
|
// Transaction is created with that signature. If setting the signature fails,
|
||||||
// a panic will be triggered.
|
// a panic will be triggered.
|
||||||
|
//
|
||||||
|
// TODO: To be removed in #470
|
||||||
func (tx Transaction) ConvertTx(chainID *big.Int) ethtypes.Transaction {
|
func (tx Transaction) ConvertTx(chainID *big.Int) ethtypes.Transaction {
|
||||||
gethTx := ethtypes.NewTransaction(
|
gethTx := ethtypes.NewTransaction(
|
||||||
tx.Data.AccountNonce, *tx.Data.Recipient, tx.Data.Amount.BigInt(),
|
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
|
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,
|
// TxDecoder returns an sdk.TxDecoder that given raw transaction bytes,
|
||||||
// attempts to decode them into a Transaction or an EmbeddedTx or returning an
|
// attempts to decode them into a valid sdk.Tx.
|
||||||
// error if decoding fails.
|
func TxDecoder(codec *wire.Codec) sdk.TxDecoder {
|
||||||
func TxDecoder(codec *wire.Codec, sdkAddress ethcmn.Address) sdk.TxDecoder {
|
|
||||||
return func(txBytes []byte) (sdk.Tx, sdk.Error) {
|
return func(txBytes []byte) (sdk.Tx, sdk.Error) {
|
||||||
var tx = Transaction{}
|
|
||||||
|
|
||||||
if len(txBytes) == 0 {
|
if len(txBytes) == 0 {
|
||||||
return nil, sdk.ErrTxDecode("txBytes are empty")
|
return nil, sdk.ErrTxDecode("txBytes are empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var tx sdk.Tx
|
||||||
|
|
||||||
// The given codec should have all the appropriate message types
|
// The given codec should have all the appropriate message types
|
||||||
// registered.
|
// registered.
|
||||||
err := codec.UnmarshalBinary(txBytes, &tx)
|
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())
|
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
|
return tx, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
186
types/tx_test.go
186
types/tx_test.go
@ -3,14 +3,12 @@ package types
|
|||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
|
||||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@ -23,64 +21,42 @@ var (
|
|||||||
|
|
||||||
testAddr1 = PrivKeyToEthAddress(testPrivKey1)
|
testAddr1 = PrivKeyToEthAddress(testPrivKey1)
|
||||||
testAddr2 = PrivKeyToEthAddress(testPrivKey2)
|
testAddr2 = PrivKeyToEthAddress(testPrivKey2)
|
||||||
|
|
||||||
testSDKAddress = GenerateEthAddress()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newTestCodec() *wire.Codec {
|
func newTestCodec() *wire.Codec {
|
||||||
codec := wire.NewCodec()
|
codec := wire.NewCodec()
|
||||||
|
|
||||||
RegisterWire(codec)
|
RegisterWire(codec)
|
||||||
|
codec.RegisterConcrete(auth.StdTx{}, "test/StdTx", nil)
|
||||||
codec.RegisterConcrete(&sdk.TestMsg{}, "test/TestMsg", nil)
|
codec.RegisterConcrete(&sdk.TestMsg{}, "test/TestMsg", nil)
|
||||||
|
wire.RegisterCrypto(codec)
|
||||||
|
|
||||||
return codec
|
return codec
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStdFee() auth.StdFee {
|
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,
|
chainID sdk.Int, msgs []sdk.Msg, pKeys []*ecdsa.PrivateKey,
|
||||||
accNums []int64, seqs []int64, fee auth.StdFee,
|
accNums []int64, seqs []int64, fee auth.StdFee,
|
||||||
) sdk.Tx {
|
) sdk.Tx {
|
||||||
|
|
||||||
sigs := make([][]byte, len(pKeys))
|
sigs := make([]auth.StdSignature, len(pKeys))
|
||||||
|
|
||||||
for i, priv := range pKeys {
|
for i, priv := range pKeys {
|
||||||
signEtx := EmbeddedTxSign{chainID.String(), accNums[i], seqs[i], msgs, newStdFee()}
|
signBytes := GetStdTxSignBytes(chainID.String(), accNums[i], seqs[i], newStdFee(), msgs, "")
|
||||||
|
|
||||||
signBytes, err := signEtx.Bytes()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sig, err := ethcrypto.Sign(signBytes, priv)
|
sig, err := ethcrypto.Sign(signBytes, priv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sigs[i] = sig
|
sigs[i] = auth.StdSignature{Signature: sig, AccountNumber: accNums[i], Sequence: seqs[i]}
|
||||||
}
|
}
|
||||||
|
|
||||||
return EmbeddedTx{msgs, fee, sigs}
|
return auth.NewStdTx(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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestEthTxs(chainID sdk.Int, pKeys []*ecdsa.PrivateKey, addrs []ethcmn.Address) []Transaction {
|
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
|
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) {
|
func TestValidation(t *testing.T) {
|
||||||
ethTxs := newTestEthTxs(
|
ethTxs := newTestEthTxs(
|
||||||
testChainID,
|
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) {
|
func TestTransactionGetMsgs(t *testing.T) {
|
||||||
ethTxs := newTestEthTxs(
|
ethTxs := newTestEthTxs(
|
||||||
testChainID,
|
testChainID,
|
||||||
@ -246,7 +124,7 @@ func TestTransactionGetMsgs(t *testing.T) {
|
|||||||
require.Equal(t, ethTxs[0], msgs[0])
|
require.Equal(t, ethTxs[0], msgs[0])
|
||||||
|
|
||||||
expectedMsgs := []sdk.Msg{sdk.NewTestMsg(sdk.AccAddress(testAddr1.Bytes()))}
|
expectedMsgs := []sdk.Msg{sdk.NewTestMsg(sdk.AccAddress(testAddr1.Bytes()))}
|
||||||
etx := newTestEmbeddedTx(
|
etx := newTestStdTx(
|
||||||
testChainID, expectedMsgs, []*ecdsa.PrivateKey{testPrivKey1},
|
testChainID, expectedMsgs, []*ecdsa.PrivateKey{testPrivKey1},
|
||||||
[]int64{0}, []int64{0}, newStdFee(),
|
[]int64{0}, []int64{0}, newStdFee(),
|
||||||
)
|
)
|
||||||
@ -256,21 +134,10 @@ func TestTransactionGetMsgs(t *testing.T) {
|
|||||||
require.Equal(t, expectedMsgs, msgs)
|
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) {
|
func TestTxDecoder(t *testing.T) {
|
||||||
testCodec := newTestCodec()
|
testCodec := newTestCodec()
|
||||||
txDecoder := TxDecoder(testCodec, testSDKAddress)
|
txDecoder := TxDecoder(testCodec)
|
||||||
msgs := []sdk.Msg{sdk.NewTestMsg(sdk.AccAddress(testAddr1.Bytes()))}
|
msgs := []sdk.Msg{sdk.NewTestMsg()}
|
||||||
|
|
||||||
// create a non-SDK Ethereum transaction
|
// create a non-SDK Ethereum transaction
|
||||||
emintTx := NewTransaction(
|
emintTx := NewTransaction(
|
||||||
@ -284,43 +151,20 @@ func TestTxDecoder(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, emintTx, tx)
|
require.Equal(t, emintTx, tx)
|
||||||
|
|
||||||
// create embedded transaction and encode
|
// create a SDK (auth.StdTx) transaction and encode
|
||||||
etx := newTestEmbeddedTx(
|
stdTx := newTestStdTx(
|
||||||
testChainID, msgs, []*ecdsa.PrivateKey{testPrivKey1},
|
testChainID, msgs, []*ecdsa.PrivateKey{testPrivKey1},
|
||||||
[]int64{0}, []int64{0}, newStdFee(),
|
[]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
|
// require the transaction to properly decode into a Transaction
|
||||||
txBytes = testCodec.MustMarshalBinary(emintTx)
|
txBytes = testCodec.MustMarshalBinary(stdTx)
|
||||||
tx, err = txDecoder(txBytes)
|
tx, err = txDecoder(txBytes)
|
||||||
require.NoError(t, err)
|
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
|
// require the decoding to fail when no transaction bytes are given
|
||||||
tx, err = txDecoder([]byte{})
|
tx, err = txDecoder([]byte{})
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Nil(t, tx)
|
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)
|
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,11 @@ package types
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
||||||
@ -39,3 +42,11 @@ func ValidateSigner(signBytes, sig []byte, signer ethcmn.Address) error {
|
|||||||
|
|
||||||
return nil
|
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[:]
|
||||||
|
}
|
||||||
|
@ -12,12 +12,8 @@ import (
|
|||||||
func TestValidateSigner(t *testing.T) {
|
func TestValidateSigner(t *testing.T) {
|
||||||
msgs := []sdk.Msg{sdk.NewTestMsg(sdk.AccAddress(testAddr1.Bytes()))}
|
msgs := []sdk.Msg{sdk.NewTestMsg(sdk.AccAddress(testAddr1.Bytes()))}
|
||||||
|
|
||||||
// create message signing structure
|
// create message signing structure and bytes
|
||||||
signEtx := EmbeddedTxSign{testChainID.String(), 0, 0, msgs, newStdFee()}
|
signBytes := GetStdTxSignBytes(testChainID.String(), 0, 0, newStdFee(), msgs, "")
|
||||||
|
|
||||||
// create signing bytes and sign
|
|
||||||
signBytes, err := signEtx.Bytes()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// require signing not to fail
|
// require signing not to fail
|
||||||
sig, err := ethcrypto.Sign(signBytes, testPrivKey1)
|
sig, err := ethcrypto.Sign(signBytes, testPrivKey1)
|
||||||
|
@ -18,5 +18,4 @@ func RegisterWire(codec *wire.Codec) {
|
|||||||
codec.RegisterConcrete(&EthSignature{}, "types/EthSignature", nil)
|
codec.RegisterConcrete(&EthSignature{}, "types/EthSignature", nil)
|
||||||
codec.RegisterConcrete(TxData{}, "types/TxData", nil)
|
codec.RegisterConcrete(TxData{}, "types/TxData", nil)
|
||||||
codec.RegisterConcrete(Transaction{}, "types/Transaction", nil)
|
codec.RegisterConcrete(Transaction{}, "types/Transaction", nil)
|
||||||
codec.RegisterConcrete(EmbeddedTx{}, "types/EmbeddedTx", nil)
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user