forked from cerc-io/laconicd-deprecated
upgrade changes cleanup (#236)
* changes from update version * app changes * cmd changes * build and send tx * fix tests * eth_rpc fixes * lint * add WithEventManager to handler ctx * changelog * go mod verify and tidy
This commit is contained in:
parent
1627ed81bf
commit
da9157e406
10
CHANGELOG.md
10
CHANGELOG.md
@ -40,3 +40,13 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
### Improvements
|
||||
|
||||
* (x/evm) [\#181](https://github.com/ChainSafe/ethermint/issues/181) Updated EVM module to the recommended module structure. [@fedekunze](https://github.com/fedekunze)
|
||||
* (app) [\#188](https://github.com/ChainSafe/ethermint/issues/186) Misc cleanup [@fedekunze](https://github.com/fedekunze):
|
||||
* (`x/evm`) Rename `EthereumTxMsg` --> `MsgEthereumTx` and `EmintMsg` --> `MsgEthermint` for consistency with SDK standards
|
||||
* Updated integration and unit tests to use `EthermintApp` as testing suite
|
||||
* Use expected keeper interface for `AccountKeeper`
|
||||
* Replaced `count` type in keeper with `int`
|
||||
* Add SDK events for transactions
|
||||
* [\#236](https://github.com/ChainSafe/ethermint/pull/236) Changes from upgrade [@fedekunze](https://github.com/fedekunze)
|
||||
* (app/ante) Moved `AnteHandler` implementation to `app/ante`
|
||||
* (keys) Marked `ExportEthKeyCommand` as **UNSAFE**
|
||||
* (x/evm) Moved `BeginBlock` and `EndBlock` to `x/evm/abci.go`
|
||||
|
@ -1,4 +1,4 @@
|
||||
package app
|
||||
package ante
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -46,15 +46,15 @@ func NewAnteHandler(ak auth.AccountKeeper, sk types.SupplyKeeper) sdk.AnteHandle
|
||||
ante.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators
|
||||
ante.NewValidateSigCountDecorator(ak),
|
||||
ante.NewDeductFeeDecorator(ak, sk),
|
||||
ante.NewSigGasConsumeDecorator(ak, consumeSigGas),
|
||||
ante.NewSigGasConsumeDecorator(ak, sigGasConsumer),
|
||||
ante.NewSigVerificationDecorator(ak),
|
||||
ante.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator
|
||||
)
|
||||
|
||||
return stdAnte(ctx, tx, sim)
|
||||
|
||||
case *evmtypes.EthereumTxMsg:
|
||||
return ethAnteHandler(ctx, ak, sk, castTx, sim)
|
||||
case evmtypes.MsgEthereumTx:
|
||||
return ethAnteHandler(ctx, ak, sk, &castTx, sim)
|
||||
|
||||
default:
|
||||
return ctx, sdk.ErrInternal(fmt.Sprintf("transaction type invalid: %T", tx))
|
||||
@ -62,7 +62,9 @@ func NewAnteHandler(ak auth.AccountKeeper, sk types.SupplyKeeper) sdk.AnteHandle
|
||||
}
|
||||
}
|
||||
|
||||
func consumeSigGas(
|
||||
// sigGasConsumer overrides the DefaultSigVerificationGasConsumer from the x/auth
|
||||
// module on the SDK. It doesn't allow ed25519 nor multisig thresholds.
|
||||
func sigGasConsumer(
|
||||
meter sdk.GasMeter, sig []byte, pubkey tmcrypto.PubKey, params types.Params,
|
||||
) error {
|
||||
switch pubkey.(type) {
|
||||
@ -89,7 +91,7 @@ func consumeSigGas(
|
||||
// prevent spam and DoS attacks.
|
||||
func ethAnteHandler(
|
||||
ctx sdk.Context, ak auth.AccountKeeper, sk types.SupplyKeeper,
|
||||
ethTxMsg *evmtypes.EthereumTxMsg, sim bool,
|
||||
ethTxMsg *evmtypes.MsgEthereumTx, sim bool,
|
||||
) (newCtx sdk.Context, err error) {
|
||||
|
||||
var senderAddr sdk.AccAddress
|
||||
@ -120,7 +122,9 @@ func ethAnteHandler(
|
||||
if r := recover(); r != nil {
|
||||
switch rType := r.(type) {
|
||||
case sdk.ErrorOutOfGas:
|
||||
log := fmt.Sprintf("out of gas in location: %v", rType.Descriptor)
|
||||
log := fmt.Sprintf("out of gas in location: %v; gasUsed: %d",
|
||||
rType.Descriptor, ctx.GasMeter().GasConsumed(),
|
||||
)
|
||||
err = sdk.ErrOutOfGas(log)
|
||||
default:
|
||||
panic(r)
|
||||
@ -139,9 +143,9 @@ func ethAnteHandler(
|
||||
// Cost calculates the fees paid to validators based on gas limit and price
|
||||
cost := new(big.Int).Mul(ethTxMsg.Data.Price, new(big.Int).SetUint64(ethTxMsg.Data.GasLimit))
|
||||
|
||||
feeAmt := sdk.Coins{
|
||||
feeAmt := sdk.NewCoins(
|
||||
sdk.NewCoin(emint.DenomDefault, sdk.NewIntFromBigInt(cost)),
|
||||
}
|
||||
)
|
||||
|
||||
err = auth.DeductFees(sk, ctx, senderAcc, feeAmt)
|
||||
if err != nil {
|
||||
@ -166,7 +170,7 @@ func ethAnteHandler(
|
||||
}
|
||||
|
||||
func validateEthTxCheckTx(
|
||||
ctx sdk.Context, ak auth.AccountKeeper, ethTxMsg *evmtypes.EthereumTxMsg,
|
||||
ctx sdk.Context, ak auth.AccountKeeper, ethTxMsg *evmtypes.MsgEthereumTx,
|
||||
) (sdk.AccAddress, error) {
|
||||
// Validate sufficient fees have been provided that meet a minimum threshold
|
||||
// defined by the proposer (for mempool purposes during CheckTx).
|
||||
@ -193,7 +197,7 @@ func validateEthTxCheckTx(
|
||||
}
|
||||
|
||||
// Validates signature and returns sender address
|
||||
func validateSignature(ctx sdk.Context, ethTxMsg *evmtypes.EthereumTxMsg) (sdk.AccAddress, error) {
|
||||
func validateSignature(ctx sdk.Context, ethTxMsg *evmtypes.MsgEthereumTx) (sdk.AccAddress, error) {
|
||||
// parse the chainID from a string to a base-10 integer
|
||||
chainID, ok := new(big.Int).SetString(ctx.ChainID(), 10)
|
||||
if !ok {
|
||||
@ -214,7 +218,7 @@ func validateSignature(ctx sdk.Context, ethTxMsg *evmtypes.EthereumTxMsg) (sdk.A
|
||||
// that the transaction uses before the transaction is executed. The gas is a
|
||||
// constant value of 21000 plus any cost inccured by additional bytes of data
|
||||
// supplied with the transaction.
|
||||
func validateIntrinsicGas(ethTxMsg *evmtypes.EthereumTxMsg) error {
|
||||
func validateIntrinsicGas(ethTxMsg *evmtypes.MsgEthereumTx) error {
|
||||
gas, err := ethcore.IntrinsicGas(ethTxMsg.Data.Payload, ethTxMsg.To() == nil, true)
|
||||
if err != nil {
|
||||
return sdk.ErrInternal(fmt.Sprintf("failed to compute intrinsic gas cost: %s", err))
|
||||
@ -222,7 +226,7 @@ func validateIntrinsicGas(ethTxMsg *evmtypes.EthereumTxMsg) error {
|
||||
|
||||
if ethTxMsg.Data.GasLimit < gas {
|
||||
return sdk.ErrInternal(
|
||||
fmt.Sprintf("intrinsic gas too low; %d < %d", ethTxMsg.Data.GasLimit, gas),
|
||||
fmt.Sprintf("intrinsic gas too low: %d < %d", ethTxMsg.Data.GasLimit, gas),
|
||||
)
|
||||
}
|
||||
|
||||
@ -232,7 +236,7 @@ func validateIntrinsicGas(ethTxMsg *evmtypes.EthereumTxMsg) error {
|
||||
// validateAccount validates the account nonce and that the account has enough
|
||||
// funds to cover the tx cost.
|
||||
func validateAccount(
|
||||
ctx sdk.Context, ak auth.AccountKeeper, ethTxMsg *evmtypes.EthereumTxMsg, signer sdk.AccAddress,
|
||||
ctx sdk.Context, ak auth.AccountKeeper, ethTxMsg *evmtypes.MsgEthereumTx, signer sdk.AccAddress,
|
||||
) error {
|
||||
|
||||
acc := ak.GetAccount(ctx, signer)
|
||||
@ -241,8 +245,9 @@ func validateAccount(
|
||||
if ctx.BlockHeight() == 0 && acc.GetAccountNumber() != 0 {
|
||||
return sdk.ErrInternal(
|
||||
fmt.Sprintf(
|
||||
"invalid account number for height zero; got %d, expected 0", acc.GetAccountNumber(),
|
||||
))
|
||||
"invalid account number for height zero (got %d)", acc.GetAccountNumber(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Validate nonce is correct
|
||||
@ -262,7 +267,7 @@ func validateAccount(
|
||||
}
|
||||
|
||||
func checkNonce(
|
||||
ctx sdk.Context, ak auth.AccountKeeper, ethTxMsg *evmtypes.EthereumTxMsg, signer sdk.AccAddress,
|
||||
ctx sdk.Context, ak auth.AccountKeeper, ethTxMsg *evmtypes.MsgEthereumTx, signer sdk.AccAddress,
|
||||
) error {
|
||||
acc := ak.GetAccount(ctx, signer)
|
||||
// Validate the transaction nonce is valid (equivalent to the sender account’s
|
||||
@ -270,7 +275,8 @@ func checkNonce(
|
||||
seq := acc.GetSequence()
|
||||
if ethTxMsg.Data.AccountNonce != seq {
|
||||
return sdk.ErrInvalidSequence(
|
||||
fmt.Sprintf("invalid nonce; got %d, expected %d", ethTxMsg.Data.AccountNonce, seq))
|
||||
fmt.Sprintf("invalid nonce; got %d, expected %d", ethTxMsg.Data.AccountNonce, seq),
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -281,7 +287,7 @@ func checkNonce(
|
||||
// proposer.
|
||||
//
|
||||
// NOTE: This should only be ran during a CheckTx mode.
|
||||
func ensureSufficientMempoolFees(ctx sdk.Context, ethTxMsg *evmtypes.EthereumTxMsg) error {
|
||||
func ensureSufficientMempoolFees(ctx sdk.Context, ethTxMsg *evmtypes.MsgEthereumTx) error {
|
||||
// fee = GP * GL
|
||||
fee := sdk.NewDecCoinFromCoin(sdk.NewInt64Coin(emint.DenomDefault, ethTxMsg.Fee().Int64()))
|
||||
|
||||
@ -297,7 +303,9 @@ func ensureSufficientMempoolFees(ctx sdk.Context, ethTxMsg *evmtypes.EthereumTxM
|
||||
if !ctx.MinGasPrices().IsZero() && !allGTE {
|
||||
// reject the transaction that does not meet the minimum fee
|
||||
return sdk.ErrInsufficientFee(
|
||||
fmt.Sprintf("insufficient fee, got: %q required: %q", fee, ctx.MinGasPrices()),
|
||||
fmt.Sprintf(
|
||||
"insufficient fee, got: %q required: %q", fee, ctx.MinGasPrices(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
301
app/ante/ante_test.go
Normal file
301
app/ante/ante_test.go
Normal file
@ -0,0 +1,301 @@
|
||||
package ante_test
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/cosmos/ethermint/app"
|
||||
"github.com/cosmos/ethermint/app/ante"
|
||||
"github.com/cosmos/ethermint/types"
|
||||
evmtypes "github.com/cosmos/ethermint/x/evm/types"
|
||||
)
|
||||
|
||||
func requireValidTx(
|
||||
t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, sim bool,
|
||||
) {
|
||||
_, err := anteHandler(ctx, tx, sim)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func requireInvalidTx(
|
||||
t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context,
|
||||
tx sdk.Tx, sim bool,
|
||||
) {
|
||||
_, err := anteHandler(ctx, tx, sim)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) TestValidEthTx() {
|
||||
suite.ctx = suite.ctx.WithBlockHeight(1)
|
||||
|
||||
addr1, priv1 := newTestAddrKey()
|
||||
addr2, _ := newTestAddrKey()
|
||||
|
||||
acc1 := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1)
|
||||
err := acc1.SetCoins(newTestCoins())
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc1)
|
||||
|
||||
acc2 := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr2)
|
||||
err = acc2.SetCoins(newTestCoins())
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc2)
|
||||
|
||||
// require a valid Ethereum tx to pass
|
||||
to := ethcmn.BytesToAddress(addr2.Bytes())
|
||||
amt := big.NewInt(32)
|
||||
gas := big.NewInt(20)
|
||||
ethMsg := evmtypes.NewMsgEthereumTx(0, &to, amt, 22000, gas, []byte("test"))
|
||||
|
||||
tx := newTestEthTx(suite.ctx, ethMsg, priv1)
|
||||
requireValidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) TestValidTx() {
|
||||
suite.ctx = suite.ctx.WithBlockHeight(1)
|
||||
|
||||
addr1, priv1 := newTestAddrKey()
|
||||
addr2, priv2 := newTestAddrKey()
|
||||
|
||||
acc1 := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1)
|
||||
err := acc1.SetCoins(newTestCoins())
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc1)
|
||||
|
||||
acc2 := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr2)
|
||||
err = acc2.SetCoins(newTestCoins())
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc2)
|
||||
|
||||
// require a valid SDK tx to pass
|
||||
fee := newTestStdFee()
|
||||
msg1 := newTestMsg(addr1, addr2)
|
||||
msgs := []sdk.Msg{msg1}
|
||||
|
||||
privKeys := []tmcrypto.PrivKey{priv1, priv2}
|
||||
accNums := []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber()}
|
||||
accSeqs := []uint64{acc1.GetSequence(), acc2.GetSequence()}
|
||||
|
||||
tx := newTestSDKTx(suite.ctx, msgs, privKeys, accNums, accSeqs, fee)
|
||||
|
||||
requireValidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) TestSDKInvalidSigs() {
|
||||
suite.ctx = suite.ctx.WithBlockHeight(1)
|
||||
|
||||
addr1, priv1 := newTestAddrKey()
|
||||
addr2, priv2 := newTestAddrKey()
|
||||
addr3, priv3 := newTestAddrKey()
|
||||
|
||||
acc1 := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1)
|
||||
err := acc1.SetCoins(newTestCoins())
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc1)
|
||||
|
||||
acc2 := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr2)
|
||||
err = acc2.SetCoins(newTestCoins())
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc2)
|
||||
|
||||
fee := newTestStdFee()
|
||||
msg1 := newTestMsg(addr1, addr2)
|
||||
|
||||
// require validation failure with no signers
|
||||
msgs := []sdk.Msg{msg1}
|
||||
|
||||
privKeys := []tmcrypto.PrivKey{}
|
||||
accNums := []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber()}
|
||||
accSeqs := []uint64{acc1.GetSequence(), acc2.GetSequence()}
|
||||
|
||||
tx := newTestSDKTx(suite.ctx, msgs, privKeys, accNums, accSeqs, fee)
|
||||
requireInvalidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false)
|
||||
|
||||
// require validation failure with invalid number of signers
|
||||
msgs = []sdk.Msg{msg1}
|
||||
|
||||
privKeys = []tmcrypto.PrivKey{priv1}
|
||||
accNums = []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber()}
|
||||
accSeqs = []uint64{acc1.GetSequence(), acc2.GetSequence()}
|
||||
|
||||
tx = newTestSDKTx(suite.ctx, msgs, privKeys, accNums, accSeqs, fee)
|
||||
requireInvalidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false)
|
||||
|
||||
// require validation failure with an invalid signer
|
||||
msg2 := newTestMsg(addr1, addr3)
|
||||
msgs = []sdk.Msg{msg1, msg2}
|
||||
|
||||
privKeys = []tmcrypto.PrivKey{priv1, priv2, priv3}
|
||||
accNums = []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber(), 0}
|
||||
accSeqs = []uint64{acc1.GetSequence(), acc2.GetSequence(), 0}
|
||||
|
||||
tx = newTestSDKTx(suite.ctx, msgs, privKeys, accNums, accSeqs, fee)
|
||||
requireInvalidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) TestSDKInvalidAcc() {
|
||||
suite.ctx = suite.ctx.WithBlockHeight(1)
|
||||
|
||||
addr1, priv1 := newTestAddrKey()
|
||||
|
||||
acc1 := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1)
|
||||
err := acc1.SetCoins(newTestCoins())
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc1)
|
||||
|
||||
fee := newTestStdFee()
|
||||
msg1 := newTestMsg(addr1)
|
||||
msgs := []sdk.Msg{msg1}
|
||||
privKeys := []tmcrypto.PrivKey{priv1}
|
||||
|
||||
// require validation failure with invalid account number
|
||||
accNums := []uint64{1}
|
||||
accSeqs := []uint64{acc1.GetSequence()}
|
||||
|
||||
tx := newTestSDKTx(suite.ctx, msgs, privKeys, accNums, accSeqs, fee)
|
||||
requireInvalidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false)
|
||||
|
||||
// require validation failure with invalid sequence (nonce)
|
||||
accNums = []uint64{acc1.GetAccountNumber()}
|
||||
accSeqs = []uint64{1}
|
||||
|
||||
tx = newTestSDKTx(suite.ctx, msgs, privKeys, accNums, accSeqs, fee)
|
||||
requireInvalidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) TestEthInvalidSig() {
|
||||
suite.ctx = suite.ctx.WithBlockHeight(1)
|
||||
|
||||
_, priv1 := newTestAddrKey()
|
||||
addr2, _ := newTestAddrKey()
|
||||
to := ethcmn.BytesToAddress(addr2.Bytes())
|
||||
amt := big.NewInt(32)
|
||||
gas := big.NewInt(20)
|
||||
ethMsg := evmtypes.NewMsgEthereumTx(0, &to, amt, 22000, gas, []byte("test"))
|
||||
|
||||
tx := newTestEthTx(suite.ctx, ethMsg, priv1)
|
||||
ctx := suite.ctx.WithChainID("4")
|
||||
requireInvalidTx(suite.T(), suite.anteHandler, ctx, tx, false)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) TestEthInvalidNonce() {
|
||||
|
||||
suite.ctx = suite.ctx.WithBlockHeight(1)
|
||||
|
||||
addr1, priv1 := newTestAddrKey()
|
||||
addr2, _ := newTestAddrKey()
|
||||
|
||||
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1)
|
||||
err := acc.SetSequence(10)
|
||||
suite.Require().NoError(err)
|
||||
err = acc.SetCoins(newTestCoins())
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
|
||||
// require a valid Ethereum tx to pass
|
||||
to := ethcmn.BytesToAddress(addr2.Bytes())
|
||||
amt := big.NewInt(32)
|
||||
gas := big.NewInt(20)
|
||||
ethMsg := evmtypes.NewMsgEthereumTx(0, &to, amt, 22000, gas, []byte("test"))
|
||||
|
||||
tx := newTestEthTx(suite.ctx, ethMsg, priv1)
|
||||
requireInvalidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) TestEthInsufficientBalance() {
|
||||
suite.ctx = suite.ctx.WithBlockHeight(1)
|
||||
|
||||
addr1, priv1 := newTestAddrKey()
|
||||
addr2, _ := newTestAddrKey()
|
||||
|
||||
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
|
||||
// require a valid Ethereum tx to pass
|
||||
to := ethcmn.BytesToAddress(addr2.Bytes())
|
||||
amt := big.NewInt(32)
|
||||
gas := big.NewInt(20)
|
||||
ethMsg := evmtypes.NewMsgEthereumTx(0, &to, amt, 22000, gas, []byte("test"))
|
||||
|
||||
tx := newTestEthTx(suite.ctx, ethMsg, priv1)
|
||||
requireInvalidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) TestEthInvalidIntrinsicGas() {
|
||||
suite.ctx = suite.ctx.WithBlockHeight(1)
|
||||
|
||||
addr1, priv1 := newTestAddrKey()
|
||||
addr2, _ := newTestAddrKey()
|
||||
|
||||
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1)
|
||||
err := acc.SetCoins(newTestCoins())
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
|
||||
// require a valid Ethereum tx to pass
|
||||
to := ethcmn.BytesToAddress(addr2.Bytes())
|
||||
amt := big.NewInt(32)
|
||||
gas := big.NewInt(20)
|
||||
gasLimit := uint64(1000)
|
||||
ethMsg := evmtypes.NewMsgEthereumTx(0, &to, amt, gasLimit, gas, []byte("test"))
|
||||
|
||||
tx := newTestEthTx(suite.ctx, ethMsg, priv1)
|
||||
requireInvalidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) TestEthInvalidMempoolFees() {
|
||||
// setup app with checkTx = true
|
||||
suite.app = app.Setup(true)
|
||||
suite.ctx = suite.app.BaseApp.NewContext(true, abci.Header{Height: 1, ChainID: "3", Time: time.Now().UTC()})
|
||||
suite.anteHandler = ante.NewAnteHandler(suite.app.AccountKeeper, suite.app.SupplyKeeper)
|
||||
|
||||
suite.ctx = suite.ctx.WithMinGasPrices(sdk.NewDecCoins(sdk.NewCoins(sdk.NewCoin(types.DenomDefault, sdk.NewInt(500000)))))
|
||||
addr1, priv1 := newTestAddrKey()
|
||||
addr2, _ := newTestAddrKey()
|
||||
|
||||
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1)
|
||||
err := acc.SetCoins(newTestCoins())
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
|
||||
// require a valid Ethereum tx to pass
|
||||
to := ethcmn.BytesToAddress(addr2.Bytes())
|
||||
amt := big.NewInt(32)
|
||||
gas := big.NewInt(20)
|
||||
ethMsg := evmtypes.NewMsgEthereumTx(0, &to, amt, 22000, gas, []byte("payload"))
|
||||
|
||||
tx := newTestEthTx(suite.ctx, ethMsg, priv1)
|
||||
requireInvalidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false)
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) TestEthInvalidChainID() {
|
||||
suite.ctx = suite.ctx.WithBlockHeight(1)
|
||||
|
||||
addr1, priv1 := newTestAddrKey()
|
||||
addr2, _ := newTestAddrKey()
|
||||
|
||||
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1)
|
||||
err := acc.SetCoins(newTestCoins())
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
|
||||
// require a valid Ethereum tx to pass
|
||||
to := ethcmn.BytesToAddress(addr2.Bytes())
|
||||
amt := big.NewInt(32)
|
||||
gas := big.NewInt(20)
|
||||
ethMsg := evmtypes.NewMsgEthereumTx(0, &to, amt, 22000, gas, []byte("test"))
|
||||
|
||||
tx := newTestEthTx(suite.ctx, ethMsg, priv1)
|
||||
ctx := suite.ctx.WithChainID("bad-chain-id")
|
||||
requireInvalidTx(suite.T(), suite.anteHandler, ctx, tx, false)
|
||||
}
|
104
app/ante/utils_test.go
Normal file
104
app/ante/utils_test.go
Normal file
@ -0,0 +1,104 @@
|
||||
package ante_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
|
||||
"github.com/cosmos/ethermint/app"
|
||||
ante "github.com/cosmos/ethermint/app/ante"
|
||||
"github.com/cosmos/ethermint/crypto"
|
||||
emint "github.com/cosmos/ethermint/types"
|
||||
evmtypes "github.com/cosmos/ethermint/x/evm/types"
|
||||
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||
)
|
||||
|
||||
type AnteTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
ctx sdk.Context
|
||||
app *app.EthermintApp
|
||||
anteHandler sdk.AnteHandler
|
||||
}
|
||||
|
||||
func (suite *AnteTestSuite) SetupTest() {
|
||||
checkTx := false
|
||||
|
||||
suite.app = app.Setup(checkTx)
|
||||
suite.app.Codec().RegisterConcrete(&sdk.TestMsg{}, "test/TestMsg", nil)
|
||||
|
||||
suite.ctx = suite.app.BaseApp.NewContext(checkTx, abci.Header{Height: 1, ChainID: "3", Time: time.Now().UTC()})
|
||||
suite.anteHandler = ante.NewAnteHandler(suite.app.AccountKeeper, suite.app.SupplyKeeper)
|
||||
}
|
||||
|
||||
func TestAnteTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(AnteTestSuite))
|
||||
}
|
||||
|
||||
func newTestMsg(addrs ...sdk.AccAddress) *sdk.TestMsg {
|
||||
return sdk.NewTestMsg(addrs...)
|
||||
}
|
||||
|
||||
func newTestCoins() sdk.Coins {
|
||||
return sdk.NewCoins(sdk.NewInt64Coin(emint.DenomDefault, 500000000))
|
||||
}
|
||||
|
||||
func newTestStdFee() auth.StdFee {
|
||||
return auth.NewStdFee(220000, sdk.NewCoins(sdk.NewInt64Coin(emint.DenomDefault, 150)))
|
||||
}
|
||||
|
||||
// GenerateAddress generates an Ethereum address.
|
||||
func newTestAddrKey() (sdk.AccAddress, tmcrypto.PrivKey) {
|
||||
privkey, _ := crypto.GenerateKey()
|
||||
addr := ethcrypto.PubkeyToAddress(privkey.ToECDSA().PublicKey)
|
||||
|
||||
return sdk.AccAddress(addr.Bytes()), privkey
|
||||
}
|
||||
|
||||
func newTestSDKTx(
|
||||
ctx sdk.Context, msgs []sdk.Msg, privs []tmcrypto.PrivKey,
|
||||
accNums []uint64, seqs []uint64, fee auth.StdFee,
|
||||
) sdk.Tx {
|
||||
|
||||
sigs := make([]auth.StdSignature, len(privs))
|
||||
for i, priv := range privs {
|
||||
signBytes := auth.StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, "")
|
||||
|
||||
sig, err := priv.Sign(signBytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sigs[i] = auth.StdSignature{
|
||||
PubKey: priv.PubKey(),
|
||||
Signature: sig,
|
||||
}
|
||||
}
|
||||
|
||||
return auth.NewStdTx(msgs, fee, sigs, "")
|
||||
}
|
||||
|
||||
func newTestEthTx(ctx sdk.Context, msg evmtypes.MsgEthereumTx, priv tmcrypto.PrivKey) sdk.Tx {
|
||||
chainID, ok := new(big.Int).SetString(ctx.ChainID(), 10)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("invalid chainID: %s", ctx.ChainID()))
|
||||
}
|
||||
|
||||
privkey, ok := priv.(crypto.PrivKeySecp256k1)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("invalid private key type: %T", priv))
|
||||
}
|
||||
|
||||
msg.Sign(chainID, privkey.ToECDSA())
|
||||
return msg
|
||||
}
|
308
app/ante_test.go
308
app/ante_test.go
@ -1,308 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/stretchr/testify/require"
|
||||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/ethermint/types"
|
||||
evmtypes "github.com/cosmos/ethermint/x/evm/types"
|
||||
)
|
||||
|
||||
func requireValidTx(
|
||||
t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, sim bool,
|
||||
) {
|
||||
_, err := anteHandler(ctx, tx, sim)
|
||||
require.True(t, err == nil)
|
||||
}
|
||||
|
||||
func requireInvalidTx(
|
||||
t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context,
|
||||
tx sdk.Tx, sim bool, code sdk.CodeType,
|
||||
) {
|
||||
|
||||
_, err := anteHandler(ctx, tx, sim)
|
||||
// require.Equal(t, code, err, fmt.Sprintf("invalid result: %v", err))
|
||||
require.Error(t, err)
|
||||
|
||||
if code == sdk.CodeOutOfGas {
|
||||
_, ok := tx.(auth.StdTx)
|
||||
require.True(t, ok, "tx must be in form auth.StdTx")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidEthTx(t *testing.T) {
|
||||
input := newTestSetup()
|
||||
input.ctx = input.ctx.WithBlockHeight(1)
|
||||
|
||||
addr1, priv1 := newTestAddrKey()
|
||||
addr2, _ := newTestAddrKey()
|
||||
|
||||
acc1 := input.accKeeper.NewAccountWithAddress(input.ctx, addr1)
|
||||
// nolint:errcheck
|
||||
acc1.SetCoins(newTestCoins())
|
||||
input.accKeeper.SetAccount(input.ctx, acc1)
|
||||
|
||||
acc2 := input.accKeeper.NewAccountWithAddress(input.ctx, addr2)
|
||||
// nolint:errcheck
|
||||
acc2.SetCoins(newTestCoins())
|
||||
input.accKeeper.SetAccount(input.ctx, acc2)
|
||||
|
||||
// require a valid Ethereum tx to pass
|
||||
to := ethcmn.BytesToAddress(addr2.Bytes())
|
||||
amt := big.NewInt(32)
|
||||
gas := big.NewInt(20)
|
||||
ethMsg := evmtypes.NewEthereumTxMsg(0, &to, amt, 22000, gas, []byte("test"))
|
||||
|
||||
tx := newTestEthTx(input.ctx, ethMsg, priv1)
|
||||
requireValidTx(t, input.anteHandler, input.ctx, tx, false)
|
||||
}
|
||||
|
||||
func TestValidTx(t *testing.T) {
|
||||
input := newTestSetup()
|
||||
input.ctx = input.ctx.WithBlockHeight(1)
|
||||
|
||||
addr1, priv1 := newTestAddrKey()
|
||||
addr2, priv2 := newTestAddrKey()
|
||||
|
||||
acc1 := input.accKeeper.NewAccountWithAddress(input.ctx, addr1)
|
||||
// nolint:errcheck
|
||||
acc1.SetCoins(newTestCoins())
|
||||
input.accKeeper.SetAccount(input.ctx, acc1)
|
||||
|
||||
acc2 := input.accKeeper.NewAccountWithAddress(input.ctx, addr2)
|
||||
// nolint:errcheck
|
||||
acc2.SetCoins(newTestCoins())
|
||||
input.accKeeper.SetAccount(input.ctx, acc2)
|
||||
|
||||
// require a valid SDK tx to pass
|
||||
fee := newTestStdFee()
|
||||
msg1 := newTestMsg(addr1, addr2)
|
||||
msgs := []sdk.Msg{msg1}
|
||||
|
||||
privKeys := []tmcrypto.PrivKey{priv1, priv2}
|
||||
accNums := []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber()}
|
||||
accSeqs := []uint64{acc1.GetSequence(), acc2.GetSequence()}
|
||||
|
||||
tx := newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee)
|
||||
|
||||
requireValidTx(t, input.anteHandler, input.ctx, tx, false)
|
||||
}
|
||||
|
||||
func TestSDKInvalidSigs(t *testing.T) {
|
||||
input := newTestSetup()
|
||||
input.ctx = input.ctx.WithBlockHeight(1)
|
||||
|
||||
addr1, priv1 := newTestAddrKey()
|
||||
addr2, priv2 := newTestAddrKey()
|
||||
addr3, priv3 := newTestAddrKey()
|
||||
|
||||
acc1 := input.accKeeper.NewAccountWithAddress(input.ctx, addr1)
|
||||
// nolint:errcheck
|
||||
acc1.SetCoins(newTestCoins())
|
||||
input.accKeeper.SetAccount(input.ctx, acc1)
|
||||
|
||||
acc2 := input.accKeeper.NewAccountWithAddress(input.ctx, addr2)
|
||||
// nolint:errcheck
|
||||
acc2.SetCoins(newTestCoins())
|
||||
input.accKeeper.SetAccount(input.ctx, acc2)
|
||||
|
||||
fee := newTestStdFee()
|
||||
msg1 := newTestMsg(addr1, addr2)
|
||||
|
||||
// require validation failure with no signers
|
||||
msgs := []sdk.Msg{msg1}
|
||||
|
||||
privKeys := []tmcrypto.PrivKey{}
|
||||
accNums := []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber()}
|
||||
accSeqs := []uint64{acc1.GetSequence(), acc2.GetSequence()}
|
||||
|
||||
tx := newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee)
|
||||
requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeNoSignatures)
|
||||
|
||||
// require validation failure with invalid number of signers
|
||||
msgs = []sdk.Msg{msg1}
|
||||
|
||||
privKeys = []tmcrypto.PrivKey{priv1}
|
||||
accNums = []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber()}
|
||||
accSeqs = []uint64{acc1.GetSequence(), acc2.GetSequence()}
|
||||
|
||||
tx = newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee)
|
||||
requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeUnauthorized)
|
||||
|
||||
// require validation failure with an invalid signer
|
||||
msg2 := newTestMsg(addr1, addr3)
|
||||
msgs = []sdk.Msg{msg1, msg2}
|
||||
|
||||
privKeys = []tmcrypto.PrivKey{priv1, priv2, priv3}
|
||||
accNums = []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber(), 0}
|
||||
accSeqs = []uint64{acc1.GetSequence(), acc2.GetSequence(), 0}
|
||||
|
||||
tx = newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee)
|
||||
requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeUnknownAddress)
|
||||
}
|
||||
|
||||
func TestSDKInvalidAcc(t *testing.T) {
|
||||
input := newTestSetup()
|
||||
input.ctx = input.ctx.WithBlockHeight(1)
|
||||
|
||||
addr1, priv1 := newTestAddrKey()
|
||||
|
||||
acc1 := input.accKeeper.NewAccountWithAddress(input.ctx, addr1)
|
||||
// nolint:errcheck
|
||||
acc1.SetCoins(newTestCoins())
|
||||
input.accKeeper.SetAccount(input.ctx, acc1)
|
||||
|
||||
fee := newTestStdFee()
|
||||
msg1 := newTestMsg(addr1)
|
||||
msgs := []sdk.Msg{msg1}
|
||||
privKeys := []tmcrypto.PrivKey{priv1}
|
||||
|
||||
// require validation failure with invalid account number
|
||||
accNums := []uint64{1}
|
||||
accSeqs := []uint64{acc1.GetSequence()}
|
||||
|
||||
tx := newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee)
|
||||
requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeUnauthorized)
|
||||
|
||||
// require validation failure with invalid sequence (nonce)
|
||||
accNums = []uint64{acc1.GetAccountNumber()}
|
||||
accSeqs = []uint64{1}
|
||||
|
||||
tx = newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee)
|
||||
requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeUnauthorized)
|
||||
}
|
||||
|
||||
func TestEthInvalidSig(t *testing.T) {
|
||||
input := newTestSetup()
|
||||
input.ctx = input.ctx.WithBlockHeight(1)
|
||||
|
||||
_, priv1 := newTestAddrKey()
|
||||
addr2, _ := newTestAddrKey()
|
||||
to := ethcmn.BytesToAddress(addr2.Bytes())
|
||||
amt := big.NewInt(32)
|
||||
gas := big.NewInt(20)
|
||||
ethMsg := evmtypes.NewEthereumTxMsg(0, &to, amt, 22000, gas, []byte("test"))
|
||||
|
||||
tx := newTestEthTx(input.ctx, ethMsg, priv1)
|
||||
ctx := input.ctx.WithChainID("4")
|
||||
requireInvalidTx(t, input.anteHandler, ctx, tx, false, sdk.CodeUnauthorized)
|
||||
}
|
||||
|
||||
func TestEthInvalidNonce(t *testing.T) {
|
||||
input := newTestSetup()
|
||||
input.ctx = input.ctx.WithBlockHeight(1)
|
||||
|
||||
addr1, priv1 := newTestAddrKey()
|
||||
addr2, _ := newTestAddrKey()
|
||||
|
||||
acc := input.accKeeper.NewAccountWithAddress(input.ctx, addr1)
|
||||
// nolint:errcheck
|
||||
acc.SetCoins(newTestCoins())
|
||||
// nolint:errcheck
|
||||
acc.SetSequence(10)
|
||||
input.accKeeper.SetAccount(input.ctx, acc)
|
||||
|
||||
// require a valid Ethereum tx to pass
|
||||
to := ethcmn.BytesToAddress(addr2.Bytes())
|
||||
amt := big.NewInt(32)
|
||||
gas := big.NewInt(20)
|
||||
ethMsg := evmtypes.NewEthereumTxMsg(0, &to, amt, 22000, gas, []byte("test"))
|
||||
|
||||
tx := newTestEthTx(input.ctx, ethMsg, priv1)
|
||||
requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeInvalidSequence)
|
||||
}
|
||||
|
||||
func TestEthInsufficientBalance(t *testing.T) {
|
||||
input := newTestSetup()
|
||||
input.ctx = input.ctx.WithBlockHeight(1)
|
||||
|
||||
addr1, priv1 := newTestAddrKey()
|
||||
addr2, _ := newTestAddrKey()
|
||||
|
||||
acc := input.accKeeper.NewAccountWithAddress(input.ctx, addr1)
|
||||
input.accKeeper.SetAccount(input.ctx, acc)
|
||||
|
||||
// require a valid Ethereum tx to pass
|
||||
to := ethcmn.BytesToAddress(addr2.Bytes())
|
||||
amt := big.NewInt(32)
|
||||
gas := big.NewInt(20)
|
||||
ethMsg := evmtypes.NewEthereumTxMsg(0, &to, amt, 22000, gas, []byte("test"))
|
||||
|
||||
tx := newTestEthTx(input.ctx, ethMsg, priv1)
|
||||
requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeInsufficientFunds)
|
||||
}
|
||||
|
||||
func TestEthInvalidIntrinsicGas(t *testing.T) {
|
||||
input := newTestSetup()
|
||||
input.ctx = input.ctx.WithBlockHeight(1)
|
||||
|
||||
addr1, priv1 := newTestAddrKey()
|
||||
addr2, _ := newTestAddrKey()
|
||||
|
||||
acc := input.accKeeper.NewAccountWithAddress(input.ctx, addr1)
|
||||
// nolint:errcheck
|
||||
acc.SetCoins(newTestCoins())
|
||||
input.accKeeper.SetAccount(input.ctx, acc)
|
||||
|
||||
// require a valid Ethereum tx to pass
|
||||
to := ethcmn.BytesToAddress(addr2.Bytes())
|
||||
amt := big.NewInt(32)
|
||||
gas := big.NewInt(20)
|
||||
gasLimit := uint64(1000)
|
||||
ethMsg := evmtypes.NewEthereumTxMsg(0, &to, amt, gasLimit, gas, []byte("test"))
|
||||
|
||||
tx := newTestEthTx(input.ctx, ethMsg, priv1)
|
||||
requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeInternal)
|
||||
}
|
||||
|
||||
func TestEthInvalidMempoolFees(t *testing.T) {
|
||||
input := newTestSetup()
|
||||
input.ctx = input.ctx.WithBlockHeight(1)
|
||||
input.ctx = input.ctx.WithMinGasPrices(sdk.DecCoins{sdk.NewDecCoin(types.DenomDefault, sdk.NewInt(500000))})
|
||||
|
||||
addr1, priv1 := newTestAddrKey()
|
||||
addr2, _ := newTestAddrKey()
|
||||
|
||||
acc := input.accKeeper.NewAccountWithAddress(input.ctx, addr1)
|
||||
// nolint:errcheck
|
||||
acc.SetCoins(newTestCoins())
|
||||
input.accKeeper.SetAccount(input.ctx, acc)
|
||||
|
||||
// require a valid Ethereum tx to pass
|
||||
to := ethcmn.BytesToAddress(addr2.Bytes())
|
||||
amt := big.NewInt(32)
|
||||
gas := big.NewInt(20)
|
||||
ethMsg := evmtypes.NewEthereumTxMsg(0, &to, amt, 22000, gas, []byte("test"))
|
||||
|
||||
tx := newTestEthTx(input.ctx, ethMsg, priv1)
|
||||
requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeInsufficientFee)
|
||||
}
|
||||
|
||||
func TestEthInvalidChainID(t *testing.T) {
|
||||
input := newTestSetup()
|
||||
input.ctx = input.ctx.WithBlockHeight(1)
|
||||
|
||||
addr1, priv1 := newTestAddrKey()
|
||||
addr2, _ := newTestAddrKey()
|
||||
|
||||
acc := input.accKeeper.NewAccountWithAddress(input.ctx, addr1)
|
||||
// nolint:errcheck
|
||||
acc.SetCoins(newTestCoins())
|
||||
input.accKeeper.SetAccount(input.ctx, acc)
|
||||
|
||||
// require a valid Ethereum tx to pass
|
||||
to := ethcmn.BytesToAddress(addr2.Bytes())
|
||||
amt := big.NewInt(32)
|
||||
gas := big.NewInt(20)
|
||||
ethMsg := evmtypes.NewEthereumTxMsg(0, &to, amt, 22000, gas, []byte("test"))
|
||||
|
||||
tx := newTestEthTx(input.ctx, ethMsg, priv1)
|
||||
ctx := input.ctx.WithChainID("bad-chain-id")
|
||||
requireInvalidTx(t, input.anteHandler, ctx, tx, false, types.CodeInvalidChainID)
|
||||
}
|
240
app/ethermint.go
240
app/ethermint.go
@ -1,15 +1,13 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
emintcrypto "github.com/cosmos/ethermint/crypto"
|
||||
"github.com/cosmos/ethermint/x/evm"
|
||||
|
||||
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
cryptokeys "github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/module"
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
@ -26,12 +24,14 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
"github.com/cosmos/cosmos-sdk/x/supply"
|
||||
|
||||
"github.com/cosmos/ethermint/app/ante"
|
||||
emintcrypto "github.com/cosmos/ethermint/crypto"
|
||||
eminttypes "github.com/cosmos/ethermint/types"
|
||||
evmtypes "github.com/cosmos/ethermint/x/evm/types"
|
||||
"github.com/cosmos/ethermint/x/evm"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
tmlog "github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
)
|
||||
|
||||
@ -44,21 +44,23 @@ var (
|
||||
// DefaultNodeHome sets the folder where the applcation data and configuration will be stored
|
||||
DefaultNodeHome = os.ExpandEnv("$HOME/.emintd")
|
||||
|
||||
// ModuleBasics is the module BasicManager is in charge of setting up basic,
|
||||
// ModuleBasics defines the module BasicManager is in charge of setting up basic,
|
||||
// non-dependant module elements, such as codec registration
|
||||
// and genesis verification.
|
||||
ModuleBasics = module.NewBasicManager(
|
||||
genutil.AppModuleBasic{},
|
||||
auth.AppModuleBasic{},
|
||||
supply.AppModuleBasic{},
|
||||
genutil.AppModuleBasic{},
|
||||
bank.AppModuleBasic{},
|
||||
staking.AppModuleBasic{},
|
||||
mint.AppModuleBasic{},
|
||||
distr.AppModuleBasic{},
|
||||
gov.NewAppModuleBasic(paramsclient.ProposalHandler, distr.ProposalHandler),
|
||||
gov.NewAppModuleBasic(
|
||||
paramsclient.ProposalHandler, distr.ProposalHandler,
|
||||
),
|
||||
params.AppModuleBasic{},
|
||||
crisis.AppModuleBasic{},
|
||||
slashing.AppModuleBasic{},
|
||||
supply.AppModuleBasic{},
|
||||
evm.AppModuleBasic{},
|
||||
)
|
||||
|
||||
@ -71,6 +73,11 @@ var (
|
||||
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
|
||||
gov.ModuleName: {supply.Burner},
|
||||
}
|
||||
|
||||
// module accounts that are allowed to receive tokens
|
||||
allowedReceivingModAcc = map[string]bool{
|
||||
distr.ModuleName: true,
|
||||
}
|
||||
)
|
||||
|
||||
// MakeCodec generates the necessary codecs for Amino
|
||||
@ -100,18 +107,21 @@ type EthermintApp struct {
|
||||
keys map[string]*sdk.KVStoreKey
|
||||
tkeys map[string]*sdk.TransientStoreKey
|
||||
|
||||
// subspaces
|
||||
subspaces map[string]params.Subspace
|
||||
|
||||
// keepers
|
||||
accountKeeper auth.AccountKeeper
|
||||
bankKeeper bank.Keeper
|
||||
supplyKeeper supply.Keeper
|
||||
stakingKeeper staking.Keeper
|
||||
slashingKeeper slashing.Keeper
|
||||
mintKeeper mint.Keeper
|
||||
distrKeeper distr.Keeper
|
||||
govKeeper gov.Keeper
|
||||
crisisKeeper crisis.Keeper
|
||||
paramsKeeper params.Keeper
|
||||
evmKeeper evm.Keeper
|
||||
AccountKeeper auth.AccountKeeper
|
||||
BankKeeper bank.Keeper
|
||||
SupplyKeeper supply.Keeper
|
||||
StakingKeeper staking.Keeper
|
||||
SlashingKeeper slashing.Keeper
|
||||
MintKeeper mint.Keeper
|
||||
DistrKeeper distr.Keeper
|
||||
GovKeeper gov.Keeper
|
||||
CrisisKeeper crisis.Keeper
|
||||
ParamsKeeper params.Keeper
|
||||
EvmKeeper evm.Keeper
|
||||
|
||||
// the module manager
|
||||
mm *module.Manager
|
||||
@ -124,18 +134,25 @@ type EthermintApp struct {
|
||||
// in a sovereign zone and as an application running with a shared security model.
|
||||
// For now, it will support only running as a sovereign application.
|
||||
func NewEthermintApp(
|
||||
logger tmlog.Logger, db dbm.DB, loadLatest bool,
|
||||
invCheckPeriod uint, baseAppOptions ...func(*bam.BaseApp)) *EthermintApp {
|
||||
logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
|
||||
invCheckPeriod uint, baseAppOptions ...func(*bam.BaseApp),
|
||||
) *EthermintApp {
|
||||
|
||||
cdc := MakeCodec()
|
||||
|
||||
bApp := bam.NewBaseApp(appName, logger, db, evmtypes.TxDecoder(cdc), baseAppOptions...)
|
||||
// use custom Ethermint transaction decoder
|
||||
bApp := bam.NewBaseApp(appName, logger, db, evm.TxDecoder(cdc), baseAppOptions...)
|
||||
bApp.SetCommitMultiStoreTracer(traceStore)
|
||||
bApp.SetAppVersion(version.Version)
|
||||
|
||||
keys := sdk.NewKVStoreKeys(bam.MainStoreKey, auth.StoreKey, staking.StoreKey,
|
||||
keys := sdk.NewKVStoreKeys(
|
||||
bam.MainStoreKey, auth.StoreKey, staking.StoreKey,
|
||||
supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey,
|
||||
gov.StoreKey, params.StoreKey, evmtypes.EvmStoreKey, evmtypes.EvmCodeKey)
|
||||
blockKey := sdk.NewKVStoreKey(evmtypes.EvmBlockKey)
|
||||
tkeys := sdk.NewTransientStoreKeys(staking.TStoreKey, params.TStoreKey)
|
||||
gov.StoreKey, params.StoreKey, evm.CodeKey, evm.StoreKey,
|
||||
)
|
||||
blockKey := sdk.NewKVStoreKey(evm.BlockKey)
|
||||
|
||||
tkeys := sdk.NewTransientStoreKeys(params.TStoreKey)
|
||||
|
||||
app := &EthermintApp{
|
||||
BaseApp: bApp,
|
||||
@ -143,89 +160,118 @@ func NewEthermintApp(
|
||||
invCheckPeriod: invCheckPeriod,
|
||||
keys: keys,
|
||||
tkeys: tkeys,
|
||||
subspaces: make(map[string]params.Subspace),
|
||||
}
|
||||
|
||||
// init params keeper and subspaces
|
||||
app.paramsKeeper = params.NewKeeper(app.cdc, keys[params.StoreKey], tkeys[params.TStoreKey], params.DefaultCodespace)
|
||||
authSubspace := app.paramsKeeper.Subspace(auth.DefaultParamspace)
|
||||
bankSubspace := app.paramsKeeper.Subspace(bank.DefaultParamspace)
|
||||
stakingSubspace := app.paramsKeeper.Subspace(staking.DefaultParamspace)
|
||||
mintSubspace := app.paramsKeeper.Subspace(mint.DefaultParamspace)
|
||||
distrSubspace := app.paramsKeeper.Subspace(distr.DefaultParamspace)
|
||||
slashingSubspace := app.paramsKeeper.Subspace(slashing.DefaultParamspace)
|
||||
govSubspace := app.paramsKeeper.Subspace(gov.DefaultParamspace).WithKeyTable(gov.ParamKeyTable())
|
||||
crisisSubspace := app.paramsKeeper.Subspace(crisis.DefaultParamspace)
|
||||
app.ParamsKeeper = params.NewKeeper(app.cdc, keys[params.StoreKey], tkeys[params.TStoreKey], params.DefaultCodespace)
|
||||
app.subspaces[auth.ModuleName] = app.ParamsKeeper.Subspace(auth.DefaultParamspace)
|
||||
app.subspaces[bank.ModuleName] = app.ParamsKeeper.Subspace(bank.DefaultParamspace)
|
||||
app.subspaces[staking.ModuleName] = app.ParamsKeeper.Subspace(staking.DefaultParamspace)
|
||||
app.subspaces[mint.ModuleName] = app.ParamsKeeper.Subspace(mint.DefaultParamspace)
|
||||
app.subspaces[distr.ModuleName] = app.ParamsKeeper.Subspace(distr.DefaultParamspace)
|
||||
app.subspaces[slashing.ModuleName] = app.ParamsKeeper.Subspace(slashing.DefaultParamspace)
|
||||
app.subspaces[gov.ModuleName] = app.ParamsKeeper.Subspace(gov.DefaultParamspace).WithKeyTable(gov.ParamKeyTable())
|
||||
app.subspaces[crisis.ModuleName] = app.ParamsKeeper.Subspace(crisis.DefaultParamspace)
|
||||
|
||||
// add keepers
|
||||
app.accountKeeper = auth.NewAccountKeeper(app.cdc, keys[auth.StoreKey], authSubspace, eminttypes.ProtoBaseAccount)
|
||||
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper, bankSubspace, bank.DefaultCodespace, app.ModuleAccountAddrs())
|
||||
app.supplyKeeper = supply.NewKeeper(app.cdc, keys[supply.StoreKey], app.accountKeeper, app.bankKeeper, maccPerms)
|
||||
stakingKeeper := staking.NewKeeper(app.cdc, keys[staking.StoreKey],
|
||||
app.supplyKeeper, stakingSubspace, staking.DefaultCodespace)
|
||||
app.mintKeeper = mint.NewKeeper(app.cdc, keys[mint.StoreKey], mintSubspace, &stakingKeeper, app.supplyKeeper, auth.FeeCollectorName)
|
||||
app.distrKeeper = distr.NewKeeper(app.cdc, keys[distr.StoreKey], distrSubspace, &stakingKeeper,
|
||||
app.supplyKeeper, distr.DefaultCodespace, auth.FeeCollectorName, app.ModuleAccountAddrs())
|
||||
app.slashingKeeper = slashing.NewKeeper(app.cdc, keys[slashing.StoreKey], &stakingKeeper,
|
||||
slashingSubspace, slashing.DefaultCodespace)
|
||||
app.crisisKeeper = crisis.NewKeeper(crisisSubspace, invCheckPeriod, app.supplyKeeper, auth.FeeCollectorName)
|
||||
app.evmKeeper = evm.NewKeeper(app.accountKeeper, keys[evmtypes.EvmStoreKey], keys[evmtypes.EvmCodeKey], blockKey, cdc)
|
||||
// use custom Ethermint account for contracts
|
||||
app.AccountKeeper = auth.NewAccountKeeper(
|
||||
app.cdc, keys[auth.StoreKey], app.subspaces[auth.ModuleName], eminttypes.ProtoBaseAccount,
|
||||
)
|
||||
app.BankKeeper = bank.NewBaseKeeper(
|
||||
app.AccountKeeper, app.subspaces[bank.ModuleName], bank.DefaultCodespace, app.ModuleAccountAddrs(),
|
||||
)
|
||||
app.SupplyKeeper = supply.NewKeeper(
|
||||
app.cdc, keys[supply.StoreKey], app.AccountKeeper, app.BankKeeper, maccPerms,
|
||||
)
|
||||
stakingKeeper := staking.NewKeeper(
|
||||
app.cdc, keys[staking.StoreKey], app.SupplyKeeper, app.subspaces[staking.ModuleName],
|
||||
staking.DefaultCodespace,
|
||||
)
|
||||
app.MintKeeper = mint.NewKeeper(
|
||||
app.cdc, keys[mint.StoreKey], app.subspaces[mint.ModuleName], &stakingKeeper,
|
||||
app.SupplyKeeper, auth.FeeCollectorName,
|
||||
)
|
||||
app.DistrKeeper = distr.NewKeeper(
|
||||
app.cdc, keys[distr.StoreKey], app.subspaces[distr.ModuleName], &stakingKeeper,
|
||||
app.SupplyKeeper, distr.DefaultCodespace, auth.FeeCollectorName, app.ModuleAccountAddrs(),
|
||||
)
|
||||
app.SlashingKeeper = slashing.NewKeeper(
|
||||
app.cdc, keys[slashing.StoreKey], &stakingKeeper, app.subspaces[slashing.ModuleName],
|
||||
slashing.DefaultCodespace,
|
||||
)
|
||||
app.CrisisKeeper = crisis.NewKeeper(
|
||||
app.subspaces[crisis.ModuleName], invCheckPeriod, app.SupplyKeeper, auth.FeeCollectorName,
|
||||
)
|
||||
app.EvmKeeper = evm.NewKeeper(
|
||||
app.cdc, blockKey, keys[evm.CodeKey], keys[evm.StoreKey], app.AccountKeeper,
|
||||
)
|
||||
|
||||
// register the proposal types
|
||||
govRouter := gov.NewRouter()
|
||||
govRouter.AddRoute(gov.RouterKey, gov.ProposalHandler).
|
||||
AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.paramsKeeper)).
|
||||
AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper))
|
||||
app.govKeeper = gov.NewKeeper(app.cdc, keys[gov.StoreKey], govSubspace,
|
||||
app.supplyKeeper, &stakingKeeper, gov.DefaultCodespace, govRouter)
|
||||
AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.ParamsKeeper)).
|
||||
AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.DistrKeeper))
|
||||
app.GovKeeper = gov.NewKeeper(
|
||||
app.cdc, keys[gov.StoreKey], app.subspaces[gov.ModuleName], app.SupplyKeeper,
|
||||
&stakingKeeper, gov.DefaultCodespace, govRouter,
|
||||
)
|
||||
|
||||
// register the staking hooks
|
||||
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
|
||||
app.stakingKeeper = *stakingKeeper.SetHooks(
|
||||
staking.NewMultiStakingHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks()),
|
||||
app.StakingKeeper = *stakingKeeper.SetHooks(
|
||||
staking.NewMultiStakingHooks(app.DistrKeeper.Hooks(), app.SlashingKeeper.Hooks()),
|
||||
)
|
||||
|
||||
// NOTE: Any module instantiated in the module manager that is later modified
|
||||
// must be passed by reference here.
|
||||
app.mm = module.NewManager(
|
||||
genutil.NewAppModule(app.accountKeeper, app.stakingKeeper, app.BaseApp.DeliverTx),
|
||||
auth.NewAppModule(app.accountKeeper),
|
||||
bank.NewAppModule(app.bankKeeper, app.accountKeeper),
|
||||
crisis.NewAppModule(&app.crisisKeeper),
|
||||
supply.NewAppModule(app.supplyKeeper, app.accountKeeper),
|
||||
distr.NewAppModule(app.distrKeeper, app.accountKeeper, app.supplyKeeper, app.stakingKeeper),
|
||||
gov.NewAppModule(app.govKeeper, app.accountKeeper, app.supplyKeeper),
|
||||
mint.NewAppModule(app.mintKeeper),
|
||||
slashing.NewAppModule(app.slashingKeeper, app.accountKeeper, app.stakingKeeper),
|
||||
staking.NewAppModule(app.stakingKeeper, app.accountKeeper, app.supplyKeeper),
|
||||
evm.NewAppModule(app.evmKeeper),
|
||||
genutil.NewAppModule(app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx),
|
||||
auth.NewAppModule(app.AccountKeeper),
|
||||
bank.NewAppModule(app.BankKeeper, app.AccountKeeper),
|
||||
crisis.NewAppModule(&app.CrisisKeeper),
|
||||
supply.NewAppModule(app.SupplyKeeper, app.AccountKeeper),
|
||||
gov.NewAppModule(app.GovKeeper, app.AccountKeeper, app.SupplyKeeper),
|
||||
mint.NewAppModule(app.MintKeeper),
|
||||
slashing.NewAppModule(app.SlashingKeeper, app.AccountKeeper, app.StakingKeeper),
|
||||
distr.NewAppModule(app.DistrKeeper, app.AccountKeeper, app.SupplyKeeper, app.StakingKeeper),
|
||||
staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper),
|
||||
evm.NewAppModule(app.EvmKeeper),
|
||||
)
|
||||
|
||||
// During begin block slashing happens after distr.BeginBlocker so that
|
||||
// there is nothing left over in the validator fee pool, so as to keep the
|
||||
// CanWithdrawInvariant invariant.
|
||||
app.mm.SetOrderBeginBlockers(evmtypes.ModuleName, mint.ModuleName, distr.ModuleName, slashing.ModuleName)
|
||||
|
||||
app.mm.SetOrderEndBlockers(evmtypes.ModuleName, crisis.ModuleName, gov.ModuleName, staking.ModuleName)
|
||||
app.mm.SetOrderBeginBlockers(
|
||||
evm.ModuleName, mint.ModuleName, distr.ModuleName, slashing.ModuleName,
|
||||
)
|
||||
app.mm.SetOrderEndBlockers(
|
||||
evm.ModuleName, crisis.ModuleName, gov.ModuleName, staking.ModuleName,
|
||||
)
|
||||
|
||||
// NOTE: The genutils module must occur after staking so that pools are
|
||||
// properly initialized with tokens from genesis accounts.
|
||||
app.mm.SetOrderInitGenesis(
|
||||
distr.ModuleName, staking.ModuleName,
|
||||
auth.ModuleName, bank.ModuleName, slashing.ModuleName, gov.ModuleName,
|
||||
mint.ModuleName, supply.ModuleName, crisis.ModuleName, genutil.ModuleName, evmtypes.ModuleName,
|
||||
auth.ModuleName, distr.ModuleName, staking.ModuleName, bank.ModuleName,
|
||||
slashing.ModuleName, gov.ModuleName, mint.ModuleName, supply.ModuleName,
|
||||
crisis.ModuleName, genutil.ModuleName, evm.ModuleName,
|
||||
)
|
||||
|
||||
app.mm.RegisterInvariants(&app.crisisKeeper)
|
||||
app.mm.RegisterInvariants(&app.CrisisKeeper)
|
||||
app.mm.RegisterRoutes(app.Router(), app.QueryRouter())
|
||||
|
||||
// initialize stores
|
||||
app.MountKVStores(keys)
|
||||
app.MountTransientStores(tkeys)
|
||||
|
||||
// Mount block hash mapping key as DB (no need for historical queries)
|
||||
// TODO: why does this need to be always StoreTypeDB?
|
||||
app.MountStore(blockKey, sdk.StoreTypeDB)
|
||||
|
||||
// initialize BaseApp
|
||||
app.SetInitChainer(app.InitChainer)
|
||||
app.SetBeginBlocker(app.BeginBlocker)
|
||||
app.SetAnteHandler(NewAnteHandler(app.accountKeeper, app.supplyKeeper))
|
||||
app.SetAnteHandler(ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper))
|
||||
app.SetEndBlocker(app.EndBlocker)
|
||||
|
||||
if loadLatest {
|
||||
@ -234,12 +280,12 @@ func NewEthermintApp(
|
||||
cmn.Exit(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
// GenesisState is the state of the blockchain is represented here as a map of raw json
|
||||
// messages key'd by a identifier string.
|
||||
type GenesisState map[string]json.RawMessage
|
||||
// Name returns the name of the App
|
||||
func (app *EthermintApp) Name() string { return app.BaseApp.Name() }
|
||||
|
||||
// BeginBlocker updates every begin block
|
||||
func (app *EthermintApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
|
||||
@ -253,7 +299,7 @@ func (app *EthermintApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) a
|
||||
|
||||
// InitChainer updates at chain initialization
|
||||
func (app *EthermintApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
var genesisState GenesisState
|
||||
var genesisState simapp.GenesisState
|
||||
app.cdc.MustUnmarshalJSON(req.AppStateBytes, &genesisState)
|
||||
return app.mm.InitGenesis(ctx, genesisState)
|
||||
}
|
||||
@ -267,8 +313,42 @@ func (app *EthermintApp) LoadHeight(height int64) error {
|
||||
func (app *EthermintApp) ModuleAccountAddrs() map[string]bool {
|
||||
modAccAddrs := make(map[string]bool)
|
||||
for acc := range maccPerms {
|
||||
modAccAddrs[app.supplyKeeper.GetModuleAddress(acc).String()] = true
|
||||
modAccAddrs[supply.NewModuleAddress(acc).String()] = true
|
||||
}
|
||||
|
||||
return modAccAddrs
|
||||
}
|
||||
|
||||
// BlacklistedAccAddrs returns all the app's module account addresses black listed for receiving tokens.
|
||||
func (app *EthermintApp) BlacklistedAccAddrs() map[string]bool {
|
||||
blacklistedAddrs := make(map[string]bool)
|
||||
for acc := range maccPerms {
|
||||
blacklistedAddrs[supply.NewModuleAddress(acc).String()] = !allowedReceivingModAcc[acc]
|
||||
}
|
||||
|
||||
return blacklistedAddrs
|
||||
}
|
||||
|
||||
// GetKey returns the KVStoreKey for the provided store key.
|
||||
//
|
||||
// NOTE: This is solely to be used for testing purposes.
|
||||
func (app *EthermintApp) GetKey(storeKey string) *sdk.KVStoreKey {
|
||||
return app.keys[storeKey]
|
||||
}
|
||||
|
||||
// Codec returns Ethermint's codec.
|
||||
//
|
||||
// NOTE: This is solely to be used for testing purposes as it may be desirable
|
||||
// for modules to register their own custom testing types.
|
||||
func (app *EthermintApp) Codec() *codec.Codec {
|
||||
return app.cdc
|
||||
}
|
||||
|
||||
// GetMaccPerms returns a copy of the module account permissions
|
||||
func GetMaccPerms() map[string][]string {
|
||||
dupMaccPerms := make(map[string][]string)
|
||||
for k, v := range maccPerms {
|
||||
dupMaccPerms[k] = v
|
||||
}
|
||||
return dupMaccPerms
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
|
||||
func TestEthermintAppExport(t *testing.T) {
|
||||
db := dbm.NewMemDB()
|
||||
app := NewEthermintApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, true, 0)
|
||||
app := NewEthermintApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, 0)
|
||||
|
||||
genesisState := ModuleBasics.DefaultGenesis()
|
||||
stateBytes, err := codec.MarshalJSONIndent(app.cdc, genesisState)
|
||||
@ -31,7 +31,7 @@ func TestEthermintAppExport(t *testing.T) {
|
||||
app.Commit()
|
||||
|
||||
// Making a new app object with the db, so that initchain hasn't been called
|
||||
app2 := NewEthermintApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, true, 0)
|
||||
app2 := NewEthermintApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, 0)
|
||||
_, _, err = app2.ExportAppStateAndValidators(false, []string{})
|
||||
require.NoError(t, err, "ExportAppStateAndValidators should not have an error")
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ func (app *EthermintApp) ExportAppStateAndValidators(
|
||||
}
|
||||
|
||||
// Write validators to staking module to be used by TM node
|
||||
validators = staking.WriteValidators(ctx, app.stakingKeeper)
|
||||
validators = staking.WriteValidators(ctx, app.StakingKeeper)
|
||||
return appState, validators, nil
|
||||
}
|
||||
|
||||
@ -61,49 +61,48 @@ func (app *EthermintApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList
|
||||
}
|
||||
|
||||
/* Just to be safe, assert the invariants on current state. */
|
||||
app.crisisKeeper.AssertInvariants(ctx)
|
||||
app.CrisisKeeper.AssertInvariants(ctx)
|
||||
|
||||
/* Handle fee distribution state. */
|
||||
|
||||
// withdraw all validator commission
|
||||
app.stakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) {
|
||||
_, _ = app.distrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator())
|
||||
app.StakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) {
|
||||
_, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator())
|
||||
return false
|
||||
})
|
||||
|
||||
// withdraw all delegator rewards
|
||||
dels := app.stakingKeeper.GetAllDelegations(ctx)
|
||||
dels := app.StakingKeeper.GetAllDelegations(ctx)
|
||||
for _, delegation := range dels {
|
||||
_, _ = app.distrKeeper.WithdrawDelegationRewards(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)
|
||||
_, _ = app.DistrKeeper.WithdrawDelegationRewards(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)
|
||||
}
|
||||
|
||||
// clear validator slash events
|
||||
app.distrKeeper.DeleteAllValidatorSlashEvents(ctx)
|
||||
app.DistrKeeper.DeleteAllValidatorSlashEvents(ctx)
|
||||
|
||||
// clear validator historical rewards
|
||||
app.distrKeeper.DeleteAllValidatorHistoricalRewards(ctx)
|
||||
app.DistrKeeper.DeleteAllValidatorHistoricalRewards(ctx)
|
||||
|
||||
// set context height to zero
|
||||
height := ctx.BlockHeight()
|
||||
ctx = ctx.WithBlockHeight(0)
|
||||
|
||||
// reinitialize all validators
|
||||
app.stakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) {
|
||||
|
||||
app.StakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) {
|
||||
// donate any unwithdrawn outstanding reward fraction tokens to the community pool
|
||||
scraps := app.distrKeeper.GetValidatorOutstandingRewards(ctx, val.GetOperator())
|
||||
feePool := app.distrKeeper.GetFeePool(ctx)
|
||||
scraps := app.DistrKeeper.GetValidatorOutstandingRewards(ctx, val.GetOperator())
|
||||
feePool := app.DistrKeeper.GetFeePool(ctx)
|
||||
feePool.CommunityPool = feePool.CommunityPool.Add(scraps)
|
||||
app.distrKeeper.SetFeePool(ctx, feePool)
|
||||
app.DistrKeeper.SetFeePool(ctx, feePool)
|
||||
|
||||
app.distrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator())
|
||||
app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator())
|
||||
return false
|
||||
})
|
||||
|
||||
// reinitialize all delegations
|
||||
for _, del := range dels {
|
||||
app.distrKeeper.Hooks().BeforeDelegationCreated(ctx, del.DelegatorAddress, del.ValidatorAddress)
|
||||
app.distrKeeper.Hooks().AfterDelegationModified(ctx, del.DelegatorAddress, del.ValidatorAddress)
|
||||
app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, del.DelegatorAddress, del.ValidatorAddress)
|
||||
app.DistrKeeper.Hooks().AfterDelegationModified(ctx, del.DelegatorAddress, del.ValidatorAddress)
|
||||
}
|
||||
|
||||
// reset context height
|
||||
@ -112,20 +111,20 @@ func (app *EthermintApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList
|
||||
/* Handle staking state. */
|
||||
|
||||
// iterate through redelegations, reset creation height
|
||||
app.stakingKeeper.IterateRedelegations(ctx, func(_ int64, red staking.Redelegation) (stop bool) {
|
||||
app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red staking.Redelegation) (stop bool) {
|
||||
for i := range red.Entries {
|
||||
red.Entries[i].CreationHeight = 0
|
||||
}
|
||||
app.stakingKeeper.SetRedelegation(ctx, red)
|
||||
app.StakingKeeper.SetRedelegation(ctx, red)
|
||||
return false
|
||||
})
|
||||
|
||||
// iterate through unbonding delegations, reset creation height
|
||||
app.stakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd staking.UnbondingDelegation) (stop bool) {
|
||||
app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd staking.UnbondingDelegation) (stop bool) {
|
||||
for i := range ubd.Entries {
|
||||
ubd.Entries[i].CreationHeight = 0
|
||||
}
|
||||
app.stakingKeeper.SetUnbondingDelegation(ctx, ubd)
|
||||
app.StakingKeeper.SetUnbondingDelegation(ctx, ubd)
|
||||
return false
|
||||
})
|
||||
|
||||
@ -137,7 +136,7 @@ func (app *EthermintApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList
|
||||
|
||||
for ; iter.Valid(); iter.Next() {
|
||||
addr := sdk.ValAddress(iter.Key()[1:])
|
||||
validator, found := app.stakingKeeper.GetValidator(ctx, addr)
|
||||
validator, found := app.StakingKeeper.GetValidator(ctx, addr)
|
||||
if !found {
|
||||
panic("expected validator, not found")
|
||||
}
|
||||
@ -147,22 +146,22 @@ func (app *EthermintApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList
|
||||
validator.Jailed = true
|
||||
}
|
||||
|
||||
app.stakingKeeper.SetValidator(ctx, validator)
|
||||
app.StakingKeeper.SetValidator(ctx, validator)
|
||||
counter++
|
||||
}
|
||||
|
||||
iter.Close()
|
||||
|
||||
_ = app.stakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||
_ = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||
|
||||
/* Handle slashing state. */
|
||||
|
||||
// reset start height on signing infos
|
||||
app.slashingKeeper.IterateValidatorSigningInfos(
|
||||
app.SlashingKeeper.IterateValidatorSigningInfos(
|
||||
ctx,
|
||||
func(addr sdk.ConsAddress, info slashing.ValidatorSigningInfo) (stop bool) {
|
||||
info.StartHeight = 0
|
||||
app.slashingKeeper.SetValidatorSigningInfo(ctx, addr, info)
|
||||
app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info)
|
||||
return false
|
||||
},
|
||||
)
|
||||
|
34
app/test_helpers.go
Normal file
34
app/test_helpers.go
Normal file
@ -0,0 +1,34 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
)
|
||||
|
||||
// Setup initializes a new EthermintApp. A Nop logger is set in EthermintApp.
|
||||
func Setup(isCheckTx bool) *EthermintApp {
|
||||
db := dbm.NewMemDB()
|
||||
app := NewEthermintApp(log.NewNopLogger(), db, nil, true, 0)
|
||||
if !isCheckTx {
|
||||
// init chain must be called to stop deliverState from being nil
|
||||
genesisState := simapp.NewDefaultGenesisState()
|
||||
stateBytes, err := codec.MarshalJSONIndent(app.Codec(), genesisState)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Initialize the chain
|
||||
app.InitChain(
|
||||
abci.RequestInitChain{
|
||||
Validators: []abci.ValidatorUpdate{},
|
||||
AppStateBytes: stateBytes,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return app
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/params"
|
||||
|
||||
"github.com/cosmos/ethermint/crypto"
|
||||
emint "github.com/cosmos/ethermint/types"
|
||||
evmtypes "github.com/cosmos/ethermint/x/evm/types"
|
||||
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
)
|
||||
|
||||
type testSetup struct {
|
||||
ctx sdk.Context
|
||||
cdc *codec.Codec
|
||||
accKeeper auth.AccountKeeper
|
||||
supplyKeeper types.SupplyKeeper
|
||||
anteHandler sdk.AnteHandler
|
||||
}
|
||||
|
||||
func newTestSetup() testSetup {
|
||||
db := dbm.NewMemDB()
|
||||
authCapKey := sdk.NewKVStoreKey("authCapKey")
|
||||
keySupply := sdk.NewKVStoreKey("keySupply")
|
||||
keyParams := sdk.NewKVStoreKey("params")
|
||||
tkeyParams := sdk.NewTransientStoreKey("transient_params")
|
||||
|
||||
ms := store.NewCommitMultiStore(db)
|
||||
ms.MountStoreWithDB(authCapKey, sdk.StoreTypeIAVL, db)
|
||||
ms.MountStoreWithDB(keySupply, sdk.StoreTypeIAVL, db)
|
||||
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
|
||||
ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeIAVL, db)
|
||||
|
||||
if err := ms.LoadLatestVersion(); err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
}
|
||||
|
||||
cdc := MakeCodec()
|
||||
cdc.RegisterConcrete(&sdk.TestMsg{}, "test/TestMsg", nil)
|
||||
|
||||
// Set params keeper and subspaces
|
||||
paramsKeeper := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace)
|
||||
authSubspace := paramsKeeper.Subspace(auth.DefaultParamspace)
|
||||
|
||||
ctx := sdk.NewContext(
|
||||
ms,
|
||||
abci.Header{ChainID: "3", Time: time.Now().UTC()},
|
||||
true,
|
||||
log.NewNopLogger(),
|
||||
)
|
||||
|
||||
// Add keepers
|
||||
accKeeper := auth.NewAccountKeeper(cdc, authCapKey, authSubspace, auth.ProtoBaseAccount)
|
||||
accKeeper.SetParams(ctx, types.DefaultParams())
|
||||
supplyKeeper := mock.NewDummySupplyKeeper(accKeeper)
|
||||
anteHandler := NewAnteHandler(accKeeper, supplyKeeper)
|
||||
|
||||
return testSetup{
|
||||
ctx: ctx,
|
||||
cdc: cdc,
|
||||
accKeeper: accKeeper,
|
||||
supplyKeeper: supplyKeeper,
|
||||
anteHandler: anteHandler,
|
||||
}
|
||||
}
|
||||
|
||||
func newTestMsg(addrs ...sdk.AccAddress) *sdk.TestMsg {
|
||||
return sdk.NewTestMsg(addrs...)
|
||||
}
|
||||
|
||||
func newTestCoins() sdk.Coins {
|
||||
return sdk.Coins{sdk.NewInt64Coin(emint.DenomDefault, 500000000)}
|
||||
}
|
||||
|
||||
func newTestStdFee() auth.StdFee {
|
||||
return auth.NewStdFee(220000, sdk.NewCoins(sdk.NewInt64Coin(emint.DenomDefault, 150)))
|
||||
}
|
||||
|
||||
// GenerateAddress generates an Ethereum address.
|
||||
func newTestAddrKey() (sdk.AccAddress, tmcrypto.PrivKey) {
|
||||
privkey, _ := crypto.GenerateKey()
|
||||
addr := ethcrypto.PubkeyToAddress(privkey.ToECDSA().PublicKey)
|
||||
|
||||
return sdk.AccAddress(addr.Bytes()), privkey
|
||||
}
|
||||
|
||||
func newTestSDKTx(
|
||||
ctx sdk.Context, msgs []sdk.Msg, privs []tmcrypto.PrivKey,
|
||||
accNums []uint64, seqs []uint64, fee auth.StdFee,
|
||||
) sdk.Tx {
|
||||
|
||||
sigs := make([]auth.StdSignature, len(privs))
|
||||
for i, priv := range privs {
|
||||
signBytes := auth.StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, "")
|
||||
|
||||
sig, err := priv.Sign(signBytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sigs[i] = auth.StdSignature{
|
||||
PubKey: priv.PubKey(),
|
||||
Signature: sig,
|
||||
}
|
||||
}
|
||||
|
||||
return auth.NewStdTx(msgs, fee, sigs, "")
|
||||
}
|
||||
|
||||
func newTestEthTx(ctx sdk.Context, msg *evmtypes.EthereumTxMsg, priv tmcrypto.PrivKey) sdk.Tx {
|
||||
chainID, ok := new(big.Int).SetString(ctx.ChainID(), 10)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("invalid chainID: %s", ctx.ChainID()))
|
||||
}
|
||||
|
||||
privkey, ok := priv.(crypto.PrivKeySecp256k1)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("invalid private key type: %T", priv))
|
||||
}
|
||||
|
||||
msg.Sign(chainID, privkey.ToECDSA())
|
||||
return msg
|
||||
}
|
@ -14,14 +14,15 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/client/input"
|
||||
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||
|
||||
emintcrypto "github.com/cosmos/ethermint/crypto"
|
||||
)
|
||||
|
||||
func exportEthKeyCommand() *cobra.Command {
|
||||
func unsafeExportEthKeyCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "export-eth-key <name>",
|
||||
Short: "Export an Ethereum private key",
|
||||
Long: `Export an Ethereum private key unencrypted to use in dev tooling **UNSAFE**`,
|
||||
Use: "unsafe-export-eth-key [name]",
|
||||
Short: "**UNSAFE** Export an Ethereum private key",
|
||||
Long: `**UNSAFE** Export an Ethereum private key unencrypted to use in dev tooling`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runExportCmd,
|
||||
}
|
||||
@ -29,12 +30,13 @@ func exportEthKeyCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func runExportCmd(cmd *cobra.Command, args []string) error {
|
||||
inBuf := bufio.NewReader(cmd.InOrStdin())
|
||||
|
||||
kb, err := clientkeys.NewKeyringFromHomeFlag(cmd.InOrStdin())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf := bufio.NewReader(cmd.InOrStdin())
|
||||
decryptPassword := ""
|
||||
conf := true
|
||||
keyringBackend := viper.GetString(flags.FlagKeyringBackend)
|
||||
@ -42,11 +44,11 @@ func runExportCmd(cmd *cobra.Command, args []string) error {
|
||||
case flags.KeyringBackendFile:
|
||||
decryptPassword, err = input.GetPassword(
|
||||
"**WARNING this is an unsafe way to export your unencrypted private key**\nEnter key password:",
|
||||
buf)
|
||||
inBuf)
|
||||
case flags.KeyringBackendOS:
|
||||
conf, err = input.GetConfirmation(
|
||||
"**WARNING** this is an unsafe way to export your unencrypted private key, are you sure?",
|
||||
buf)
|
||||
inBuf)
|
||||
}
|
||||
if err != nil || !conf {
|
||||
return err
|
||||
|
@ -4,12 +4,13 @@ import (
|
||||
"bufio"
|
||||
"io"
|
||||
|
||||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
|
||||
emintCrypto "github.com/cosmos/ethermint/crypto"
|
||||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
@ -46,7 +47,7 @@ func keyCommands() *cobra.Command {
|
||||
clientkeys.ParseKeyStringCommand(),
|
||||
clientkeys.MigrateCommand(),
|
||||
flags.LineBreak,
|
||||
exportEthKeyCommand(),
|
||||
unsafeExportEthKeyCommand(),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
@ -1,34 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
emintapp "github.com/cosmos/ethermint/app"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/cosmos/ethermint/app"
|
||||
emintcrypto "github.com/cosmos/ethermint/crypto"
|
||||
"github.com/cosmos/ethermint/rpc"
|
||||
|
||||
"github.com/tendermint/go-amino"
|
||||
tmamino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
||||
"github.com/tendermint/tendermint/libs/cli"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||
sdkrpc "github.com/cosmos/cosmos-sdk/client/rpc"
|
||||
clientrpc "github.com/cosmos/cosmos-sdk/client/rpc"
|
||||
cryptokeys "github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
tmamino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
||||
"github.com/tendermint/tendermint/libs/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Configure cobra to sort commands
|
||||
cobra.EnableCommandSorting = false
|
||||
|
||||
cdc := emintapp.MakeCodec()
|
||||
cdc := app.MakeCodec()
|
||||
|
||||
tmamino.RegisterKeyType(emintcrypto.PubKeySecp256k1{}, emintcrypto.PubKeyAminoName)
|
||||
tmamino.RegisterKeyType(emintcrypto.PrivKeySecp256k1{}, emintcrypto.PrivKeyAminoName)
|
||||
@ -45,7 +49,7 @@ func main() {
|
||||
|
||||
rootCmd := &cobra.Command{
|
||||
Use: "emintcli",
|
||||
Short: "Ethermint Client",
|
||||
Short: "Command line interface for interacting with emintd",
|
||||
}
|
||||
|
||||
// Add --chain-id to persistent flags and mark it required
|
||||
@ -56,20 +60,24 @@ func main() {
|
||||
|
||||
// Construct Root Command
|
||||
rootCmd.AddCommand(
|
||||
sdkrpc.StatusCommand(),
|
||||
client.ConfigCmd(emintapp.DefaultCLIHome),
|
||||
clientrpc.StatusCommand(),
|
||||
client.ConfigCmd(app.DefaultCLIHome),
|
||||
queryCmd(cdc),
|
||||
txCmd(cdc),
|
||||
rpc.EmintServeCmd(cdc),
|
||||
client.LineBreak,
|
||||
keyCommands(),
|
||||
client.LineBreak,
|
||||
version.Cmd,
|
||||
client.NewCompletionCmd(rootCmd, true),
|
||||
)
|
||||
|
||||
executor := cli.PrepareMainCmd(rootCmd, "EM", emintapp.DefaultCLIHome)
|
||||
// Add flags and prefix all env exposed with EM
|
||||
executor := cli.PrepareMainCmd(rootCmd, "EM", app.DefaultCLIHome)
|
||||
|
||||
err := executor.Execute()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panic(fmt.Errorf("failed executing CLI command: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,7 +97,7 @@ func queryCmd(cdc *amino.Codec) *cobra.Command {
|
||||
)
|
||||
|
||||
// add modules' query commands
|
||||
emintapp.ModuleBasics.AddQueryCommands(queryCmd, cdc)
|
||||
app.ModuleBasics.AddQueryCommands(queryCmd, cdc)
|
||||
|
||||
return queryCmd
|
||||
}
|
||||
@ -104,14 +112,27 @@ func txCmd(cdc *amino.Codec) *cobra.Command {
|
||||
bankcmd.SendTxCmd(cdc),
|
||||
client.LineBreak,
|
||||
authcmd.GetSignCommand(cdc),
|
||||
authcmd.GetMultiSignCommand(cdc),
|
||||
client.LineBreak,
|
||||
authcmd.GetBroadcastCommand(cdc),
|
||||
authcmd.GetEncodeCommand(cdc),
|
||||
authcmd.GetDecodeCommand(cdc),
|
||||
client.LineBreak,
|
||||
)
|
||||
|
||||
// add modules' tx commands
|
||||
emintapp.ModuleBasics.AddTxCommands(txCmd, cdc)
|
||||
app.ModuleBasics.AddTxCommands(txCmd, cdc)
|
||||
|
||||
// remove auth and bank commands as they're mounted under the root tx command
|
||||
var cmdsToRemove []*cobra.Command
|
||||
|
||||
for _, cmd := range txCmd.Commands() {
|
||||
if cmd.Use == auth.ModuleName || cmd.Use == bank.ModuleName {
|
||||
cmdsToRemove = append(cmdsToRemove, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
txCmd.RemoveCommand(cmdsToRemove...)
|
||||
|
||||
return txCmd
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package genaccounts
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@ -19,6 +19,8 @@ import (
|
||||
authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting"
|
||||
"github.com/cosmos/cosmos-sdk/x/genutil"
|
||||
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
|
||||
ethermint "github.com/cosmos/ethermint/types"
|
||||
)
|
||||
|
||||
@ -96,7 +98,10 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa
|
||||
return errors.New("invalid vesting parameters; must supply start and end time or end time")
|
||||
}
|
||||
} else {
|
||||
genAccount = ethermint.Account{BaseAccount: baseAccount}
|
||||
genAccount = ethermint.Account{
|
||||
BaseAccount: baseAccount,
|
||||
CodeHash: ethcrypto.Keccak256(nil),
|
||||
}
|
||||
}
|
||||
|
||||
if err := genAccount.Validate(); err != nil {
|
@ -23,17 +23,20 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/cosmos/ethermint/app"
|
||||
"github.com/cosmos/ethermint/client/genaccounts"
|
||||
emintcrypto "github.com/cosmos/ethermint/crypto"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmamino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
||||
"github.com/tendermint/tendermint/libs/cli"
|
||||
tmlog "github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
)
|
||||
|
||||
const flagInvCheckPeriod = "inv-check-period"
|
||||
|
||||
var invCheckPeriod uint
|
||||
|
||||
func main() {
|
||||
cobra.EnableCommandSorting = false
|
||||
|
||||
@ -65,12 +68,14 @@ func main() {
|
||||
withChainIDValidation(genutilcli.InitCmd(ctx, cdc, app.ModuleBasics, app.DefaultNodeHome)),
|
||||
genutilcli.CollectGenTxsCmd(ctx, cdc, auth.GenesisAccountIterator{}, app.DefaultNodeHome),
|
||||
genutilcli.GenTxCmd(
|
||||
ctx, cdc, app.ModuleBasics, staking.AppModuleBasic{}, auth.GenesisAccountIterator{}, app.DefaultNodeHome, app.DefaultCLIHome,
|
||||
ctx, cdc, app.ModuleBasics, staking.AppModuleBasic{}, auth.GenesisAccountIterator{},
|
||||
app.DefaultNodeHome, app.DefaultCLIHome,
|
||||
),
|
||||
genutilcli.ValidateGenesisCmd(ctx, cdc, app.ModuleBasics),
|
||||
|
||||
// AddGenesisAccountCmd allows users to add accounts to the genesis file
|
||||
genaccounts.AddGenesisAccountCmd(ctx, cdc, app.DefaultNodeHome, app.DefaultCLIHome),
|
||||
AddGenesisAccountCmd(ctx, cdc, app.DefaultNodeHome, app.DefaultCLIHome),
|
||||
client.NewCompletionCmd(rootCmd, true),
|
||||
)
|
||||
|
||||
// Tendermint node base commands
|
||||
@ -78,23 +83,25 @@ func main() {
|
||||
|
||||
// prepare and add flags
|
||||
executor := cli.PrepareBaseCmd(rootCmd, "EM", app.DefaultNodeHome)
|
||||
rootCmd.PersistentFlags().UintVar(&invCheckPeriod, flagInvCheckPeriod,
|
||||
0, "Assert registered invariants every N blocks")
|
||||
err := executor.Execute()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func newApp(logger tmlog.Logger, db dbm.DB, traceStore io.Writer) abci.Application {
|
||||
return app.NewEthermintApp(logger, db, true, 0,
|
||||
func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application {
|
||||
return app.NewEthermintApp(logger, db, traceStore, true, 0,
|
||||
baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning"))))
|
||||
}
|
||||
|
||||
func exportAppStateAndTMValidators(
|
||||
logger tmlog.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailWhiteList []string,
|
||||
logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailWhiteList []string,
|
||||
) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||
|
||||
if height != -1 {
|
||||
emintApp := app.NewEthermintApp(logger, db, true, 0)
|
||||
emintApp := app.NewEthermintApp(logger, db, traceStore, true, 0)
|
||||
err := emintApp.LoadHeight(height)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@ -102,7 +109,7 @@ func exportAppStateAndTMValidators(
|
||||
return emintApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
|
||||
}
|
||||
|
||||
emintApp := app.NewEthermintApp(logger, db, true, 0)
|
||||
emintApp := app.NewEthermintApp(logger, db, traceStore, true, 0)
|
||||
|
||||
return emintApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
|
||||
}
|
||||
@ -119,8 +126,7 @@ func withChainIDValidation(baseCmd *cobra.Command) *cobra.Command {
|
||||
// Verify that the chain-id entered is a base 10 integer
|
||||
_, ok := new(big.Int).SetString(chainIDFlag, 10)
|
||||
if !ok {
|
||||
return fmt.Errorf(
|
||||
fmt.Sprintf("invalid chainID: %s, must be base-10 integer format", chainIDFlag))
|
||||
return fmt.Errorf("invalid chainID: %s, must be base-10 integer format", chainIDFlag)
|
||||
}
|
||||
|
||||
return baseRunE(cmd, args)
|
||||
|
2
go.mod
2
go.mod
@ -53,3 +53,5 @@ require (
|
||||
gopkg.in/urfave/cli.v1 v1.20.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
)
|
||||
|
||||
replace github.com/cosmos/cosmos-sdk => github.com/cosmos/cosmos-sdk v0.34.4-0.20191213112149-d7b0f4b9b4fb
|
||||
|
2
go.sum
2
go.sum
@ -314,8 +314,6 @@ github.com/spf13/cobra v0.0.1 h1:zZh3X5aZbdnoj+4XkaBxKfhO4ot82icYdhhREIAXIj8=
|
||||
github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs=
|
||||
github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v0.0.7 h1:FfTH+vuMXOas8jmfb5/M7dzEYx7LpcLb7a0LPe34uOU=
|
||||
github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
|
@ -44,14 +44,11 @@ var (
|
||||
flagBlockchain string
|
||||
flagCPUProfile string
|
||||
|
||||
// miner501 = ethcmn.HexToAddress("0x35e8e5dC5FBd97c5b421A80B596C030a2Be2A04D")
|
||||
genInvestor = ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0")
|
||||
|
||||
// paramsKey = sdk.NewKVStoreKey("params")
|
||||
// tParamsKey = sdk.NewTransientStoreKey("transient_params")
|
||||
accKey = sdk.NewKVStoreKey("acc")
|
||||
storageKey = sdk.NewKVStoreKey(evmtypes.EvmStoreKey)
|
||||
codeKey = sdk.NewKVStoreKey(evmtypes.EvmCodeKey)
|
||||
storageKey = sdk.NewKVStoreKey(evmtypes.StoreKey)
|
||||
codeKey = sdk.NewKVStoreKey(evmtypes.CodeKey)
|
||||
|
||||
logger = tmlog.NewNopLogger()
|
||||
|
||||
@ -104,7 +101,7 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, ak auth.Accoun
|
||||
ms := cms.CacheMultiStore()
|
||||
ctx := sdk.NewContext(ms, abci.Header{}, false, logger)
|
||||
|
||||
stateDB := evmtypes.NewCommitStateDB(ctx, ak, storageKey, codeKey)
|
||||
stateDB := evmtypes.NewCommitStateDB(ctx, codeKey, storageKey, ak)
|
||||
|
||||
// sort the addresses and insertion of key/value pairs matters
|
||||
genAddrs := make([]string, len(genBlock.Alloc))
|
||||
@ -270,7 +267,7 @@ func TestImportBlocks(t *testing.T) {
|
||||
}
|
||||
|
||||
func createStateDB(ctx sdk.Context, ak auth.AccountKeeper) *evmtypes.CommitStateDB {
|
||||
stateDB := evmtypes.NewCommitStateDB(ctx, ak, storageKey, codeKey)
|
||||
stateDB := evmtypes.NewCommitStateDB(ctx, codeKey, storageKey, ak)
|
||||
return stateDB
|
||||
}
|
||||
|
||||
@ -346,12 +343,18 @@ func applyTransaction(config *ethparams.ChainConfig, bc ethcore.ChainContext, au
|
||||
return nil, 0, err
|
||||
}
|
||||
// Update the state with pending changes
|
||||
var root []byte
|
||||
var intRoot ethcmn.Hash
|
||||
if config.IsByzantium(header.Number) {
|
||||
statedb.Finalise(true)
|
||||
err = statedb.Finalise(true)
|
||||
} else {
|
||||
root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
|
||||
intRoot, err = statedb.IntermediateRoot(config.IsEIP158(header.Number))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, gas, err
|
||||
}
|
||||
|
||||
root := intRoot.Bytes()
|
||||
*usedGas += gas
|
||||
|
||||
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
|
||||
@ -364,7 +367,7 @@ func applyTransaction(config *ethparams.ChainConfig, bc ethcore.ChainContext, au
|
||||
receipt.ContractAddress = ethcrypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
|
||||
}
|
||||
// Set the receipt logs and create a bloom for filtering
|
||||
receipt.Logs = statedb.GetLogs(tx.Hash())
|
||||
receipt.Logs, err = statedb.GetLogs(tx.Hash())
|
||||
receipt.Bloom = ethtypes.CreateBloom(ethtypes.Receipts{receipt})
|
||||
receipt.BlockHash = statedb.BlockHash()
|
||||
receipt.BlockNumber = header.Number
|
||||
|
149
rpc/eth_api.go
149
rpc/eth_api.go
@ -2,13 +2,15 @@ package rpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
emintcrypto "github.com/cosmos/ethermint/crypto"
|
||||
params "github.com/cosmos/ethermint/rpc/args"
|
||||
emint "github.com/cosmos/ethermint/types"
|
||||
@ -29,10 +31,10 @@ import (
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authutils "github.com/cosmos/cosmos-sdk/x/auth/client/utils"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// PublicEthAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec.
|
||||
@ -210,7 +212,7 @@ func (e *PublicEthAPI) getBlockTransactionCountByNumber(number int64) *hexutil.U
|
||||
return nil
|
||||
}
|
||||
|
||||
n := hexutil.Uint(block.Block.NumTxs)
|
||||
n := hexutil.Uint(len(block.Block.Txs))
|
||||
return &n
|
||||
}
|
||||
|
||||
@ -277,8 +279,7 @@ func (e *PublicEthAPI) SendTransaction(args params.SendTxArgs) (common.Hash, err
|
||||
// parse the chainID from a string to a base-10 integer
|
||||
intChainID, ok := new(big.Int).SetString(chainID, 10)
|
||||
if !ok {
|
||||
return common.Hash{}, fmt.Errorf(
|
||||
fmt.Sprintf("invalid chainID: %s, must be integer format", chainID))
|
||||
return common.Hash{}, fmt.Errorf("invalid chainID: %s, must be integer format", chainID)
|
||||
}
|
||||
|
||||
// Sign transaction
|
||||
@ -306,7 +307,7 @@ func (e *PublicEthAPI) SendTransaction(args params.SendTxArgs) (common.Hash, err
|
||||
|
||||
// SendRawTransaction send a raw Ethereum transaction.
|
||||
func (e *PublicEthAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error) {
|
||||
tx := new(types.EthereumTxMsg)
|
||||
tx := new(types.MsgEthereumTx)
|
||||
|
||||
// RLP decode raw transaction bytes
|
||||
if err := rlp.DecodeBytes(data, tx); err != nil {
|
||||
@ -351,9 +352,12 @@ func (e *PublicEthAPI) Call(args CallArgs, blockNr rpc.BlockNumber, overrides *m
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
_, _, ret, err := types.DecodeReturnData(result.Data)
|
||||
data, err := types.DecodeResultData(result.Data)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
return (hexutil.Bytes)(ret), err
|
||||
return (hexutil.Bytes)(data.Ret), nil
|
||||
}
|
||||
|
||||
// account indicates the overriding fields of account during the execution of
|
||||
@ -370,8 +374,9 @@ type account struct {
|
||||
StateDiff *map[common.Hash]common.Hash `json:"stateDiff"`
|
||||
}
|
||||
|
||||
// DoCall performs a simulated call operation through the evm
|
||||
func (e *PublicEthAPI) doCall(args CallArgs, blockNr rpc.BlockNumber, globalGasCap *big.Int) (sdk.Result, error) {
|
||||
// DoCall performs a simulated call operation through the evm. It returns the
|
||||
// estimated gas used on the operation or an error if fails.
|
||||
func (e *PublicEthAPI) doCall(args CallArgs, blockNr rpc.BlockNumber, globalGasCap *big.Int) (*sdk.Result, error) {
|
||||
// Set height for historical queries
|
||||
ctx := e.cliCtx
|
||||
if blockNr.Int64() != 0 {
|
||||
@ -425,7 +430,7 @@ func (e *PublicEthAPI) doCall(args CallArgs, blockNr rpc.BlockNumber, globalGasC
|
||||
}
|
||||
|
||||
// Create new call message
|
||||
msg := types.NewEmintMsg(0, &toAddr, sdk.NewIntFromBigInt(value), gas,
|
||||
msg := types.NewMsgEthermint(0, &toAddr, sdk.NewIntFromBigInt(value), gas,
|
||||
sdk.NewIntFromBigInt(gasPrice), data, sdk.AccAddress(addr.Bytes()))
|
||||
|
||||
// Generate tx to be used to simulate (signature isn't needed)
|
||||
@ -436,24 +441,26 @@ func (e *PublicEthAPI) doCall(args CallArgs, blockNr rpc.BlockNumber, globalGasC
|
||||
txEncoder := authutils.GetTxEncoder(ctx.Codec)
|
||||
txBytes, err := txEncoder(tx)
|
||||
if err != nil {
|
||||
return sdk.Result{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Transaction simulation through query
|
||||
res, _, err := ctx.QueryWithData("app/simulate", txBytes)
|
||||
if err != nil {
|
||||
return sdk.Result{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var simResult sdk.Result
|
||||
if err = e.cliCtx.Codec.UnmarshalBinaryLengthPrefixed(res, &simResult); err != nil {
|
||||
return sdk.Result{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return simResult, nil
|
||||
return &simResult, nil
|
||||
}
|
||||
|
||||
// EstimateGas estimates gas usage for the given smart contract call.
|
||||
// EstimateGas returns an estimate of gas usage for the given smart contract call.
|
||||
// It adds 1,000 gas to the returned value instead of using the gas adjustment
|
||||
// param from the SDK.
|
||||
func (e *PublicEthAPI) EstimateGas(args CallArgs) (hexutil.Uint64, error) {
|
||||
result, err := e.doCall(args, 0, big.NewInt(emint.DefaultRPCGasLimit))
|
||||
if err != nil {
|
||||
@ -482,31 +489,37 @@ func (e *PublicEthAPI) GetBlockByNumber(blockNum BlockNumber, fullTx bool) (map[
|
||||
return e.getEthBlockByNumber(value, fullTx)
|
||||
}
|
||||
|
||||
func (e *PublicEthAPI) getEthBlockByNumber(value int64, fullTx bool) (map[string]interface{}, error) {
|
||||
func (e *PublicEthAPI) getEthBlockByNumber(height int64, fullTx bool) (map[string]interface{}, error) {
|
||||
// Remove this check when 0 query is fixed ref: (https://github.com/tendermint/tendermint/issues/4014)
|
||||
var blkNumPtr *int64
|
||||
if value != 0 {
|
||||
blkNumPtr = &value
|
||||
if height != 0 {
|
||||
blkNumPtr = &height
|
||||
}
|
||||
|
||||
block, err := e.cliCtx.Client.Block(blkNumPtr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
header := block.BlockMeta.Header
|
||||
header := block.Block.Header
|
||||
|
||||
gasLimit, err := e.getGasLimit()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var gasUsed *big.Int
|
||||
var transactions []interface{}
|
||||
var (
|
||||
gasUsed *big.Int
|
||||
transactions []interface{}
|
||||
)
|
||||
|
||||
if fullTx {
|
||||
// Populate full transaction data
|
||||
transactions, gasUsed = convertTransactionsToRPC(e.cliCtx, block.Block.Txs,
|
||||
common.BytesToHash(header.Hash()), uint64(header.Height))
|
||||
transactions, gasUsed, err = convertTransactionsToRPC(
|
||||
e.cliCtx, block.Block.Txs, common.BytesToHash(header.Hash()), uint64(header.Height),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// TODO: Gas used not saved and cannot be calculated by hashes
|
||||
// Return slice of transaction hashes
|
||||
@ -553,19 +566,24 @@ func formatBlock(
|
||||
}
|
||||
}
|
||||
|
||||
func convertTransactionsToRPC(cliCtx context.CLIContext, txs []tmtypes.Tx, blockHash common.Hash, height uint64) ([]interface{}, *big.Int) {
|
||||
func convertTransactionsToRPC(cliCtx context.CLIContext, txs []tmtypes.Tx, blockHash common.Hash, height uint64) ([]interface{}, *big.Int, error) {
|
||||
transactions := make([]interface{}, len(txs))
|
||||
gasUsed := big.NewInt(0)
|
||||
|
||||
for i, tx := range txs {
|
||||
ethTx, err := bytesToEthTx(cliCtx, tx)
|
||||
if err != nil {
|
||||
continue
|
||||
return nil, nil, err
|
||||
}
|
||||
// TODO: Remove gas usage calculation if saving gasUsed per block
|
||||
gasUsed.Add(gasUsed, ethTx.Fee())
|
||||
transactions[i] = newRPCTransaction(ethTx, blockHash, &height, uint64(i))
|
||||
transactions[i], err = newRPCTransaction(*ethTx, blockHash, &height, uint64(i))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
return transactions, gasUsed
|
||||
|
||||
return transactions, gasUsed, nil
|
||||
}
|
||||
|
||||
// Transaction represents a transaction returned to RPC clients.
|
||||
@ -586,23 +604,30 @@ type Transaction struct {
|
||||
S *hexutil.Big `json:"s"`
|
||||
}
|
||||
|
||||
func bytesToEthTx(cliCtx context.CLIContext, bz []byte) (*types.EthereumTxMsg, error) {
|
||||
func bytesToEthTx(cliCtx context.CLIContext, bz []byte) (*types.MsgEthereumTx, error) {
|
||||
var stdTx sdk.Tx
|
||||
err := cliCtx.Codec.UnmarshalBinaryLengthPrefixed(bz, &stdTx)
|
||||
ethTx, ok := stdTx.(*types.EthereumTxMsg)
|
||||
if !ok || err != nil {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ethTx, ok := stdTx.(types.MsgEthereumTx)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid transaction type, must be an amino encoded Ethereum transaction")
|
||||
}
|
||||
return ethTx, nil
|
||||
return ðTx, nil
|
||||
}
|
||||
|
||||
// newRPCTransaction returns a transaction that will serialize to the RPC
|
||||
// representation, with the given location metadata set (if available).
|
||||
func newRPCTransaction(tx *types.EthereumTxMsg, blockHash common.Hash, blockNumber *uint64, index uint64) *Transaction {
|
||||
func newRPCTransaction(tx types.MsgEthereumTx, blockHash common.Hash, blockNumber *uint64, index uint64) (*Transaction, error) {
|
||||
// Verify signature and retrieve sender address
|
||||
from, _ := tx.VerifySig(tx.ChainID())
|
||||
from, err := tx.VerifySig(tx.ChainID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := &Transaction{
|
||||
result := Transaction{
|
||||
From: from,
|
||||
Gas: hexutil.Uint64(tx.Data.GasLimit),
|
||||
GasPrice: (*hexutil.Big)(tx.Data.Price),
|
||||
@ -615,12 +640,14 @@ func newRPCTransaction(tx *types.EthereumTxMsg, blockHash common.Hash, blockNumb
|
||||
R: (*hexutil.Big)(tx.Data.R),
|
||||
S: (*hexutil.Big)(tx.Data.S),
|
||||
}
|
||||
|
||||
if blockHash != (common.Hash{}) {
|
||||
result.BlockHash = &blockHash
|
||||
result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(*blockNumber))
|
||||
result.TransactionIndex = (*hexutil.Uint64)(&index)
|
||||
}
|
||||
return result
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// GetTransactionByHash returns the transaction identified by hash.
|
||||
@ -636,7 +663,7 @@ func (e *PublicEthAPI) GetTransactionByHash(hash common.Hash) (*Transaction, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockHash := common.BytesToHash(block.BlockMeta.Header.Hash())
|
||||
blockHash := common.BytesToHash(block.Block.Header.Hash())
|
||||
|
||||
ethTx, err := bytesToEthTx(e.cliCtx, tx.Tx)
|
||||
if err != nil {
|
||||
@ -644,7 +671,7 @@ func (e *PublicEthAPI) GetTransactionByHash(hash common.Hash) (*Transaction, err
|
||||
}
|
||||
|
||||
height := uint64(tx.Height)
|
||||
return newRPCTransaction(ethTx, blockHash, &height, uint64(tx.Index)), nil
|
||||
return newRPCTransaction(*ethTx, blockHash, &height, uint64(tx.Index))
|
||||
}
|
||||
|
||||
// GetTransactionByBlockHashAndIndex returns the transaction identified by hash and index.
|
||||
@ -670,7 +697,7 @@ func (e *PublicEthAPI) getTransactionByBlockNumberAndIndex(number int64, idx hex
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
header := block.BlockMeta.Header
|
||||
header := block.Block.Header
|
||||
|
||||
txs := block.Block.Txs
|
||||
if uint64(idx) >= uint64(len(txs)) {
|
||||
@ -682,8 +709,7 @@ func (e *PublicEthAPI) getTransactionByBlockNumberAndIndex(number int64, idx hex
|
||||
}
|
||||
|
||||
height := uint64(header.Height)
|
||||
transaction := newRPCTransaction(ethTx, common.BytesToHash(header.Hash()), &height, uint64(idx))
|
||||
return transaction, nil
|
||||
return newRPCTransaction(*ethTx, common.BytesToHash(header.Hash()), &height, uint64(idx))
|
||||
}
|
||||
|
||||
// GetTransactionReceipt returns the transaction receipt identified by hash.
|
||||
@ -699,7 +725,7 @@ func (e *PublicEthAPI) GetTransactionReceipt(hash common.Hash) (map[string]inter
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockHash := common.BytesToHash(block.BlockMeta.Header.Hash())
|
||||
blockHash := common.BytesToHash(block.Block.Header.Hash())
|
||||
|
||||
// Convert tx bytes to eth transaction
|
||||
ethTx, err := bytesToEthTx(e.cliCtx, tx.Tx)
|
||||
@ -726,7 +752,10 @@ func (e *PublicEthAPI) GetTransactionReceipt(hash common.Hash) (map[string]inter
|
||||
e.cliCtx.Codec.MustUnmarshalJSON(res, &logs)
|
||||
|
||||
txData := tx.TxResult.GetData()
|
||||
contractAddress, bloomFilter, _, _ := types.DecodeReturnData(txData)
|
||||
data, err := types.DecodeResultData(txData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"blockHash": blockHash,
|
||||
@ -739,12 +768,12 @@ func (e *PublicEthAPI) GetTransactionReceipt(hash common.Hash) (map[string]inter
|
||||
"cumulativeGasUsed": nil, // ignore until needed
|
||||
"contractAddress": nil,
|
||||
"logs": logs.Logs,
|
||||
"logsBloom": bloomFilter,
|
||||
"logsBloom": data.Bloom,
|
||||
"status": status,
|
||||
}
|
||||
|
||||
if contractAddress != (common.Address{}) {
|
||||
fields["contractAddress"] = contractAddress
|
||||
if data.Address != (common.Address{}) {
|
||||
fields["contractAddress"] = data.Address
|
||||
}
|
||||
|
||||
return fields, nil
|
||||
@ -766,7 +795,11 @@ func (e *PublicEthAPI) PendingTransactions() ([]*Transaction, error) {
|
||||
}
|
||||
|
||||
// * Should check signer and reference against accounts the node manages in future
|
||||
rpcTx := newRPCTransaction(ethTx, common.Hash{}, nil, 0)
|
||||
rpcTx, err := newRPCTransaction(*ethTx, common.Hash{}, nil, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
transactions = append(transactions, rpcTx)
|
||||
}
|
||||
|
||||
@ -871,13 +904,14 @@ func (e *PublicEthAPI) getGasLimit() (int64, error) {
|
||||
}
|
||||
|
||||
// generateFromArgs populates tx message with args (used in RPC API)
|
||||
func (e *PublicEthAPI) generateFromArgs(args params.SendTxArgs) (msg *types.EthereumTxMsg, err error) {
|
||||
var nonce uint64
|
||||
|
||||
var gasLimit uint64
|
||||
func (e *PublicEthAPI) generateFromArgs(args params.SendTxArgs) (*types.MsgEthereumTx, error) {
|
||||
var (
|
||||
nonce uint64
|
||||
gasLimit uint64
|
||||
err error
|
||||
)
|
||||
|
||||
amount := (*big.Int)(args.Value)
|
||||
|
||||
gasPrice := (*big.Int)(args.GasPrice)
|
||||
|
||||
if args.GasPrice == nil {
|
||||
@ -899,7 +933,7 @@ func (e *PublicEthAPI) generateFromArgs(args params.SendTxArgs) (msg *types.Ethe
|
||||
}
|
||||
|
||||
if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) {
|
||||
return nil, fmt.Errorf(`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`)
|
||||
return nil, errors.New(`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`)
|
||||
}
|
||||
|
||||
// Sets input to either Input or Data, if both are set and not equal error above returns
|
||||
@ -916,6 +950,7 @@ func (e *PublicEthAPI) generateFromArgs(args params.SendTxArgs) (msg *types.Ethe
|
||||
return nil, fmt.Errorf("contract creation without any data provided")
|
||||
}
|
||||
}
|
||||
|
||||
if args.Gas == nil {
|
||||
callArgs := CallArgs{
|
||||
From: &args.From,
|
||||
@ -925,11 +960,15 @@ func (e *PublicEthAPI) generateFromArgs(args params.SendTxArgs) (msg *types.Ethe
|
||||
Value: args.Value,
|
||||
Data: args.Data,
|
||||
}
|
||||
g, _ := e.EstimateGas(callArgs)
|
||||
g, err := e.EstimateGas(callArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gasLimit = uint64(g)
|
||||
} else {
|
||||
gasLimit = (uint64)(*args.Gas)
|
||||
}
|
||||
msg := types.NewMsgEthereumTx(nonce, args.To, amount, gasLimit, gasPrice, input)
|
||||
|
||||
return types.NewEthereumTxMsg(nonce, args.To, amount, gasLimit, gasPrice, input), nil
|
||||
return &msg, nil
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
|
||||
"github.com/cosmos/ethermint/version"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -92,51 +93,34 @@ func TestEth_protocolVersion(t *testing.T) {
|
||||
expectedRes := hexutil.Uint(version.ProtocolVersion)
|
||||
|
||||
rpcRes, err := call("eth_protocolVersion", []string{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
var res hexutil.Uint
|
||||
err = res.UnmarshalJSON(rpcRes.Result)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Logf("Got protocol version: %s\n", res.String())
|
||||
|
||||
if res != expectedRes {
|
||||
t.Fatalf("expected: %s got: %s\n", expectedRes.String(), rpcRes.Result)
|
||||
}
|
||||
require.Equal(t, expectedRes, res, "expected: %s got: %s\n", expectedRes.String(), rpcRes.Result)
|
||||
}
|
||||
|
||||
func TestEth_blockNumber(t *testing.T) {
|
||||
rpcRes, err := call("eth_blockNumber", []string{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
var res hexutil.Uint64
|
||||
err = res.UnmarshalJSON(rpcRes.Result)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Logf("Got block number: %s\n", res.String())
|
||||
}
|
||||
|
||||
func TestEth_GetBalance(t *testing.T) {
|
||||
rpcRes, err := call("eth_getBalance", []string{addrA, "0x0"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
var res hexutil.Big
|
||||
err = res.UnmarshalJSON(rpcRes.Result)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Logf("Got balance %s for %s\n", res.String(), addrA)
|
||||
|
||||
@ -149,40 +133,27 @@ func TestEth_GetBalance(t *testing.T) {
|
||||
func TestEth_GetStorageAt(t *testing.T) {
|
||||
expectedRes := hexutil.Bytes{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
rpcRes, err := call("eth_getStorageAt", []string{addrA, string(addrAStoreKey), "0x0"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
var storage hexutil.Bytes
|
||||
err = storage.UnmarshalJSON(rpcRes.Result)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Logf("Got value [%X] for %s with key %X\n", storage, addrA, addrAStoreKey)
|
||||
|
||||
if !bytes.Equal(storage, expectedRes) {
|
||||
t.Errorf("expected: %d (%d bytes) got: %d (%d bytes)", expectedRes, len(expectedRes), storage, len(storage))
|
||||
}
|
||||
require.True(t, bytes.Equal(storage, expectedRes), "expected: %d (%d bytes) got: %d (%d bytes)", expectedRes, len(expectedRes), storage, len(storage))
|
||||
}
|
||||
|
||||
func TestEth_GetCode(t *testing.T) {
|
||||
expectedRes := hexutil.Bytes{}
|
||||
rpcRes, err := call("eth_getCode", []string{addrA, "0x0"})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
var code hexutil.Bytes
|
||||
err = code.UnmarshalJSON(rpcRes.Result)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Logf("Got code [%X] for %s\n", code, addrA)
|
||||
if !bytes.Equal(expectedRes, code) {
|
||||
t.Errorf("expected: %X got: %X", expectedRes, code)
|
||||
}
|
||||
require.True(t, bytes.Equal(expectedRes, code), "expected: %X got: %X", expectedRes, code)
|
||||
}
|
||||
|
45
x/evm/abci.go
Normal file
45
x/evm/abci.go
Normal file
@ -0,0 +1,45 @@
|
||||
package evm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// BeginBlock sets the Bloom and Hash mappings and resets the Bloom filter and
|
||||
// the transaction count to 0.
|
||||
func BeginBlock(k Keeper, ctx sdk.Context, req abci.RequestBeginBlock) {
|
||||
// Consider removing this when using evm as module without web3 API
|
||||
bloom := ethtypes.BytesToBloom(k.Bloom.Bytes())
|
||||
err := k.SetBlockBloomMapping(ctx, bloom, req.Header.GetHeight()-1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
k.SetBlockHashMapping(ctx, req.Header.LastBlockId.GetHash(), req.Header.GetHeight()-1)
|
||||
k.Bloom = big.NewInt(0)
|
||||
k.TxCount = 0
|
||||
}
|
||||
|
||||
// EndBlock updates the accounts and commits states objects to the KV Store
|
||||
func EndBlock(k Keeper, ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
|
||||
// Gas costs are handled within msg handler so costs should be ignored
|
||||
ctx = ctx.WithBlockGasMeter(sdk.NewInfiniteGasMeter())
|
||||
|
||||
// Update account balances before committing other parts of state
|
||||
k.CommitStateDB.UpdateAccounts()
|
||||
|
||||
// Commit state objects to KV store
|
||||
_, err := k.CommitStateDB.WithContext(ctx).Commit(true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Clear accounts cache after account data has been committed
|
||||
k.CommitStateDB.ClearStateObjects()
|
||||
|
||||
return []abci.ValidatorUpdate{}
|
||||
}
|
@ -5,7 +5,13 @@ import (
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
)
|
||||
|
||||
// nolint
|
||||
const (
|
||||
ModuleName = types.ModuleName
|
||||
StoreKey = types.StoreKey
|
||||
CodeKey = types.StoreKey
|
||||
BlockKey = types.BlockKey
|
||||
RouterKey = types.RouterKey
|
||||
QueryProtocolVersion = types.QueryProtocolVersion
|
||||
QueryBalance = types.QueryBalance
|
||||
QueryBlockNumber = types.QueryBlockNumber
|
||||
@ -19,10 +25,13 @@ const (
|
||||
QueryAccount = types.QueryAccount
|
||||
)
|
||||
|
||||
// nolint
|
||||
var (
|
||||
NewKeeper = keeper.NewKeeper
|
||||
TxDecoder = types.TxDecoder
|
||||
)
|
||||
|
||||
//nolint
|
||||
type (
|
||||
Keeper = keeper.Keeper
|
||||
QueryResAccount = types.QueryResAccount
|
||||
|
@ -85,7 +85,7 @@ func GetCmdGenTx(cdc *codec.Codec) *cobra.Command {
|
||||
}
|
||||
|
||||
// TODO: Potentially allow overriding of gas price and gas limit
|
||||
msg := types.NewEmintMsg(seq, &toAddr, sdk.NewInt(amount), txBldr.Gas(),
|
||||
msg := types.NewMsgEthermint(seq, &toAddr, sdk.NewInt(amount), txBldr.Gas(),
|
||||
sdk.NewInt(emint.DefaultGasPrice), data, from)
|
||||
|
||||
err = msg.ValidateBasic()
|
||||
@ -137,7 +137,7 @@ func GetCmdGenCreateTx(cdc *codec.Codec) *cobra.Command {
|
||||
}
|
||||
|
||||
// TODO: Potentially allow overriding of gas price and gas limit
|
||||
msg := types.NewEmintMsg(seq, nil, sdk.NewInt(amount), txBldr.Gas(),
|
||||
msg := types.NewMsgEthermint(seq, nil, sdk.NewInt(amount), txBldr.Gas(),
|
||||
sdk.NewInt(emint.DefaultGasPrice), data, from)
|
||||
|
||||
err = msg.ValidateBasic()
|
||||
|
112
x/evm/handler.go
112
x/evm/handler.go
@ -18,23 +18,20 @@ import (
|
||||
func NewHandler(k Keeper) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
switch msg := msg.(type) {
|
||||
case types.EthereumTxMsg:
|
||||
return handleETHTxMsg(ctx, k, msg)
|
||||
case *types.EmintMsg:
|
||||
return handleEmintMsg(ctx, k, *msg)
|
||||
case types.MsgEthereumTx:
|
||||
return HandleMsgEthereumTx(ctx, k, msg)
|
||||
case types.MsgEthermint:
|
||||
return HandleMsgEthermint(ctx, k, msg)
|
||||
default:
|
||||
errMsg := fmt.Sprintf("Unrecognized ethermint Msg type: %v", msg.Type())
|
||||
errMsg := fmt.Sprintf("unrecognized ethermint msg type: %v", msg.Type())
|
||||
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle an Ethereum specific tx
|
||||
func handleETHTxMsg(ctx sdk.Context, k Keeper, msg types.EthereumTxMsg) sdk.Result {
|
||||
if err := msg.ValidateBasic(); err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
// HandleMsgEthereumTx handles an Ethereum specific tx
|
||||
func HandleMsgEthereumTx(ctx sdk.Context, k Keeper, msg types.MsgEthereumTx) sdk.Result {
|
||||
ctx = ctx.WithEventManager(sdk.NewEventManager())
|
||||
// parse the chainID from a string to a base-10 integer
|
||||
intChainID, ok := new(big.Int).SetString(ctx.ChainID(), 10)
|
||||
if !ok {
|
||||
@ -44,14 +41,14 @@ func handleETHTxMsg(ctx sdk.Context, k Keeper, msg types.EthereumTxMsg) sdk.Resu
|
||||
// Verify signature and retrieve sender address
|
||||
sender, err := msg.VerifySig(intChainID)
|
||||
if err != nil {
|
||||
return emint.ErrInvalidSender(err.Error()).Result()
|
||||
return sdk.ResultFromError(err)
|
||||
}
|
||||
|
||||
// Encode transaction by default Tx encoder
|
||||
txEncoder := authutils.GetTxEncoder(types.ModuleCdc)
|
||||
txBytes, err := txEncoder(msg)
|
||||
if err != nil {
|
||||
return sdk.ErrInternal(err.Error()).Result()
|
||||
return sdk.ResultFromError(err)
|
||||
}
|
||||
txHash := tmtypes.Tx(txBytes).Hash()
|
||||
ethHash := common.BytesToHash(txHash)
|
||||
@ -70,21 +67,53 @@ func handleETHTxMsg(ctx sdk.Context, k Keeper, msg types.EthereumTxMsg) sdk.Resu
|
||||
Simulate: ctx.IsCheckTx(),
|
||||
}
|
||||
// Prepare db for logs
|
||||
k.CommitStateDB.Prepare(ethHash, common.Hash{}, k.TxCount.Get())
|
||||
k.TxCount.Increment()
|
||||
k.CommitStateDB.Prepare(ethHash, common.Hash{}, k.TxCount)
|
||||
k.TxCount++
|
||||
|
||||
bloom, res := st.TransitionCSDB(ctx)
|
||||
if res.IsOK() {
|
||||
k.Bloom.Or(k.Bloom, bloom)
|
||||
// TODO: move to keeper
|
||||
returnData, err := st.TransitionCSDB(ctx)
|
||||
if err != nil {
|
||||
return sdk.ResultFromError(err)
|
||||
}
|
||||
return res
|
||||
|
||||
// update block bloom filter
|
||||
k.Bloom.Or(k.Bloom, returnData.Bloom)
|
||||
|
||||
// update transaction logs in KVStore
|
||||
err = k.SetTransactionLogs(ctx, returnData.Logs, txHash)
|
||||
if err != nil {
|
||||
return sdk.ResultFromError(err)
|
||||
}
|
||||
|
||||
ctx.EventManager().EmitEvents(sdk.Events{
|
||||
sdk.NewEvent(
|
||||
types.EventTypeEthereumTx,
|
||||
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Data.Amount.String()),
|
||||
),
|
||||
sdk.NewEvent(
|
||||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
||||
sdk.NewAttribute(sdk.AttributeKeySender, sender.String()),
|
||||
),
|
||||
})
|
||||
|
||||
if msg.Data.Recipient != nil {
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeEthereumTx,
|
||||
sdk.NewAttribute(types.AttributeKeyRecipient, msg.Data.Recipient.String()),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// set the events to the result
|
||||
returnData.Result.Events = ctx.EventManager().Events()
|
||||
return *returnData.Result
|
||||
}
|
||||
|
||||
func handleEmintMsg(ctx sdk.Context, k Keeper, msg types.EmintMsg) sdk.Result {
|
||||
if err := msg.ValidateBasic(); err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
// HandleMsgEthermint handles a MsgEthermint
|
||||
func HandleMsgEthermint(ctx sdk.Context, k Keeper, msg types.MsgEthermint) sdk.Result {
|
||||
ctx = ctx.WithEventManager(sdk.NewEventManager())
|
||||
// parse the chainID from a string to a base-10 integer
|
||||
intChainID, ok := new(big.Int).SetString(ctx.ChainID(), 10)
|
||||
if !ok {
|
||||
@ -109,9 +138,36 @@ func handleEmintMsg(ctx sdk.Context, k Keeper, msg types.EmintMsg) sdk.Result {
|
||||
}
|
||||
|
||||
// Prepare db for logs
|
||||
k.CommitStateDB.Prepare(common.Hash{}, common.Hash{}, k.TxCount.Get()) // Cannot provide tx hash
|
||||
k.TxCount.Increment()
|
||||
k.CommitStateDB.Prepare(common.Hash{}, common.Hash{}, k.TxCount) // Cannot provide tx hash
|
||||
k.TxCount++
|
||||
|
||||
_, res := st.TransitionCSDB(ctx)
|
||||
return res
|
||||
returnData, err := st.TransitionCSDB(ctx)
|
||||
if err != nil {
|
||||
return sdk.ResultFromError(err)
|
||||
}
|
||||
|
||||
ctx.EventManager().EmitEvents(sdk.Events{
|
||||
sdk.NewEvent(
|
||||
types.EventTypeEthermint,
|
||||
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
|
||||
),
|
||||
sdk.NewEvent(
|
||||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
||||
sdk.NewAttribute(sdk.AttributeKeySender, msg.From.String()),
|
||||
),
|
||||
})
|
||||
|
||||
if msg.Recipient != nil {
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeEthermint,
|
||||
sdk.NewAttribute(types.AttributeKeyRecipient, msg.Recipient.String()),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// set the events to the result
|
||||
returnData.Result.Events = ctx.EventManager().Events()
|
||||
return *returnData.Result
|
||||
}
|
||||
|
89
x/evm/handler_test.go
Normal file
89
x/evm/handler_test.go
Normal file
@ -0,0 +1,89 @@
|
||||
package evm_test
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
|
||||
"github.com/cosmos/ethermint/app"
|
||||
"github.com/cosmos/ethermint/x/evm"
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
type EvmTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
ctx sdk.Context
|
||||
handler sdk.Handler
|
||||
app *app.EthermintApp
|
||||
}
|
||||
|
||||
func (suite *EvmTestSuite) SetupTest() {
|
||||
checkTx := false
|
||||
|
||||
suite.app = app.Setup(checkTx)
|
||||
suite.ctx = suite.app.BaseApp.NewContext(checkTx, abci.Header{Height: 1, ChainID: "3", Time: time.Now().UTC()})
|
||||
suite.handler = evm.NewHandler(suite.app.EvmKeeper)
|
||||
}
|
||||
|
||||
func TestEvmTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(EvmTestSuite))
|
||||
}
|
||||
|
||||
func (suite *EvmTestSuite) TestHandler_Logs() {
|
||||
// Test contract:
|
||||
|
||||
// pragma solidity ^0.5.1;
|
||||
|
||||
// contract Test {
|
||||
// event Hello(uint256 indexed world);
|
||||
|
||||
// constructor() public {
|
||||
// emit Hello(17);
|
||||
// }
|
||||
// }
|
||||
|
||||
// {
|
||||
// "linkReferences": {},
|
||||
// "object": "6080604052348015600f57600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a2603580604b6000396000f3fe6080604052600080fdfea165627a7a723058206cab665f0f557620554bb45adf266708d2bd349b8a4314bdff205ee8440e3c240029",
|
||||
// "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x11 PUSH32 0x775A94827B8FD9B519D36CD827093C664F93347070A554F65E4A6F56CD738898 PUSH1 0x40 MLOAD PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG2 PUSH1 0x35 DUP1 PUSH1 0x4B PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT INVALID LOG1 PUSH6 0x627A7A723058 KECCAK256 PUSH13 0xAB665F0F557620554BB45ADF26 PUSH8 0x8D2BD349B8A4314 0xbd SELFDESTRUCT KECCAK256 0x5e 0xe8 DIFFICULTY 0xe EXTCODECOPY 0x24 STOP 0x29 ",
|
||||
// "sourceMap": "25:119:0:-;;;90:52;8:9:-1;5:2;;;30:1;27;20:12;5:2;90:52:0;132:2;126:9;;;;;;;;;;25:119;;;;;;"
|
||||
// }
|
||||
|
||||
gasLimit := uint64(100000)
|
||||
gasPrice := big.NewInt(1000000)
|
||||
|
||||
priv, err := crypto.GenerateKey()
|
||||
suite.Require().NoError(err, "failed to create key")
|
||||
|
||||
bytecode := common.FromHex("0x6080604052348015600f57600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a2603580604b6000396000f3fe6080604052600080fdfea165627a7a723058206cab665f0f557620554bb45adf266708d2bd349b8a4314bdff205ee8440e3c240029")
|
||||
tx := types.NewMsgEthereumTx(1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode)
|
||||
tx.Sign(big.NewInt(3), priv)
|
||||
|
||||
result := suite.handler(suite.ctx, tx)
|
||||
suite.Require().True(result.IsOK())
|
||||
|
||||
resultData, err := types.DecodeResultData(result.Data)
|
||||
suite.Require().NoError(err, "failed to decode result data")
|
||||
|
||||
suite.Require().Equal(len(resultData.Logs), 1)
|
||||
suite.Require().Equal(len(resultData.Logs[0].Topics), 2)
|
||||
|
||||
hash := []byte{1}
|
||||
err = suite.app.EvmKeeper.SetTransactionLogs(suite.ctx, resultData.Logs, hash)
|
||||
suite.Require().NoError(err, "failed to set logs")
|
||||
|
||||
logs, err := suite.app.EvmKeeper.GetTransactionLogs(suite.ctx, hash)
|
||||
suite.Require().NoError(err, "failed to get logs")
|
||||
|
||||
suite.Require().Equal(logs, resultData.Logs)
|
||||
}
|
@ -2,6 +2,8 @@ package keeper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
@ -9,7 +11,6 @@ import (
|
||||
ethvm "github.com/ethereum/go-ethereum/core/vm"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
ethstate "github.com/ethereum/go-ethereum/core/state"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
@ -24,35 +25,22 @@ type Keeper struct {
|
||||
cdc *codec.Codec
|
||||
// Store key required to update the block bloom filter mappings needed for the
|
||||
// Web3 API
|
||||
storeKey sdk.StoreKey
|
||||
blockKey sdk.StoreKey
|
||||
CommitStateDB *types.CommitStateDB
|
||||
TxCount *count
|
||||
TxCount int
|
||||
Bloom *big.Int
|
||||
}
|
||||
|
||||
// TODO: move to types
|
||||
type count int
|
||||
|
||||
func (c *count) Get() int {
|
||||
return (int)(*c)
|
||||
}
|
||||
|
||||
func (c *count) Increment() {
|
||||
*c++
|
||||
}
|
||||
|
||||
func (c *count) Reset() {
|
||||
*c = 0
|
||||
}
|
||||
|
||||
// NewKeeper generates new evm module keeper
|
||||
func NewKeeper(ak auth.AccountKeeper, storageKey, codeKey,
|
||||
storeKey sdk.StoreKey, cdc *codec.Codec) Keeper {
|
||||
func NewKeeper(
|
||||
cdc *codec.Codec, blockKey, codeKey, storeKey sdk.StoreKey,
|
||||
ak types.AccountKeeper,
|
||||
) Keeper {
|
||||
return Keeper{
|
||||
cdc: cdc,
|
||||
storeKey: storeKey,
|
||||
CommitStateDB: types.NewCommitStateDB(sdk.Context{}, ak, storageKey, codeKey),
|
||||
TxCount: new(count),
|
||||
blockKey: blockKey,
|
||||
CommitStateDB: types.NewCommitStateDB(sdk.Context{}, codeKey, storeKey, ak),
|
||||
TxCount: 0,
|
||||
Bloom: big.NewInt(0),
|
||||
}
|
||||
}
|
||||
@ -64,21 +52,23 @@ func NewKeeper(ak auth.AccountKeeper, storageKey, codeKey,
|
||||
|
||||
// SetBlockHashMapping sets the mapping from block consensus hash to block height
|
||||
func (k *Keeper) SetBlockHashMapping(ctx sdk.Context, hash []byte, height int64) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
store := ctx.KVStore(k.blockKey)
|
||||
if !bytes.Equal(hash, []byte{}) {
|
||||
store.Set(hash, k.cdc.MustMarshalBinaryLengthPrefixed(height))
|
||||
bz := sdk.Uint64ToBigEndian(uint64(height))
|
||||
store.Set(hash, bz)
|
||||
}
|
||||
}
|
||||
|
||||
// GetBlockHashMapping gets block height from block consensus hash
|
||||
func (k *Keeper) GetBlockHashMapping(ctx sdk.Context, hash []byte) (height int64) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
store := ctx.KVStore(k.blockKey)
|
||||
bz := store.Get(hash)
|
||||
if bytes.Equal(bz, []byte{}) {
|
||||
panic(fmt.Errorf("block with hash %s not found", ethcmn.BytesToHash(hash)))
|
||||
}
|
||||
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &height)
|
||||
return
|
||||
|
||||
height = int64(binary.BigEndian.Uint64(bz))
|
||||
return height
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -87,23 +77,54 @@ func (k *Keeper) GetBlockHashMapping(ctx sdk.Context, hash []byte) (height int64
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// SetBlockBloomMapping sets the mapping from block height to bloom bits
|
||||
func (k *Keeper) SetBlockBloomMapping(ctx sdk.Context, bloom ethtypes.Bloom, height int64) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
heightHash := k.cdc.MustMarshalBinaryLengthPrefixed(height)
|
||||
if !bytes.Equal(heightHash, []byte{}) {
|
||||
store.Set(heightHash, bloom.Bytes())
|
||||
func (k *Keeper) SetBlockBloomMapping(ctx sdk.Context, bloom ethtypes.Bloom, height int64) error {
|
||||
store := ctx.KVStore(k.blockKey)
|
||||
bz := sdk.Uint64ToBigEndian(uint64(height))
|
||||
if len(bz) == 0 {
|
||||
return fmt.Errorf("block with bloombits %v not found", bloom)
|
||||
}
|
||||
|
||||
store.Set(types.BloomKey(bz), bloom.Bytes())
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBlockBloomMapping gets bloombits from block height
|
||||
func (k *Keeper) GetBlockBloomMapping(ctx sdk.Context, height int64) ethtypes.Bloom {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
heightHash := k.cdc.MustMarshalBinaryLengthPrefixed(height)
|
||||
bloom := store.Get(heightHash)
|
||||
if bytes.Equal(heightHash, []byte{}) {
|
||||
panic(fmt.Errorf("block with bloombits %s not found", bloom))
|
||||
func (k *Keeper) GetBlockBloomMapping(ctx sdk.Context, height int64) (ethtypes.Bloom, error) {
|
||||
store := ctx.KVStore(k.blockKey)
|
||||
bz := sdk.Uint64ToBigEndian(uint64(height))
|
||||
if len(bz) == 0 {
|
||||
return ethtypes.BytesToBloom([]byte{}), fmt.Errorf("block with height %d not found", height)
|
||||
}
|
||||
return ethtypes.BytesToBloom(bloom)
|
||||
|
||||
bloom := store.Get(types.BloomKey(bz))
|
||||
if len(bloom) == 0 {
|
||||
return ethtypes.BytesToBloom([]byte{}), fmt.Errorf("block with bloombits %v not found", bloom)
|
||||
}
|
||||
|
||||
return ethtypes.BytesToBloom(bloom), nil
|
||||
}
|
||||
|
||||
// SetBlockLogs sets the transaction's logs in the KVStore
|
||||
func (k *Keeper) SetTransactionLogs(ctx sdk.Context, logs []*ethtypes.Log, hash []byte) error {
|
||||
store := ctx.KVStore(k.blockKey)
|
||||
encLogs, err := types.EncodeLogs(logs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
store.Set(types.LogsKey(hash), encLogs)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBlockLogs gets the logs for a transaction from the KVStore
|
||||
func (k *Keeper) GetTransactionLogs(ctx sdk.Context, hash []byte) ([]*ethtypes.Log, error) {
|
||||
store := ctx.KVStore(k.blockKey)
|
||||
encLogs := store.Get(types.LogsKey(hash))
|
||||
if len(encLogs) == 0 {
|
||||
return nil, errors.New("cannot get transaction logs")
|
||||
}
|
||||
|
||||
return types.DecodeLogs(encLogs)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -225,13 +246,18 @@ func (k *Keeper) GetCommittedState(ctx sdk.Context, addr ethcmn.Address, hash et
|
||||
}
|
||||
|
||||
// GetLogs calls CommitStateDB.GetLogs using the passed in context
|
||||
func (k *Keeper) GetLogs(ctx sdk.Context, hash ethcmn.Hash) []*ethtypes.Log {
|
||||
return k.CommitStateDB.WithContext(ctx).GetLogs(hash)
|
||||
func (k *Keeper) GetLogs(ctx sdk.Context, hash ethcmn.Hash) ([]*ethtypes.Log, error) {
|
||||
logs, err := k.CommitStateDB.WithContext(ctx).GetLogs(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
// Logs calls CommitStateDB.Logs using the passed in context
|
||||
func (k *Keeper) Logs(ctx sdk.Context) []*ethtypes.Log {
|
||||
return k.CommitStateDB.WithContext(ctx).Logs()
|
||||
// AllLogs calls CommitStateDB.AllLogs using the passed in context
|
||||
func (k *Keeper) AllLogs(ctx sdk.Context) []*ethtypes.Log {
|
||||
return k.CommitStateDB.WithContext(ctx).AllLogs()
|
||||
}
|
||||
|
||||
// GetRefund calls CommitStateDB.GetRefund using the passed in context
|
||||
@ -264,13 +290,17 @@ func (k *Keeper) Commit(ctx sdk.Context, deleteEmptyObjects bool) (root ethcmn.H
|
||||
}
|
||||
|
||||
// Finalise calls CommitStateDB.Finalise using the passed in context
|
||||
func (k *Keeper) Finalise(ctx sdk.Context, deleteEmptyObjects bool) {
|
||||
k.CommitStateDB.WithContext(ctx).Finalise(deleteEmptyObjects)
|
||||
func (k *Keeper) Finalise(ctx sdk.Context, deleteEmptyObjects bool) error {
|
||||
return k.CommitStateDB.WithContext(ctx).Finalise(deleteEmptyObjects)
|
||||
}
|
||||
|
||||
// IntermediateRoot calls CommitStateDB.IntermediateRoot using the passed in context
|
||||
func (k *Keeper) IntermediateRoot(ctx sdk.Context, deleteEmptyObjects bool) {
|
||||
k.CommitStateDB.WithContext(ctx).IntermediateRoot(deleteEmptyObjects)
|
||||
func (k *Keeper) IntermediateRoot(ctx sdk.Context, deleteEmptyObjects bool) error {
|
||||
_, err := k.CommitStateDB.WithContext(ctx).IntermediateRoot(deleteEmptyObjects)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -1,110 +1,78 @@
|
||||
package keeper
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/params"
|
||||
|
||||
"github.com/cosmos/ethermint/types"
|
||||
evmtypes "github.com/cosmos/ethermint/x/evm/types"
|
||||
"github.com/cosmos/ethermint/app"
|
||||
"github.com/cosmos/ethermint/x/evm/keeper"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmlog "github.com/tendermint/tendermint/libs/log"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
)
|
||||
|
||||
var (
|
||||
address = ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b4c1")
|
||||
var address = ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b4c1")
|
||||
|
||||
accKey = sdk.NewKVStoreKey("acc")
|
||||
storageKey = sdk.NewKVStoreKey(evmtypes.EvmStoreKey)
|
||||
codeKey = sdk.NewKVStoreKey(evmtypes.EvmCodeKey)
|
||||
blockKey = sdk.NewKVStoreKey(evmtypes.EvmBlockKey)
|
||||
type KeeperTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
logger = tmlog.NewNopLogger()
|
||||
)
|
||||
|
||||
func newTestCodec() *codec.Codec {
|
||||
cdc := codec.New()
|
||||
|
||||
evmtypes.RegisterCodec(cdc)
|
||||
types.RegisterCodec(cdc)
|
||||
auth.RegisterCodec(cdc)
|
||||
sdk.RegisterCodec(cdc)
|
||||
codec.RegisterCrypto(cdc)
|
||||
|
||||
return cdc
|
||||
ctx sdk.Context
|
||||
querier sdk.Querier
|
||||
app *app.EthermintApp
|
||||
}
|
||||
|
||||
func TestDBStorage(t *testing.T) {
|
||||
// create logger, codec and root multi-store
|
||||
cdc := newTestCodec()
|
||||
func (suite *KeeperTestSuite) SetupTest() {
|
||||
checkTx := false
|
||||
|
||||
// The ParamsKeeper handles parameter storage for the application
|
||||
keyParams := sdk.NewKVStoreKey(params.StoreKey)
|
||||
tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey)
|
||||
paramsKeeper := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace)
|
||||
// Set specific supspaces
|
||||
authSubspace := paramsKeeper.Subspace(auth.DefaultParamspace)
|
||||
ak := auth.NewAccountKeeper(cdc, accKey, authSubspace, types.ProtoBaseAccount)
|
||||
ek := NewKeeper(ak, storageKey, codeKey, blockKey, cdc)
|
||||
suite.app = app.Setup(checkTx)
|
||||
suite.ctx = suite.app.BaseApp.NewContext(checkTx, abci.Header{Height: 1, ChainID: "3", Time: time.Now().UTC()})
|
||||
suite.querier = keeper.NewQuerier(suite.app.EvmKeeper)
|
||||
}
|
||||
|
||||
db := dbm.NewMemDB()
|
||||
cms := store.NewCommitMultiStore(db)
|
||||
// mount stores
|
||||
keys := []*sdk.KVStoreKey{accKey, storageKey, codeKey, blockKey}
|
||||
for _, key := range keys {
|
||||
cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, nil)
|
||||
}
|
||||
|
||||
// load latest version (root)
|
||||
err := cms.LoadLatestVersion()
|
||||
require.NoError(t, err)
|
||||
|
||||
// First execution
|
||||
ms := cms.CacheMultiStore()
|
||||
ctx := sdk.NewContext(ms, abci.Header{}, false, logger)
|
||||
ctx = ctx.WithBlockHeight(1)
|
||||
func TestKeeperTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(KeeperTestSuite))
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestDBStorage() {
|
||||
// Perform state transitions
|
||||
ek.SetBalance(ctx, address, big.NewInt(5))
|
||||
ek.SetNonce(ctx, address, 4)
|
||||
ek.SetState(ctx, address, ethcmn.HexToHash("0x2"), ethcmn.HexToHash("0x3"))
|
||||
ek.SetCode(ctx, address, []byte{0x1})
|
||||
suite.app.EvmKeeper.CreateAccount(suite.ctx, address)
|
||||
suite.app.EvmKeeper.SetBalance(suite.ctx, address, big.NewInt(5))
|
||||
suite.app.EvmKeeper.SetNonce(suite.ctx, address, 4)
|
||||
suite.app.EvmKeeper.SetState(suite.ctx, address, ethcmn.HexToHash("0x2"), ethcmn.HexToHash("0x3"))
|
||||
suite.app.EvmKeeper.SetCode(suite.ctx, address, []byte{0x1})
|
||||
|
||||
// Test block hash mapping functionality
|
||||
ek.SetBlockHashMapping(ctx, ethcmn.FromHex("0x0d87a3a5f73140f46aac1bf419263e4e94e87c292f25007700ab7f2060e2af68"), 7)
|
||||
ek.SetBlockHashMapping(ctx, []byte{0x43, 0x32}, 8)
|
||||
suite.app.EvmKeeper.SetBlockHashMapping(suite.ctx, ethcmn.FromHex("0x0d87a3a5f73140f46aac1bf419263e4e94e87c292f25007700ab7f2060e2af68"), 7)
|
||||
suite.app.EvmKeeper.SetBlockHashMapping(suite.ctx, []byte{0x43, 0x32}, 8)
|
||||
|
||||
// Test block height mapping functionality
|
||||
testBloom := ethtypes.BytesToBloom([]byte{0x1, 0x3})
|
||||
ek.SetBlockBloomMapping(ctx, testBloom, 4)
|
||||
err := suite.app.EvmKeeper.SetBlockBloomMapping(suite.ctx, testBloom, 4)
|
||||
suite.Require().NoError(err, "failed to set block bloom mapping")
|
||||
|
||||
// Get those state transitions
|
||||
require.Equal(t, ek.GetBalance(ctx, address).Cmp(big.NewInt(5)), 0)
|
||||
require.Equal(t, ek.GetNonce(ctx, address), uint64(4))
|
||||
require.Equal(t, ek.GetState(ctx, address, ethcmn.HexToHash("0x2")), ethcmn.HexToHash("0x3"))
|
||||
require.Equal(t, ek.GetCode(ctx, address), []byte{0x1})
|
||||
suite.Require().Equal(suite.app.EvmKeeper.GetBalance(suite.ctx, address).Cmp(big.NewInt(5)), 0)
|
||||
suite.Require().Equal(suite.app.EvmKeeper.GetNonce(suite.ctx, address), uint64(4))
|
||||
suite.Require().Equal(suite.app.EvmKeeper.GetState(suite.ctx, address, ethcmn.HexToHash("0x2")), ethcmn.HexToHash("0x3"))
|
||||
suite.Require().Equal(suite.app.EvmKeeper.GetCode(suite.ctx, address), []byte{0x1})
|
||||
|
||||
require.Equal(t, ek.GetBlockHashMapping(ctx, ethcmn.FromHex("0x0d87a3a5f73140f46aac1bf419263e4e94e87c292f25007700ab7f2060e2af68")), int64(7))
|
||||
require.Equal(t, ek.GetBlockHashMapping(ctx, []byte{0x43, 0x32}), int64(8))
|
||||
|
||||
require.Equal(t, ek.GetBlockBloomMapping(ctx, 4), testBloom)
|
||||
suite.Require().Equal(suite.app.EvmKeeper.GetBlockHashMapping(suite.ctx, ethcmn.FromHex("0x0d87a3a5f73140f46aac1bf419263e4e94e87c292f25007700ab7f2060e2af68")), int64(7))
|
||||
suite.Require().Equal(suite.app.EvmKeeper.GetBlockHashMapping(suite.ctx, []byte{0x43, 0x32}), int64(8))
|
||||
bloom, err := suite.app.EvmKeeper.GetBlockBloomMapping(suite.ctx, 4)
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(bloom, testBloom)
|
||||
|
||||
// commit stateDB
|
||||
_, err = ek.Commit(ctx, false)
|
||||
require.NoError(t, err, "failed to commit StateDB")
|
||||
_, err = suite.app.EvmKeeper.Commit(suite.ctx, false)
|
||||
suite.Require().NoError(err, "failed to commit StateDB")
|
||||
|
||||
// simulate BaseApp EndBlocker commitment
|
||||
ms.Write()
|
||||
cms.Commit()
|
||||
suite.app.Commit()
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
@ -15,7 +16,7 @@ import (
|
||||
|
||||
// NewQuerier is the module level router for state queries
|
||||
func NewQuerier(keeper Keeper) sdk.Querier {
|
||||
return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) {
|
||||
return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, sdk.Error) {
|
||||
switch path[0] {
|
||||
case types.QueryProtocolVersion:
|
||||
return queryProtocolVersion(keeper)
|
||||
@ -48,140 +49,146 @@ func NewQuerier(keeper Keeper) sdk.Querier {
|
||||
func queryProtocolVersion(keeper Keeper) ([]byte, sdk.Error) {
|
||||
vers := version.ProtocolVersion
|
||||
|
||||
res, err := codec.MarshalJSONIndent(keeper.cdc, hexutil.Uint(vers))
|
||||
bz, err := codec.MarshalJSONIndent(keeper.cdc, hexutil.Uint(vers))
|
||||
if err != nil {
|
||||
panic("could not marshal result to JSON")
|
||||
return nil, sdk.ErrInternal(err.Error())
|
||||
}
|
||||
|
||||
return res, nil
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
func queryBalance(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||
addr := ethcmn.HexToAddress(path[1])
|
||||
balance := keeper.GetBalance(ctx, addr)
|
||||
|
||||
bRes := types.QueryResBalance{Balance: utils.MarshalBigInt(balance)}
|
||||
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
|
||||
res := types.QueryResBalance{Balance: utils.MarshalBigInt(balance)}
|
||||
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
|
||||
if err != nil {
|
||||
panic("could not marshal result to JSON: " + err.Error())
|
||||
return nil, sdk.ErrInternal(err.Error())
|
||||
}
|
||||
|
||||
return res, nil
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
func queryBlockNumber(ctx sdk.Context, keeper Keeper) ([]byte, sdk.Error) {
|
||||
num := ctx.BlockHeight()
|
||||
bnRes := types.QueryResBlockNumber{Number: num}
|
||||
res, err := codec.MarshalJSONIndent(keeper.cdc, bnRes)
|
||||
bz, err := codec.MarshalJSONIndent(keeper.cdc, bnRes)
|
||||
if err != nil {
|
||||
panic("could not marshal result to JSON: " + err.Error())
|
||||
return nil, sdk.ErrInternal(err.Error())
|
||||
}
|
||||
|
||||
return res, nil
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
func queryStorage(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||
addr := ethcmn.HexToAddress(path[1])
|
||||
key := ethcmn.HexToHash(path[2])
|
||||
val := keeper.GetState(ctx, addr, key)
|
||||
bRes := types.QueryResStorage{Value: val.Bytes()}
|
||||
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
|
||||
res := types.QueryResStorage{Value: val.Bytes()}
|
||||
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
|
||||
if err != nil {
|
||||
panic("could not marshal result to JSON: " + err.Error())
|
||||
return nil, sdk.ErrInternal(err.Error())
|
||||
}
|
||||
return res, nil
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
func queryCode(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||
addr := ethcmn.HexToAddress(path[1])
|
||||
code := keeper.GetCode(ctx, addr)
|
||||
cRes := types.QueryResCode{Code: code}
|
||||
res, err := codec.MarshalJSONIndent(keeper.cdc, cRes)
|
||||
res := types.QueryResCode{Code: code}
|
||||
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
|
||||
if err != nil {
|
||||
panic("could not marshal result to JSON: " + err.Error())
|
||||
return nil, sdk.ErrInternal(err.Error())
|
||||
}
|
||||
|
||||
return res, nil
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
func queryNonce(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||
addr := ethcmn.HexToAddress(path[1])
|
||||
nonce := keeper.GetNonce(ctx, addr)
|
||||
nRes := types.QueryResNonce{Nonce: nonce}
|
||||
res, err := codec.MarshalJSONIndent(keeper.cdc, nRes)
|
||||
bz, err := codec.MarshalJSONIndent(keeper.cdc, nRes)
|
||||
if err != nil {
|
||||
panic("could not marshal result to JSON: " + err.Error())
|
||||
return nil, sdk.ErrInternal(err.Error())
|
||||
}
|
||||
|
||||
return res, nil
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
func queryHashToHeight(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||
blockHash := ethcmn.FromHex(path[1])
|
||||
blockNumber := keeper.GetBlockHashMapping(ctx, blockHash)
|
||||
|
||||
bRes := types.QueryResBlockNumber{Number: blockNumber}
|
||||
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
|
||||
res := types.QueryResBlockNumber{Number: blockNumber}
|
||||
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
|
||||
if err != nil {
|
||||
panic("could not marshal result to JSON: " + err.Error())
|
||||
return nil, sdk.ErrInternal(err.Error())
|
||||
}
|
||||
|
||||
return res, nil
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
func queryBlockLogsBloom(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||
num, err := strconv.ParseInt(path[1], 10, 64)
|
||||
if err != nil {
|
||||
panic("could not unmarshall block number: " + err.Error())
|
||||
return nil, sdk.ErrInternal(fmt.Sprintf("could not unmarshall block number: %s", err.Error()))
|
||||
}
|
||||
|
||||
bloom := keeper.GetBlockBloomMapping(ctx, num)
|
||||
|
||||
bRes := types.QueryBloomFilter{Bloom: bloom}
|
||||
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
|
||||
bloom, err := keeper.GetBlockBloomMapping(ctx, num)
|
||||
if err != nil {
|
||||
panic("could not marshal result to JSON: " + err.Error())
|
||||
return nil, sdk.ErrInternal(fmt.Sprintf("failed to get block bloom mapping: %s", err.Error()))
|
||||
}
|
||||
|
||||
return res, nil
|
||||
res := types.QueryBloomFilter{Bloom: bloom}
|
||||
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
|
||||
if err != nil {
|
||||
return nil, sdk.ErrInternal(err.Error())
|
||||
}
|
||||
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
func queryTxLogs(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||
txHash := ethcmn.HexToHash(path[1])
|
||||
logs := keeper.GetLogs(ctx, txHash)
|
||||
|
||||
bRes := types.QueryETHLogs{Logs: logs}
|
||||
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
|
||||
logs, err := keeper.GetLogs(ctx, txHash)
|
||||
if err != nil {
|
||||
panic("could not marshal result to JSON: " + err.Error())
|
||||
return nil, sdk.ErrInternal(err.Error())
|
||||
}
|
||||
|
||||
return res, nil
|
||||
res := types.QueryETHLogs{Logs: logs}
|
||||
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
|
||||
if err != nil {
|
||||
return nil, sdk.ErrInternal(err.Error())
|
||||
}
|
||||
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
func queryLogs(ctx sdk.Context, keeper Keeper) ([]byte, sdk.Error) {
|
||||
logs := keeper.Logs(ctx)
|
||||
logs := keeper.AllLogs(ctx)
|
||||
|
||||
lRes := types.QueryETHLogs{Logs: logs}
|
||||
l, err := codec.MarshalJSONIndent(keeper.cdc, lRes)
|
||||
res := types.QueryETHLogs{Logs: logs}
|
||||
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
|
||||
if err != nil {
|
||||
panic("could not marshal result to JSON: " + err.Error())
|
||||
return nil, sdk.ErrInternal(err.Error())
|
||||
}
|
||||
return l, nil
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
func queryAccount(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||
addr := ethcmn.HexToAddress(path[1])
|
||||
so := keeper.GetOrNewStateObject(ctx, addr)
|
||||
|
||||
lRes := types.QueryResAccount{
|
||||
res := types.QueryResAccount{
|
||||
Balance: utils.MarshalBigInt(so.Balance()),
|
||||
CodeHash: so.CodeHash(),
|
||||
Nonce: so.Nonce(),
|
||||
}
|
||||
l, err := codec.MarshalJSONIndent(keeper.cdc, lRes)
|
||||
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
|
||||
if err != nil {
|
||||
panic("could not marshal result to JSON: " + err.Error())
|
||||
return nil, sdk.ErrInternal(err.Error())
|
||||
}
|
||||
return l, nil
|
||||
return bz, nil
|
||||
}
|
||||
|
@ -2,19 +2,20 @@ package evm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/module"
|
||||
|
||||
"github.com/cosmos/ethermint/x/evm/client/cli"
|
||||
"github.com/cosmos/ethermint/x/evm/keeper"
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/spf13/cobra"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
var _ module.AppModuleBasic = AppModuleBasic{}
|
||||
@ -107,33 +108,13 @@ func (am AppModule) NewQuerierHandler() sdk.Querier {
|
||||
}
|
||||
|
||||
// BeginBlock function for module at start of each block
|
||||
func (am AppModule) BeginBlock(ctx sdk.Context, bl abci.RequestBeginBlock) {
|
||||
// Consider removing this when using evm as module without web3 API
|
||||
bloom := ethtypes.BytesToBloom(am.keeper.Bloom.Bytes())
|
||||
am.keeper.SetBlockBloomMapping(ctx, bloom, bl.Header.GetHeight()-1)
|
||||
am.keeper.SetBlockHashMapping(ctx, bl.Header.LastBlockId.GetHash(), bl.Header.GetHeight()-1)
|
||||
am.keeper.Bloom = big.NewInt(0)
|
||||
am.keeper.TxCount.Reset()
|
||||
func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
|
||||
BeginBlock(am.keeper, ctx, req)
|
||||
}
|
||||
|
||||
// EndBlock function for module at end of block
|
||||
func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
|
||||
// Gas costs are handled within msg handler so costs should be ignored
|
||||
ebCtx := ctx.WithBlockGasMeter(sdk.NewInfiniteGasMeter())
|
||||
|
||||
// Update account balances before committing other parts of state
|
||||
am.keeper.CommitStateDB.UpdateAccounts()
|
||||
|
||||
// Commit state objects to KV store
|
||||
_, err := am.keeper.CommitStateDB.WithContext(ebCtx).Commit(true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Clear accounts cache after account data has been committed
|
||||
am.keeper.CommitStateDB.ClearStateObjects()
|
||||
|
||||
return []abci.ValidatorUpdate{}
|
||||
func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate {
|
||||
return EndBlock(am.keeper, ctx, req)
|
||||
}
|
||||
|
||||
// InitGenesis instantiates the genesis state
|
||||
|
@ -9,14 +9,13 @@ var ModuleCdc = codec.New()
|
||||
|
||||
func init() {
|
||||
cdc := codec.New()
|
||||
|
||||
codec.RegisterCrypto(cdc)
|
||||
|
||||
ModuleCdc = cdc.Seal()
|
||||
}
|
||||
|
||||
// RegisterCodec registers concrete types and interfaces on the given codec.
|
||||
func RegisterCodec(cdc *codec.Codec) {
|
||||
cdc.RegisterConcrete(&EthereumTxMsg{}, "ethermint/MsgEthereumTx", nil)
|
||||
cdc.RegisterConcrete(&EmintMsg{}, "ethermint/MsgEmint", nil)
|
||||
cdc.RegisterConcrete(MsgEthereumTx{}, "ethermint/MsgEthereumTx", nil)
|
||||
cdc.RegisterConcrete(MsgEthermint{}, "ethermint/MsgEthermint", nil)
|
||||
cdc.RegisterConcrete(EncodableTxData{}, "ethermint/EncodableTxData", nil)
|
||||
}
|
||||
|
@ -1,12 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
ethstate "github.com/ethereum/go-ethereum/core/state"
|
||||
)
|
||||
|
||||
// RawDump returns a raw state dump.
|
||||
//
|
||||
// TODO: Implement if we need it, especially for the RPC API.
|
||||
func (csdb *CommitStateDB) RawDump() ethstate.Dump {
|
||||
return ethstate.Dump{}
|
||||
}
|
@ -4,21 +4,22 @@ import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/cosmos/ethermint/types"
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
var (
|
||||
_ sdk.Msg = EmintMsg{}
|
||||
_ sdk.Msg = MsgEthermint{}
|
||||
)
|
||||
|
||||
const (
|
||||
// TypeEmintMsg defines the type string of Emint message
|
||||
TypeEmintMsg = "emint_tx"
|
||||
// TypeMsgEthermint defines the type string of Ethermint message
|
||||
TypeMsgEthermint = "ethermint"
|
||||
)
|
||||
|
||||
// EmintMsg implements a cosmos equivalent structure for Ethereum transactions
|
||||
type EmintMsg struct {
|
||||
// MsgEthermint implements a cosmos equivalent structure for Ethereum transactions
|
||||
type MsgEthermint struct {
|
||||
AccountNonce uint64 `json:"nonce"`
|
||||
Price sdk.Int `json:"gasPrice"`
|
||||
GasLimit uint64 `json:"gas"`
|
||||
@ -30,12 +31,12 @@ type EmintMsg struct {
|
||||
From sdk.AccAddress `json:"from"`
|
||||
}
|
||||
|
||||
// NewEmintMsg returns a reference to a new Ethermint transaction
|
||||
func NewEmintMsg(
|
||||
// NewMsgEthermint returns a reference to a new Ethermint transaction
|
||||
func NewMsgEthermint(
|
||||
nonce uint64, to *sdk.AccAddress, amount sdk.Int,
|
||||
gasLimit uint64, gasPrice sdk.Int, payload []byte, from sdk.AccAddress,
|
||||
) EmintMsg {
|
||||
return EmintMsg{
|
||||
) MsgEthermint {
|
||||
return MsgEthermint{
|
||||
AccountNonce: nonce,
|
||||
Price: gasPrice,
|
||||
GasLimit: gasLimit,
|
||||
@ -47,18 +48,18 @@ func NewEmintMsg(
|
||||
}
|
||||
|
||||
// Route should return the name of the module
|
||||
func (msg EmintMsg) Route() string { return RouterKey }
|
||||
func (msg MsgEthermint) Route() string { return RouterKey }
|
||||
|
||||
// Type returns the action of the message
|
||||
func (msg EmintMsg) Type() string { return TypeEmintMsg }
|
||||
func (msg MsgEthermint) Type() string { return TypeMsgEthermint }
|
||||
|
||||
// GetSignBytes encodes the message for signing
|
||||
func (msg EmintMsg) GetSignBytes() []byte {
|
||||
func (msg MsgEthermint) GetSignBytes() []byte {
|
||||
return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg))
|
||||
}
|
||||
|
||||
// ValidateBasic runs stateless checks on the message
|
||||
func (msg EmintMsg) ValidateBasic() sdk.Error {
|
||||
func (msg MsgEthermint) ValidateBasic() sdk.Error {
|
||||
if msg.Price.Sign() != 1 {
|
||||
return types.ErrInvalidValue(fmt.Sprintf("Price must be positive: %x", msg.Price))
|
||||
}
|
||||
@ -72,13 +73,13 @@ func (msg EmintMsg) ValidateBasic() sdk.Error {
|
||||
}
|
||||
|
||||
// GetSigners defines whose signature is required
|
||||
func (msg EmintMsg) GetSigners() []sdk.AccAddress {
|
||||
func (msg MsgEthermint) GetSigners() []sdk.AccAddress {
|
||||
return []sdk.AccAddress{msg.From}
|
||||
}
|
||||
|
||||
// To returns the recipient address of the transaction. It returns nil if the
|
||||
// transaction is a contract creation.
|
||||
func (msg EmintMsg) To() *ethcmn.Address {
|
||||
func (msg MsgEthermint) To() *ethcmn.Address {
|
||||
if msg.Recipient == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -8,19 +8,19 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
)
|
||||
|
||||
func TestEmintMsg(t *testing.T) {
|
||||
func TestMsgEthermint(t *testing.T) {
|
||||
addr := newSdkAddress()
|
||||
fromAddr := newSdkAddress()
|
||||
|
||||
msg := NewEmintMsg(0, &addr, sdk.NewInt(1), 100000, sdk.NewInt(2), []byte("test"), fromAddr)
|
||||
msg := NewMsgEthermint(0, &addr, sdk.NewInt(1), 100000, sdk.NewInt(2), []byte("test"), fromAddr)
|
||||
require.NotNil(t, msg)
|
||||
require.Equal(t, msg.Recipient, &addr)
|
||||
|
||||
require.Equal(t, msg.Route(), RouterKey)
|
||||
require.Equal(t, msg.Type(), TypeEmintMsg)
|
||||
require.Equal(t, msg.Type(), TypeMsgEthermint)
|
||||
}
|
||||
|
||||
func TestEmintMsgValidation(t *testing.T) {
|
||||
func TestMsgEthermintValidation(t *testing.T) {
|
||||
testCases := []struct {
|
||||
nonce uint64
|
||||
to *sdk.AccAddress
|
||||
@ -38,7 +38,7 @@ func TestEmintMsgValidation(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
msg := NewEmintMsg(tc.nonce, tc.to, tc.amount, tc.gasLimit, tc.gasPrice, tc.payload, tc.from)
|
||||
msg := NewMsgEthermint(tc.nonce, tc.to, tc.amount, tc.gasLimit, tc.gasPrice, tc.payload, tc.from)
|
||||
|
||||
if tc.expectPass {
|
||||
require.Nil(t, msg.ValidateBasic(), "test: %v", i)
|
||||
@ -52,13 +52,13 @@ func TestEmintEncodingAndDecoding(t *testing.T) {
|
||||
addr := newSdkAddress()
|
||||
fromAddr := newSdkAddress()
|
||||
|
||||
msg := NewEmintMsg(0, &addr, sdk.NewInt(1), 100000, sdk.NewInt(2), []byte("test"), fromAddr)
|
||||
msg := NewMsgEthermint(0, &addr, sdk.NewInt(1), 100000, sdk.NewInt(2), []byte("test"), fromAddr)
|
||||
|
||||
raw, err := cdc.MarshalBinaryBare(msg)
|
||||
raw, err := ModuleCdc.MarshalBinaryBare(msg)
|
||||
require.NoError(t, err)
|
||||
|
||||
var msg2 EmintMsg
|
||||
err = cdc.UnmarshalBinaryBare(raw, &msg2)
|
||||
var msg2 MsgEthermint
|
||||
err = ModuleCdc.UnmarshalBinaryBare(raw, &msg2)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, msg.AccountNonce, msg2.AccountNonce)
|
||||
|
11
x/evm/types/events.go
Normal file
11
x/evm/types/events.go
Normal file
@ -0,0 +1,11 @@
|
||||
package types
|
||||
|
||||
// Evm module events
|
||||
const (
|
||||
EventTypeEthermint = TypeMsgEthermint
|
||||
EventTypeEthereumTx = TypeMsgEthereumTx
|
||||
|
||||
AttributeKeyContractAddress = "contract"
|
||||
AttributeKeyRecipient = "recipient"
|
||||
AttributeValueCategory = ModuleName
|
||||
)
|
14
x/evm/types/expected_keepers.go
Normal file
14
x/evm/types/expected_keepers.go
Normal file
@ -0,0 +1,14 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
|
||||
)
|
||||
|
||||
// AccountKeeper defines the expected account keeper interface
|
||||
type AccountKeeper interface {
|
||||
NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) authexported.Account
|
||||
GetAccount(ctx sdk.Context, addr sdk.AccAddress) authexported.Account
|
||||
SetAccount(ctx sdk.Context, account authexported.Account)
|
||||
RemoveAccount(ctx sdk.Context, account authexported.Account)
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/cosmos/ethermint/types"
|
||||
@ -28,10 +28,10 @@ type (
|
||||
func ValidateGenesis(data GenesisState) error {
|
||||
for _, acct := range data.Accounts {
|
||||
if len(acct.Address.Bytes()) == 0 {
|
||||
return fmt.Errorf("invalid GenesisAccount Error: Missing Address")
|
||||
return errors.New("invalid GenesisAccount: address cannot be empty")
|
||||
}
|
||||
if acct.Balance == nil {
|
||||
return fmt.Errorf("invalid GenesisAccount Error: Missing Balance")
|
||||
return errors.New("invalid GenesisAccount: balance cannot be empty")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -4,13 +4,24 @@ const (
|
||||
// ModuleName string name of module
|
||||
ModuleName = "evm"
|
||||
|
||||
// EvmStoreKey key for ethereum storage data
|
||||
EvmStoreKey = "evmstore"
|
||||
// EvmCodeKey key for ethereum code data
|
||||
EvmCodeKey = "evmcode"
|
||||
// EvmBlockKey key for ethereum block data
|
||||
EvmBlockKey = "evmblock"
|
||||
// StoreKey key for ethereum storage data (StateDB)
|
||||
StoreKey = ModuleName
|
||||
// CodeKey key for ethereum code data
|
||||
CodeKey = ModuleName + "code"
|
||||
// BlockKey key
|
||||
BlockKey = ModuleName + "block"
|
||||
|
||||
// RouterKey uses module name for routing
|
||||
RouterKey = ModuleName
|
||||
)
|
||||
|
||||
var bloomPrefix = []byte("bloom")
|
||||
var logsPrefix = []byte("logs")
|
||||
|
||||
func BloomKey(key []byte) []byte {
|
||||
return append(bloomPrefix, key...)
|
||||
}
|
||||
|
||||
func LogsKey(key []byte) []byte {
|
||||
return append(logsPrefix, key...)
|
||||
}
|
||||
|
@ -19,21 +19,20 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
_ sdk.Msg = EthereumTxMsg{}
|
||||
_ sdk.Tx = EthereumTxMsg{}
|
||||
_ sdk.Msg = MsgEthereumTx{}
|
||||
_ sdk.Tx = MsgEthereumTx{}
|
||||
)
|
||||
|
||||
var big8 = big.NewInt(8)
|
||||
|
||||
// message type and route constants
|
||||
const (
|
||||
TypeEthereumTxMsg = "ethereum_tx"
|
||||
RouteEthereumTxMsg = RouterKey
|
||||
TypeMsgEthereumTx = "ethereum"
|
||||
)
|
||||
|
||||
// EthereumTxMsg encapsulates an Ethereum transaction as an SDK message.
|
||||
// MsgEthereumTx encapsulates an Ethereum transaction as an SDK message.
|
||||
type (
|
||||
EthereumTxMsg struct {
|
||||
MsgEthereumTx struct {
|
||||
Data TxData
|
||||
|
||||
// caches
|
||||
@ -69,28 +68,28 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
// NewEthereumTxMsg returns a reference to a new Ethereum transaction message.
|
||||
func NewEthereumTxMsg(
|
||||
// NewMsgEthereumTx returns a reference to a new Ethereum transaction message.
|
||||
func NewMsgEthereumTx(
|
||||
nonce uint64, to *ethcmn.Address, amount *big.Int,
|
||||
gasLimit uint64, gasPrice *big.Int, payload []byte,
|
||||
) *EthereumTxMsg {
|
||||
) MsgEthereumTx {
|
||||
|
||||
return newEthereumTxMsg(nonce, to, amount, gasLimit, gasPrice, payload)
|
||||
return newMsgEthereumTx(nonce, to, amount, gasLimit, gasPrice, payload)
|
||||
}
|
||||
|
||||
// NewEthereumTxMsgContract returns a reference to a new Ethereum transaction
|
||||
// NewMsgEthereumTxContract returns a reference to a new Ethereum transaction
|
||||
// message designated for contract creation.
|
||||
func NewEthereumTxMsgContract(
|
||||
func NewMsgEthereumTxContract(
|
||||
nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, payload []byte,
|
||||
) *EthereumTxMsg {
|
||||
) MsgEthereumTx {
|
||||
|
||||
return newEthereumTxMsg(nonce, nil, amount, gasLimit, gasPrice, payload)
|
||||
return newMsgEthereumTx(nonce, nil, amount, gasLimit, gasPrice, payload)
|
||||
}
|
||||
|
||||
func newEthereumTxMsg(
|
||||
func newMsgEthereumTx(
|
||||
nonce uint64, to *ethcmn.Address, amount *big.Int,
|
||||
gasLimit uint64, gasPrice *big.Int, payload []byte,
|
||||
) *EthereumTxMsg {
|
||||
) MsgEthereumTx {
|
||||
|
||||
if len(payload) > 0 {
|
||||
payload = ethcmn.CopyBytes(payload)
|
||||
@ -115,20 +114,20 @@ func newEthereumTxMsg(
|
||||
txData.Price.Set(gasPrice)
|
||||
}
|
||||
|
||||
return &EthereumTxMsg{Data: txData}
|
||||
return MsgEthereumTx{Data: txData}
|
||||
}
|
||||
|
||||
// Route returns the route value of an EthereumTxMsg.
|
||||
func (msg EthereumTxMsg) Route() string { return RouteEthereumTxMsg }
|
||||
// Route returns the route value of an MsgEthereumTx.
|
||||
func (msg MsgEthereumTx) Route() string { return RouterKey }
|
||||
|
||||
// Type returns the type value of an EthereumTxMsg.
|
||||
func (msg EthereumTxMsg) Type() string { return TypeEthereumTxMsg }
|
||||
// Type returns the type value of an MsgEthereumTx.
|
||||
func (msg MsgEthereumTx) Type() string { return TypeMsgEthereumTx }
|
||||
|
||||
// ValidateBasic implements the sdk.Msg interface. It performs basic validation
|
||||
// checks of a Transaction. If returns an sdk.Error if validation fails.
|
||||
func (msg EthereumTxMsg) ValidateBasic() sdk.Error {
|
||||
// checks of a Transaction. If returns an error if validation fails.
|
||||
func (msg MsgEthereumTx) ValidateBasic() sdk.Error {
|
||||
if msg.Data.Price.Sign() != 1 {
|
||||
return types.ErrInvalidValue(fmt.Sprintf("Price must be positive: %x", msg.Data.Price))
|
||||
return types.ErrInvalidValue(fmt.Sprintf("price must be positive: %x", msg.Data.Price))
|
||||
}
|
||||
|
||||
// Amount can be 0
|
||||
@ -141,16 +140,12 @@ func (msg EthereumTxMsg) ValidateBasic() sdk.Error {
|
||||
|
||||
// To returns the recipient address of the transaction. It returns nil if the
|
||||
// transaction is a contract creation.
|
||||
func (msg EthereumTxMsg) To() *ethcmn.Address {
|
||||
if msg.Data.Recipient == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg MsgEthereumTx) To() *ethcmn.Address {
|
||||
return msg.Data.Recipient
|
||||
}
|
||||
|
||||
// GetMsgs returns a single EthereumTxMsg as an sdk.Msg.
|
||||
func (msg EthereumTxMsg) GetMsgs() []sdk.Msg {
|
||||
// GetMsgs returns a single MsgEthereumTx as an sdk.Msg.
|
||||
func (msg MsgEthereumTx) GetMsgs() []sdk.Msg {
|
||||
return []sdk.Msg{msg}
|
||||
}
|
||||
|
||||
@ -159,7 +154,7 @@ func (msg EthereumTxMsg) GetMsgs() []sdk.Msg {
|
||||
//
|
||||
// NOTE: This method cannot be used as a chain ID is needed to recover the signer
|
||||
// from the signature. Use 'VerifySig' instead.
|
||||
func (msg EthereumTxMsg) GetSigners() []sdk.AccAddress {
|
||||
func (msg MsgEthereumTx) GetSigners() []sdk.AccAddress {
|
||||
panic("must use 'VerifySig' with a chain ID to get the signer")
|
||||
}
|
||||
|
||||
@ -168,13 +163,13 @@ func (msg EthereumTxMsg) GetSigners() []sdk.AccAddress {
|
||||
//
|
||||
// NOTE: This method cannot be used as a chain ID is needed to create valid bytes
|
||||
// to sign over. Use 'RLPSignBytes' instead.
|
||||
func (msg EthereumTxMsg) GetSignBytes() []byte {
|
||||
func (msg MsgEthereumTx) GetSignBytes() []byte {
|
||||
panic("must use 'RLPSignBytes' with a chain ID to get the valid bytes to sign")
|
||||
}
|
||||
|
||||
// RLPSignBytes returns the RLP hash of an Ethereum transaction message with a
|
||||
// given chainID used for signing.
|
||||
func (msg EthereumTxMsg) RLPSignBytes(chainID *big.Int) ethcmn.Hash {
|
||||
func (msg MsgEthereumTx) RLPSignBytes(chainID *big.Int) ethcmn.Hash {
|
||||
return rlpHash([]interface{}{
|
||||
msg.Data.AccountNonce,
|
||||
msg.Data.Price,
|
||||
@ -187,12 +182,12 @@ func (msg EthereumTxMsg) RLPSignBytes(chainID *big.Int) ethcmn.Hash {
|
||||
}
|
||||
|
||||
// EncodeRLP implements the rlp.Encoder interface.
|
||||
func (msg *EthereumTxMsg) EncodeRLP(w io.Writer) error {
|
||||
func (msg *MsgEthereumTx) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, &msg.Data)
|
||||
}
|
||||
|
||||
// DecodeRLP implements the rlp.Decoder interface.
|
||||
func (msg *EthereumTxMsg) DecodeRLP(s *rlp.Stream) error {
|
||||
func (msg *MsgEthereumTx) DecodeRLP(s *rlp.Stream) error {
|
||||
_, size, _ := s.Kind()
|
||||
|
||||
err := s.Decode(&msg.Data)
|
||||
@ -204,7 +199,7 @@ func (msg *EthereumTxMsg) DecodeRLP(s *rlp.Stream) error {
|
||||
}
|
||||
|
||||
// Hash hashes the RLP encoding of a transaction.
|
||||
func (msg *EthereumTxMsg) Hash() ethcmn.Hash {
|
||||
func (msg *MsgEthereumTx) Hash() ethcmn.Hash {
|
||||
if hash := msg.hash.Load(); hash != nil {
|
||||
return hash.(ethcmn.Hash)
|
||||
}
|
||||
@ -219,7 +214,7 @@ func (msg *EthereumTxMsg) Hash() ethcmn.Hash {
|
||||
// takes a private key and chainID to sign an Ethereum transaction according to
|
||||
// EIP155 standard. It mutates the transaction as it populates the V, R, S
|
||||
// fields of the Transaction's Signature.
|
||||
func (msg *EthereumTxMsg) Sign(chainID *big.Int, priv *ecdsa.PrivateKey) {
|
||||
func (msg *MsgEthereumTx) Sign(chainID *big.Int, priv *ecdsa.PrivateKey) {
|
||||
txHash := msg.RLPSignBytes(chainID)
|
||||
|
||||
sig, err := ethcrypto.Sign(txHash[:], priv)
|
||||
@ -252,7 +247,7 @@ func (msg *EthereumTxMsg) Sign(chainID *big.Int, priv *ecdsa.PrivateKey) {
|
||||
|
||||
// VerifySig attempts to verify a Transaction's signature for a given chainID.
|
||||
// A derived address is returned upon success or an error if recovery fails.
|
||||
func (msg *EthereumTxMsg) VerifySig(chainID *big.Int) (ethcmn.Address, error) {
|
||||
func (msg *MsgEthereumTx) VerifySig(chainID *big.Int) (ethcmn.Address, error) {
|
||||
signer := ethtypes.NewEIP155Signer(chainID)
|
||||
|
||||
if sc := msg.from.Load(); sc != nil {
|
||||
@ -284,19 +279,19 @@ func (msg *EthereumTxMsg) VerifySig(chainID *big.Int) (ethcmn.Address, error) {
|
||||
}
|
||||
|
||||
// Cost returns amount + gasprice * gaslimit.
|
||||
func (msg EthereumTxMsg) Cost() *big.Int {
|
||||
func (msg MsgEthereumTx) Cost() *big.Int {
|
||||
total := msg.Fee()
|
||||
total.Add(total, msg.Data.Amount)
|
||||
return total
|
||||
}
|
||||
|
||||
// Fee returns gasprice * gaslimit.
|
||||
func (msg EthereumTxMsg) Fee() *big.Int {
|
||||
func (msg MsgEthereumTx) Fee() *big.Int {
|
||||
return new(big.Int).Mul(msg.Data.Price, new(big.Int).SetUint64(msg.Data.GasLimit))
|
||||
}
|
||||
|
||||
// ChainID returns which chain id this transaction was signed for (if at all)
|
||||
func (msg *EthereumTxMsg) ChainID() *big.Int {
|
||||
func (msg *MsgEthereumTx) ChainID() *big.Int {
|
||||
return deriveChainID(msg.Data.V)
|
||||
}
|
||||
|
||||
@ -317,7 +312,7 @@ func deriveChainID(v *big.Int) *big.Int {
|
||||
// Auxiliary
|
||||
|
||||
// TxDecoder returns an sdk.TxDecoder that can decode both auth.StdTx and
|
||||
// EthereumTxMsg transactions.
|
||||
// MsgEthereumTx transactions.
|
||||
func TxDecoder(cdc *codec.Codec) sdk.TxDecoder {
|
||||
return func(txBytes []byte) (sdk.Tx, sdk.Error) {
|
||||
var tx sdk.Tx
|
||||
@ -328,7 +323,6 @@ func TxDecoder(cdc *codec.Codec) sdk.TxDecoder {
|
||||
|
||||
err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return nil, sdk.ErrTxDecode("failed to decode tx").TraceSDK(err.Error())
|
||||
}
|
||||
|
||||
|
@ -1,23 +1,11 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/ethermint/utils"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
var cdc = codec.New()
|
||||
|
||||
func init() {
|
||||
RegisterAmino(cdc)
|
||||
}
|
||||
|
||||
// RegisterAmino registers all crypto related types in the given (amino) codec.
|
||||
func RegisterAmino(cdc *codec.Codec) {
|
||||
cdc.RegisterConcrete(EncodableTxData{}, "ethermint/EncodedMessage", nil)
|
||||
}
|
||||
|
||||
// EncodableTxData implements the Ethereum transaction data structure. It is used
|
||||
// solely as intended in Ethereum abiding by the protocol.
|
||||
type EncodableTxData struct {
|
||||
@ -38,12 +26,12 @@ type EncodableTxData struct {
|
||||
}
|
||||
|
||||
func marshalAmino(td EncodableTxData) (string, error) {
|
||||
bz, err := cdc.MarshalBinaryBare(td)
|
||||
bz, err := ModuleCdc.MarshalBinaryBare(td)
|
||||
return string(bz), err
|
||||
}
|
||||
|
||||
func unmarshalAmino(td *EncodableTxData, text string) error {
|
||||
return cdc.UnmarshalBinaryBare([]byte(text), td)
|
||||
return ModuleCdc.UnmarshalBinaryBare([]byte(text), td)
|
||||
}
|
||||
|
||||
// MarshalAmino defines custom encoding scheme for TxData
|
||||
|
@ -18,17 +18,17 @@ import (
|
||||
func TestMsgEthereumTx(t *testing.T) {
|
||||
addr := GenerateEthAddress()
|
||||
|
||||
msg1 := NewEthereumTxMsg(0, &addr, nil, 100000, nil, []byte("test"))
|
||||
msg1 := NewMsgEthereumTx(0, &addr, nil, 100000, nil, []byte("test"))
|
||||
require.NotNil(t, msg1)
|
||||
require.Equal(t, *msg1.Data.Recipient, addr)
|
||||
|
||||
msg2 := NewEthereumTxMsgContract(0, nil, 100000, nil, []byte("test"))
|
||||
msg2 := NewMsgEthereumTxContract(0, nil, 100000, nil, []byte("test"))
|
||||
require.NotNil(t, msg2)
|
||||
require.Nil(t, msg2.Data.Recipient)
|
||||
|
||||
msg3 := NewEthereumTxMsg(0, &addr, nil, 100000, nil, []byte("test"))
|
||||
require.Equal(t, msg3.Route(), RouteEthereumTxMsg)
|
||||
require.Equal(t, msg3.Type(), TypeEthereumTxMsg)
|
||||
msg3 := NewMsgEthereumTx(0, &addr, nil, 100000, nil, []byte("test"))
|
||||
require.Equal(t, msg3.Route(), RouterKey)
|
||||
require.Equal(t, msg3.Type(), TypeMsgEthereumTx)
|
||||
require.Panics(t, func() { msg3.GetSigners() })
|
||||
require.Panics(t, func() { msg3.GetSignBytes() })
|
||||
}
|
||||
@ -49,7 +49,7 @@ func TestMsgEthereumTxValidation(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
msg := NewEthereumTxMsg(tc.nonce, &tc.to, tc.amount, tc.gasLimit, tc.gasPrice, tc.payload)
|
||||
msg := NewMsgEthereumTx(tc.nonce, &tc.to, tc.amount, tc.gasLimit, tc.gasPrice, tc.payload)
|
||||
|
||||
if tc.expectPass {
|
||||
require.Nil(t, msg.ValidateBasic(), "test: %v", i)
|
||||
@ -63,26 +63,26 @@ func TestMsgEthereumTxRLPSignBytes(t *testing.T) {
|
||||
addr := ethcmn.BytesToAddress([]byte("test_address"))
|
||||
chainID := big.NewInt(3)
|
||||
|
||||
msg := NewEthereumTxMsg(0, &addr, nil, 100000, nil, []byte("test"))
|
||||
msg := NewMsgEthereumTx(0, &addr, nil, 100000, nil, []byte("test"))
|
||||
hash := msg.RLPSignBytes(chainID)
|
||||
require.Equal(t, "5BD30E35AD27449390B14C91E6BCFDCAADF8FE44EF33680E3BC200FC0DC083C7", fmt.Sprintf("%X", hash))
|
||||
}
|
||||
|
||||
func TestMsgEthereumTxRLPEncode(t *testing.T) {
|
||||
addr := ethcmn.BytesToAddress([]byte("test_address"))
|
||||
msg := NewEthereumTxMsg(0, &addr, nil, 100000, nil, []byte("test"))
|
||||
msg := NewMsgEthereumTx(0, &addr, nil, 100000, nil, []byte("test"))
|
||||
|
||||
raw, err := rlp.EncodeToBytes(msg)
|
||||
raw, err := rlp.EncodeToBytes(&msg)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, ethcmn.FromHex("E48080830186A0940000000000000000746573745F61646472657373808474657374808080"), raw)
|
||||
}
|
||||
|
||||
func TestMsgEthereumTxRLPDecode(t *testing.T) {
|
||||
var msg EthereumTxMsg
|
||||
var msg MsgEthereumTx
|
||||
|
||||
raw := ethcmn.FromHex("E48080830186A0940000000000000000746573745F61646472657373808474657374808080")
|
||||
addr := ethcmn.BytesToAddress([]byte("test_address"))
|
||||
expectedMsg := NewEthereumTxMsg(0, &addr, nil, 100000, nil, []byte("test"))
|
||||
expectedMsg := NewMsgEthereumTx(0, &addr, nil, 100000, nil, []byte("test"))
|
||||
|
||||
err := rlp.Decode(bytes.NewReader(raw), &msg)
|
||||
require.NoError(t, err)
|
||||
@ -91,7 +91,7 @@ func TestMsgEthereumTxRLPDecode(t *testing.T) {
|
||||
|
||||
func TestMsgEthereumTxHash(t *testing.T) {
|
||||
addr := ethcmn.BytesToAddress([]byte("test_address"))
|
||||
msg := NewEthereumTxMsg(0, &addr, nil, 100000, nil, []byte("test"))
|
||||
msg := NewMsgEthereumTx(0, &addr, nil, 100000, nil, []byte("test"))
|
||||
|
||||
hash := msg.Hash()
|
||||
require.Equal(t, "E2AA2E68E7586AE9700F1D3D643330866B6AC2B6CA4C804F7C85ECB11D0B0B29", fmt.Sprintf("%X", hash))
|
||||
@ -106,7 +106,7 @@ func TestMsgEthereumTxSig(t *testing.T) {
|
||||
addr2 := ethcmn.BytesToAddress(priv2.PubKey().Address().Bytes())
|
||||
|
||||
// require valid signature passes validation
|
||||
msg := NewEthereumTxMsg(0, &addr1, nil, 100000, nil, []byte("test"))
|
||||
msg := NewMsgEthereumTx(0, &addr1, nil, 100000, nil, []byte("test"))
|
||||
msg.Sign(chainID, priv1.ToECDSA())
|
||||
|
||||
signer, err := msg.VerifySig(chainID)
|
||||
@ -115,7 +115,7 @@ func TestMsgEthereumTxSig(t *testing.T) {
|
||||
require.NotEqual(t, addr2, signer)
|
||||
|
||||
// require invalid chain ID fail validation
|
||||
msg = NewEthereumTxMsg(0, &addr1, nil, 100000, nil, []byte("test"))
|
||||
msg = NewMsgEthereumTx(0, &addr1, nil, 100000, nil, []byte("test"))
|
||||
msg.Sign(chainID, priv1.ToECDSA())
|
||||
|
||||
signer, err = msg.VerifySig(big.NewInt(4))
|
||||
@ -125,7 +125,7 @@ func TestMsgEthereumTxSig(t *testing.T) {
|
||||
|
||||
func TestMsgEthereumTxAmino(t *testing.T) {
|
||||
addr := GenerateEthAddress()
|
||||
msg := NewEthereumTxMsg(5, &addr, big.NewInt(1), 100000, big.NewInt(3), []byte("test"))
|
||||
msg := NewMsgEthereumTx(5, &addr, big.NewInt(1), 100000, big.NewInt(3), []byte("test"))
|
||||
|
||||
msg.Data.V = big.NewInt(1)
|
||||
msg.Data.R = big.NewInt(2)
|
||||
@ -134,7 +134,7 @@ func TestMsgEthereumTxAmino(t *testing.T) {
|
||||
raw, err := ModuleCdc.MarshalBinaryBare(msg)
|
||||
require.NoError(t, err)
|
||||
|
||||
var msg2 EthereumTxMsg
|
||||
var msg2 MsgEthereumTx
|
||||
|
||||
err = ModuleCdc.UnmarshalBinaryBare(raw, &msg2)
|
||||
require.NoError(t, err)
|
||||
|
@ -73,20 +73,21 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func newObject(db *CommitStateDB, accProto authexported.Account) *stateObject {
|
||||
acc, ok := accProto.(*types.Account)
|
||||
func newStateObject(db *CommitStateDB, accProto authexported.Account) *stateObject {
|
||||
ethermintAccount, ok := accProto.(*types.Account)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("invalid account type for state object: %T", accProto))
|
||||
}
|
||||
|
||||
if acc.CodeHash == nil {
|
||||
acc.CodeHash = emptyCodeHash
|
||||
// set empty code hash
|
||||
if ethermintAccount.CodeHash == nil {
|
||||
ethermintAccount.CodeHash = emptyCodeHash
|
||||
}
|
||||
|
||||
return &stateObject{
|
||||
stateDB: db,
|
||||
account: acc,
|
||||
address: ethcmn.BytesToAddress(acc.Address.Bytes()),
|
||||
account: ethermintAccount,
|
||||
address: ethcmn.BytesToAddress(ethermintAccount.GetAddress().Bytes()),
|
||||
originStorage: make(types.Storage),
|
||||
dirtyStorage: make(types.Storage),
|
||||
}
|
||||
@ -188,7 +189,7 @@ func (so *stateObject) setBalance(amount sdk.Int) {
|
||||
so.account.SetBalance(amount)
|
||||
}
|
||||
|
||||
// SetNonce sets the state object's nonce (sequence number).
|
||||
// SetNonce sets the state object's nonce (i.e sequence number of the account).
|
||||
func (so *stateObject) SetNonce(nonce uint64) {
|
||||
so.stateDB.journal.append(nonceChange{
|
||||
account: &so.address,
|
||||
@ -199,6 +200,9 @@ func (so *stateObject) SetNonce(nonce uint64) {
|
||||
}
|
||||
|
||||
func (so *stateObject) setNonce(nonce uint64) {
|
||||
if so.account == nil {
|
||||
panic("state object account is empty")
|
||||
}
|
||||
so.account.Sequence = nonce
|
||||
}
|
||||
|
||||
@ -216,7 +220,7 @@ func (so *stateObject) markSuicided() {
|
||||
// commitState commits all dirty storage to a KVStore.
|
||||
func (so *stateObject) commitState() {
|
||||
ctx := so.stateDB.ctx
|
||||
store := ctx.KVStore(so.stateDB.storageKey)
|
||||
store := ctx.KVStore(so.stateDB.storeKey)
|
||||
|
||||
for key, value := range so.dirtyStorage {
|
||||
delete(so.dirtyStorage, key)
|
||||
@ -258,22 +262,32 @@ func (so stateObject) Address() ethcmn.Address {
|
||||
|
||||
// Balance returns the state object's current balance.
|
||||
func (so *stateObject) Balance() *big.Int {
|
||||
return so.account.Balance().BigInt()
|
||||
balance := so.account.Balance().BigInt()
|
||||
if balance == nil {
|
||||
return zeroBalance
|
||||
}
|
||||
return balance
|
||||
}
|
||||
|
||||
// CodeHash returns the state object's code hash.
|
||||
func (so *stateObject) CodeHash() []byte {
|
||||
if so.account == nil || len(so.account.CodeHash) == 0 {
|
||||
return emptyCodeHash
|
||||
}
|
||||
return so.account.CodeHash
|
||||
}
|
||||
|
||||
// Nonce returns the state object's current nonce (sequence number).
|
||||
func (so *stateObject) Nonce() uint64 {
|
||||
if so.account == nil {
|
||||
return 0
|
||||
}
|
||||
return so.account.Sequence
|
||||
}
|
||||
|
||||
// Code returns the contract code associated with this object, if any.
|
||||
func (so *stateObject) Code(_ ethstate.Database) []byte {
|
||||
if so.code != nil {
|
||||
if len(so.code) > 0 {
|
||||
return so.code
|
||||
}
|
||||
|
||||
@ -286,10 +300,9 @@ func (so *stateObject) Code(_ ethstate.Database) []byte {
|
||||
code := store.Get(so.CodeHash())
|
||||
|
||||
if len(code) == 0 {
|
||||
so.setError(fmt.Errorf("failed to get code hash %x for address: %x", so.CodeHash(), so.Address()))
|
||||
so.setError(fmt.Errorf("failed to get code hash %x for address %s", so.CodeHash(), so.Address().String()))
|
||||
}
|
||||
|
||||
so.code = code
|
||||
return code
|
||||
}
|
||||
|
||||
@ -321,7 +334,7 @@ func (so *stateObject) GetCommittedState(_ ethstate.Database, key ethcmn.Hash) e
|
||||
|
||||
// otherwise load the value from the KVStore
|
||||
ctx := so.stateDB.ctx
|
||||
store := ctx.KVStore(so.stateDB.storageKey)
|
||||
store := ctx.KVStore(so.stateDB.storeKey)
|
||||
rawValue := store.Get(prefixKey.Bytes())
|
||||
|
||||
if len(rawValue) > 0 {
|
||||
@ -341,7 +354,7 @@ func (so *stateObject) GetCommittedState(_ ethstate.Database, key ethcmn.Hash) e
|
||||
func (so *stateObject) ReturnGas(gas *big.Int) {}
|
||||
|
||||
func (so *stateObject) deepCopy(db *CommitStateDB) *stateObject {
|
||||
newStateObj := newObject(db, so.account)
|
||||
newStateObj := newStateObject(db, so.account)
|
||||
|
||||
newStateObj.code = so.code
|
||||
newStateObj.dirtyStorage = so.dirtyStorage.Copy()
|
||||
@ -355,9 +368,11 @@ func (so *stateObject) deepCopy(db *CommitStateDB) *stateObject {
|
||||
|
||||
// empty returns whether the account is considered empty.
|
||||
func (so *stateObject) empty() bool {
|
||||
return so.account.Sequence == 0 &&
|
||||
so.account.Balance().Sign() == 0 &&
|
||||
bytes.Equal(so.account.CodeHash, emptyCodeHash)
|
||||
return so.account == nil ||
|
||||
(so.account != nil &&
|
||||
so.account.Sequence == 0 &&
|
||||
so.account.Balance().Sign() == 0 &&
|
||||
bytes.Equal(so.account.CodeHash, emptyCodeHash))
|
||||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
|
@ -1,6 +1,7 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@ -27,14 +28,24 @@ type StateTransition struct {
|
||||
Simulate bool
|
||||
}
|
||||
|
||||
// ReturnData represents what's returned from a transition
|
||||
type ReturnData struct {
|
||||
Logs []*ethtypes.Log
|
||||
Bloom *big.Int
|
||||
Result *sdk.Result
|
||||
}
|
||||
|
||||
// TODO: move to keeper
|
||||
// TransitionCSDB performs an evm state transition from a transaction
|
||||
func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*big.Int, sdk.Result) {
|
||||
// TODO: update godoc, it doesn't explain what it does in depth.
|
||||
func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*ReturnData, error) {
|
||||
returnData := new(ReturnData)
|
||||
|
||||
contractCreation := st.Recipient == nil
|
||||
|
||||
cost, err := core.IntrinsicGas(st.Payload, contractCreation, true)
|
||||
if err != nil {
|
||||
return nil, sdk.ErrOutOfGas("invalid intrinsic gas for transaction").Result()
|
||||
return nil, fmt.Errorf("invalid intrinsic gas for transaction: %s", err.Error())
|
||||
}
|
||||
|
||||
// This gas limit the the transaction gas limit with intrinsic gas subtracted
|
||||
@ -68,21 +79,20 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*big.Int, sdk.Result)
|
||||
CanTransfer: core.CanTransfer,
|
||||
Transfer: core.Transfer,
|
||||
Origin: st.Sender,
|
||||
Coinbase: common.Address{},
|
||||
Coinbase: common.Address{}, // TODO: explain why this is empty
|
||||
BlockNumber: big.NewInt(ctx.BlockHeight()),
|
||||
Time: big.NewInt(ctx.BlockHeader().Time.Unix()),
|
||||
Difficulty: big.NewInt(0x30000), // unused
|
||||
Difficulty: big.NewInt(0), // unused. Only required in PoW context
|
||||
GasLimit: gasLimit,
|
||||
GasPrice: ctx.MinGasPrices().AmountOf(emint.DenomDefault).Int,
|
||||
}
|
||||
|
||||
vmenv := vm.NewEVM(context, csdb, GenerateChainConfig(st.ChainID), vm.Config{})
|
||||
evm := vm.NewEVM(context, csdb, GenerateChainConfig(st.ChainID), vm.Config{})
|
||||
|
||||
var (
|
||||
ret []byte
|
||||
leftOverGas uint64
|
||||
addr common.Address
|
||||
vmerr error
|
||||
senderRef = vm.AccountRef(st.Sender)
|
||||
)
|
||||
|
||||
@ -91,52 +101,77 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*big.Int, sdk.Result)
|
||||
// Set nonce of sender account before evm state transition for usage in generating Create address
|
||||
st.Csdb.SetNonce(st.Sender, st.AccountNonce)
|
||||
|
||||
if contractCreation {
|
||||
ret, addr, leftOverGas, vmerr = vmenv.Create(senderRef, st.Payload, gasLimit, st.Amount)
|
||||
} else {
|
||||
switch contractCreation {
|
||||
case true:
|
||||
ret, addr, leftOverGas, err = evm.Create(senderRef, st.Payload, gasLimit, st.Amount)
|
||||
default:
|
||||
// Increment the nonce for the next transaction (just for evm state transition)
|
||||
csdb.SetNonce(st.Sender, csdb.GetNonce(st.Sender)+1)
|
||||
|
||||
ret, leftOverGas, vmerr = vmenv.Call(senderRef, *st.Recipient, st.Payload, gasLimit, st.Amount)
|
||||
ret, leftOverGas, err = evm.Call(senderRef, *st.Recipient, st.Payload, gasLimit, st.Amount)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gasConsumed := gasLimit - leftOverGas
|
||||
|
||||
// Resets nonce to value pre state transition
|
||||
st.Csdb.SetNonce(st.Sender, currentNonce)
|
||||
|
||||
// Generate bloom filter to be saved in tx receipt data
|
||||
bloomInt := big.NewInt(0)
|
||||
var bloomFilter ethtypes.Bloom
|
||||
var logs []*ethtypes.Log
|
||||
if st.THash != nil && !st.Simulate {
|
||||
logs := csdb.GetLogs(*st.THash)
|
||||
logs, err = csdb.GetLogs(*st.THash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bloomInt = ethtypes.LogsBloom(logs)
|
||||
bloomFilter = ethtypes.BytesToBloom(bloomInt.Bytes())
|
||||
}
|
||||
|
||||
// Encode all necessary data into slice of bytes to return in sdk result
|
||||
returnData := EncodeReturnData(addr, bloomFilter, ret)
|
||||
res := &ResultData{
|
||||
Address: addr,
|
||||
Bloom: bloomFilter,
|
||||
Logs: logs,
|
||||
Ret: ret,
|
||||
}
|
||||
|
||||
resultData, err := EncodeResultData(res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// handle errors
|
||||
if vmerr != nil {
|
||||
res := emint.ErrVMExecution(vmerr.Error()).Result()
|
||||
if vmerr == vm.ErrOutOfGas || vmerr == vm.ErrCodeStoreOutOfGas {
|
||||
res = sdk.ErrOutOfGas("EVM execution went out of gas").Result()
|
||||
if err != nil {
|
||||
if err == vm.ErrOutOfGas || err == vm.ErrCodeStoreOutOfGas {
|
||||
return nil, fmt.Errorf("evm execution went out of gas: %s", err.Error())
|
||||
}
|
||||
res.Data = returnData
|
||||
|
||||
// Consume gas before returning
|
||||
ctx.GasMeter().ConsumeGas(gasLimit-leftOverGas, "EVM execution consumption")
|
||||
return nil, res
|
||||
ctx.GasMeter().ConsumeGas(gasConsumed, "EVM execution consumption")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: Refund unused gas here, if intended in future
|
||||
|
||||
if !st.Simulate {
|
||||
// Finalise state if not a simulated transaction
|
||||
st.Csdb.Finalise(true) // Change to depend on config
|
||||
// TODO: change to depend on config
|
||||
if err := st.Csdb.Finalise(true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Consume gas from evm execution
|
||||
// Out of gas check does not need to be done here since it is done within the EVM execution
|
||||
ctx.WithGasMeter(currentGasMeter).GasMeter().ConsumeGas(gasLimit-leftOverGas, "EVM execution consumption")
|
||||
ctx.WithGasMeter(currentGasMeter).GasMeter().ConsumeGas(gasConsumed, "EVM execution consumption")
|
||||
|
||||
return bloomInt, sdk.Result{Data: returnData, GasUsed: st.GasLimit - leftOverGas}
|
||||
returnData.Logs = logs
|
||||
returnData.Bloom = bloomInt
|
||||
returnData.Result = &sdk.Result{Data: resultData, GasUsed: gasConsumed}
|
||||
return returnData, nil
|
||||
}
|
||||
|
@ -7,8 +7,6 @@ import (
|
||||
"sync"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
|
||||
emint "github.com/cosmos/ethermint/types"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
@ -41,9 +39,9 @@ type CommitStateDB struct {
|
||||
// StateDB interface. Perhaps there is a better way.
|
||||
ctx sdk.Context
|
||||
|
||||
ak auth.AccountKeeper
|
||||
storageKey sdk.StoreKey
|
||||
codeKey sdk.StoreKey
|
||||
codeKey sdk.StoreKey
|
||||
storeKey sdk.StoreKey // i.e storage key
|
||||
accountKeeper AccountKeeper
|
||||
|
||||
// maps that hold 'live' objects, which will get modified while processing a
|
||||
// state transition
|
||||
@ -84,12 +82,14 @@ type CommitStateDB struct {
|
||||
//
|
||||
// CONTRACT: Stores used for state must be cache-wrapped as the ordering of the
|
||||
// key/value space matters in determining the merkle root.
|
||||
func NewCommitStateDB(ctx sdk.Context, ak auth.AccountKeeper, storageKey, codeKey sdk.StoreKey) *CommitStateDB {
|
||||
func NewCommitStateDB(
|
||||
ctx sdk.Context, codeKey, storeKey sdk.StoreKey, ak AccountKeeper,
|
||||
) *CommitStateDB {
|
||||
return &CommitStateDB{
|
||||
ctx: ctx,
|
||||
ak: ak,
|
||||
storageKey: storageKey,
|
||||
codeKey: codeKey,
|
||||
storeKey: storeKey,
|
||||
accountKeeper: ak,
|
||||
stateObjects: make(map[ethcmn.Address]*stateObject),
|
||||
stateObjectsDirty: make(map[ethcmn.Address]struct{}),
|
||||
logs: make(map[ethcmn.Hash][]*ethtypes.Log),
|
||||
@ -288,12 +288,28 @@ func (csdb *CommitStateDB) GetCommittedState(addr ethcmn.Address, hash ethcmn.Ha
|
||||
}
|
||||
|
||||
// GetLogs returns the current logs for a given hash in the state.
|
||||
func (csdb *CommitStateDB) GetLogs(hash ethcmn.Hash) []*ethtypes.Log {
|
||||
return csdb.logs[hash]
|
||||
func (csdb *CommitStateDB) GetLogs(hash ethcmn.Hash) ([]*ethtypes.Log, error) {
|
||||
if csdb.logs[hash] != nil {
|
||||
return csdb.logs[hash], nil
|
||||
}
|
||||
|
||||
store := csdb.ctx.KVStore(csdb.storeKey)
|
||||
|
||||
encLogs := store.Get(LogsKey(hash[:]))
|
||||
if len(encLogs) == 0 {
|
||||
return []*ethtypes.Log{}, nil
|
||||
}
|
||||
|
||||
logs, err := DecodeLogs(encLogs)
|
||||
if err != nil {
|
||||
return []*ethtypes.Log{}, err
|
||||
}
|
||||
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
// Logs returns all the current logs in the state.
|
||||
func (csdb *CommitStateDB) Logs() []*ethtypes.Log {
|
||||
func (csdb *CommitStateDB) AllLogs() []*ethtypes.Log {
|
||||
// nolint: prealloc
|
||||
var logs []*ethtypes.Log
|
||||
for _, lgs := range csdb.logs {
|
||||
@ -364,7 +380,9 @@ func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (ethcmn.Hash, error)
|
||||
}
|
||||
|
||||
// update the object in the KVStore
|
||||
csdb.updateStateObject(so)
|
||||
if err := csdb.updateStateObject(so); err != nil {
|
||||
return ethcmn.Hash{}, err
|
||||
}
|
||||
}
|
||||
|
||||
delete(csdb.stateObjectsDirty, addr)
|
||||
@ -379,7 +397,7 @@ func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (ethcmn.Hash, error)
|
||||
// Finalise finalizes the state objects (accounts) state by setting their state,
|
||||
// removing the csdb destructed objects and clearing the journal as well as the
|
||||
// refunds.
|
||||
func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) {
|
||||
func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) error {
|
||||
for addr := range csdb.journal.dirties {
|
||||
so, exist := csdb.stateObjects[addr]
|
||||
if !exist {
|
||||
@ -401,7 +419,9 @@ func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) {
|
||||
// Set all the dirty state storage items for the state object in the
|
||||
// KVStore and finally set the account in the account mapper.
|
||||
so.commitState()
|
||||
csdb.updateStateObject(so)
|
||||
if err := csdb.updateStateObject(so); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
csdb.stateObjectsDirty[addr] = struct{}{}
|
||||
@ -409,6 +429,7 @@ func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) {
|
||||
|
||||
// invalidate journal because reverting across transactions is not allowed
|
||||
csdb.clearJournalAndRefund()
|
||||
return nil
|
||||
}
|
||||
|
||||
// IntermediateRoot returns the current root hash of the state. It is called in
|
||||
@ -418,21 +439,24 @@ func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) {
|
||||
// NOTE: The SDK has not concept or method of getting any intermediate merkle
|
||||
// root as commitment of the merkle-ized tree doesn't happen until the
|
||||
// BaseApps' EndBlocker.
|
||||
func (csdb *CommitStateDB) IntermediateRoot(deleteEmptyObjects bool) ethcmn.Hash {
|
||||
csdb.Finalise(deleteEmptyObjects)
|
||||
func (csdb *CommitStateDB) IntermediateRoot(deleteEmptyObjects bool) (ethcmn.Hash, error) {
|
||||
if err := csdb.Finalise(deleteEmptyObjects); err != nil {
|
||||
return ethcmn.Hash{}, err
|
||||
}
|
||||
|
||||
return ethcmn.Hash{}
|
||||
return ethcmn.Hash{}, nil
|
||||
}
|
||||
|
||||
// updateStateObject writes the given state object to the store.
|
||||
func (csdb *CommitStateDB) updateStateObject(so *stateObject) {
|
||||
csdb.ak.SetAccount(csdb.ctx, so.account)
|
||||
func (csdb *CommitStateDB) updateStateObject(so *stateObject) error {
|
||||
csdb.accountKeeper.SetAccount(csdb.ctx, so.account)
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteStateObject removes the given state object from the state store.
|
||||
func (csdb *CommitStateDB) deleteStateObject(so *stateObject) {
|
||||
so.deleted = true
|
||||
csdb.ak.RemoveAccount(csdb.ctx, so.account)
|
||||
csdb.accountKeeper.RemoveAccount(csdb.ctx, so.account)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -543,10 +567,10 @@ func (csdb *CommitStateDB) Reset(_ ethcmn.Hash) error {
|
||||
// UpdateAccounts updates the nonce and coin balances of accounts
|
||||
func (csdb *CommitStateDB) UpdateAccounts() {
|
||||
for addr, so := range csdb.stateObjects {
|
||||
currAcc := csdb.ak.GetAccount(csdb.ctx, sdk.AccAddress(addr.Bytes()))
|
||||
currAcc := csdb.accountKeeper.GetAccount(csdb.ctx, sdk.AccAddress(addr.Bytes()))
|
||||
emintAcc, ok := currAcc.(*emint.Account)
|
||||
if ok {
|
||||
if (so.Balance() != emintAcc.Balance().BigInt()) || (so.Nonce() != emintAcc.GetSequence()) {
|
||||
if so.Balance() != emintAcc.Balance().BigInt() || so.Nonce() != emintAcc.GetSequence() {
|
||||
// If queried account's balance or nonce are invalid, update the account pointer
|
||||
so.account = emintAcc
|
||||
}
|
||||
@ -602,9 +626,9 @@ func (csdb *CommitStateDB) Copy() *CommitStateDB {
|
||||
// copy all the basic fields, initialize the memory ones
|
||||
state := &CommitStateDB{
|
||||
ctx: csdb.ctx,
|
||||
ak: csdb.ak,
|
||||
storageKey: csdb.storageKey,
|
||||
codeKey: csdb.codeKey,
|
||||
storeKey: csdb.storeKey,
|
||||
accountKeeper: csdb.accountKeeper,
|
||||
stateObjects: make(map[ethcmn.Address]*stateObject, len(csdb.journal.dirties)),
|
||||
stateObjectsDirty: make(map[ethcmn.Address]struct{}, len(csdb.journal.dirties)),
|
||||
refund: csdb.refund,
|
||||
@ -663,8 +687,9 @@ func (csdb *CommitStateDB) ForEachStorage(addr ethcmn.Address, cb func(key, valu
|
||||
return nil
|
||||
}
|
||||
|
||||
store := csdb.ctx.KVStore(csdb.storageKey)
|
||||
store := csdb.ctx.KVStore(csdb.storeKey)
|
||||
iter := sdk.KVStorePrefixIterator(store, so.Address().Bytes())
|
||||
defer iter.Close()
|
||||
|
||||
for ; iter.Valid(); iter.Next() {
|
||||
key := ethcmn.BytesToHash(iter.Key())
|
||||
@ -678,7 +703,6 @@ func (csdb *CommitStateDB) ForEachStorage(addr ethcmn.Address, cb func(key, valu
|
||||
cb(key, ethcmn.BytesToHash(value))
|
||||
}
|
||||
|
||||
iter.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -698,8 +722,9 @@ func (csdb *CommitStateDB) GetOrNewStateObject(addr ethcmn.Address) StateObject
|
||||
func (csdb *CommitStateDB) createObject(addr ethcmn.Address) (newObj, prevObj *stateObject) {
|
||||
prevObj = csdb.getStateObject(addr)
|
||||
|
||||
acc := csdb.ak.NewAccountWithAddress(csdb.ctx, sdk.AccAddress(addr.Bytes()))
|
||||
newObj = newObject(csdb, acc)
|
||||
acc := csdb.accountKeeper.NewAccountWithAddress(csdb.ctx, sdk.AccAddress(addr.Bytes()))
|
||||
|
||||
newObj = newStateObject(csdb, acc)
|
||||
newObj.setNonce(0) // sets the object to dirty
|
||||
|
||||
if prevObj == nil {
|
||||
@ -732,14 +757,14 @@ func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *sta
|
||||
}
|
||||
|
||||
// otherwise, attempt to fetch the account from the account mapper
|
||||
acc := csdb.ak.GetAccount(csdb.ctx, addr.Bytes())
|
||||
acc := csdb.accountKeeper.GetAccount(csdb.ctx, addr.Bytes())
|
||||
if acc == nil {
|
||||
csdb.setError(fmt.Errorf("no account found for address: %X", addr.Bytes()))
|
||||
return nil
|
||||
}
|
||||
|
||||
// insert the state object into the live set
|
||||
so := newObject(csdb, acc)
|
||||
so := newStateObject(csdb, acc)
|
||||
csdb.setStateObject(so)
|
||||
|
||||
return so
|
||||
@ -748,3 +773,10 @@ func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *sta
|
||||
func (csdb *CommitStateDB) setStateObject(so *stateObject) {
|
||||
csdb.stateObjects[so.Address()] = so
|
||||
}
|
||||
|
||||
// RawDump returns a raw state dump.
|
||||
//
|
||||
// TODO: Implement if we need it, especially for the RPC API.
|
||||
func (csdb *CommitStateDB) RawDump() ethstate.Dump {
|
||||
return ethstate.Dump{}
|
||||
}
|
||||
|
@ -1,80 +1,40 @@
|
||||
package types
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
sdkstore "github.com/cosmos/cosmos-sdk/store/types"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/params"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
|
||||
"github.com/cosmos/ethermint/types"
|
||||
"github.com/cosmos/ethermint/app"
|
||||
"github.com/cosmos/ethermint/x/evm/keeper"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmlog "github.com/tendermint/tendermint/libs/log"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
)
|
||||
|
||||
func newTestCodec() *codec.Codec {
|
||||
cdc := codec.New()
|
||||
// nolint: unused
|
||||
type StateDBTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
RegisterCodec(cdc)
|
||||
types.RegisterCodec(cdc)
|
||||
auth.RegisterCodec(cdc)
|
||||
sdk.RegisterCodec(cdc)
|
||||
codec.RegisterCrypto(cdc)
|
||||
|
||||
return cdc
|
||||
ctx sdk.Context
|
||||
querier sdk.Querier
|
||||
app *app.EthermintApp
|
||||
}
|
||||
|
||||
func setupStateDB() (*CommitStateDB, error) {
|
||||
accKey := sdk.NewKVStoreKey("acc")
|
||||
storageKey := sdk.NewKVStoreKey(EvmStoreKey)
|
||||
codeKey := sdk.NewKVStoreKey(EvmCodeKey)
|
||||
logger := tmlog.NewNopLogger()
|
||||
func (suite *StateDBTestSuite) SetupTest() {
|
||||
checkTx := false
|
||||
|
||||
db := dbm.NewMemDB()
|
||||
|
||||
// create logger, codec and root multi-store
|
||||
cdc := newTestCodec()
|
||||
cms := store.NewCommitMultiStore(db)
|
||||
|
||||
// The ParamsKeeper handles parameter storage for the application
|
||||
keyParams := sdk.NewKVStoreKey(params.StoreKey)
|
||||
tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey)
|
||||
paramsKeeper := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace)
|
||||
// Set specific supspaces
|
||||
authSubspace := paramsKeeper.Subspace(auth.DefaultParamspace)
|
||||
ak := auth.NewAccountKeeper(cdc, accKey, authSubspace, types.ProtoBaseAccount)
|
||||
|
||||
// mount stores
|
||||
keys := []*sdk.KVStoreKey{accKey, storageKey, codeKey}
|
||||
for _, key := range keys {
|
||||
cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, nil)
|
||||
}
|
||||
|
||||
cms.SetPruning(sdkstore.PruneNothing)
|
||||
|
||||
// load latest version (root)
|
||||
if err := cms.LoadLatestVersion(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ms := cms.CacheMultiStore()
|
||||
ctx := sdk.NewContext(ms, abci.Header{}, false, logger)
|
||||
return NewCommitStateDB(ctx, ak, storageKey, codeKey), nil
|
||||
suite.app = app.Setup(checkTx)
|
||||
suite.ctx = suite.app.BaseApp.NewContext(checkTx, abci.Header{Height: 1})
|
||||
suite.querier = keeper.NewQuerier(suite.app.EvmKeeper)
|
||||
}
|
||||
|
||||
func TestBloomFilter(t *testing.T) {
|
||||
stateDB, err := setupStateDB()
|
||||
require.NoError(t, err)
|
||||
func (suite *StateDBTestSuite) TestBloomFilter() {
|
||||
stateDB := suite.app.EvmKeeper.CommitStateDB
|
||||
|
||||
// Prepare db for logs
|
||||
tHash := ethcmn.BytesToHash([]byte{0x1})
|
||||
@ -87,14 +47,15 @@ func TestBloomFilter(t *testing.T) {
|
||||
stateDB.AddLog(&log)
|
||||
|
||||
// Get log from db
|
||||
logs := stateDB.GetLogs(tHash)
|
||||
require.Equal(t, len(logs), 1)
|
||||
logs, err := stateDB.GetLogs(tHash)
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(len(logs), 1)
|
||||
|
||||
// get logs bloom from the log
|
||||
bloomInt := ethtypes.LogsBloom(logs)
|
||||
bloomFilter := ethtypes.BytesToBloom(bloomInt.Bytes())
|
||||
|
||||
// Check to make sure bloom filter will succeed on
|
||||
require.True(t, ethtypes.BloomLookup(bloomFilter, contractAddress))
|
||||
require.False(t, ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2))))
|
||||
suite.Require().True(ethtypes.BloomLookup(bloomFilter, contractAddress))
|
||||
suite.Require().False(ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2))))
|
||||
}
|
||||
|
@ -13,11 +13,6 @@ import (
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
const (
|
||||
bloomIdx = ethcmn.AddressLength
|
||||
returnIdx = bloomIdx + ethtypes.BloomByteLength
|
||||
)
|
||||
|
||||
// GenerateEthAddress generates an Ethereum address.
|
||||
func GenerateEthAddress() ethcmn.Address {
|
||||
priv, err := crypto.GenerateKey()
|
||||
@ -52,23 +47,41 @@ func rlpHash(x interface{}) (hash ethcmn.Hash) {
|
||||
return hash
|
||||
}
|
||||
|
||||
// ResultData represents the data returned in an sdk.Result
|
||||
type ResultData struct {
|
||||
Address ethcmn.Address
|
||||
Bloom ethtypes.Bloom
|
||||
Logs []*ethtypes.Log
|
||||
Ret []byte
|
||||
}
|
||||
|
||||
// EncodeReturnData takes all of the necessary data from the EVM execution
|
||||
// and returns the data as a byte slice
|
||||
func EncodeReturnData(addr ethcmn.Address, bloom ethtypes.Bloom, evmRet []byte) []byte {
|
||||
// Append address, bloom, evm return bytes in that order
|
||||
returnData := append(addr.Bytes(), bloom.Bytes()...)
|
||||
return append(returnData, evmRet...)
|
||||
// and returns the data as a byte slice encoded with amino
|
||||
func EncodeResultData(data *ResultData) ([]byte, error) {
|
||||
return ModuleCdc.MarshalBinaryLengthPrefixed(data)
|
||||
}
|
||||
|
||||
// DecodeReturnData decodes the byte slice of values to their respective types
|
||||
func DecodeReturnData(bytes []byte) (addr ethcmn.Address, bloom ethtypes.Bloom, ret []byte, err error) {
|
||||
if len(bytes) >= returnIdx {
|
||||
addr = ethcmn.BytesToAddress(bytes[:bloomIdx])
|
||||
bloom = ethtypes.BytesToBloom(bytes[bloomIdx:returnIdx])
|
||||
ret = bytes[returnIdx:]
|
||||
} else {
|
||||
err = fmt.Errorf("invalid format for encoded data, message must be an EVM state transition")
|
||||
// DecodeResultData decodes an amino-encoded byte slice into ReturnData
|
||||
func DecodeResultData(in []byte) (ResultData, error) {
|
||||
data := new(ResultData)
|
||||
err := ModuleCdc.UnmarshalBinaryLengthPrefixed(in, data)
|
||||
if err != nil {
|
||||
return ResultData{}, err
|
||||
}
|
||||
|
||||
return
|
||||
return *data, nil
|
||||
}
|
||||
|
||||
// EncodeLogs encodes an array of logs using amino
|
||||
func EncodeLogs(logs []*ethtypes.Log) ([]byte, error) {
|
||||
return ModuleCdc.MarshalBinaryLengthPrefixed(logs)
|
||||
}
|
||||
|
||||
// DecodeLogs decodes an amino-encoded byte array into an array of logs
|
||||
func DecodeLogs(in []byte) ([]*ethtypes.Log, error) {
|
||||
logs := []*ethtypes.Log{}
|
||||
err := ModuleCdc.UnmarshalBinaryLengthPrefixed(in, &logs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return logs, nil
|
||||
}
|
||||
|
@ -13,12 +13,23 @@ func TestEvmDataEncoding(t *testing.T) {
|
||||
bloom := ethtypes.BytesToBloom([]byte{0x1, 0x3})
|
||||
ret := []byte{0x5, 0x8}
|
||||
|
||||
encoded := EncodeReturnData(addr, bloom, ret)
|
||||
|
||||
decAddr, decBloom, decRet, err := DecodeReturnData(encoded)
|
||||
data := &ResultData{
|
||||
Address: addr,
|
||||
Bloom: bloom,
|
||||
Logs: []*ethtypes.Log{{
|
||||
Data: []byte{1, 2, 3, 4},
|
||||
BlockNumber: 17,
|
||||
}},
|
||||
Ret: ret,
|
||||
}
|
||||
|
||||
enc, err := EncodeResultData(data)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, addr, decAddr)
|
||||
require.Equal(t, bloom, decBloom)
|
||||
require.Equal(t, ret, decRet)
|
||||
|
||||
res, err := DecodeResultData(enc)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, addr, res.Address)
|
||||
require.Equal(t, bloom, res.Bloom)
|
||||
require.Equal(t, data.Logs, res.Logs)
|
||||
require.Equal(t, ret, res.Ret)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user