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
|
### Improvements
|
||||||
|
|
||||||
* (x/evm) [\#181](https://github.com/ChainSafe/ethermint/issues/181) Updated EVM module to the recommended module structure. [@fedekunze](https://github.com/fedekunze)
|
* (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 (
|
import (
|
||||||
"fmt"
|
"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.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators
|
||||||
ante.NewValidateSigCountDecorator(ak),
|
ante.NewValidateSigCountDecorator(ak),
|
||||||
ante.NewDeductFeeDecorator(ak, sk),
|
ante.NewDeductFeeDecorator(ak, sk),
|
||||||
ante.NewSigGasConsumeDecorator(ak, consumeSigGas),
|
ante.NewSigGasConsumeDecorator(ak, sigGasConsumer),
|
||||||
ante.NewSigVerificationDecorator(ak),
|
ante.NewSigVerificationDecorator(ak),
|
||||||
ante.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator
|
ante.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator
|
||||||
)
|
)
|
||||||
|
|
||||||
return stdAnte(ctx, tx, sim)
|
return stdAnte(ctx, tx, sim)
|
||||||
|
|
||||||
case *evmtypes.EthereumTxMsg:
|
case evmtypes.MsgEthereumTx:
|
||||||
return ethAnteHandler(ctx, ak, sk, castTx, sim)
|
return ethAnteHandler(ctx, ak, sk, &castTx, sim)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return ctx, sdk.ErrInternal(fmt.Sprintf("transaction type invalid: %T", tx))
|
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,
|
meter sdk.GasMeter, sig []byte, pubkey tmcrypto.PubKey, params types.Params,
|
||||||
) error {
|
) error {
|
||||||
switch pubkey.(type) {
|
switch pubkey.(type) {
|
||||||
@ -89,7 +91,7 @@ func consumeSigGas(
|
|||||||
// prevent spam and DoS attacks.
|
// prevent spam and DoS attacks.
|
||||||
func ethAnteHandler(
|
func ethAnteHandler(
|
||||||
ctx sdk.Context, ak auth.AccountKeeper, sk types.SupplyKeeper,
|
ctx sdk.Context, ak auth.AccountKeeper, sk types.SupplyKeeper,
|
||||||
ethTxMsg *evmtypes.EthereumTxMsg, sim bool,
|
ethTxMsg *evmtypes.MsgEthereumTx, sim bool,
|
||||||
) (newCtx sdk.Context, err error) {
|
) (newCtx sdk.Context, err error) {
|
||||||
|
|
||||||
var senderAddr sdk.AccAddress
|
var senderAddr sdk.AccAddress
|
||||||
@ -120,7 +122,9 @@ func ethAnteHandler(
|
|||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
switch rType := r.(type) {
|
switch rType := r.(type) {
|
||||||
case sdk.ErrorOutOfGas:
|
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)
|
err = sdk.ErrOutOfGas(log)
|
||||||
default:
|
default:
|
||||||
panic(r)
|
panic(r)
|
||||||
@ -139,9 +143,9 @@ func ethAnteHandler(
|
|||||||
// Cost calculates the fees paid to validators based on gas limit and price
|
// 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))
|
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)),
|
sdk.NewCoin(emint.DenomDefault, sdk.NewIntFromBigInt(cost)),
|
||||||
}
|
)
|
||||||
|
|
||||||
err = auth.DeductFees(sk, ctx, senderAcc, feeAmt)
|
err = auth.DeductFees(sk, ctx, senderAcc, feeAmt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -166,7 +170,7 @@ func ethAnteHandler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func validateEthTxCheckTx(
|
func validateEthTxCheckTx(
|
||||||
ctx sdk.Context, ak auth.AccountKeeper, ethTxMsg *evmtypes.EthereumTxMsg,
|
ctx sdk.Context, ak auth.AccountKeeper, ethTxMsg *evmtypes.MsgEthereumTx,
|
||||||
) (sdk.AccAddress, error) {
|
) (sdk.AccAddress, error) {
|
||||||
// Validate sufficient fees have been provided that meet a minimum threshold
|
// Validate sufficient fees have been provided that meet a minimum threshold
|
||||||
// defined by the proposer (for mempool purposes during CheckTx).
|
// defined by the proposer (for mempool purposes during CheckTx).
|
||||||
@ -193,7 +197,7 @@ func validateEthTxCheckTx(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validates signature and returns sender address
|
// 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
|
// parse the chainID from a string to a base-10 integer
|
||||||
chainID, ok := new(big.Int).SetString(ctx.ChainID(), 10)
|
chainID, ok := new(big.Int).SetString(ctx.ChainID(), 10)
|
||||||
if !ok {
|
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
|
// 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
|
// constant value of 21000 plus any cost inccured by additional bytes of data
|
||||||
// supplied with the transaction.
|
// 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)
|
gas, err := ethcore.IntrinsicGas(ethTxMsg.Data.Payload, ethTxMsg.To() == nil, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sdk.ErrInternal(fmt.Sprintf("failed to compute intrinsic gas cost: %s", err))
|
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 {
|
if ethTxMsg.Data.GasLimit < gas {
|
||||||
return sdk.ErrInternal(
|
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
|
// validateAccount validates the account nonce and that the account has enough
|
||||||
// funds to cover the tx cost.
|
// funds to cover the tx cost.
|
||||||
func validateAccount(
|
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 {
|
) error {
|
||||||
|
|
||||||
acc := ak.GetAccount(ctx, signer)
|
acc := ak.GetAccount(ctx, signer)
|
||||||
@ -241,8 +245,9 @@ func validateAccount(
|
|||||||
if ctx.BlockHeight() == 0 && acc.GetAccountNumber() != 0 {
|
if ctx.BlockHeight() == 0 && acc.GetAccountNumber() != 0 {
|
||||||
return sdk.ErrInternal(
|
return sdk.ErrInternal(
|
||||||
fmt.Sprintf(
|
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
|
// Validate nonce is correct
|
||||||
@ -262,7 +267,7 @@ func validateAccount(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func checkNonce(
|
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 {
|
) error {
|
||||||
acc := ak.GetAccount(ctx, signer)
|
acc := ak.GetAccount(ctx, signer)
|
||||||
// Validate the transaction nonce is valid (equivalent to the sender account’s
|
// Validate the transaction nonce is valid (equivalent to the sender account’s
|
||||||
@ -270,7 +275,8 @@ func checkNonce(
|
|||||||
seq := acc.GetSequence()
|
seq := acc.GetSequence()
|
||||||
if ethTxMsg.Data.AccountNonce != seq {
|
if ethTxMsg.Data.AccountNonce != seq {
|
||||||
return sdk.ErrInvalidSequence(
|
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
|
return nil
|
||||||
@ -281,7 +287,7 @@ func checkNonce(
|
|||||||
// proposer.
|
// proposer.
|
||||||
//
|
//
|
||||||
// NOTE: This should only be ran during a CheckTx mode.
|
// 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 = GP * GL
|
||||||
fee := sdk.NewDecCoinFromCoin(sdk.NewInt64Coin(emint.DenomDefault, ethTxMsg.Fee().Int64()))
|
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 {
|
if !ctx.MinGasPrices().IsZero() && !allGTE {
|
||||||
// reject the transaction that does not meet the minimum fee
|
// reject the transaction that does not meet the minimum fee
|
||||||
return sdk.ErrInsufficientFee(
|
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
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
emintcrypto "github.com/cosmos/ethermint/crypto"
|
|
||||||
"github.com/cosmos/ethermint/x/evm"
|
|
||||||
|
|
||||||
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
cryptokeys "github.com/cosmos/cosmos-sdk/crypto/keys"
|
cryptokeys "github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||||
|
"github.com/cosmos/cosmos-sdk/simapp"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/types/module"
|
"github.com/cosmos/cosmos-sdk/types/module"
|
||||||
"github.com/cosmos/cosmos-sdk/version"
|
"github.com/cosmos/cosmos-sdk/version"
|
||||||
@ -26,12 +24,14 @@ import (
|
|||||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||||
"github.com/cosmos/cosmos-sdk/x/supply"
|
"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"
|
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"
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
cmn "github.com/tendermint/tendermint/libs/common"
|
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"
|
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 sets the folder where the applcation data and configuration will be stored
|
||||||
DefaultNodeHome = os.ExpandEnv("$HOME/.emintd")
|
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
|
// non-dependant module elements, such as codec registration
|
||||||
// and genesis verification.
|
// and genesis verification.
|
||||||
ModuleBasics = module.NewBasicManager(
|
ModuleBasics = module.NewBasicManager(
|
||||||
genutil.AppModuleBasic{},
|
|
||||||
auth.AppModuleBasic{},
|
auth.AppModuleBasic{},
|
||||||
|
supply.AppModuleBasic{},
|
||||||
|
genutil.AppModuleBasic{},
|
||||||
bank.AppModuleBasic{},
|
bank.AppModuleBasic{},
|
||||||
staking.AppModuleBasic{},
|
staking.AppModuleBasic{},
|
||||||
mint.AppModuleBasic{},
|
mint.AppModuleBasic{},
|
||||||
distr.AppModuleBasic{},
|
distr.AppModuleBasic{},
|
||||||
gov.NewAppModuleBasic(paramsclient.ProposalHandler, distr.ProposalHandler),
|
gov.NewAppModuleBasic(
|
||||||
|
paramsclient.ProposalHandler, distr.ProposalHandler,
|
||||||
|
),
|
||||||
params.AppModuleBasic{},
|
params.AppModuleBasic{},
|
||||||
crisis.AppModuleBasic{},
|
crisis.AppModuleBasic{},
|
||||||
slashing.AppModuleBasic{},
|
slashing.AppModuleBasic{},
|
||||||
supply.AppModuleBasic{},
|
|
||||||
evm.AppModuleBasic{},
|
evm.AppModuleBasic{},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -71,6 +73,11 @@ var (
|
|||||||
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
|
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
|
||||||
gov.ModuleName: {supply.Burner},
|
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
|
// MakeCodec generates the necessary codecs for Amino
|
||||||
@ -100,18 +107,21 @@ type EthermintApp struct {
|
|||||||
keys map[string]*sdk.KVStoreKey
|
keys map[string]*sdk.KVStoreKey
|
||||||
tkeys map[string]*sdk.TransientStoreKey
|
tkeys map[string]*sdk.TransientStoreKey
|
||||||
|
|
||||||
|
// subspaces
|
||||||
|
subspaces map[string]params.Subspace
|
||||||
|
|
||||||
// keepers
|
// keepers
|
||||||
accountKeeper auth.AccountKeeper
|
AccountKeeper auth.AccountKeeper
|
||||||
bankKeeper bank.Keeper
|
BankKeeper bank.Keeper
|
||||||
supplyKeeper supply.Keeper
|
SupplyKeeper supply.Keeper
|
||||||
stakingKeeper staking.Keeper
|
StakingKeeper staking.Keeper
|
||||||
slashingKeeper slashing.Keeper
|
SlashingKeeper slashing.Keeper
|
||||||
mintKeeper mint.Keeper
|
MintKeeper mint.Keeper
|
||||||
distrKeeper distr.Keeper
|
DistrKeeper distr.Keeper
|
||||||
govKeeper gov.Keeper
|
GovKeeper gov.Keeper
|
||||||
crisisKeeper crisis.Keeper
|
CrisisKeeper crisis.Keeper
|
||||||
paramsKeeper params.Keeper
|
ParamsKeeper params.Keeper
|
||||||
evmKeeper evm.Keeper
|
EvmKeeper evm.Keeper
|
||||||
|
|
||||||
// the module manager
|
// the module manager
|
||||||
mm *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.
|
// 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.
|
// For now, it will support only running as a sovereign application.
|
||||||
func NewEthermintApp(
|
func NewEthermintApp(
|
||||||
logger tmlog.Logger, db dbm.DB, loadLatest bool,
|
logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
|
||||||
invCheckPeriod uint, baseAppOptions ...func(*bam.BaseApp)) *EthermintApp {
|
invCheckPeriod uint, baseAppOptions ...func(*bam.BaseApp),
|
||||||
|
) *EthermintApp {
|
||||||
|
|
||||||
cdc := MakeCodec()
|
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)
|
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,
|
supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey,
|
||||||
gov.StoreKey, params.StoreKey, evmtypes.EvmStoreKey, evmtypes.EvmCodeKey)
|
gov.StoreKey, params.StoreKey, evm.CodeKey, evm.StoreKey,
|
||||||
blockKey := sdk.NewKVStoreKey(evmtypes.EvmBlockKey)
|
)
|
||||||
tkeys := sdk.NewTransientStoreKeys(staking.TStoreKey, params.TStoreKey)
|
blockKey := sdk.NewKVStoreKey(evm.BlockKey)
|
||||||
|
|
||||||
|
tkeys := sdk.NewTransientStoreKeys(params.TStoreKey)
|
||||||
|
|
||||||
app := &EthermintApp{
|
app := &EthermintApp{
|
||||||
BaseApp: bApp,
|
BaseApp: bApp,
|
||||||
@ -143,89 +160,118 @@ func NewEthermintApp(
|
|||||||
invCheckPeriod: invCheckPeriod,
|
invCheckPeriod: invCheckPeriod,
|
||||||
keys: keys,
|
keys: keys,
|
||||||
tkeys: tkeys,
|
tkeys: tkeys,
|
||||||
|
subspaces: make(map[string]params.Subspace),
|
||||||
}
|
}
|
||||||
|
|
||||||
// init params keeper and subspaces
|
// init params keeper and subspaces
|
||||||
app.paramsKeeper = params.NewKeeper(app.cdc, keys[params.StoreKey], tkeys[params.TStoreKey], params.DefaultCodespace)
|
app.ParamsKeeper = params.NewKeeper(app.cdc, keys[params.StoreKey], tkeys[params.TStoreKey], params.DefaultCodespace)
|
||||||
authSubspace := app.paramsKeeper.Subspace(auth.DefaultParamspace)
|
app.subspaces[auth.ModuleName] = app.ParamsKeeper.Subspace(auth.DefaultParamspace)
|
||||||
bankSubspace := app.paramsKeeper.Subspace(bank.DefaultParamspace)
|
app.subspaces[bank.ModuleName] = app.ParamsKeeper.Subspace(bank.DefaultParamspace)
|
||||||
stakingSubspace := app.paramsKeeper.Subspace(staking.DefaultParamspace)
|
app.subspaces[staking.ModuleName] = app.ParamsKeeper.Subspace(staking.DefaultParamspace)
|
||||||
mintSubspace := app.paramsKeeper.Subspace(mint.DefaultParamspace)
|
app.subspaces[mint.ModuleName] = app.ParamsKeeper.Subspace(mint.DefaultParamspace)
|
||||||
distrSubspace := app.paramsKeeper.Subspace(distr.DefaultParamspace)
|
app.subspaces[distr.ModuleName] = app.ParamsKeeper.Subspace(distr.DefaultParamspace)
|
||||||
slashingSubspace := app.paramsKeeper.Subspace(slashing.DefaultParamspace)
|
app.subspaces[slashing.ModuleName] = app.ParamsKeeper.Subspace(slashing.DefaultParamspace)
|
||||||
govSubspace := app.paramsKeeper.Subspace(gov.DefaultParamspace).WithKeyTable(gov.ParamKeyTable())
|
app.subspaces[gov.ModuleName] = app.ParamsKeeper.Subspace(gov.DefaultParamspace).WithKeyTable(gov.ParamKeyTable())
|
||||||
crisisSubspace := app.paramsKeeper.Subspace(crisis.DefaultParamspace)
|
app.subspaces[crisis.ModuleName] = app.ParamsKeeper.Subspace(crisis.DefaultParamspace)
|
||||||
|
|
||||||
// add keepers
|
// use custom Ethermint account for contracts
|
||||||
app.accountKeeper = auth.NewAccountKeeper(app.cdc, keys[auth.StoreKey], authSubspace, eminttypes.ProtoBaseAccount)
|
app.AccountKeeper = auth.NewAccountKeeper(
|
||||||
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper, bankSubspace, bank.DefaultCodespace, app.ModuleAccountAddrs())
|
app.cdc, keys[auth.StoreKey], app.subspaces[auth.ModuleName], eminttypes.ProtoBaseAccount,
|
||||||
app.supplyKeeper = supply.NewKeeper(app.cdc, keys[supply.StoreKey], app.accountKeeper, app.bankKeeper, maccPerms)
|
)
|
||||||
stakingKeeper := staking.NewKeeper(app.cdc, keys[staking.StoreKey],
|
app.BankKeeper = bank.NewBaseKeeper(
|
||||||
app.supplyKeeper, stakingSubspace, staking.DefaultCodespace)
|
app.AccountKeeper, app.subspaces[bank.ModuleName], bank.DefaultCodespace, app.ModuleAccountAddrs(),
|
||||||
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 = supply.NewKeeper(
|
||||||
app.supplyKeeper, distr.DefaultCodespace, auth.FeeCollectorName, app.ModuleAccountAddrs())
|
app.cdc, keys[supply.StoreKey], app.AccountKeeper, app.BankKeeper, maccPerms,
|
||||||
app.slashingKeeper = slashing.NewKeeper(app.cdc, keys[slashing.StoreKey], &stakingKeeper,
|
)
|
||||||
slashingSubspace, slashing.DefaultCodespace)
|
stakingKeeper := staking.NewKeeper(
|
||||||
app.crisisKeeper = crisis.NewKeeper(crisisSubspace, invCheckPeriod, app.supplyKeeper, auth.FeeCollectorName)
|
app.cdc, keys[staking.StoreKey], app.SupplyKeeper, app.subspaces[staking.ModuleName],
|
||||||
app.evmKeeper = evm.NewKeeper(app.accountKeeper, keys[evmtypes.EvmStoreKey], keys[evmtypes.EvmCodeKey], blockKey, cdc)
|
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
|
// register the proposal types
|
||||||
govRouter := gov.NewRouter()
|
govRouter := gov.NewRouter()
|
||||||
govRouter.AddRoute(gov.RouterKey, gov.ProposalHandler).
|
govRouter.AddRoute(gov.RouterKey, gov.ProposalHandler).
|
||||||
AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.paramsKeeper)).
|
AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.ParamsKeeper)).
|
||||||
AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper))
|
AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.DistrKeeper))
|
||||||
app.govKeeper = gov.NewKeeper(app.cdc, keys[gov.StoreKey], govSubspace,
|
app.GovKeeper = gov.NewKeeper(
|
||||||
app.supplyKeeper, &stakingKeeper, gov.DefaultCodespace, govRouter)
|
app.cdc, keys[gov.StoreKey], app.subspaces[gov.ModuleName], app.SupplyKeeper,
|
||||||
|
&stakingKeeper, gov.DefaultCodespace, govRouter,
|
||||||
|
)
|
||||||
|
|
||||||
// register the staking hooks
|
// register the staking hooks
|
||||||
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
|
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
|
||||||
app.stakingKeeper = *stakingKeeper.SetHooks(
|
app.StakingKeeper = *stakingKeeper.SetHooks(
|
||||||
staking.NewMultiStakingHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks()),
|
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(
|
app.mm = module.NewManager(
|
||||||
genutil.NewAppModule(app.accountKeeper, app.stakingKeeper, app.BaseApp.DeliverTx),
|
genutil.NewAppModule(app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx),
|
||||||
auth.NewAppModule(app.accountKeeper),
|
auth.NewAppModule(app.AccountKeeper),
|
||||||
bank.NewAppModule(app.bankKeeper, app.accountKeeper),
|
bank.NewAppModule(app.BankKeeper, app.AccountKeeper),
|
||||||
crisis.NewAppModule(&app.crisisKeeper),
|
crisis.NewAppModule(&app.CrisisKeeper),
|
||||||
supply.NewAppModule(app.supplyKeeper, app.accountKeeper),
|
supply.NewAppModule(app.SupplyKeeper, app.AccountKeeper),
|
||||||
distr.NewAppModule(app.distrKeeper, app.accountKeeper, app.supplyKeeper, app.stakingKeeper),
|
gov.NewAppModule(app.GovKeeper, app.AccountKeeper, app.SupplyKeeper),
|
||||||
gov.NewAppModule(app.govKeeper, app.accountKeeper, app.supplyKeeper),
|
mint.NewAppModule(app.MintKeeper),
|
||||||
mint.NewAppModule(app.mintKeeper),
|
slashing.NewAppModule(app.SlashingKeeper, app.AccountKeeper, app.StakingKeeper),
|
||||||
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),
|
staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper),
|
||||||
evm.NewAppModule(app.evmKeeper),
|
evm.NewAppModule(app.EvmKeeper),
|
||||||
)
|
)
|
||||||
|
|
||||||
// During begin block slashing happens after distr.BeginBlocker so that
|
// 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
|
// there is nothing left over in the validator fee pool, so as to keep the
|
||||||
// CanWithdrawInvariant invariant.
|
// CanWithdrawInvariant invariant.
|
||||||
app.mm.SetOrderBeginBlockers(evmtypes.ModuleName, mint.ModuleName, distr.ModuleName, slashing.ModuleName)
|
app.mm.SetOrderBeginBlockers(
|
||||||
|
evm.ModuleName, mint.ModuleName, distr.ModuleName, slashing.ModuleName,
|
||||||
app.mm.SetOrderEndBlockers(evmtypes.ModuleName, crisis.ModuleName, gov.ModuleName, staking.ModuleName)
|
)
|
||||||
|
app.mm.SetOrderEndBlockers(
|
||||||
|
evm.ModuleName, crisis.ModuleName, gov.ModuleName, staking.ModuleName,
|
||||||
|
)
|
||||||
|
|
||||||
// NOTE: The genutils module must occur after staking so that pools are
|
// NOTE: The genutils module must occur after staking so that pools are
|
||||||
// properly initialized with tokens from genesis accounts.
|
// properly initialized with tokens from genesis accounts.
|
||||||
app.mm.SetOrderInitGenesis(
|
app.mm.SetOrderInitGenesis(
|
||||||
distr.ModuleName, staking.ModuleName,
|
auth.ModuleName, distr.ModuleName, staking.ModuleName, bank.ModuleName,
|
||||||
auth.ModuleName, bank.ModuleName, slashing.ModuleName, gov.ModuleName,
|
slashing.ModuleName, gov.ModuleName, mint.ModuleName, supply.ModuleName,
|
||||||
mint.ModuleName, supply.ModuleName, crisis.ModuleName, genutil.ModuleName, evmtypes.ModuleName,
|
crisis.ModuleName, genutil.ModuleName, evm.ModuleName,
|
||||||
)
|
)
|
||||||
|
|
||||||
app.mm.RegisterInvariants(&app.crisisKeeper)
|
app.mm.RegisterInvariants(&app.CrisisKeeper)
|
||||||
app.mm.RegisterRoutes(app.Router(), app.QueryRouter())
|
app.mm.RegisterRoutes(app.Router(), app.QueryRouter())
|
||||||
|
|
||||||
// initialize stores
|
// initialize stores
|
||||||
app.MountKVStores(keys)
|
app.MountKVStores(keys)
|
||||||
app.MountTransientStores(tkeys)
|
app.MountTransientStores(tkeys)
|
||||||
|
|
||||||
// Mount block hash mapping key as DB (no need for historical queries)
|
// 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)
|
app.MountStore(blockKey, sdk.StoreTypeDB)
|
||||||
|
|
||||||
// initialize BaseApp
|
// initialize BaseApp
|
||||||
app.SetInitChainer(app.InitChainer)
|
app.SetInitChainer(app.InitChainer)
|
||||||
app.SetBeginBlocker(app.BeginBlocker)
|
app.SetBeginBlocker(app.BeginBlocker)
|
||||||
app.SetAnteHandler(NewAnteHandler(app.accountKeeper, app.supplyKeeper))
|
app.SetAnteHandler(ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper))
|
||||||
app.SetEndBlocker(app.EndBlocker)
|
app.SetEndBlocker(app.EndBlocker)
|
||||||
|
|
||||||
if loadLatest {
|
if loadLatest {
|
||||||
@ -234,12 +280,12 @@ func NewEthermintApp(
|
|||||||
cmn.Exit(err.Error())
|
cmn.Exit(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenesisState is the state of the blockchain is represented here as a map of raw json
|
// Name returns the name of the App
|
||||||
// messages key'd by a identifier string.
|
func (app *EthermintApp) Name() string { return app.BaseApp.Name() }
|
||||||
type GenesisState map[string]json.RawMessage
|
|
||||||
|
|
||||||
// BeginBlocker updates every begin block
|
// BeginBlocker updates every begin block
|
||||||
func (app *EthermintApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
|
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
|
// InitChainer updates at chain initialization
|
||||||
func (app *EthermintApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
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)
|
app.cdc.MustUnmarshalJSON(req.AppStateBytes, &genesisState)
|
||||||
return app.mm.InitGenesis(ctx, 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 {
|
func (app *EthermintApp) ModuleAccountAddrs() map[string]bool {
|
||||||
modAccAddrs := make(map[string]bool)
|
modAccAddrs := make(map[string]bool)
|
||||||
for acc := range maccPerms {
|
for acc := range maccPerms {
|
||||||
modAccAddrs[app.supplyKeeper.GetModuleAddress(acc).String()] = true
|
modAccAddrs[supply.NewModuleAddress(acc).String()] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return modAccAddrs
|
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) {
|
func TestEthermintAppExport(t *testing.T) {
|
||||||
db := dbm.NewMemDB()
|
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()
|
genesisState := ModuleBasics.DefaultGenesis()
|
||||||
stateBytes, err := codec.MarshalJSONIndent(app.cdc, genesisState)
|
stateBytes, err := codec.MarshalJSONIndent(app.cdc, genesisState)
|
||||||
@ -31,7 +31,7 @@ func TestEthermintAppExport(t *testing.T) {
|
|||||||
app.Commit()
|
app.Commit()
|
||||||
|
|
||||||
// Making a new app object with the db, so that initchain hasn't been called
|
// 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{})
|
_, _, err = app2.ExportAppStateAndValidators(false, []string{})
|
||||||
require.NoError(t, err, "ExportAppStateAndValidators should not have an error")
|
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
|
// 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
|
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. */
|
/* Just to be safe, assert the invariants on current state. */
|
||||||
app.crisisKeeper.AssertInvariants(ctx)
|
app.CrisisKeeper.AssertInvariants(ctx)
|
||||||
|
|
||||||
/* Handle fee distribution state. */
|
/* Handle fee distribution state. */
|
||||||
|
|
||||||
// withdraw all validator commission
|
// withdraw all validator commission
|
||||||
app.stakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) {
|
app.StakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) {
|
||||||
_, _ = app.distrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator())
|
_, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator())
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
// withdraw all delegator rewards
|
// withdraw all delegator rewards
|
||||||
dels := app.stakingKeeper.GetAllDelegations(ctx)
|
dels := app.StakingKeeper.GetAllDelegations(ctx)
|
||||||
for _, delegation := range dels {
|
for _, delegation := range dels {
|
||||||
_, _ = app.distrKeeper.WithdrawDelegationRewards(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)
|
_, _ = app.DistrKeeper.WithdrawDelegationRewards(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear validator slash events
|
// clear validator slash events
|
||||||
app.distrKeeper.DeleteAllValidatorSlashEvents(ctx)
|
app.DistrKeeper.DeleteAllValidatorSlashEvents(ctx)
|
||||||
|
|
||||||
// clear validator historical rewards
|
// clear validator historical rewards
|
||||||
app.distrKeeper.DeleteAllValidatorHistoricalRewards(ctx)
|
app.DistrKeeper.DeleteAllValidatorHistoricalRewards(ctx)
|
||||||
|
|
||||||
// set context height to zero
|
// set context height to zero
|
||||||
height := ctx.BlockHeight()
|
height := ctx.BlockHeight()
|
||||||
ctx = ctx.WithBlockHeight(0)
|
ctx = ctx.WithBlockHeight(0)
|
||||||
|
|
||||||
// reinitialize all validators
|
// 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
|
// donate any unwithdrawn outstanding reward fraction tokens to the community pool
|
||||||
scraps := app.distrKeeper.GetValidatorOutstandingRewards(ctx, val.GetOperator())
|
scraps := app.DistrKeeper.GetValidatorOutstandingRewards(ctx, val.GetOperator())
|
||||||
feePool := app.distrKeeper.GetFeePool(ctx)
|
feePool := app.DistrKeeper.GetFeePool(ctx)
|
||||||
feePool.CommunityPool = feePool.CommunityPool.Add(scraps)
|
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
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
// reinitialize all delegations
|
// reinitialize all delegations
|
||||||
for _, del := range dels {
|
for _, del := range dels {
|
||||||
app.distrKeeper.Hooks().BeforeDelegationCreated(ctx, del.DelegatorAddress, del.ValidatorAddress)
|
app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, del.DelegatorAddress, del.ValidatorAddress)
|
||||||
app.distrKeeper.Hooks().AfterDelegationModified(ctx, del.DelegatorAddress, del.ValidatorAddress)
|
app.DistrKeeper.Hooks().AfterDelegationModified(ctx, del.DelegatorAddress, del.ValidatorAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset context height
|
// reset context height
|
||||||
@ -112,20 +111,20 @@ func (app *EthermintApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList
|
|||||||
/* Handle staking state. */
|
/* Handle staking state. */
|
||||||
|
|
||||||
// iterate through redelegations, reset creation height
|
// 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 {
|
for i := range red.Entries {
|
||||||
red.Entries[i].CreationHeight = 0
|
red.Entries[i].CreationHeight = 0
|
||||||
}
|
}
|
||||||
app.stakingKeeper.SetRedelegation(ctx, red)
|
app.StakingKeeper.SetRedelegation(ctx, red)
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
// iterate through unbonding delegations, reset creation height
|
// 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 {
|
for i := range ubd.Entries {
|
||||||
ubd.Entries[i].CreationHeight = 0
|
ubd.Entries[i].CreationHeight = 0
|
||||||
}
|
}
|
||||||
app.stakingKeeper.SetUnbondingDelegation(ctx, ubd)
|
app.StakingKeeper.SetUnbondingDelegation(ctx, ubd)
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -137,7 +136,7 @@ func (app *EthermintApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList
|
|||||||
|
|
||||||
for ; iter.Valid(); iter.Next() {
|
for ; iter.Valid(); iter.Next() {
|
||||||
addr := sdk.ValAddress(iter.Key()[1:])
|
addr := sdk.ValAddress(iter.Key()[1:])
|
||||||
validator, found := app.stakingKeeper.GetValidator(ctx, addr)
|
validator, found := app.StakingKeeper.GetValidator(ctx, addr)
|
||||||
if !found {
|
if !found {
|
||||||
panic("expected validator, not found")
|
panic("expected validator, not found")
|
||||||
}
|
}
|
||||||
@ -147,22 +146,22 @@ func (app *EthermintApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList
|
|||||||
validator.Jailed = true
|
validator.Jailed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
app.stakingKeeper.SetValidator(ctx, validator)
|
app.StakingKeeper.SetValidator(ctx, validator)
|
||||||
counter++
|
counter++
|
||||||
}
|
}
|
||||||
|
|
||||||
iter.Close()
|
iter.Close()
|
||||||
|
|
||||||
_ = app.stakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
_ = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
|
||||||
/* Handle slashing state. */
|
/* Handle slashing state. */
|
||||||
|
|
||||||
// reset start height on signing infos
|
// reset start height on signing infos
|
||||||
app.slashingKeeper.IterateValidatorSigningInfos(
|
app.SlashingKeeper.IterateValidatorSigningInfos(
|
||||||
ctx,
|
ctx,
|
||||||
func(addr sdk.ConsAddress, info slashing.ValidatorSigningInfo) (stop bool) {
|
func(addr sdk.ConsAddress, info slashing.ValidatorSigningInfo) (stop bool) {
|
||||||
info.StartHeight = 0
|
info.StartHeight = 0
|
||||||
app.slashingKeeper.SetValidatorSigningInfo(ctx, addr, info)
|
app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info)
|
||||||
return false
|
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/flags"
|
||||||
"github.com/cosmos/cosmos-sdk/client/input"
|
"github.com/cosmos/cosmos-sdk/client/input"
|
||||||
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
|
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||||
|
|
||||||
emintcrypto "github.com/cosmos/ethermint/crypto"
|
emintcrypto "github.com/cosmos/ethermint/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func exportEthKeyCommand() *cobra.Command {
|
func unsafeExportEthKeyCommand() *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "export-eth-key <name>",
|
Use: "unsafe-export-eth-key [name]",
|
||||||
Short: "Export an Ethereum private key",
|
Short: "**UNSAFE** Export an Ethereum private key",
|
||||||
Long: `Export an Ethereum private key unencrypted to use in dev tooling **UNSAFE**`,
|
Long: `**UNSAFE** Export an Ethereum private key unencrypted to use in dev tooling`,
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
RunE: runExportCmd,
|
RunE: runExportCmd,
|
||||||
}
|
}
|
||||||
@ -29,12 +30,13 @@ func exportEthKeyCommand() *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runExportCmd(cmd *cobra.Command, args []string) error {
|
func runExportCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
inBuf := bufio.NewReader(cmd.InOrStdin())
|
||||||
|
|
||||||
kb, err := clientkeys.NewKeyringFromHomeFlag(cmd.InOrStdin())
|
kb, err := clientkeys.NewKeyringFromHomeFlag(cmd.InOrStdin())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := bufio.NewReader(cmd.InOrStdin())
|
|
||||||
decryptPassword := ""
|
decryptPassword := ""
|
||||||
conf := true
|
conf := true
|
||||||
keyringBackend := viper.GetString(flags.FlagKeyringBackend)
|
keyringBackend := viper.GetString(flags.FlagKeyringBackend)
|
||||||
@ -42,11 +44,11 @@ func runExportCmd(cmd *cobra.Command, args []string) error {
|
|||||||
case flags.KeyringBackendFile:
|
case flags.KeyringBackendFile:
|
||||||
decryptPassword, err = input.GetPassword(
|
decryptPassword, err = input.GetPassword(
|
||||||
"**WARNING this is an unsafe way to export your unencrypted private key**\nEnter key password:",
|
"**WARNING this is an unsafe way to export your unencrypted private key**\nEnter key password:",
|
||||||
buf)
|
inBuf)
|
||||||
case flags.KeyringBackendOS:
|
case flags.KeyringBackendOS:
|
||||||
conf, err = input.GetConfirmation(
|
conf, err = input.GetConfirmation(
|
||||||
"**WARNING** this is an unsafe way to export your unencrypted private key, are you sure?",
|
"**WARNING** this is an unsafe way to export your unencrypted private key, are you sure?",
|
||||||
buf)
|
inBuf)
|
||||||
}
|
}
|
||||||
if err != nil || !conf {
|
if err != nil || !conf {
|
||||||
return err
|
return err
|
||||||
|
@ -4,12 +4,13 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
tmcrypto "github.com/tendermint/tendermint/crypto"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
|
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||||
|
|
||||||
emintCrypto "github.com/cosmos/ethermint/crypto"
|
emintCrypto "github.com/cosmos/ethermint/crypto"
|
||||||
tmcrypto "github.com/tendermint/tendermint/crypto"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
@ -46,7 +47,7 @@ func keyCommands() *cobra.Command {
|
|||||||
clientkeys.ParseKeyStringCommand(),
|
clientkeys.ParseKeyStringCommand(),
|
||||||
clientkeys.MigrateCommand(),
|
clientkeys.MigrateCommand(),
|
||||||
flags.LineBreak,
|
flags.LineBreak,
|
||||||
exportEthKeyCommand(),
|
unsafeExportEthKeyCommand(),
|
||||||
)
|
)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,38 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"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"
|
emintcrypto "github.com/cosmos/ethermint/crypto"
|
||||||
"github.com/cosmos/ethermint/rpc"
|
"github.com/cosmos/ethermint/rpc"
|
||||||
|
|
||||||
"github.com/tendermint/go-amino"
|
"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"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
|
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"
|
cryptokeys "github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
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"
|
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"
|
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() {
|
func main() {
|
||||||
|
// Configure cobra to sort commands
|
||||||
cobra.EnableCommandSorting = false
|
cobra.EnableCommandSorting = false
|
||||||
|
|
||||||
cdc := emintapp.MakeCodec()
|
cdc := app.MakeCodec()
|
||||||
|
|
||||||
tmamino.RegisterKeyType(emintcrypto.PubKeySecp256k1{}, emintcrypto.PubKeyAminoName)
|
tmamino.RegisterKeyType(emintcrypto.PubKeySecp256k1{}, emintcrypto.PubKeyAminoName)
|
||||||
tmamino.RegisterKeyType(emintcrypto.PrivKeySecp256k1{}, emintcrypto.PrivKeyAminoName)
|
tmamino.RegisterKeyType(emintcrypto.PrivKeySecp256k1{}, emintcrypto.PrivKeyAminoName)
|
||||||
@ -45,7 +49,7 @@ func main() {
|
|||||||
|
|
||||||
rootCmd := &cobra.Command{
|
rootCmd := &cobra.Command{
|
||||||
Use: "emintcli",
|
Use: "emintcli",
|
||||||
Short: "Ethermint Client",
|
Short: "Command line interface for interacting with emintd",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add --chain-id to persistent flags and mark it required
|
// Add --chain-id to persistent flags and mark it required
|
||||||
@ -56,20 +60,24 @@ func main() {
|
|||||||
|
|
||||||
// Construct Root Command
|
// Construct Root Command
|
||||||
rootCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
sdkrpc.StatusCommand(),
|
clientrpc.StatusCommand(),
|
||||||
client.ConfigCmd(emintapp.DefaultCLIHome),
|
client.ConfigCmd(app.DefaultCLIHome),
|
||||||
queryCmd(cdc),
|
queryCmd(cdc),
|
||||||
txCmd(cdc),
|
txCmd(cdc),
|
||||||
rpc.EmintServeCmd(cdc),
|
rpc.EmintServeCmd(cdc),
|
||||||
client.LineBreak,
|
client.LineBreak,
|
||||||
keyCommands(),
|
keyCommands(),
|
||||||
client.LineBreak,
|
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()
|
err := executor.Execute()
|
||||||
if err != nil {
|
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
|
// add modules' query commands
|
||||||
emintapp.ModuleBasics.AddQueryCommands(queryCmd, cdc)
|
app.ModuleBasics.AddQueryCommands(queryCmd, cdc)
|
||||||
|
|
||||||
return queryCmd
|
return queryCmd
|
||||||
}
|
}
|
||||||
@ -104,14 +112,27 @@ func txCmd(cdc *amino.Codec) *cobra.Command {
|
|||||||
bankcmd.SendTxCmd(cdc),
|
bankcmd.SendTxCmd(cdc),
|
||||||
client.LineBreak,
|
client.LineBreak,
|
||||||
authcmd.GetSignCommand(cdc),
|
authcmd.GetSignCommand(cdc),
|
||||||
|
authcmd.GetMultiSignCommand(cdc),
|
||||||
client.LineBreak,
|
client.LineBreak,
|
||||||
authcmd.GetBroadcastCommand(cdc),
|
authcmd.GetBroadcastCommand(cdc),
|
||||||
authcmd.GetEncodeCommand(cdc),
|
authcmd.GetEncodeCommand(cdc),
|
||||||
|
authcmd.GetDecodeCommand(cdc),
|
||||||
client.LineBreak,
|
client.LineBreak,
|
||||||
)
|
)
|
||||||
|
|
||||||
// add modules' tx commands
|
// 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
|
return txCmd
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package genaccounts
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
@ -19,6 +19,8 @@ import (
|
|||||||
authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting"
|
authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting"
|
||||||
"github.com/cosmos/cosmos-sdk/x/genutil"
|
"github.com/cosmos/cosmos-sdk/x/genutil"
|
||||||
|
|
||||||
|
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
||||||
ethermint "github.com/cosmos/ethermint/types"
|
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")
|
return errors.New("invalid vesting parameters; must supply start and end time or end time")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
genAccount = ethermint.Account{BaseAccount: baseAccount}
|
genAccount = ethermint.Account{
|
||||||
|
BaseAccount: baseAccount,
|
||||||
|
CodeHash: ethcrypto.Keccak256(nil),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := genAccount.Validate(); err != nil {
|
if err := genAccount.Validate(); err != nil {
|
@ -23,17 +23,20 @@ import (
|
|||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/cosmos/ethermint/app"
|
"github.com/cosmos/ethermint/app"
|
||||||
"github.com/cosmos/ethermint/client/genaccounts"
|
|
||||||
emintcrypto "github.com/cosmos/ethermint/crypto"
|
emintcrypto "github.com/cosmos/ethermint/crypto"
|
||||||
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
tmamino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
tmamino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
||||||
"github.com/tendermint/tendermint/libs/cli"
|
"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"
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
dbm "github.com/tendermint/tm-db"
|
dbm "github.com/tendermint/tm-db"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const flagInvCheckPeriod = "inv-check-period"
|
||||||
|
|
||||||
|
var invCheckPeriod uint
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cobra.EnableCommandSorting = false
|
cobra.EnableCommandSorting = false
|
||||||
|
|
||||||
@ -65,12 +68,14 @@ func main() {
|
|||||||
withChainIDValidation(genutilcli.InitCmd(ctx, cdc, app.ModuleBasics, app.DefaultNodeHome)),
|
withChainIDValidation(genutilcli.InitCmd(ctx, cdc, app.ModuleBasics, app.DefaultNodeHome)),
|
||||||
genutilcli.CollectGenTxsCmd(ctx, cdc, auth.GenesisAccountIterator{}, app.DefaultNodeHome),
|
genutilcli.CollectGenTxsCmd(ctx, cdc, auth.GenesisAccountIterator{}, app.DefaultNodeHome),
|
||||||
genutilcli.GenTxCmd(
|
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),
|
genutilcli.ValidateGenesisCmd(ctx, cdc, app.ModuleBasics),
|
||||||
|
|
||||||
// AddGenesisAccountCmd allows users to add accounts to the genesis file
|
// 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
|
// Tendermint node base commands
|
||||||
@ -78,23 +83,25 @@ func main() {
|
|||||||
|
|
||||||
// prepare and add flags
|
// prepare and add flags
|
||||||
executor := cli.PrepareBaseCmd(rootCmd, "EM", app.DefaultNodeHome)
|
executor := cli.PrepareBaseCmd(rootCmd, "EM", app.DefaultNodeHome)
|
||||||
|
rootCmd.PersistentFlags().UintVar(&invCheckPeriod, flagInvCheckPeriod,
|
||||||
|
0, "Assert registered invariants every N blocks")
|
||||||
err := executor.Execute()
|
err := executor.Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newApp(logger tmlog.Logger, db dbm.DB, traceStore io.Writer) abci.Application {
|
func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application {
|
||||||
return app.NewEthermintApp(logger, db, true, 0,
|
return app.NewEthermintApp(logger, db, traceStore, true, 0,
|
||||||
baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning"))))
|
baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning"))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func exportAppStateAndTMValidators(
|
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) {
|
) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||||
|
|
||||||
if height != -1 {
|
if height != -1 {
|
||||||
emintApp := app.NewEthermintApp(logger, db, true, 0)
|
emintApp := app.NewEthermintApp(logger, db, traceStore, true, 0)
|
||||||
err := emintApp.LoadHeight(height)
|
err := emintApp.LoadHeight(height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@ -102,7 +109,7 @@ func exportAppStateAndTMValidators(
|
|||||||
return emintApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
|
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)
|
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
|
// Verify that the chain-id entered is a base 10 integer
|
||||||
_, ok := new(big.Int).SetString(chainIDFlag, 10)
|
_, ok := new(big.Int).SetString(chainIDFlag, 10)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf("invalid chainID: %s, must be base-10 integer format", chainIDFlag)
|
||||||
fmt.Sprintf("invalid chainID: %s, must be base-10 integer format", chainIDFlag))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseRunE(cmd, args)
|
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/urfave/cli.v1 v1.20.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.2.8
|
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.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
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.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 h1:FfTH+vuMXOas8jmfb5/M7dzEYx7LpcLb7a0LPe34uOU=
|
||||||
github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||||
|
@ -44,14 +44,11 @@ var (
|
|||||||
flagBlockchain string
|
flagBlockchain string
|
||||||
flagCPUProfile string
|
flagCPUProfile string
|
||||||
|
|
||||||
// miner501 = ethcmn.HexToAddress("0x35e8e5dC5FBd97c5b421A80B596C030a2Be2A04D")
|
|
||||||
genInvestor = ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0")
|
genInvestor = ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0")
|
||||||
|
|
||||||
// paramsKey = sdk.NewKVStoreKey("params")
|
|
||||||
// tParamsKey = sdk.NewTransientStoreKey("transient_params")
|
|
||||||
accKey = sdk.NewKVStoreKey("acc")
|
accKey = sdk.NewKVStoreKey("acc")
|
||||||
storageKey = sdk.NewKVStoreKey(evmtypes.EvmStoreKey)
|
storageKey = sdk.NewKVStoreKey(evmtypes.StoreKey)
|
||||||
codeKey = sdk.NewKVStoreKey(evmtypes.EvmCodeKey)
|
codeKey = sdk.NewKVStoreKey(evmtypes.CodeKey)
|
||||||
|
|
||||||
logger = tmlog.NewNopLogger()
|
logger = tmlog.NewNopLogger()
|
||||||
|
|
||||||
@ -104,7 +101,7 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, ak auth.Accoun
|
|||||||
ms := cms.CacheMultiStore()
|
ms := cms.CacheMultiStore()
|
||||||
ctx := sdk.NewContext(ms, abci.Header{}, false, logger)
|
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
|
// sort the addresses and insertion of key/value pairs matters
|
||||||
genAddrs := make([]string, len(genBlock.Alloc))
|
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 {
|
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
|
return stateDB
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,12 +343,18 @@ func applyTransaction(config *ethparams.ChainConfig, bc ethcore.ChainContext, au
|
|||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
// Update the state with pending changes
|
// Update the state with pending changes
|
||||||
var root []byte
|
var intRoot ethcmn.Hash
|
||||||
if config.IsByzantium(header.Number) {
|
if config.IsByzantium(header.Number) {
|
||||||
statedb.Finalise(true)
|
err = statedb.Finalise(true)
|
||||||
} else {
|
} 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
|
*usedGas += gas
|
||||||
|
|
||||||
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
|
// 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())
|
receipt.ContractAddress = ethcrypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
|
||||||
}
|
}
|
||||||
// Set the receipt logs and create a bloom for filtering
|
// 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.Bloom = ethtypes.CreateBloom(ethtypes.Receipts{receipt})
|
||||||
receipt.BlockHash = statedb.BlockHash()
|
receipt.BlockHash = statedb.BlockHash()
|
||||||
receipt.BlockNumber = header.Number
|
receipt.BlockNumber = header.Number
|
||||||
|
149
rpc/eth_api.go
149
rpc/eth_api.go
@ -2,13 +2,15 @@ package rpc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
emintcrypto "github.com/cosmos/ethermint/crypto"
|
emintcrypto "github.com/cosmos/ethermint/crypto"
|
||||||
params "github.com/cosmos/ethermint/rpc/args"
|
params "github.com/cosmos/ethermint/rpc/args"
|
||||||
emint "github.com/cosmos/ethermint/types"
|
emint "github.com/cosmos/ethermint/types"
|
||||||
@ -29,10 +31,10 @@ import (
|
|||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/context"
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
authutils "github.com/cosmos/cosmos-sdk/x/auth/client/utils"
|
authutils "github.com/cosmos/cosmos-sdk/x/auth/client/utils"
|
||||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
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.
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
n := hexutil.Uint(block.Block.NumTxs)
|
n := hexutil.Uint(len(block.Block.Txs))
|
||||||
return &n
|
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
|
// parse the chainID from a string to a base-10 integer
|
||||||
intChainID, ok := new(big.Int).SetString(chainID, 10)
|
intChainID, ok := new(big.Int).SetString(chainID, 10)
|
||||||
if !ok {
|
if !ok {
|
||||||
return common.Hash{}, fmt.Errorf(
|
return common.Hash{}, fmt.Errorf("invalid chainID: %s, must be integer format", chainID)
|
||||||
fmt.Sprintf("invalid chainID: %s, must be integer format", chainID))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign transaction
|
// Sign transaction
|
||||||
@ -306,7 +307,7 @@ func (e *PublicEthAPI) SendTransaction(args params.SendTxArgs) (common.Hash, err
|
|||||||
|
|
||||||
// SendRawTransaction send a raw Ethereum transaction.
|
// SendRawTransaction send a raw Ethereum transaction.
|
||||||
func (e *PublicEthAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error) {
|
func (e *PublicEthAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error) {
|
||||||
tx := new(types.EthereumTxMsg)
|
tx := new(types.MsgEthereumTx)
|
||||||
|
|
||||||
// RLP decode raw transaction bytes
|
// RLP decode raw transaction bytes
|
||||||
if err := rlp.DecodeBytes(data, tx); err != nil {
|
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
|
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
|
// 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"`
|
StateDiff *map[common.Hash]common.Hash `json:"stateDiff"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoCall performs a simulated call operation through the evm
|
// DoCall performs a simulated call operation through the evm. It returns the
|
||||||
func (e *PublicEthAPI) doCall(args CallArgs, blockNr rpc.BlockNumber, globalGasCap *big.Int) (sdk.Result, error) {
|
// 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
|
// Set height for historical queries
|
||||||
ctx := e.cliCtx
|
ctx := e.cliCtx
|
||||||
if blockNr.Int64() != 0 {
|
if blockNr.Int64() != 0 {
|
||||||
@ -425,7 +430,7 @@ func (e *PublicEthAPI) doCall(args CallArgs, blockNr rpc.BlockNumber, globalGasC
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create new call message
|
// 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()))
|
sdk.NewIntFromBigInt(gasPrice), data, sdk.AccAddress(addr.Bytes()))
|
||||||
|
|
||||||
// Generate tx to be used to simulate (signature isn't needed)
|
// 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)
|
txEncoder := authutils.GetTxEncoder(ctx.Codec)
|
||||||
txBytes, err := txEncoder(tx)
|
txBytes, err := txEncoder(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sdk.Result{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transaction simulation through query
|
// Transaction simulation through query
|
||||||
res, _, err := ctx.QueryWithData("app/simulate", txBytes)
|
res, _, err := ctx.QueryWithData("app/simulate", txBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sdk.Result{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var simResult sdk.Result
|
var simResult sdk.Result
|
||||||
if err = e.cliCtx.Codec.UnmarshalBinaryLengthPrefixed(res, &simResult); err != nil {
|
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) {
|
func (e *PublicEthAPI) EstimateGas(args CallArgs) (hexutil.Uint64, error) {
|
||||||
result, err := e.doCall(args, 0, big.NewInt(emint.DefaultRPCGasLimit))
|
result, err := e.doCall(args, 0, big.NewInt(emint.DefaultRPCGasLimit))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -482,31 +489,37 @@ func (e *PublicEthAPI) GetBlockByNumber(blockNum BlockNumber, fullTx bool) (map[
|
|||||||
return e.getEthBlockByNumber(value, fullTx)
|
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)
|
// Remove this check when 0 query is fixed ref: (https://github.com/tendermint/tendermint/issues/4014)
|
||||||
var blkNumPtr *int64
|
var blkNumPtr *int64
|
||||||
if value != 0 {
|
if height != 0 {
|
||||||
blkNumPtr = &value
|
blkNumPtr = &height
|
||||||
}
|
}
|
||||||
|
|
||||||
block, err := e.cliCtx.Client.Block(blkNumPtr)
|
block, err := e.cliCtx.Client.Block(blkNumPtr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
header := block.BlockMeta.Header
|
header := block.Block.Header
|
||||||
|
|
||||||
gasLimit, err := e.getGasLimit()
|
gasLimit, err := e.getGasLimit()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var gasUsed *big.Int
|
var (
|
||||||
var transactions []interface{}
|
gasUsed *big.Int
|
||||||
|
transactions []interface{}
|
||||||
|
)
|
||||||
|
|
||||||
if fullTx {
|
if fullTx {
|
||||||
// Populate full transaction data
|
// Populate full transaction data
|
||||||
transactions, gasUsed = convertTransactionsToRPC(e.cliCtx, block.Block.Txs,
|
transactions, gasUsed, err = convertTransactionsToRPC(
|
||||||
common.BytesToHash(header.Hash()), uint64(header.Height))
|
e.cliCtx, block.Block.Txs, common.BytesToHash(header.Hash()), uint64(header.Height),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO: Gas used not saved and cannot be calculated by hashes
|
// TODO: Gas used not saved and cannot be calculated by hashes
|
||||||
// Return slice of transaction 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))
|
transactions := make([]interface{}, len(txs))
|
||||||
gasUsed := big.NewInt(0)
|
gasUsed := big.NewInt(0)
|
||||||
|
|
||||||
for i, tx := range txs {
|
for i, tx := range txs {
|
||||||
ethTx, err := bytesToEthTx(cliCtx, tx)
|
ethTx, err := bytesToEthTx(cliCtx, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
// TODO: Remove gas usage calculation if saving gasUsed per block
|
// TODO: Remove gas usage calculation if saving gasUsed per block
|
||||||
gasUsed.Add(gasUsed, ethTx.Fee())
|
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.
|
// Transaction represents a transaction returned to RPC clients.
|
||||||
@ -586,23 +604,30 @@ type Transaction struct {
|
|||||||
S *hexutil.Big `json:"s"`
|
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
|
var stdTx sdk.Tx
|
||||||
err := cliCtx.Codec.UnmarshalBinaryLengthPrefixed(bz, &stdTx)
|
err := cliCtx.Codec.UnmarshalBinaryLengthPrefixed(bz, &stdTx)
|
||||||
ethTx, ok := stdTx.(*types.EthereumTxMsg)
|
if err != nil {
|
||||||
if !ok || 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 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
|
// newRPCTransaction returns a transaction that will serialize to the RPC
|
||||||
// representation, with the given location metadata set (if available).
|
// 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
|
// 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,
|
From: from,
|
||||||
Gas: hexutil.Uint64(tx.Data.GasLimit),
|
Gas: hexutil.Uint64(tx.Data.GasLimit),
|
||||||
GasPrice: (*hexutil.Big)(tx.Data.Price),
|
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),
|
R: (*hexutil.Big)(tx.Data.R),
|
||||||
S: (*hexutil.Big)(tx.Data.S),
|
S: (*hexutil.Big)(tx.Data.S),
|
||||||
}
|
}
|
||||||
|
|
||||||
if blockHash != (common.Hash{}) {
|
if blockHash != (common.Hash{}) {
|
||||||
result.BlockHash = &blockHash
|
result.BlockHash = &blockHash
|
||||||
result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(*blockNumber))
|
result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(*blockNumber))
|
||||||
result.TransactionIndex = (*hexutil.Uint64)(&index)
|
result.TransactionIndex = (*hexutil.Uint64)(&index)
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransactionByHash returns the transaction identified by hash.
|
// GetTransactionByHash returns the transaction identified by hash.
|
||||||
@ -636,7 +663,7 @@ func (e *PublicEthAPI) GetTransactionByHash(hash common.Hash) (*Transaction, err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
blockHash := common.BytesToHash(block.BlockMeta.Header.Hash())
|
blockHash := common.BytesToHash(block.Block.Header.Hash())
|
||||||
|
|
||||||
ethTx, err := bytesToEthTx(e.cliCtx, tx.Tx)
|
ethTx, err := bytesToEthTx(e.cliCtx, tx.Tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -644,7 +671,7 @@ func (e *PublicEthAPI) GetTransactionByHash(hash common.Hash) (*Transaction, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
height := uint64(tx.Height)
|
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.
|
// GetTransactionByBlockHashAndIndex returns the transaction identified by hash and index.
|
||||||
@ -670,7 +697,7 @@ func (e *PublicEthAPI) getTransactionByBlockNumberAndIndex(number int64, idx hex
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
header := block.BlockMeta.Header
|
header := block.Block.Header
|
||||||
|
|
||||||
txs := block.Block.Txs
|
txs := block.Block.Txs
|
||||||
if uint64(idx) >= uint64(len(txs)) {
|
if uint64(idx) >= uint64(len(txs)) {
|
||||||
@ -682,8 +709,7 @@ func (e *PublicEthAPI) getTransactionByBlockNumberAndIndex(number int64, idx hex
|
|||||||
}
|
}
|
||||||
|
|
||||||
height := uint64(header.Height)
|
height := uint64(header.Height)
|
||||||
transaction := newRPCTransaction(ethTx, common.BytesToHash(header.Hash()), &height, uint64(idx))
|
return newRPCTransaction(*ethTx, common.BytesToHash(header.Hash()), &height, uint64(idx))
|
||||||
return transaction, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransactionReceipt returns the transaction receipt identified by hash.
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
blockHash := common.BytesToHash(block.BlockMeta.Header.Hash())
|
blockHash := common.BytesToHash(block.Block.Header.Hash())
|
||||||
|
|
||||||
// Convert tx bytes to eth transaction
|
// Convert tx bytes to eth transaction
|
||||||
ethTx, err := bytesToEthTx(e.cliCtx, tx.Tx)
|
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)
|
e.cliCtx.Codec.MustUnmarshalJSON(res, &logs)
|
||||||
|
|
||||||
txData := tx.TxResult.GetData()
|
txData := tx.TxResult.GetData()
|
||||||
contractAddress, bloomFilter, _, _ := types.DecodeReturnData(txData)
|
data, err := types.DecodeResultData(txData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
fields := map[string]interface{}{
|
||||||
"blockHash": blockHash,
|
"blockHash": blockHash,
|
||||||
@ -739,12 +768,12 @@ func (e *PublicEthAPI) GetTransactionReceipt(hash common.Hash) (map[string]inter
|
|||||||
"cumulativeGasUsed": nil, // ignore until needed
|
"cumulativeGasUsed": nil, // ignore until needed
|
||||||
"contractAddress": nil,
|
"contractAddress": nil,
|
||||||
"logs": logs.Logs,
|
"logs": logs.Logs,
|
||||||
"logsBloom": bloomFilter,
|
"logsBloom": data.Bloom,
|
||||||
"status": status,
|
"status": status,
|
||||||
}
|
}
|
||||||
|
|
||||||
if contractAddress != (common.Address{}) {
|
if data.Address != (common.Address{}) {
|
||||||
fields["contractAddress"] = contractAddress
|
fields["contractAddress"] = data.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
return fields, nil
|
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
|
// * 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)
|
transactions = append(transactions, rpcTx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -871,13 +904,14 @@ func (e *PublicEthAPI) getGasLimit() (int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generateFromArgs populates tx message with args (used in RPC API)
|
// generateFromArgs populates tx message with args (used in RPC API)
|
||||||
func (e *PublicEthAPI) generateFromArgs(args params.SendTxArgs) (msg *types.EthereumTxMsg, err error) {
|
func (e *PublicEthAPI) generateFromArgs(args params.SendTxArgs) (*types.MsgEthereumTx, error) {
|
||||||
var nonce uint64
|
var (
|
||||||
|
nonce uint64
|
||||||
var gasLimit uint64
|
gasLimit uint64
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
amount := (*big.Int)(args.Value)
|
amount := (*big.Int)(args.Value)
|
||||||
|
|
||||||
gasPrice := (*big.Int)(args.GasPrice)
|
gasPrice := (*big.Int)(args.GasPrice)
|
||||||
|
|
||||||
if args.GasPrice == nil {
|
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) {
|
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
|
// 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")
|
return nil, fmt.Errorf("contract creation without any data provided")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.Gas == nil {
|
if args.Gas == nil {
|
||||||
callArgs := CallArgs{
|
callArgs := CallArgs{
|
||||||
From: &args.From,
|
From: &args.From,
|
||||||
@ -925,11 +960,15 @@ func (e *PublicEthAPI) generateFromArgs(args params.SendTxArgs) (msg *types.Ethe
|
|||||||
Value: args.Value,
|
Value: args.Value,
|
||||||
Data: args.Data,
|
Data: args.Data,
|
||||||
}
|
}
|
||||||
g, _ := e.EstimateGas(callArgs)
|
g, err := e.EstimateGas(callArgs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
gasLimit = uint64(g)
|
gasLimit = uint64(g)
|
||||||
} else {
|
} else {
|
||||||
gasLimit = (uint64)(*args.Gas)
|
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/cosmos/ethermint/version"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -92,51 +93,34 @@ func TestEth_protocolVersion(t *testing.T) {
|
|||||||
expectedRes := hexutil.Uint(version.ProtocolVersion)
|
expectedRes := hexutil.Uint(version.ProtocolVersion)
|
||||||
|
|
||||||
rpcRes, err := call("eth_protocolVersion", []string{})
|
rpcRes, err := call("eth_protocolVersion", []string{})
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var res hexutil.Uint
|
var res hexutil.Uint
|
||||||
err = res.UnmarshalJSON(rpcRes.Result)
|
err = res.UnmarshalJSON(rpcRes.Result)
|
||||||
|
require.NoError(t, err)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Got protocol version: %s\n", res.String())
|
t.Logf("Got protocol version: %s\n", res.String())
|
||||||
|
require.Equal(t, expectedRes, res, "expected: %s got: %s\n", expectedRes.String(), rpcRes.Result)
|
||||||
if res != expectedRes {
|
|
||||||
t.Fatalf("expected: %s got: %s\n", expectedRes.String(), rpcRes.Result)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEth_blockNumber(t *testing.T) {
|
func TestEth_blockNumber(t *testing.T) {
|
||||||
rpcRes, err := call("eth_blockNumber", []string{})
|
rpcRes, err := call("eth_blockNumber", []string{})
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
var res hexutil.Uint64
|
var res hexutil.Uint64
|
||||||
err = res.UnmarshalJSON(rpcRes.Result)
|
err = res.UnmarshalJSON(rpcRes.Result)
|
||||||
|
require.NoError(t, err)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Got block number: %s\n", res.String())
|
t.Logf("Got block number: %s\n", res.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEth_GetBalance(t *testing.T) {
|
func TestEth_GetBalance(t *testing.T) {
|
||||||
rpcRes, err := call("eth_getBalance", []string{addrA, "0x0"})
|
rpcRes, err := call("eth_getBalance", []string{addrA, "0x0"})
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var res hexutil.Big
|
var res hexutil.Big
|
||||||
err = res.UnmarshalJSON(rpcRes.Result)
|
err = res.UnmarshalJSON(rpcRes.Result)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Got balance %s for %s\n", res.String(), addrA)
|
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) {
|
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}
|
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"})
|
rpcRes, err := call("eth_getStorageAt", []string{addrA, string(addrAStoreKey), "0x0"})
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var storage hexutil.Bytes
|
var storage hexutil.Bytes
|
||||||
err = storage.UnmarshalJSON(rpcRes.Result)
|
err = storage.UnmarshalJSON(rpcRes.Result)
|
||||||
|
require.NoError(t, err)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Got value [%X] for %s with key %X\n", storage, addrA, addrAStoreKey)
|
t.Logf("Got value [%X] for %s with key %X\n", storage, addrA, addrAStoreKey)
|
||||||
|
|
||||||
if !bytes.Equal(storage, expectedRes) {
|
require.True(t, bytes.Equal(storage, expectedRes), "expected: %d (%d bytes) got: %d (%d bytes)", expectedRes, len(expectedRes), storage, len(storage))
|
||||||
t.Errorf("expected: %d (%d bytes) got: %d (%d bytes)", expectedRes, len(expectedRes), storage, len(storage))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEth_GetCode(t *testing.T) {
|
func TestEth_GetCode(t *testing.T) {
|
||||||
expectedRes := hexutil.Bytes{}
|
expectedRes := hexutil.Bytes{}
|
||||||
rpcRes, err := call("eth_getCode", []string{addrA, "0x0"})
|
rpcRes, err := call("eth_getCode", []string{addrA, "0x0"})
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var code hexutil.Bytes
|
var code hexutil.Bytes
|
||||||
err = code.UnmarshalJSON(rpcRes.Result)
|
err = code.UnmarshalJSON(rpcRes.Result)
|
||||||
|
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Got code [%X] for %s\n", code, addrA)
|
t.Logf("Got code [%X] for %s\n", code, addrA)
|
||||||
if !bytes.Equal(expectedRes, code) {
|
require.True(t, bytes.Equal(expectedRes, code), "expected: %X got: %X", expectedRes, code)
|
||||||
t.Errorf("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"
|
"github.com/cosmos/ethermint/x/evm/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// nolint
|
||||||
const (
|
const (
|
||||||
|
ModuleName = types.ModuleName
|
||||||
|
StoreKey = types.StoreKey
|
||||||
|
CodeKey = types.StoreKey
|
||||||
|
BlockKey = types.BlockKey
|
||||||
|
RouterKey = types.RouterKey
|
||||||
QueryProtocolVersion = types.QueryProtocolVersion
|
QueryProtocolVersion = types.QueryProtocolVersion
|
||||||
QueryBalance = types.QueryBalance
|
QueryBalance = types.QueryBalance
|
||||||
QueryBlockNumber = types.QueryBlockNumber
|
QueryBlockNumber = types.QueryBlockNumber
|
||||||
@ -19,10 +25,13 @@ const (
|
|||||||
QueryAccount = types.QueryAccount
|
QueryAccount = types.QueryAccount
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// nolint
|
||||||
var (
|
var (
|
||||||
NewKeeper = keeper.NewKeeper
|
NewKeeper = keeper.NewKeeper
|
||||||
|
TxDecoder = types.TxDecoder
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//nolint
|
||||||
type (
|
type (
|
||||||
Keeper = keeper.Keeper
|
Keeper = keeper.Keeper
|
||||||
QueryResAccount = types.QueryResAccount
|
QueryResAccount = types.QueryResAccount
|
||||||
|
@ -85,7 +85,7 @@ func GetCmdGenTx(cdc *codec.Codec) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Potentially allow overriding of gas price and gas limit
|
// 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)
|
sdk.NewInt(emint.DefaultGasPrice), data, from)
|
||||||
|
|
||||||
err = msg.ValidateBasic()
|
err = msg.ValidateBasic()
|
||||||
@ -137,7 +137,7 @@ func GetCmdGenCreateTx(cdc *codec.Codec) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Potentially allow overriding of gas price and gas limit
|
// 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)
|
sdk.NewInt(emint.DefaultGasPrice), data, from)
|
||||||
|
|
||||||
err = msg.ValidateBasic()
|
err = msg.ValidateBasic()
|
||||||
|
110
x/evm/handler.go
110
x/evm/handler.go
@ -18,23 +18,20 @@ import (
|
|||||||
func NewHandler(k Keeper) sdk.Handler {
|
func NewHandler(k Keeper) sdk.Handler {
|
||||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case types.EthereumTxMsg:
|
case types.MsgEthereumTx:
|
||||||
return handleETHTxMsg(ctx, k, msg)
|
return HandleMsgEthereumTx(ctx, k, msg)
|
||||||
case *types.EmintMsg:
|
case types.MsgEthermint:
|
||||||
return handleEmintMsg(ctx, k, *msg)
|
return HandleMsgEthermint(ctx, k, msg)
|
||||||
default:
|
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()
|
return sdk.ErrUnknownRequest(errMsg).Result()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle an Ethereum specific tx
|
// HandleMsgEthereumTx handles an Ethereum specific tx
|
||||||
func handleETHTxMsg(ctx sdk.Context, k Keeper, msg types.EthereumTxMsg) sdk.Result {
|
func HandleMsgEthereumTx(ctx sdk.Context, k Keeper, msg types.MsgEthereumTx) sdk.Result {
|
||||||
if err := msg.ValidateBasic(); err != nil {
|
ctx = ctx.WithEventManager(sdk.NewEventManager())
|
||||||
return err.Result()
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse the chainID from a string to a base-10 integer
|
// parse the chainID from a string to a base-10 integer
|
||||||
intChainID, ok := new(big.Int).SetString(ctx.ChainID(), 10)
|
intChainID, ok := new(big.Int).SetString(ctx.ChainID(), 10)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -44,14 +41,14 @@ func handleETHTxMsg(ctx sdk.Context, k Keeper, msg types.EthereumTxMsg) sdk.Resu
|
|||||||
// Verify signature and retrieve sender address
|
// Verify signature and retrieve sender address
|
||||||
sender, err := msg.VerifySig(intChainID)
|
sender, err := msg.VerifySig(intChainID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return emint.ErrInvalidSender(err.Error()).Result()
|
return sdk.ResultFromError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode transaction by default Tx encoder
|
// Encode transaction by default Tx encoder
|
||||||
txEncoder := authutils.GetTxEncoder(types.ModuleCdc)
|
txEncoder := authutils.GetTxEncoder(types.ModuleCdc)
|
||||||
txBytes, err := txEncoder(msg)
|
txBytes, err := txEncoder(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sdk.ErrInternal(err.Error()).Result()
|
return sdk.ResultFromError(err)
|
||||||
}
|
}
|
||||||
txHash := tmtypes.Tx(txBytes).Hash()
|
txHash := tmtypes.Tx(txBytes).Hash()
|
||||||
ethHash := common.BytesToHash(txHash)
|
ethHash := common.BytesToHash(txHash)
|
||||||
@ -70,21 +67,53 @@ func handleETHTxMsg(ctx sdk.Context, k Keeper, msg types.EthereumTxMsg) sdk.Resu
|
|||||||
Simulate: ctx.IsCheckTx(),
|
Simulate: ctx.IsCheckTx(),
|
||||||
}
|
}
|
||||||
// Prepare db for logs
|
// Prepare db for logs
|
||||||
k.CommitStateDB.Prepare(ethHash, common.Hash{}, k.TxCount.Get())
|
k.CommitStateDB.Prepare(ethHash, common.Hash{}, k.TxCount)
|
||||||
k.TxCount.Increment()
|
k.TxCount++
|
||||||
|
|
||||||
bloom, res := st.TransitionCSDB(ctx)
|
// TODO: move to keeper
|
||||||
if res.IsOK() {
|
returnData, err := st.TransitionCSDB(ctx)
|
||||||
k.Bloom.Or(k.Bloom, bloom)
|
if err != nil {
|
||||||
}
|
return sdk.ResultFromError(err)
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleEmintMsg(ctx sdk.Context, k Keeper, msg types.EmintMsg) sdk.Result {
|
// update block bloom filter
|
||||||
if err := msg.ValidateBasic(); err != nil {
|
k.Bloom.Or(k.Bloom, returnData.Bloom)
|
||||||
return err.Result()
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
// parse the chainID from a string to a base-10 integer
|
||||||
intChainID, ok := new(big.Int).SetString(ctx.ChainID(), 10)
|
intChainID, ok := new(big.Int).SetString(ctx.ChainID(), 10)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -109,9 +138,36 @@ func handleEmintMsg(ctx sdk.Context, k Keeper, msg types.EmintMsg) sdk.Result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prepare db for logs
|
// Prepare db for logs
|
||||||
k.CommitStateDB.Prepare(common.Hash{}, common.Hash{}, k.TxCount.Get()) // Cannot provide tx hash
|
k.CommitStateDB.Prepare(common.Hash{}, common.Hash{}, k.TxCount) // Cannot provide tx hash
|
||||||
k.TxCount.Increment()
|
k.TxCount++
|
||||||
|
|
||||||
_, res := st.TransitionCSDB(ctx)
|
returnData, err := st.TransitionCSDB(ctx)
|
||||||
return res
|
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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
@ -9,7 +11,6 @@ import (
|
|||||||
ethvm "github.com/ethereum/go-ethereum/core/vm"
|
ethvm "github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
"github.com/cosmos/ethermint/x/evm/types"
|
"github.com/cosmos/ethermint/x/evm/types"
|
||||||
ethstate "github.com/ethereum/go-ethereum/core/state"
|
ethstate "github.com/ethereum/go-ethereum/core/state"
|
||||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
@ -24,35 +25,22 @@ type Keeper struct {
|
|||||||
cdc *codec.Codec
|
cdc *codec.Codec
|
||||||
// Store key required to update the block bloom filter mappings needed for the
|
// Store key required to update the block bloom filter mappings needed for the
|
||||||
// Web3 API
|
// Web3 API
|
||||||
storeKey sdk.StoreKey
|
blockKey sdk.StoreKey
|
||||||
CommitStateDB *types.CommitStateDB
|
CommitStateDB *types.CommitStateDB
|
||||||
TxCount *count
|
TxCount int
|
||||||
Bloom *big.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
|
// NewKeeper generates new evm module keeper
|
||||||
func NewKeeper(ak auth.AccountKeeper, storageKey, codeKey,
|
func NewKeeper(
|
||||||
storeKey sdk.StoreKey, cdc *codec.Codec) Keeper {
|
cdc *codec.Codec, blockKey, codeKey, storeKey sdk.StoreKey,
|
||||||
|
ak types.AccountKeeper,
|
||||||
|
) Keeper {
|
||||||
return Keeper{
|
return Keeper{
|
||||||
cdc: cdc,
|
cdc: cdc,
|
||||||
storeKey: storeKey,
|
blockKey: blockKey,
|
||||||
CommitStateDB: types.NewCommitStateDB(sdk.Context{}, ak, storageKey, codeKey),
|
CommitStateDB: types.NewCommitStateDB(sdk.Context{}, codeKey, storeKey, ak),
|
||||||
TxCount: new(count),
|
TxCount: 0,
|
||||||
Bloom: big.NewInt(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
|
// SetBlockHashMapping sets the mapping from block consensus hash to block height
|
||||||
func (k *Keeper) SetBlockHashMapping(ctx sdk.Context, hash []byte, height int64) {
|
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{}) {
|
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
|
// GetBlockHashMapping gets block height from block consensus hash
|
||||||
func (k *Keeper) GetBlockHashMapping(ctx sdk.Context, hash []byte) (height int64) {
|
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)
|
bz := store.Get(hash)
|
||||||
if bytes.Equal(bz, []byte{}) {
|
if bytes.Equal(bz, []byte{}) {
|
||||||
panic(fmt.Errorf("block with hash %s not found", ethcmn.BytesToHash(hash)))
|
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
|
// SetBlockBloomMapping sets the mapping from block height to bloom bits
|
||||||
func (k *Keeper) SetBlockBloomMapping(ctx sdk.Context, bloom ethtypes.Bloom, height int64) {
|
func (k *Keeper) SetBlockBloomMapping(ctx sdk.Context, bloom ethtypes.Bloom, height int64) error {
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.blockKey)
|
||||||
heightHash := k.cdc.MustMarshalBinaryLengthPrefixed(height)
|
bz := sdk.Uint64ToBigEndian(uint64(height))
|
||||||
if !bytes.Equal(heightHash, []byte{}) {
|
if len(bz) == 0 {
|
||||||
store.Set(heightHash, bloom.Bytes())
|
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
|
// GetBlockBloomMapping gets bloombits from block height
|
||||||
func (k *Keeper) GetBlockBloomMapping(ctx sdk.Context, height int64) ethtypes.Bloom {
|
func (k *Keeper) GetBlockBloomMapping(ctx sdk.Context, height int64) (ethtypes.Bloom, error) {
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.blockKey)
|
||||||
heightHash := k.cdc.MustMarshalBinaryLengthPrefixed(height)
|
bz := sdk.Uint64ToBigEndian(uint64(height))
|
||||||
bloom := store.Get(heightHash)
|
if len(bz) == 0 {
|
||||||
if bytes.Equal(heightHash, []byte{}) {
|
return ethtypes.BytesToBloom([]byte{}), fmt.Errorf("block with height %d not found", height)
|
||||||
panic(fmt.Errorf("block with bloombits %s not found", bloom))
|
|
||||||
}
|
}
|
||||||
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
|
// GetLogs calls CommitStateDB.GetLogs using the passed in context
|
||||||
func (k *Keeper) GetLogs(ctx sdk.Context, hash ethcmn.Hash) []*ethtypes.Log {
|
func (k *Keeper) GetLogs(ctx sdk.Context, hash ethcmn.Hash) ([]*ethtypes.Log, error) {
|
||||||
return k.CommitStateDB.WithContext(ctx).GetLogs(hash)
|
logs, err := k.CommitStateDB.WithContext(ctx).GetLogs(hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logs calls CommitStateDB.Logs using the passed in context
|
return logs, nil
|
||||||
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
|
// 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
|
// Finalise calls CommitStateDB.Finalise using the passed in context
|
||||||
func (k *Keeper) Finalise(ctx sdk.Context, deleteEmptyObjects bool) {
|
func (k *Keeper) Finalise(ctx sdk.Context, deleteEmptyObjects bool) error {
|
||||||
k.CommitStateDB.WithContext(ctx).Finalise(deleteEmptyObjects)
|
return k.CommitStateDB.WithContext(ctx).Finalise(deleteEmptyObjects)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IntermediateRoot calls CommitStateDB.IntermediateRoot using the passed in context
|
// IntermediateRoot calls CommitStateDB.IntermediateRoot using the passed in context
|
||||||
func (k *Keeper) IntermediateRoot(ctx sdk.Context, deleteEmptyObjects bool) {
|
func (k *Keeper) IntermediateRoot(ctx sdk.Context, deleteEmptyObjects bool) error {
|
||||||
k.CommitStateDB.WithContext(ctx).IntermediateRoot(deleteEmptyObjects)
|
_, err := k.CommitStateDB.WithContext(ctx).IntermediateRoot(deleteEmptyObjects)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -1,110 +1,78 @@
|
|||||||
package keeper
|
package keeper_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"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"
|
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"
|
"github.com/cosmos/ethermint/app"
|
||||||
evmtypes "github.com/cosmos/ethermint/x/evm/types"
|
"github.com/cosmos/ethermint/x/evm/keeper"
|
||||||
|
|
||||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
tmlog "github.com/tendermint/tendermint/libs/log"
|
|
||||||
dbm "github.com/tendermint/tm-db"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var address = ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b4c1")
|
||||||
address = ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b4c1")
|
|
||||||
|
|
||||||
accKey = sdk.NewKVStoreKey("acc")
|
type KeeperTestSuite struct {
|
||||||
storageKey = sdk.NewKVStoreKey(evmtypes.EvmStoreKey)
|
suite.Suite
|
||||||
codeKey = sdk.NewKVStoreKey(evmtypes.EvmCodeKey)
|
|
||||||
blockKey = sdk.NewKVStoreKey(evmtypes.EvmBlockKey)
|
|
||||||
|
|
||||||
logger = tmlog.NewNopLogger()
|
ctx sdk.Context
|
||||||
)
|
querier sdk.Querier
|
||||||
|
app *app.EthermintApp
|
||||||
func newTestCodec() *codec.Codec {
|
|
||||||
cdc := codec.New()
|
|
||||||
|
|
||||||
evmtypes.RegisterCodec(cdc)
|
|
||||||
types.RegisterCodec(cdc)
|
|
||||||
auth.RegisterCodec(cdc)
|
|
||||||
sdk.RegisterCodec(cdc)
|
|
||||||
codec.RegisterCrypto(cdc)
|
|
||||||
|
|
||||||
return cdc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDBStorage(t *testing.T) {
|
func (suite *KeeperTestSuite) SetupTest() {
|
||||||
// create logger, codec and root multi-store
|
checkTx := false
|
||||||
cdc := newTestCodec()
|
|
||||||
|
|
||||||
// The ParamsKeeper handles parameter storage for the application
|
suite.app = app.Setup(checkTx)
|
||||||
keyParams := sdk.NewKVStoreKey(params.StoreKey)
|
suite.ctx = suite.app.BaseApp.NewContext(checkTx, abci.Header{Height: 1, ChainID: "3", Time: time.Now().UTC()})
|
||||||
tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey)
|
suite.querier = keeper.NewQuerier(suite.app.EvmKeeper)
|
||||||
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)
|
|
||||||
|
|
||||||
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)
|
func TestKeeperTestSuite(t *testing.T) {
|
||||||
err := cms.LoadLatestVersion()
|
suite.Run(t, new(KeeperTestSuite))
|
||||||
require.NoError(t, err)
|
}
|
||||||
|
|
||||||
// First execution
|
|
||||||
ms := cms.CacheMultiStore()
|
|
||||||
ctx := sdk.NewContext(ms, abci.Header{}, false, logger)
|
|
||||||
ctx = ctx.WithBlockHeight(1)
|
|
||||||
|
|
||||||
|
func (suite *KeeperTestSuite) TestDBStorage() {
|
||||||
// Perform state transitions
|
// Perform state transitions
|
||||||
ek.SetBalance(ctx, address, big.NewInt(5))
|
suite.app.EvmKeeper.CreateAccount(suite.ctx, address)
|
||||||
ek.SetNonce(ctx, address, 4)
|
suite.app.EvmKeeper.SetBalance(suite.ctx, address, big.NewInt(5))
|
||||||
ek.SetState(ctx, address, ethcmn.HexToHash("0x2"), ethcmn.HexToHash("0x3"))
|
suite.app.EvmKeeper.SetNonce(suite.ctx, address, 4)
|
||||||
ek.SetCode(ctx, address, []byte{0x1})
|
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
|
// Test block hash mapping functionality
|
||||||
ek.SetBlockHashMapping(ctx, ethcmn.FromHex("0x0d87a3a5f73140f46aac1bf419263e4e94e87c292f25007700ab7f2060e2af68"), 7)
|
suite.app.EvmKeeper.SetBlockHashMapping(suite.ctx, ethcmn.FromHex("0x0d87a3a5f73140f46aac1bf419263e4e94e87c292f25007700ab7f2060e2af68"), 7)
|
||||||
ek.SetBlockHashMapping(ctx, []byte{0x43, 0x32}, 8)
|
suite.app.EvmKeeper.SetBlockHashMapping(suite.ctx, []byte{0x43, 0x32}, 8)
|
||||||
|
|
||||||
// Test block height mapping functionality
|
// Test block height mapping functionality
|
||||||
testBloom := ethtypes.BytesToBloom([]byte{0x1, 0x3})
|
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
|
// Get those state transitions
|
||||||
require.Equal(t, ek.GetBalance(ctx, address).Cmp(big.NewInt(5)), 0)
|
suite.Require().Equal(suite.app.EvmKeeper.GetBalance(suite.ctx, address).Cmp(big.NewInt(5)), 0)
|
||||||
require.Equal(t, ek.GetNonce(ctx, address), uint64(4))
|
suite.Require().Equal(suite.app.EvmKeeper.GetNonce(suite.ctx, address), uint64(4))
|
||||||
require.Equal(t, ek.GetState(ctx, address, ethcmn.HexToHash("0x2")), ethcmn.HexToHash("0x3"))
|
suite.Require().Equal(suite.app.EvmKeeper.GetState(suite.ctx, address, ethcmn.HexToHash("0x2")), ethcmn.HexToHash("0x3"))
|
||||||
require.Equal(t, ek.GetCode(ctx, address), []byte{0x1})
|
suite.Require().Equal(suite.app.EvmKeeper.GetCode(suite.ctx, address), []byte{0x1})
|
||||||
|
|
||||||
require.Equal(t, ek.GetBlockHashMapping(ctx, ethcmn.FromHex("0x0d87a3a5f73140f46aac1bf419263e4e94e87c292f25007700ab7f2060e2af68")), int64(7))
|
suite.Require().Equal(suite.app.EvmKeeper.GetBlockHashMapping(suite.ctx, ethcmn.FromHex("0x0d87a3a5f73140f46aac1bf419263e4e94e87c292f25007700ab7f2060e2af68")), int64(7))
|
||||||
require.Equal(t, ek.GetBlockHashMapping(ctx, []byte{0x43, 0x32}), int64(8))
|
suite.Require().Equal(suite.app.EvmKeeper.GetBlockHashMapping(suite.ctx, []byte{0x43, 0x32}), int64(8))
|
||||||
|
bloom, err := suite.app.EvmKeeper.GetBlockBloomMapping(suite.ctx, 4)
|
||||||
require.Equal(t, ek.GetBlockBloomMapping(ctx, 4), testBloom)
|
suite.Require().NoError(err)
|
||||||
|
suite.Require().Equal(bloom, testBloom)
|
||||||
|
|
||||||
// commit stateDB
|
// commit stateDB
|
||||||
_, err = ek.Commit(ctx, false)
|
_, err = suite.app.EvmKeeper.Commit(suite.ctx, false)
|
||||||
require.NoError(t, err, "failed to commit StateDB")
|
suite.Require().NoError(err, "failed to commit StateDB")
|
||||||
|
|
||||||
// simulate BaseApp EndBlocker commitment
|
// simulate BaseApp EndBlocker commitment
|
||||||
ms.Write()
|
suite.app.Commit()
|
||||||
cms.Commit()
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package keeper
|
package keeper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
@ -15,7 +16,7 @@ import (
|
|||||||
|
|
||||||
// NewQuerier is the module level router for state queries
|
// NewQuerier is the module level router for state queries
|
||||||
func NewQuerier(keeper Keeper) sdk.Querier {
|
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] {
|
switch path[0] {
|
||||||
case types.QueryProtocolVersion:
|
case types.QueryProtocolVersion:
|
||||||
return queryProtocolVersion(keeper)
|
return queryProtocolVersion(keeper)
|
||||||
@ -48,140 +49,146 @@ func NewQuerier(keeper Keeper) sdk.Querier {
|
|||||||
func queryProtocolVersion(keeper Keeper) ([]byte, sdk.Error) {
|
func queryProtocolVersion(keeper Keeper) ([]byte, sdk.Error) {
|
||||||
vers := version.ProtocolVersion
|
vers := version.ProtocolVersion
|
||||||
|
|
||||||
res, err := codec.MarshalJSONIndent(keeper.cdc, hexutil.Uint(vers))
|
bz, err := codec.MarshalJSONIndent(keeper.cdc, hexutil.Uint(vers))
|
||||||
if err != nil {
|
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) {
|
func queryBalance(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||||
addr := ethcmn.HexToAddress(path[1])
|
addr := ethcmn.HexToAddress(path[1])
|
||||||
balance := keeper.GetBalance(ctx, addr)
|
balance := keeper.GetBalance(ctx, addr)
|
||||||
|
|
||||||
bRes := types.QueryResBalance{Balance: utils.MarshalBigInt(balance)}
|
res := types.QueryResBalance{Balance: utils.MarshalBigInt(balance)}
|
||||||
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
|
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
|
||||||
if err != nil {
|
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) {
|
func queryBlockNumber(ctx sdk.Context, keeper Keeper) ([]byte, sdk.Error) {
|
||||||
num := ctx.BlockHeight()
|
num := ctx.BlockHeight()
|
||||||
bnRes := types.QueryResBlockNumber{Number: num}
|
bnRes := types.QueryResBlockNumber{Number: num}
|
||||||
res, err := codec.MarshalJSONIndent(keeper.cdc, bnRes)
|
bz, err := codec.MarshalJSONIndent(keeper.cdc, bnRes)
|
||||||
if err != nil {
|
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) {
|
func queryStorage(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||||
addr := ethcmn.HexToAddress(path[1])
|
addr := ethcmn.HexToAddress(path[1])
|
||||||
key := ethcmn.HexToHash(path[2])
|
key := ethcmn.HexToHash(path[2])
|
||||||
val := keeper.GetState(ctx, addr, key)
|
val := keeper.GetState(ctx, addr, key)
|
||||||
bRes := types.QueryResStorage{Value: val.Bytes()}
|
res := types.QueryResStorage{Value: val.Bytes()}
|
||||||
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
|
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
|
||||||
if err != nil {
|
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) {
|
func queryCode(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||||
addr := ethcmn.HexToAddress(path[1])
|
addr := ethcmn.HexToAddress(path[1])
|
||||||
code := keeper.GetCode(ctx, addr)
|
code := keeper.GetCode(ctx, addr)
|
||||||
cRes := types.QueryResCode{Code: code}
|
res := types.QueryResCode{Code: code}
|
||||||
res, err := codec.MarshalJSONIndent(keeper.cdc, cRes)
|
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
|
||||||
if err != nil {
|
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) {
|
func queryNonce(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||||
addr := ethcmn.HexToAddress(path[1])
|
addr := ethcmn.HexToAddress(path[1])
|
||||||
nonce := keeper.GetNonce(ctx, addr)
|
nonce := keeper.GetNonce(ctx, addr)
|
||||||
nRes := types.QueryResNonce{Nonce: nonce}
|
nRes := types.QueryResNonce{Nonce: nonce}
|
||||||
res, err := codec.MarshalJSONIndent(keeper.cdc, nRes)
|
bz, err := codec.MarshalJSONIndent(keeper.cdc, nRes)
|
||||||
if err != nil {
|
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) {
|
func queryHashToHeight(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||||
blockHash := ethcmn.FromHex(path[1])
|
blockHash := ethcmn.FromHex(path[1])
|
||||||
blockNumber := keeper.GetBlockHashMapping(ctx, blockHash)
|
blockNumber := keeper.GetBlockHashMapping(ctx, blockHash)
|
||||||
|
|
||||||
bRes := types.QueryResBlockNumber{Number: blockNumber}
|
res := types.QueryResBlockNumber{Number: blockNumber}
|
||||||
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
|
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
|
||||||
if err != nil {
|
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) {
|
func queryBlockLogsBloom(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||||
num, err := strconv.ParseInt(path[1], 10, 64)
|
num, err := strconv.ParseInt(path[1], 10, 64)
|
||||||
if err != nil {
|
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)
|
bloom, err := keeper.GetBlockBloomMapping(ctx, num)
|
||||||
|
|
||||||
bRes := types.QueryBloomFilter{Bloom: bloom}
|
|
||||||
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
|
|
||||||
if err != nil {
|
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) {
|
func queryTxLogs(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||||
txHash := ethcmn.HexToHash(path[1])
|
txHash := ethcmn.HexToHash(path[1])
|
||||||
logs := keeper.GetLogs(ctx, txHash)
|
logs, err := keeper.GetLogs(ctx, txHash)
|
||||||
|
|
||||||
bRes := types.QueryETHLogs{Logs: logs}
|
|
||||||
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
|
|
||||||
if err != nil {
|
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) {
|
func queryLogs(ctx sdk.Context, keeper Keeper) ([]byte, sdk.Error) {
|
||||||
logs := keeper.Logs(ctx)
|
logs := keeper.AllLogs(ctx)
|
||||||
|
|
||||||
lRes := types.QueryETHLogs{Logs: logs}
|
res := types.QueryETHLogs{Logs: logs}
|
||||||
l, err := codec.MarshalJSONIndent(keeper.cdc, lRes)
|
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
|
||||||
if err != nil {
|
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) {
|
func queryAccount(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||||
addr := ethcmn.HexToAddress(path[1])
|
addr := ethcmn.HexToAddress(path[1])
|
||||||
so := keeper.GetOrNewStateObject(ctx, addr)
|
so := keeper.GetOrNewStateObject(ctx, addr)
|
||||||
|
|
||||||
lRes := types.QueryResAccount{
|
res := types.QueryResAccount{
|
||||||
Balance: utils.MarshalBigInt(so.Balance()),
|
Balance: utils.MarshalBigInt(so.Balance()),
|
||||||
CodeHash: so.CodeHash(),
|
CodeHash: so.CodeHash(),
|
||||||
Nonce: so.Nonce(),
|
Nonce: so.Nonce(),
|
||||||
}
|
}
|
||||||
l, err := codec.MarshalJSONIndent(keeper.cdc, lRes)
|
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
|
||||||
if err != nil {
|
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 (
|
import (
|
||||||
"encoding/json"
|
"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/client/context"
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/types/module"
|
"github.com/cosmos/cosmos-sdk/types/module"
|
||||||
|
|
||||||
"github.com/cosmos/ethermint/x/evm/client/cli"
|
"github.com/cosmos/ethermint/x/evm/client/cli"
|
||||||
"github.com/cosmos/ethermint/x/evm/keeper"
|
"github.com/cosmos/ethermint/x/evm/keeper"
|
||||||
"github.com/cosmos/ethermint/x/evm/types"
|
"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{}
|
var _ module.AppModuleBasic = AppModuleBasic{}
|
||||||
@ -107,33 +108,13 @@ func (am AppModule) NewQuerierHandler() sdk.Querier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BeginBlock function for module at start of each block
|
// BeginBlock function for module at start of each block
|
||||||
func (am AppModule) BeginBlock(ctx sdk.Context, bl abci.RequestBeginBlock) {
|
func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
|
||||||
// Consider removing this when using evm as module without web3 API
|
BeginBlock(am.keeper, ctx, req)
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EndBlock function for module at end of block
|
// EndBlock function for module at end of block
|
||||||
func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
|
func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate {
|
||||||
// Gas costs are handled within msg handler so costs should be ignored
|
return EndBlock(am.keeper, ctx, req)
|
||||||
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{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitGenesis instantiates the genesis state
|
// InitGenesis instantiates the genesis state
|
||||||
|
@ -9,14 +9,13 @@ var ModuleCdc = codec.New()
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cdc := codec.New()
|
cdc := codec.New()
|
||||||
|
|
||||||
codec.RegisterCrypto(cdc)
|
codec.RegisterCrypto(cdc)
|
||||||
|
|
||||||
ModuleCdc = cdc.Seal()
|
ModuleCdc = cdc.Seal()
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterCodec registers concrete types and interfaces on the given codec.
|
// RegisterCodec registers concrete types and interfaces on the given codec.
|
||||||
func RegisterCodec(cdc *codec.Codec) {
|
func RegisterCodec(cdc *codec.Codec) {
|
||||||
cdc.RegisterConcrete(&EthereumTxMsg{}, "ethermint/MsgEthereumTx", nil)
|
cdc.RegisterConcrete(MsgEthereumTx{}, "ethermint/MsgEthereumTx", nil)
|
||||||
cdc.RegisterConcrete(&EmintMsg{}, "ethermint/MsgEmint", 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"
|
"fmt"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
"github.com/cosmos/ethermint/types"
|
"github.com/cosmos/ethermint/types"
|
||||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ sdk.Msg = EmintMsg{}
|
_ sdk.Msg = MsgEthermint{}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// TypeEmintMsg defines the type string of Emint message
|
// TypeMsgEthermint defines the type string of Ethermint message
|
||||||
TypeEmintMsg = "emint_tx"
|
TypeMsgEthermint = "ethermint"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EmintMsg implements a cosmos equivalent structure for Ethereum transactions
|
// MsgEthermint implements a cosmos equivalent structure for Ethereum transactions
|
||||||
type EmintMsg struct {
|
type MsgEthermint struct {
|
||||||
AccountNonce uint64 `json:"nonce"`
|
AccountNonce uint64 `json:"nonce"`
|
||||||
Price sdk.Int `json:"gasPrice"`
|
Price sdk.Int `json:"gasPrice"`
|
||||||
GasLimit uint64 `json:"gas"`
|
GasLimit uint64 `json:"gas"`
|
||||||
@ -30,12 +31,12 @@ type EmintMsg struct {
|
|||||||
From sdk.AccAddress `json:"from"`
|
From sdk.AccAddress `json:"from"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEmintMsg returns a reference to a new Ethermint transaction
|
// NewMsgEthermint returns a reference to a new Ethermint transaction
|
||||||
func NewEmintMsg(
|
func NewMsgEthermint(
|
||||||
nonce uint64, to *sdk.AccAddress, amount sdk.Int,
|
nonce uint64, to *sdk.AccAddress, amount sdk.Int,
|
||||||
gasLimit uint64, gasPrice sdk.Int, payload []byte, from sdk.AccAddress,
|
gasLimit uint64, gasPrice sdk.Int, payload []byte, from sdk.AccAddress,
|
||||||
) EmintMsg {
|
) MsgEthermint {
|
||||||
return EmintMsg{
|
return MsgEthermint{
|
||||||
AccountNonce: nonce,
|
AccountNonce: nonce,
|
||||||
Price: gasPrice,
|
Price: gasPrice,
|
||||||
GasLimit: gasLimit,
|
GasLimit: gasLimit,
|
||||||
@ -47,18 +48,18 @@ func NewEmintMsg(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Route should return the name of the module
|
// 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
|
// 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
|
// GetSignBytes encodes the message for signing
|
||||||
func (msg EmintMsg) GetSignBytes() []byte {
|
func (msg MsgEthermint) GetSignBytes() []byte {
|
||||||
return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg))
|
return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateBasic runs stateless checks on the message
|
// ValidateBasic runs stateless checks on the message
|
||||||
func (msg EmintMsg) ValidateBasic() sdk.Error {
|
func (msg MsgEthermint) ValidateBasic() sdk.Error {
|
||||||
if msg.Price.Sign() != 1 {
|
if msg.Price.Sign() != 1 {
|
||||||
return types.ErrInvalidValue(fmt.Sprintf("Price must be positive: %x", msg.Price))
|
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
|
// GetSigners defines whose signature is required
|
||||||
func (msg EmintMsg) GetSigners() []sdk.AccAddress {
|
func (msg MsgEthermint) GetSigners() []sdk.AccAddress {
|
||||||
return []sdk.AccAddress{msg.From}
|
return []sdk.AccAddress{msg.From}
|
||||||
}
|
}
|
||||||
|
|
||||||
// To returns the recipient address of the transaction. It returns nil if the
|
// To returns the recipient address of the transaction. It returns nil if the
|
||||||
// transaction is a contract creation.
|
// transaction is a contract creation.
|
||||||
func (msg EmintMsg) To() *ethcmn.Address {
|
func (msg MsgEthermint) To() *ethcmn.Address {
|
||||||
if msg.Recipient == nil {
|
if msg.Recipient == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -8,19 +8,19 @@ import (
|
|||||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEmintMsg(t *testing.T) {
|
func TestMsgEthermint(t *testing.T) {
|
||||||
addr := newSdkAddress()
|
addr := newSdkAddress()
|
||||||
fromAddr := 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.NotNil(t, msg)
|
||||||
require.Equal(t, msg.Recipient, &addr)
|
require.Equal(t, msg.Recipient, &addr)
|
||||||
|
|
||||||
require.Equal(t, msg.Route(), RouterKey)
|
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 {
|
testCases := []struct {
|
||||||
nonce uint64
|
nonce uint64
|
||||||
to *sdk.AccAddress
|
to *sdk.AccAddress
|
||||||
@ -38,7 +38,7 @@ func TestEmintMsgValidation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range testCases {
|
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 {
|
if tc.expectPass {
|
||||||
require.Nil(t, msg.ValidateBasic(), "test: %v", i)
|
require.Nil(t, msg.ValidateBasic(), "test: %v", i)
|
||||||
@ -52,13 +52,13 @@ func TestEmintEncodingAndDecoding(t *testing.T) {
|
|||||||
addr := newSdkAddress()
|
addr := newSdkAddress()
|
||||||
fromAddr := 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)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var msg2 EmintMsg
|
var msg2 MsgEthermint
|
||||||
err = cdc.UnmarshalBinaryBare(raw, &msg2)
|
err = ModuleCdc.UnmarshalBinaryBare(raw, &msg2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, msg.AccountNonce, msg2.AccountNonce)
|
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
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/cosmos/ethermint/types"
|
"github.com/cosmos/ethermint/types"
|
||||||
@ -28,10 +28,10 @@ type (
|
|||||||
func ValidateGenesis(data GenesisState) error {
|
func ValidateGenesis(data GenesisState) error {
|
||||||
for _, acct := range data.Accounts {
|
for _, acct := range data.Accounts {
|
||||||
if len(acct.Address.Bytes()) == 0 {
|
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 {
|
if acct.Balance == nil {
|
||||||
return fmt.Errorf("invalid GenesisAccount Error: Missing Balance")
|
return errors.New("invalid GenesisAccount: balance cannot be empty")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -4,13 +4,24 @@ const (
|
|||||||
// ModuleName string name of module
|
// ModuleName string name of module
|
||||||
ModuleName = "evm"
|
ModuleName = "evm"
|
||||||
|
|
||||||
// EvmStoreKey key for ethereum storage data
|
// StoreKey key for ethereum storage data (StateDB)
|
||||||
EvmStoreKey = "evmstore"
|
StoreKey = ModuleName
|
||||||
// EvmCodeKey key for ethereum code data
|
// CodeKey key for ethereum code data
|
||||||
EvmCodeKey = "evmcode"
|
CodeKey = ModuleName + "code"
|
||||||
// EvmBlockKey key for ethereum block data
|
// BlockKey key
|
||||||
EvmBlockKey = "evmblock"
|
BlockKey = ModuleName + "block"
|
||||||
|
|
||||||
// RouterKey uses module name for routing
|
// RouterKey uses module name for routing
|
||||||
RouterKey = ModuleName
|
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 (
|
var (
|
||||||
_ sdk.Msg = EthereumTxMsg{}
|
_ sdk.Msg = MsgEthereumTx{}
|
||||||
_ sdk.Tx = EthereumTxMsg{}
|
_ sdk.Tx = MsgEthereumTx{}
|
||||||
)
|
)
|
||||||
|
|
||||||
var big8 = big.NewInt(8)
|
var big8 = big.NewInt(8)
|
||||||
|
|
||||||
// message type and route constants
|
// message type and route constants
|
||||||
const (
|
const (
|
||||||
TypeEthereumTxMsg = "ethereum_tx"
|
TypeMsgEthereumTx = "ethereum"
|
||||||
RouteEthereumTxMsg = RouterKey
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// EthereumTxMsg encapsulates an Ethereum transaction as an SDK message.
|
// MsgEthereumTx encapsulates an Ethereum transaction as an SDK message.
|
||||||
type (
|
type (
|
||||||
EthereumTxMsg struct {
|
MsgEthereumTx struct {
|
||||||
Data TxData
|
Data TxData
|
||||||
|
|
||||||
// caches
|
// caches
|
||||||
@ -69,28 +68,28 @@ type (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewEthereumTxMsg returns a reference to a new Ethereum transaction message.
|
// NewMsgEthereumTx returns a reference to a new Ethereum transaction message.
|
||||||
func NewEthereumTxMsg(
|
func NewMsgEthereumTx(
|
||||||
nonce uint64, to *ethcmn.Address, amount *big.Int,
|
nonce uint64, to *ethcmn.Address, amount *big.Int,
|
||||||
gasLimit uint64, gasPrice *big.Int, payload []byte,
|
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.
|
// message designated for contract creation.
|
||||||
func NewEthereumTxMsgContract(
|
func NewMsgEthereumTxContract(
|
||||||
nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, payload []byte,
|
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,
|
nonce uint64, to *ethcmn.Address, amount *big.Int,
|
||||||
gasLimit uint64, gasPrice *big.Int, payload []byte,
|
gasLimit uint64, gasPrice *big.Int, payload []byte,
|
||||||
) *EthereumTxMsg {
|
) MsgEthereumTx {
|
||||||
|
|
||||||
if len(payload) > 0 {
|
if len(payload) > 0 {
|
||||||
payload = ethcmn.CopyBytes(payload)
|
payload = ethcmn.CopyBytes(payload)
|
||||||
@ -115,20 +114,20 @@ func newEthereumTxMsg(
|
|||||||
txData.Price.Set(gasPrice)
|
txData.Price.Set(gasPrice)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &EthereumTxMsg{Data: txData}
|
return MsgEthereumTx{Data: txData}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route returns the route value of an EthereumTxMsg.
|
// Route returns the route value of an MsgEthereumTx.
|
||||||
func (msg EthereumTxMsg) Route() string { return RouteEthereumTxMsg }
|
func (msg MsgEthereumTx) Route() string { return RouterKey }
|
||||||
|
|
||||||
// Type returns the type value of an EthereumTxMsg.
|
// Type returns the type value of an MsgEthereumTx.
|
||||||
func (msg EthereumTxMsg) Type() string { return TypeEthereumTxMsg }
|
func (msg MsgEthereumTx) Type() string { return TypeMsgEthereumTx }
|
||||||
|
|
||||||
// ValidateBasic implements the sdk.Msg interface. It performs basic validation
|
// ValidateBasic implements the sdk.Msg interface. It performs basic validation
|
||||||
// checks of a Transaction. If returns an sdk.Error if validation fails.
|
// checks of a Transaction. If returns an error if validation fails.
|
||||||
func (msg EthereumTxMsg) ValidateBasic() sdk.Error {
|
func (msg MsgEthereumTx) ValidateBasic() sdk.Error {
|
||||||
if msg.Data.Price.Sign() != 1 {
|
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
|
// 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
|
// To returns the recipient address of the transaction. It returns nil if the
|
||||||
// transaction is a contract creation.
|
// transaction is a contract creation.
|
||||||
func (msg EthereumTxMsg) To() *ethcmn.Address {
|
func (msg MsgEthereumTx) To() *ethcmn.Address {
|
||||||
if msg.Data.Recipient == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return msg.Data.Recipient
|
return msg.Data.Recipient
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMsgs returns a single EthereumTxMsg as an sdk.Msg.
|
// GetMsgs returns a single MsgEthereumTx as an sdk.Msg.
|
||||||
func (msg EthereumTxMsg) GetMsgs() []sdk.Msg {
|
func (msg MsgEthereumTx) GetMsgs() []sdk.Msg {
|
||||||
return []sdk.Msg{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
|
// NOTE: This method cannot be used as a chain ID is needed to recover the signer
|
||||||
// from the signature. Use 'VerifySig' instead.
|
// 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")
|
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
|
// NOTE: This method cannot be used as a chain ID is needed to create valid bytes
|
||||||
// to sign over. Use 'RLPSignBytes' instead.
|
// 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")
|
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
|
// RLPSignBytes returns the RLP hash of an Ethereum transaction message with a
|
||||||
// given chainID used for signing.
|
// 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{}{
|
return rlpHash([]interface{}{
|
||||||
msg.Data.AccountNonce,
|
msg.Data.AccountNonce,
|
||||||
msg.Data.Price,
|
msg.Data.Price,
|
||||||
@ -187,12 +182,12 @@ func (msg EthereumTxMsg) RLPSignBytes(chainID *big.Int) ethcmn.Hash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// EncodeRLP implements the rlp.Encoder interface.
|
// 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)
|
return rlp.Encode(w, &msg.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeRLP implements the rlp.Decoder interface.
|
// 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()
|
_, size, _ := s.Kind()
|
||||||
|
|
||||||
err := s.Decode(&msg.Data)
|
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.
|
// 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 {
|
if hash := msg.hash.Load(); hash != nil {
|
||||||
return hash.(ethcmn.Hash)
|
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
|
// 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
|
// EIP155 standard. It mutates the transaction as it populates the V, R, S
|
||||||
// fields of the Transaction's Signature.
|
// 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)
|
txHash := msg.RLPSignBytes(chainID)
|
||||||
|
|
||||||
sig, err := ethcrypto.Sign(txHash[:], priv)
|
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.
|
// 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.
|
// 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)
|
signer := ethtypes.NewEIP155Signer(chainID)
|
||||||
|
|
||||||
if sc := msg.from.Load(); sc != nil {
|
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.
|
// Cost returns amount + gasprice * gaslimit.
|
||||||
func (msg EthereumTxMsg) Cost() *big.Int {
|
func (msg MsgEthereumTx) Cost() *big.Int {
|
||||||
total := msg.Fee()
|
total := msg.Fee()
|
||||||
total.Add(total, msg.Data.Amount)
|
total.Add(total, msg.Data.Amount)
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fee returns gasprice * gaslimit.
|
// 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))
|
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)
|
// 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)
|
return deriveChainID(msg.Data.V)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,7 +312,7 @@ func deriveChainID(v *big.Int) *big.Int {
|
|||||||
// Auxiliary
|
// Auxiliary
|
||||||
|
|
||||||
// TxDecoder returns an sdk.TxDecoder that can decode both auth.StdTx and
|
// TxDecoder returns an sdk.TxDecoder that can decode both auth.StdTx and
|
||||||
// EthereumTxMsg transactions.
|
// MsgEthereumTx transactions.
|
||||||
func TxDecoder(cdc *codec.Codec) sdk.TxDecoder {
|
func TxDecoder(cdc *codec.Codec) sdk.TxDecoder {
|
||||||
return func(txBytes []byte) (sdk.Tx, sdk.Error) {
|
return func(txBytes []byte) (sdk.Tx, sdk.Error) {
|
||||||
var tx sdk.Tx
|
var tx sdk.Tx
|
||||||
@ -328,7 +323,6 @@ func TxDecoder(cdc *codec.Codec) sdk.TxDecoder {
|
|||||||
|
|
||||||
err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
|
err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
|
||||||
return nil, sdk.ErrTxDecode("failed to decode tx").TraceSDK(err.Error())
|
return nil, sdk.ErrTxDecode("failed to decode tx").TraceSDK(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,23 +1,11 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
"github.com/cosmos/ethermint/utils"
|
"github.com/cosmos/ethermint/utils"
|
||||||
|
|
||||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
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
|
// EncodableTxData implements the Ethereum transaction data structure. It is used
|
||||||
// solely as intended in Ethereum abiding by the protocol.
|
// solely as intended in Ethereum abiding by the protocol.
|
||||||
type EncodableTxData struct {
|
type EncodableTxData struct {
|
||||||
@ -38,12 +26,12 @@ type EncodableTxData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func marshalAmino(td EncodableTxData) (string, error) {
|
func marshalAmino(td EncodableTxData) (string, error) {
|
||||||
bz, err := cdc.MarshalBinaryBare(td)
|
bz, err := ModuleCdc.MarshalBinaryBare(td)
|
||||||
return string(bz), err
|
return string(bz), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalAmino(td *EncodableTxData, text string) error {
|
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
|
// MarshalAmino defines custom encoding scheme for TxData
|
||||||
|
@ -18,17 +18,17 @@ import (
|
|||||||
func TestMsgEthereumTx(t *testing.T) {
|
func TestMsgEthereumTx(t *testing.T) {
|
||||||
addr := GenerateEthAddress()
|
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.NotNil(t, msg1)
|
||||||
require.Equal(t, *msg1.Data.Recipient, addr)
|
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.NotNil(t, msg2)
|
||||||
require.Nil(t, msg2.Data.Recipient)
|
require.Nil(t, msg2.Data.Recipient)
|
||||||
|
|
||||||
msg3 := NewEthereumTxMsg(0, &addr, nil, 100000, nil, []byte("test"))
|
msg3 := NewMsgEthereumTx(0, &addr, nil, 100000, nil, []byte("test"))
|
||||||
require.Equal(t, msg3.Route(), RouteEthereumTxMsg)
|
require.Equal(t, msg3.Route(), RouterKey)
|
||||||
require.Equal(t, msg3.Type(), TypeEthereumTxMsg)
|
require.Equal(t, msg3.Type(), TypeMsgEthereumTx)
|
||||||
require.Panics(t, func() { msg3.GetSigners() })
|
require.Panics(t, func() { msg3.GetSigners() })
|
||||||
require.Panics(t, func() { msg3.GetSignBytes() })
|
require.Panics(t, func() { msg3.GetSignBytes() })
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@ func TestMsgEthereumTxValidation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range testCases {
|
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 {
|
if tc.expectPass {
|
||||||
require.Nil(t, msg.ValidateBasic(), "test: %v", i)
|
require.Nil(t, msg.ValidateBasic(), "test: %v", i)
|
||||||
@ -63,26 +63,26 @@ func TestMsgEthereumTxRLPSignBytes(t *testing.T) {
|
|||||||
addr := ethcmn.BytesToAddress([]byte("test_address"))
|
addr := ethcmn.BytesToAddress([]byte("test_address"))
|
||||||
chainID := big.NewInt(3)
|
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)
|
hash := msg.RLPSignBytes(chainID)
|
||||||
require.Equal(t, "5BD30E35AD27449390B14C91E6BCFDCAADF8FE44EF33680E3BC200FC0DC083C7", fmt.Sprintf("%X", hash))
|
require.Equal(t, "5BD30E35AD27449390B14C91E6BCFDCAADF8FE44EF33680E3BC200FC0DC083C7", fmt.Sprintf("%X", hash))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMsgEthereumTxRLPEncode(t *testing.T) {
|
func TestMsgEthereumTxRLPEncode(t *testing.T) {
|
||||||
addr := ethcmn.BytesToAddress([]byte("test_address"))
|
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.NoError(t, err)
|
||||||
require.Equal(t, ethcmn.FromHex("E48080830186A0940000000000000000746573745F61646472657373808474657374808080"), raw)
|
require.Equal(t, ethcmn.FromHex("E48080830186A0940000000000000000746573745F61646472657373808474657374808080"), raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMsgEthereumTxRLPDecode(t *testing.T) {
|
func TestMsgEthereumTxRLPDecode(t *testing.T) {
|
||||||
var msg EthereumTxMsg
|
var msg MsgEthereumTx
|
||||||
|
|
||||||
raw := ethcmn.FromHex("E48080830186A0940000000000000000746573745F61646472657373808474657374808080")
|
raw := ethcmn.FromHex("E48080830186A0940000000000000000746573745F61646472657373808474657374808080")
|
||||||
addr := ethcmn.BytesToAddress([]byte("test_address"))
|
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)
|
err := rlp.Decode(bytes.NewReader(raw), &msg)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -91,7 +91,7 @@ func TestMsgEthereumTxRLPDecode(t *testing.T) {
|
|||||||
|
|
||||||
func TestMsgEthereumTxHash(t *testing.T) {
|
func TestMsgEthereumTxHash(t *testing.T) {
|
||||||
addr := ethcmn.BytesToAddress([]byte("test_address"))
|
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()
|
hash := msg.Hash()
|
||||||
require.Equal(t, "E2AA2E68E7586AE9700F1D3D643330866B6AC2B6CA4C804F7C85ECB11D0B0B29", fmt.Sprintf("%X", 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())
|
addr2 := ethcmn.BytesToAddress(priv2.PubKey().Address().Bytes())
|
||||||
|
|
||||||
// require valid signature passes validation
|
// 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())
|
msg.Sign(chainID, priv1.ToECDSA())
|
||||||
|
|
||||||
signer, err := msg.VerifySig(chainID)
|
signer, err := msg.VerifySig(chainID)
|
||||||
@ -115,7 +115,7 @@ func TestMsgEthereumTxSig(t *testing.T) {
|
|||||||
require.NotEqual(t, addr2, signer)
|
require.NotEqual(t, addr2, signer)
|
||||||
|
|
||||||
// require invalid chain ID fail validation
|
// 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())
|
msg.Sign(chainID, priv1.ToECDSA())
|
||||||
|
|
||||||
signer, err = msg.VerifySig(big.NewInt(4))
|
signer, err = msg.VerifySig(big.NewInt(4))
|
||||||
@ -125,7 +125,7 @@ func TestMsgEthereumTxSig(t *testing.T) {
|
|||||||
|
|
||||||
func TestMsgEthereumTxAmino(t *testing.T) {
|
func TestMsgEthereumTxAmino(t *testing.T) {
|
||||||
addr := GenerateEthAddress()
|
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.V = big.NewInt(1)
|
||||||
msg.Data.R = big.NewInt(2)
|
msg.Data.R = big.NewInt(2)
|
||||||
@ -134,7 +134,7 @@ func TestMsgEthereumTxAmino(t *testing.T) {
|
|||||||
raw, err := ModuleCdc.MarshalBinaryBare(msg)
|
raw, err := ModuleCdc.MarshalBinaryBare(msg)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var msg2 EthereumTxMsg
|
var msg2 MsgEthereumTx
|
||||||
|
|
||||||
err = ModuleCdc.UnmarshalBinaryBare(raw, &msg2)
|
err = ModuleCdc.UnmarshalBinaryBare(raw, &msg2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -73,20 +73,21 @@ type (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func newObject(db *CommitStateDB, accProto authexported.Account) *stateObject {
|
func newStateObject(db *CommitStateDB, accProto authexported.Account) *stateObject {
|
||||||
acc, ok := accProto.(*types.Account)
|
ethermintAccount, ok := accProto.(*types.Account)
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(fmt.Sprintf("invalid account type for state object: %T", accProto))
|
panic(fmt.Sprintf("invalid account type for state object: %T", accProto))
|
||||||
}
|
}
|
||||||
|
|
||||||
if acc.CodeHash == nil {
|
// set empty code hash
|
||||||
acc.CodeHash = emptyCodeHash
|
if ethermintAccount.CodeHash == nil {
|
||||||
|
ethermintAccount.CodeHash = emptyCodeHash
|
||||||
}
|
}
|
||||||
|
|
||||||
return &stateObject{
|
return &stateObject{
|
||||||
stateDB: db,
|
stateDB: db,
|
||||||
account: acc,
|
account: ethermintAccount,
|
||||||
address: ethcmn.BytesToAddress(acc.Address.Bytes()),
|
address: ethcmn.BytesToAddress(ethermintAccount.GetAddress().Bytes()),
|
||||||
originStorage: make(types.Storage),
|
originStorage: make(types.Storage),
|
||||||
dirtyStorage: make(types.Storage),
|
dirtyStorage: make(types.Storage),
|
||||||
}
|
}
|
||||||
@ -188,7 +189,7 @@ func (so *stateObject) setBalance(amount sdk.Int) {
|
|||||||
so.account.SetBalance(amount)
|
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) {
|
func (so *stateObject) SetNonce(nonce uint64) {
|
||||||
so.stateDB.journal.append(nonceChange{
|
so.stateDB.journal.append(nonceChange{
|
||||||
account: &so.address,
|
account: &so.address,
|
||||||
@ -199,6 +200,9 @@ func (so *stateObject) SetNonce(nonce uint64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
so.account.Sequence = nonce
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +220,7 @@ func (so *stateObject) markSuicided() {
|
|||||||
// commitState commits all dirty storage to a KVStore.
|
// commitState commits all dirty storage to a KVStore.
|
||||||
func (so *stateObject) commitState() {
|
func (so *stateObject) commitState() {
|
||||||
ctx := so.stateDB.ctx
|
ctx := so.stateDB.ctx
|
||||||
store := ctx.KVStore(so.stateDB.storageKey)
|
store := ctx.KVStore(so.stateDB.storeKey)
|
||||||
|
|
||||||
for key, value := range so.dirtyStorage {
|
for key, value := range so.dirtyStorage {
|
||||||
delete(so.dirtyStorage, key)
|
delete(so.dirtyStorage, key)
|
||||||
@ -258,22 +262,32 @@ func (so stateObject) Address() ethcmn.Address {
|
|||||||
|
|
||||||
// Balance returns the state object's current balance.
|
// Balance returns the state object's current balance.
|
||||||
func (so *stateObject) Balance() *big.Int {
|
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.
|
// CodeHash returns the state object's code hash.
|
||||||
func (so *stateObject) CodeHash() []byte {
|
func (so *stateObject) CodeHash() []byte {
|
||||||
|
if so.account == nil || len(so.account.CodeHash) == 0 {
|
||||||
|
return emptyCodeHash
|
||||||
|
}
|
||||||
return so.account.CodeHash
|
return so.account.CodeHash
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nonce returns the state object's current nonce (sequence number).
|
// Nonce returns the state object's current nonce (sequence number).
|
||||||
func (so *stateObject) Nonce() uint64 {
|
func (so *stateObject) Nonce() uint64 {
|
||||||
|
if so.account == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
return so.account.Sequence
|
return so.account.Sequence
|
||||||
}
|
}
|
||||||
|
|
||||||
// Code returns the contract code associated with this object, if any.
|
// Code returns the contract code associated with this object, if any.
|
||||||
func (so *stateObject) Code(_ ethstate.Database) []byte {
|
func (so *stateObject) Code(_ ethstate.Database) []byte {
|
||||||
if so.code != nil {
|
if len(so.code) > 0 {
|
||||||
return so.code
|
return so.code
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,10 +300,9 @@ func (so *stateObject) Code(_ ethstate.Database) []byte {
|
|||||||
code := store.Get(so.CodeHash())
|
code := store.Get(so.CodeHash())
|
||||||
|
|
||||||
if len(code) == 0 {
|
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
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,7 +334,7 @@ func (so *stateObject) GetCommittedState(_ ethstate.Database, key ethcmn.Hash) e
|
|||||||
|
|
||||||
// otherwise load the value from the KVStore
|
// otherwise load the value from the KVStore
|
||||||
ctx := so.stateDB.ctx
|
ctx := so.stateDB.ctx
|
||||||
store := ctx.KVStore(so.stateDB.storageKey)
|
store := ctx.KVStore(so.stateDB.storeKey)
|
||||||
rawValue := store.Get(prefixKey.Bytes())
|
rawValue := store.Get(prefixKey.Bytes())
|
||||||
|
|
||||||
if len(rawValue) > 0 {
|
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) ReturnGas(gas *big.Int) {}
|
||||||
|
|
||||||
func (so *stateObject) deepCopy(db *CommitStateDB) *stateObject {
|
func (so *stateObject) deepCopy(db *CommitStateDB) *stateObject {
|
||||||
newStateObj := newObject(db, so.account)
|
newStateObj := newStateObject(db, so.account)
|
||||||
|
|
||||||
newStateObj.code = so.code
|
newStateObj.code = so.code
|
||||||
newStateObj.dirtyStorage = so.dirtyStorage.Copy()
|
newStateObj.dirtyStorage = so.dirtyStorage.Copy()
|
||||||
@ -355,9 +368,11 @@ func (so *stateObject) deepCopy(db *CommitStateDB) *stateObject {
|
|||||||
|
|
||||||
// empty returns whether the account is considered empty.
|
// empty returns whether the account is considered empty.
|
||||||
func (so *stateObject) empty() bool {
|
func (so *stateObject) empty() bool {
|
||||||
return so.account.Sequence == 0 &&
|
return so.account == nil ||
|
||||||
|
(so.account != nil &&
|
||||||
|
so.account.Sequence == 0 &&
|
||||||
so.account.Balance().Sign() == 0 &&
|
so.account.Balance().Sign() == 0 &&
|
||||||
bytes.Equal(so.account.CodeHash, emptyCodeHash)
|
bytes.Equal(so.account.CodeHash, emptyCodeHash))
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeRLP implements rlp.Encoder.
|
// EncodeRLP implements rlp.Encoder.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
@ -27,14 +28,24 @@ type StateTransition struct {
|
|||||||
Simulate bool
|
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
|
// 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
|
contractCreation := st.Recipient == nil
|
||||||
|
|
||||||
cost, err := core.IntrinsicGas(st.Payload, contractCreation, true)
|
cost, err := core.IntrinsicGas(st.Payload, contractCreation, true)
|
||||||
if err != nil {
|
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
|
// 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,
|
CanTransfer: core.CanTransfer,
|
||||||
Transfer: core.Transfer,
|
Transfer: core.Transfer,
|
||||||
Origin: st.Sender,
|
Origin: st.Sender,
|
||||||
Coinbase: common.Address{},
|
Coinbase: common.Address{}, // TODO: explain why this is empty
|
||||||
BlockNumber: big.NewInt(ctx.BlockHeight()),
|
BlockNumber: big.NewInt(ctx.BlockHeight()),
|
||||||
Time: big.NewInt(ctx.BlockHeader().Time.Unix()),
|
Time: big.NewInt(ctx.BlockHeader().Time.Unix()),
|
||||||
Difficulty: big.NewInt(0x30000), // unused
|
Difficulty: big.NewInt(0), // unused. Only required in PoW context
|
||||||
GasLimit: gasLimit,
|
GasLimit: gasLimit,
|
||||||
GasPrice: ctx.MinGasPrices().AmountOf(emint.DenomDefault).Int,
|
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 (
|
var (
|
||||||
ret []byte
|
ret []byte
|
||||||
leftOverGas uint64
|
leftOverGas uint64
|
||||||
addr common.Address
|
addr common.Address
|
||||||
vmerr error
|
|
||||||
senderRef = vm.AccountRef(st.Sender)
|
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
|
// Set nonce of sender account before evm state transition for usage in generating Create address
|
||||||
st.Csdb.SetNonce(st.Sender, st.AccountNonce)
|
st.Csdb.SetNonce(st.Sender, st.AccountNonce)
|
||||||
|
|
||||||
if contractCreation {
|
switch contractCreation {
|
||||||
ret, addr, leftOverGas, vmerr = vmenv.Create(senderRef, st.Payload, gasLimit, st.Amount)
|
case true:
|
||||||
} else {
|
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)
|
// Increment the nonce for the next transaction (just for evm state transition)
|
||||||
csdb.SetNonce(st.Sender, csdb.GetNonce(st.Sender)+1)
|
csdb.SetNonce(st.Sender, csdb.GetNonce(st.Sender)+1)
|
||||||
|
ret, leftOverGas, err = evm.Call(senderRef, *st.Recipient, st.Payload, gasLimit, st.Amount)
|
||||||
ret, leftOverGas, vmerr = vmenv.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
|
// Resets nonce to value pre state transition
|
||||||
st.Csdb.SetNonce(st.Sender, currentNonce)
|
st.Csdb.SetNonce(st.Sender, currentNonce)
|
||||||
|
|
||||||
// Generate bloom filter to be saved in tx receipt data
|
// Generate bloom filter to be saved in tx receipt data
|
||||||
bloomInt := big.NewInt(0)
|
bloomInt := big.NewInt(0)
|
||||||
var bloomFilter ethtypes.Bloom
|
var bloomFilter ethtypes.Bloom
|
||||||
|
var logs []*ethtypes.Log
|
||||||
if st.THash != nil && !st.Simulate {
|
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)
|
bloomInt = ethtypes.LogsBloom(logs)
|
||||||
bloomFilter = ethtypes.BytesToBloom(bloomInt.Bytes())
|
bloomFilter = ethtypes.BytesToBloom(bloomInt.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode all necessary data into slice of bytes to return in sdk result
|
// 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
|
// handle errors
|
||||||
if vmerr != nil {
|
if err != nil {
|
||||||
res := emint.ErrVMExecution(vmerr.Error()).Result()
|
if err == vm.ErrOutOfGas || err == vm.ErrCodeStoreOutOfGas {
|
||||||
if vmerr == vm.ErrOutOfGas || vmerr == vm.ErrCodeStoreOutOfGas {
|
return nil, fmt.Errorf("evm execution went out of gas: %s", err.Error())
|
||||||
res = sdk.ErrOutOfGas("EVM execution went out of gas").Result()
|
|
||||||
}
|
}
|
||||||
res.Data = returnData
|
|
||||||
// Consume gas before returning
|
// Consume gas before returning
|
||||||
ctx.GasMeter().ConsumeGas(gasLimit-leftOverGas, "EVM execution consumption")
|
ctx.GasMeter().ConsumeGas(gasConsumed, "EVM execution consumption")
|
||||||
return nil, res
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Refund unused gas here, if intended in future
|
// TODO: Refund unused gas here, if intended in future
|
||||||
|
|
||||||
if !st.Simulate {
|
if !st.Simulate {
|
||||||
// Finalise state if not a simulated transaction
|
// 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
|
// Consume gas from evm execution
|
||||||
// Out of gas check does not need to be done here since it is done within the 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"
|
"sync"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
|
|
||||||
emint "github.com/cosmos/ethermint/types"
|
emint "github.com/cosmos/ethermint/types"
|
||||||
|
|
||||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||||
@ -41,9 +39,9 @@ type CommitStateDB struct {
|
|||||||
// StateDB interface. Perhaps there is a better way.
|
// StateDB interface. Perhaps there is a better way.
|
||||||
ctx sdk.Context
|
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
|
// maps that hold 'live' objects, which will get modified while processing a
|
||||||
// state transition
|
// state transition
|
||||||
@ -84,12 +82,14 @@ type CommitStateDB struct {
|
|||||||
//
|
//
|
||||||
// CONTRACT: Stores used for state must be cache-wrapped as the ordering of the
|
// CONTRACT: Stores used for state must be cache-wrapped as the ordering of the
|
||||||
// key/value space matters in determining the merkle root.
|
// 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{
|
return &CommitStateDB{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
ak: ak,
|
|
||||||
storageKey: storageKey,
|
|
||||||
codeKey: codeKey,
|
codeKey: codeKey,
|
||||||
|
storeKey: storeKey,
|
||||||
|
accountKeeper: ak,
|
||||||
stateObjects: make(map[ethcmn.Address]*stateObject),
|
stateObjects: make(map[ethcmn.Address]*stateObject),
|
||||||
stateObjectsDirty: make(map[ethcmn.Address]struct{}),
|
stateObjectsDirty: make(map[ethcmn.Address]struct{}),
|
||||||
logs: make(map[ethcmn.Hash][]*ethtypes.Log),
|
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.
|
// GetLogs returns the current logs for a given hash in the state.
|
||||||
func (csdb *CommitStateDB) GetLogs(hash ethcmn.Hash) []*ethtypes.Log {
|
func (csdb *CommitStateDB) GetLogs(hash ethcmn.Hash) ([]*ethtypes.Log, error) {
|
||||||
return csdb.logs[hash]
|
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.
|
// Logs returns all the current logs in the state.
|
||||||
func (csdb *CommitStateDB) Logs() []*ethtypes.Log {
|
func (csdb *CommitStateDB) AllLogs() []*ethtypes.Log {
|
||||||
// nolint: prealloc
|
// nolint: prealloc
|
||||||
var logs []*ethtypes.Log
|
var logs []*ethtypes.Log
|
||||||
for _, lgs := range csdb.logs {
|
for _, lgs := range csdb.logs {
|
||||||
@ -364,7 +380,9 @@ func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (ethcmn.Hash, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update the object in the KVStore
|
// update the object in the KVStore
|
||||||
csdb.updateStateObject(so)
|
if err := csdb.updateStateObject(so); err != nil {
|
||||||
|
return ethcmn.Hash{}, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(csdb.stateObjectsDirty, addr)
|
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,
|
// Finalise finalizes the state objects (accounts) state by setting their state,
|
||||||
// removing the csdb destructed objects and clearing the journal as well as the
|
// removing the csdb destructed objects and clearing the journal as well as the
|
||||||
// refunds.
|
// refunds.
|
||||||
func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) {
|
func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) error {
|
||||||
for addr := range csdb.journal.dirties {
|
for addr := range csdb.journal.dirties {
|
||||||
so, exist := csdb.stateObjects[addr]
|
so, exist := csdb.stateObjects[addr]
|
||||||
if !exist {
|
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
|
// Set all the dirty state storage items for the state object in the
|
||||||
// KVStore and finally set the account in the account mapper.
|
// KVStore and finally set the account in the account mapper.
|
||||||
so.commitState()
|
so.commitState()
|
||||||
csdb.updateStateObject(so)
|
if err := csdb.updateStateObject(so); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
csdb.stateObjectsDirty[addr] = struct{}{}
|
csdb.stateObjectsDirty[addr] = struct{}{}
|
||||||
@ -409,6 +429,7 @@ func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) {
|
|||||||
|
|
||||||
// invalidate journal because reverting across transactions is not allowed
|
// invalidate journal because reverting across transactions is not allowed
|
||||||
csdb.clearJournalAndRefund()
|
csdb.clearJournalAndRefund()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IntermediateRoot returns the current root hash of the state. It is called in
|
// 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
|
// 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
|
// root as commitment of the merkle-ized tree doesn't happen until the
|
||||||
// BaseApps' EndBlocker.
|
// BaseApps' EndBlocker.
|
||||||
func (csdb *CommitStateDB) IntermediateRoot(deleteEmptyObjects bool) ethcmn.Hash {
|
func (csdb *CommitStateDB) IntermediateRoot(deleteEmptyObjects bool) (ethcmn.Hash, error) {
|
||||||
csdb.Finalise(deleteEmptyObjects)
|
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.
|
// updateStateObject writes the given state object to the store.
|
||||||
func (csdb *CommitStateDB) updateStateObject(so *stateObject) {
|
func (csdb *CommitStateDB) updateStateObject(so *stateObject) error {
|
||||||
csdb.ak.SetAccount(csdb.ctx, so.account)
|
csdb.accountKeeper.SetAccount(csdb.ctx, so.account)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteStateObject removes the given state object from the state store.
|
// deleteStateObject removes the given state object from the state store.
|
||||||
func (csdb *CommitStateDB) deleteStateObject(so *stateObject) {
|
func (csdb *CommitStateDB) deleteStateObject(so *stateObject) {
|
||||||
so.deleted = true
|
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
|
// UpdateAccounts updates the nonce and coin balances of accounts
|
||||||
func (csdb *CommitStateDB) UpdateAccounts() {
|
func (csdb *CommitStateDB) UpdateAccounts() {
|
||||||
for addr, so := range csdb.stateObjects {
|
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)
|
emintAcc, ok := currAcc.(*emint.Account)
|
||||||
if ok {
|
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
|
// If queried account's balance or nonce are invalid, update the account pointer
|
||||||
so.account = emintAcc
|
so.account = emintAcc
|
||||||
}
|
}
|
||||||
@ -602,9 +626,9 @@ func (csdb *CommitStateDB) Copy() *CommitStateDB {
|
|||||||
// copy all the basic fields, initialize the memory ones
|
// copy all the basic fields, initialize the memory ones
|
||||||
state := &CommitStateDB{
|
state := &CommitStateDB{
|
||||||
ctx: csdb.ctx,
|
ctx: csdb.ctx,
|
||||||
ak: csdb.ak,
|
|
||||||
storageKey: csdb.storageKey,
|
|
||||||
codeKey: csdb.codeKey,
|
codeKey: csdb.codeKey,
|
||||||
|
storeKey: csdb.storeKey,
|
||||||
|
accountKeeper: csdb.accountKeeper,
|
||||||
stateObjects: make(map[ethcmn.Address]*stateObject, len(csdb.journal.dirties)),
|
stateObjects: make(map[ethcmn.Address]*stateObject, len(csdb.journal.dirties)),
|
||||||
stateObjectsDirty: make(map[ethcmn.Address]struct{}, len(csdb.journal.dirties)),
|
stateObjectsDirty: make(map[ethcmn.Address]struct{}, len(csdb.journal.dirties)),
|
||||||
refund: csdb.refund,
|
refund: csdb.refund,
|
||||||
@ -663,8 +687,9 @@ func (csdb *CommitStateDB) ForEachStorage(addr ethcmn.Address, cb func(key, valu
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
store := csdb.ctx.KVStore(csdb.storageKey)
|
store := csdb.ctx.KVStore(csdb.storeKey)
|
||||||
iter := sdk.KVStorePrefixIterator(store, so.Address().Bytes())
|
iter := sdk.KVStorePrefixIterator(store, so.Address().Bytes())
|
||||||
|
defer iter.Close()
|
||||||
|
|
||||||
for ; iter.Valid(); iter.Next() {
|
for ; iter.Valid(); iter.Next() {
|
||||||
key := ethcmn.BytesToHash(iter.Key())
|
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))
|
cb(key, ethcmn.BytesToHash(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
iter.Close()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -698,8 +722,9 @@ func (csdb *CommitStateDB) GetOrNewStateObject(addr ethcmn.Address) StateObject
|
|||||||
func (csdb *CommitStateDB) createObject(addr ethcmn.Address) (newObj, prevObj *stateObject) {
|
func (csdb *CommitStateDB) createObject(addr ethcmn.Address) (newObj, prevObj *stateObject) {
|
||||||
prevObj = csdb.getStateObject(addr)
|
prevObj = csdb.getStateObject(addr)
|
||||||
|
|
||||||
acc := csdb.ak.NewAccountWithAddress(csdb.ctx, sdk.AccAddress(addr.Bytes()))
|
acc := csdb.accountKeeper.NewAccountWithAddress(csdb.ctx, sdk.AccAddress(addr.Bytes()))
|
||||||
newObj = newObject(csdb, acc)
|
|
||||||
|
newObj = newStateObject(csdb, acc)
|
||||||
newObj.setNonce(0) // sets the object to dirty
|
newObj.setNonce(0) // sets the object to dirty
|
||||||
|
|
||||||
if prevObj == nil {
|
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
|
// 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 {
|
if acc == nil {
|
||||||
csdb.setError(fmt.Errorf("no account found for address: %X", addr.Bytes()))
|
csdb.setError(fmt.Errorf("no account found for address: %X", addr.Bytes()))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert the state object into the live set
|
// insert the state object into the live set
|
||||||
so := newObject(csdb, acc)
|
so := newStateObject(csdb, acc)
|
||||||
csdb.setStateObject(so)
|
csdb.setStateObject(so)
|
||||||
|
|
||||||
return so
|
return so
|
||||||
@ -748,3 +773,10 @@ func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *sta
|
|||||||
func (csdb *CommitStateDB) setStateObject(so *stateObject) {
|
func (csdb *CommitStateDB) setStateObject(so *stateObject) {
|
||||||
csdb.stateObjects[so.Address()] = so
|
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 (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/cosmos/cosmos-sdk/store"
|
|
||||||
sdkstore "github.com/cosmos/cosmos-sdk/store/types"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
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"
|
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
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"
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
tmlog "github.com/tendermint/tendermint/libs/log"
|
|
||||||
dbm "github.com/tendermint/tm-db"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newTestCodec() *codec.Codec {
|
// nolint: unused
|
||||||
cdc := codec.New()
|
type StateDBTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
|
||||||
RegisterCodec(cdc)
|
ctx sdk.Context
|
||||||
types.RegisterCodec(cdc)
|
querier sdk.Querier
|
||||||
auth.RegisterCodec(cdc)
|
app *app.EthermintApp
|
||||||
sdk.RegisterCodec(cdc)
|
|
||||||
codec.RegisterCrypto(cdc)
|
|
||||||
|
|
||||||
return cdc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupStateDB() (*CommitStateDB, error) {
|
func (suite *StateDBTestSuite) SetupTest() {
|
||||||
accKey := sdk.NewKVStoreKey("acc")
|
checkTx := false
|
||||||
storageKey := sdk.NewKVStoreKey(EvmStoreKey)
|
|
||||||
codeKey := sdk.NewKVStoreKey(EvmCodeKey)
|
|
||||||
logger := tmlog.NewNopLogger()
|
|
||||||
|
|
||||||
db := dbm.NewMemDB()
|
suite.app = app.Setup(checkTx)
|
||||||
|
suite.ctx = suite.app.BaseApp.NewContext(checkTx, abci.Header{Height: 1})
|
||||||
// create logger, codec and root multi-store
|
suite.querier = keeper.NewQuerier(suite.app.EvmKeeper)
|
||||||
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)
|
func (suite *StateDBTestSuite) TestBloomFilter() {
|
||||||
|
stateDB := suite.app.EvmKeeper.CommitStateDB
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBloomFilter(t *testing.T) {
|
|
||||||
stateDB, err := setupStateDB()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Prepare db for logs
|
// Prepare db for logs
|
||||||
tHash := ethcmn.BytesToHash([]byte{0x1})
|
tHash := ethcmn.BytesToHash([]byte{0x1})
|
||||||
@ -87,14 +47,15 @@ func TestBloomFilter(t *testing.T) {
|
|||||||
stateDB.AddLog(&log)
|
stateDB.AddLog(&log)
|
||||||
|
|
||||||
// Get log from db
|
// Get log from db
|
||||||
logs := stateDB.GetLogs(tHash)
|
logs, err := stateDB.GetLogs(tHash)
|
||||||
require.Equal(t, len(logs), 1)
|
suite.Require().NoError(err)
|
||||||
|
suite.Require().Equal(len(logs), 1)
|
||||||
|
|
||||||
// get logs bloom from the log
|
// get logs bloom from the log
|
||||||
bloomInt := ethtypes.LogsBloom(logs)
|
bloomInt := ethtypes.LogsBloom(logs)
|
||||||
bloomFilter := ethtypes.BytesToBloom(bloomInt.Bytes())
|
bloomFilter := ethtypes.BytesToBloom(bloomInt.Bytes())
|
||||||
|
|
||||||
// Check to make sure bloom filter will succeed on
|
// Check to make sure bloom filter will succeed on
|
||||||
require.True(t, ethtypes.BloomLookup(bloomFilter, contractAddress))
|
suite.Require().True(ethtypes.BloomLookup(bloomFilter, contractAddress))
|
||||||
require.False(t, ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2))))
|
suite.Require().False(ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2))))
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,6 @@ import (
|
|||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
bloomIdx = ethcmn.AddressLength
|
|
||||||
returnIdx = bloomIdx + ethtypes.BloomByteLength
|
|
||||||
)
|
|
||||||
|
|
||||||
// GenerateEthAddress generates an Ethereum address.
|
// GenerateEthAddress generates an Ethereum address.
|
||||||
func GenerateEthAddress() ethcmn.Address {
|
func GenerateEthAddress() ethcmn.Address {
|
||||||
priv, err := crypto.GenerateKey()
|
priv, err := crypto.GenerateKey()
|
||||||
@ -52,23 +47,41 @@ func rlpHash(x interface{}) (hash ethcmn.Hash) {
|
|||||||
return 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
|
// EncodeReturnData takes all of the necessary data from the EVM execution
|
||||||
// and returns the data as a byte slice
|
// and returns the data as a byte slice encoded with amino
|
||||||
func EncodeReturnData(addr ethcmn.Address, bloom ethtypes.Bloom, evmRet []byte) []byte {
|
func EncodeResultData(data *ResultData) ([]byte, error) {
|
||||||
// Append address, bloom, evm return bytes in that order
|
return ModuleCdc.MarshalBinaryLengthPrefixed(data)
|
||||||
returnData := append(addr.Bytes(), bloom.Bytes()...)
|
|
||||||
return append(returnData, evmRet...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeReturnData decodes the byte slice of values to their respective types
|
// DecodeResultData decodes an amino-encoded byte slice into ReturnData
|
||||||
func DecodeReturnData(bytes []byte) (addr ethcmn.Address, bloom ethtypes.Bloom, ret []byte, err error) {
|
func DecodeResultData(in []byte) (ResultData, error) {
|
||||||
if len(bytes) >= returnIdx {
|
data := new(ResultData)
|
||||||
addr = ethcmn.BytesToAddress(bytes[:bloomIdx])
|
err := ModuleCdc.UnmarshalBinaryLengthPrefixed(in, data)
|
||||||
bloom = ethtypes.BytesToBloom(bytes[bloomIdx:returnIdx])
|
if err != nil {
|
||||||
ret = bytes[returnIdx:]
|
return ResultData{}, err
|
||||||
} else {
|
}
|
||||||
err = fmt.Errorf("invalid format for encoded data, message must be an EVM state transition")
|
return *data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
// 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})
|
bloom := ethtypes.BytesToBloom([]byte{0x1, 0x3})
|
||||||
ret := []byte{0x5, 0x8}
|
ret := []byte{0x5, 0x8}
|
||||||
|
|
||||||
encoded := EncodeReturnData(addr, bloom, ret)
|
data := &ResultData{
|
||||||
|
Address: addr,
|
||||||
decAddr, decBloom, decRet, err := DecodeReturnData(encoded)
|
Bloom: bloom,
|
||||||
|
Logs: []*ethtypes.Log{{
|
||||||
require.NoError(t, err)
|
Data: []byte{1, 2, 3, 4},
|
||||||
require.Equal(t, addr, decAddr)
|
BlockNumber: 17,
|
||||||
require.Equal(t, bloom, decBloom)
|
}},
|
||||||
require.Equal(t, ret, decRet)
|
Ret: ret,
|
||||||
|
}
|
||||||
|
|
||||||
|
enc, err := EncodeResultData(data)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
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