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:
Federico Kunze 2020-04-01 15:49:21 -03:00 committed by GitHub
parent 1627ed81bf
commit da9157e406
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1631 additions and 1235 deletions

View File

@ -40,3 +40,13 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Improvements
* (x/evm) [\#181](https://github.com/ChainSafe/ethermint/issues/181) Updated EVM module to the recommended module structure. [@fedekunze](https://github.com/fedekunze)
* (app) [\#188](https://github.com/ChainSafe/ethermint/issues/186) Misc cleanup [@fedekunze](https://github.com/fedekunze):
* (`x/evm`) Rename `EthereumTxMsg` --> `MsgEthereumTx` and `EmintMsg` --> `MsgEthermint` for consistency with SDK standards
* Updated integration and unit tests to use `EthermintApp` as testing suite
* Use expected keeper interface for `AccountKeeper`
* Replaced `count` type in keeper with `int`
* Add SDK events for transactions
* [\#236](https://github.com/ChainSafe/ethermint/pull/236) Changes from upgrade [@fedekunze](https://github.com/fedekunze)
* (app/ante) Moved `AnteHandler` implementation to `app/ante`
* (keys) Marked `ExportEthKeyCommand` as **UNSAFE**
* (x/evm) Moved `BeginBlock` and `EndBlock` to `x/evm/abci.go`

View File

@ -1,4 +1,4 @@
package app
package ante
import (
"fmt"
@ -46,15 +46,15 @@ func NewAnteHandler(ak auth.AccountKeeper, sk types.SupplyKeeper) sdk.AnteHandle
ante.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators
ante.NewValidateSigCountDecorator(ak),
ante.NewDeductFeeDecorator(ak, sk),
ante.NewSigGasConsumeDecorator(ak, consumeSigGas),
ante.NewSigGasConsumeDecorator(ak, sigGasConsumer),
ante.NewSigVerificationDecorator(ak),
ante.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator
)
return stdAnte(ctx, tx, sim)
case *evmtypes.EthereumTxMsg:
return ethAnteHandler(ctx, ak, sk, castTx, sim)
case evmtypes.MsgEthereumTx:
return ethAnteHandler(ctx, ak, sk, &castTx, sim)
default:
return ctx, sdk.ErrInternal(fmt.Sprintf("transaction type invalid: %T", tx))
@ -62,7 +62,9 @@ func NewAnteHandler(ak auth.AccountKeeper, sk types.SupplyKeeper) sdk.AnteHandle
}
}
func consumeSigGas(
// sigGasConsumer overrides the DefaultSigVerificationGasConsumer from the x/auth
// module on the SDK. It doesn't allow ed25519 nor multisig thresholds.
func sigGasConsumer(
meter sdk.GasMeter, sig []byte, pubkey tmcrypto.PubKey, params types.Params,
) error {
switch pubkey.(type) {
@ -89,7 +91,7 @@ func consumeSigGas(
// prevent spam and DoS attacks.
func ethAnteHandler(
ctx sdk.Context, ak auth.AccountKeeper, sk types.SupplyKeeper,
ethTxMsg *evmtypes.EthereumTxMsg, sim bool,
ethTxMsg *evmtypes.MsgEthereumTx, sim bool,
) (newCtx sdk.Context, err error) {
var senderAddr sdk.AccAddress
@ -120,7 +122,9 @@ func ethAnteHandler(
if r := recover(); r != nil {
switch rType := r.(type) {
case sdk.ErrorOutOfGas:
log := fmt.Sprintf("out of gas in location: %v", rType.Descriptor)
log := fmt.Sprintf("out of gas in location: %v; gasUsed: %d",
rType.Descriptor, ctx.GasMeter().GasConsumed(),
)
err = sdk.ErrOutOfGas(log)
default:
panic(r)
@ -139,9 +143,9 @@ func ethAnteHandler(
// Cost calculates the fees paid to validators based on gas limit and price
cost := new(big.Int).Mul(ethTxMsg.Data.Price, new(big.Int).SetUint64(ethTxMsg.Data.GasLimit))
feeAmt := sdk.Coins{
feeAmt := sdk.NewCoins(
sdk.NewCoin(emint.DenomDefault, sdk.NewIntFromBigInt(cost)),
}
)
err = auth.DeductFees(sk, ctx, senderAcc, feeAmt)
if err != nil {
@ -166,7 +170,7 @@ func ethAnteHandler(
}
func validateEthTxCheckTx(
ctx sdk.Context, ak auth.AccountKeeper, ethTxMsg *evmtypes.EthereumTxMsg,
ctx sdk.Context, ak auth.AccountKeeper, ethTxMsg *evmtypes.MsgEthereumTx,
) (sdk.AccAddress, error) {
// Validate sufficient fees have been provided that meet a minimum threshold
// defined by the proposer (for mempool purposes during CheckTx).
@ -193,7 +197,7 @@ func validateEthTxCheckTx(
}
// Validates signature and returns sender address
func validateSignature(ctx sdk.Context, ethTxMsg *evmtypes.EthereumTxMsg) (sdk.AccAddress, error) {
func validateSignature(ctx sdk.Context, ethTxMsg *evmtypes.MsgEthereumTx) (sdk.AccAddress, error) {
// parse the chainID from a string to a base-10 integer
chainID, ok := new(big.Int).SetString(ctx.ChainID(), 10)
if !ok {
@ -214,7 +218,7 @@ func validateSignature(ctx sdk.Context, ethTxMsg *evmtypes.EthereumTxMsg) (sdk.A
// that the transaction uses before the transaction is executed. The gas is a
// constant value of 21000 plus any cost inccured by additional bytes of data
// supplied with the transaction.
func validateIntrinsicGas(ethTxMsg *evmtypes.EthereumTxMsg) error {
func validateIntrinsicGas(ethTxMsg *evmtypes.MsgEthereumTx) error {
gas, err := ethcore.IntrinsicGas(ethTxMsg.Data.Payload, ethTxMsg.To() == nil, true)
if err != nil {
return sdk.ErrInternal(fmt.Sprintf("failed to compute intrinsic gas cost: %s", err))
@ -222,7 +226,7 @@ func validateIntrinsicGas(ethTxMsg *evmtypes.EthereumTxMsg) error {
if ethTxMsg.Data.GasLimit < gas {
return sdk.ErrInternal(
fmt.Sprintf("intrinsic gas too low; %d < %d", ethTxMsg.Data.GasLimit, gas),
fmt.Sprintf("intrinsic gas too low: %d < %d", ethTxMsg.Data.GasLimit, gas),
)
}
@ -232,7 +236,7 @@ func validateIntrinsicGas(ethTxMsg *evmtypes.EthereumTxMsg) error {
// validateAccount validates the account nonce and that the account has enough
// funds to cover the tx cost.
func validateAccount(
ctx sdk.Context, ak auth.AccountKeeper, ethTxMsg *evmtypes.EthereumTxMsg, signer sdk.AccAddress,
ctx sdk.Context, ak auth.AccountKeeper, ethTxMsg *evmtypes.MsgEthereumTx, signer sdk.AccAddress,
) error {
acc := ak.GetAccount(ctx, signer)
@ -241,8 +245,9 @@ func validateAccount(
if ctx.BlockHeight() == 0 && acc.GetAccountNumber() != 0 {
return sdk.ErrInternal(
fmt.Sprintf(
"invalid account number for height zero; got %d, expected 0", acc.GetAccountNumber(),
))
"invalid account number for height zero (got %d)", acc.GetAccountNumber(),
),
)
}
// Validate nonce is correct
@ -262,7 +267,7 @@ func validateAccount(
}
func checkNonce(
ctx sdk.Context, ak auth.AccountKeeper, ethTxMsg *evmtypes.EthereumTxMsg, signer sdk.AccAddress,
ctx sdk.Context, ak auth.AccountKeeper, ethTxMsg *evmtypes.MsgEthereumTx, signer sdk.AccAddress,
) error {
acc := ak.GetAccount(ctx, signer)
// Validate the transaction nonce is valid (equivalent to the sender accounts
@ -270,7 +275,8 @@ func checkNonce(
seq := acc.GetSequence()
if ethTxMsg.Data.AccountNonce != seq {
return sdk.ErrInvalidSequence(
fmt.Sprintf("invalid nonce; got %d, expected %d", ethTxMsg.Data.AccountNonce, seq))
fmt.Sprintf("invalid nonce; got %d, expected %d", ethTxMsg.Data.AccountNonce, seq),
)
}
return nil
@ -281,7 +287,7 @@ func checkNonce(
// proposer.
//
// NOTE: This should only be ran during a CheckTx mode.
func ensureSufficientMempoolFees(ctx sdk.Context, ethTxMsg *evmtypes.EthereumTxMsg) error {
func ensureSufficientMempoolFees(ctx sdk.Context, ethTxMsg *evmtypes.MsgEthereumTx) error {
// fee = GP * GL
fee := sdk.NewDecCoinFromCoin(sdk.NewInt64Coin(emint.DenomDefault, ethTxMsg.Fee().Int64()))
@ -297,7 +303,9 @@ func ensureSufficientMempoolFees(ctx sdk.Context, ethTxMsg *evmtypes.EthereumTxM
if !ctx.MinGasPrices().IsZero() && !allGTE {
// reject the transaction that does not meet the minimum fee
return sdk.ErrInsufficientFee(
fmt.Sprintf("insufficient fee, got: %q required: %q", fee, ctx.MinGasPrices()),
fmt.Sprintf(
"insufficient fee, got: %q required: %q", fee, ctx.MinGasPrices(),
),
)
}

301
app/ante/ante_test.go Normal file
View 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
View 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
}

View File

@ -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)
}

View File

@ -1,15 +1,13 @@
package app
import (
"encoding/json"
"io"
"os"
emintcrypto "github.com/cosmos/ethermint/crypto"
"github.com/cosmos/ethermint/x/evm"
bam "github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
cryptokeys "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/version"
@ -26,12 +24,14 @@ import (
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/supply"
"github.com/cosmos/ethermint/app/ante"
emintcrypto "github.com/cosmos/ethermint/crypto"
eminttypes "github.com/cosmos/ethermint/types"
evmtypes "github.com/cosmos/ethermint/x/evm/types"
"github.com/cosmos/ethermint/x/evm"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
tmlog "github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/libs/log"
dbm "github.com/tendermint/tm-db"
)
@ -44,21 +44,23 @@ var (
// DefaultNodeHome sets the folder where the applcation data and configuration will be stored
DefaultNodeHome = os.ExpandEnv("$HOME/.emintd")
// ModuleBasics is the module BasicManager is in charge of setting up basic,
// ModuleBasics defines the module BasicManager is in charge of setting up basic,
// non-dependant module elements, such as codec registration
// and genesis verification.
ModuleBasics = module.NewBasicManager(
genutil.AppModuleBasic{},
auth.AppModuleBasic{},
supply.AppModuleBasic{},
genutil.AppModuleBasic{},
bank.AppModuleBasic{},
staking.AppModuleBasic{},
mint.AppModuleBasic{},
distr.AppModuleBasic{},
gov.NewAppModuleBasic(paramsclient.ProposalHandler, distr.ProposalHandler),
gov.NewAppModuleBasic(
paramsclient.ProposalHandler, distr.ProposalHandler,
),
params.AppModuleBasic{},
crisis.AppModuleBasic{},
slashing.AppModuleBasic{},
supply.AppModuleBasic{},
evm.AppModuleBasic{},
)
@ -71,6 +73,11 @@ var (
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
gov.ModuleName: {supply.Burner},
}
// module accounts that are allowed to receive tokens
allowedReceivingModAcc = map[string]bool{
distr.ModuleName: true,
}
)
// MakeCodec generates the necessary codecs for Amino
@ -100,18 +107,21 @@ type EthermintApp struct {
keys map[string]*sdk.KVStoreKey
tkeys map[string]*sdk.TransientStoreKey
// subspaces
subspaces map[string]params.Subspace
// keepers
accountKeeper auth.AccountKeeper
bankKeeper bank.Keeper
supplyKeeper supply.Keeper
stakingKeeper staking.Keeper
slashingKeeper slashing.Keeper
mintKeeper mint.Keeper
distrKeeper distr.Keeper
govKeeper gov.Keeper
crisisKeeper crisis.Keeper
paramsKeeper params.Keeper
evmKeeper evm.Keeper
AccountKeeper auth.AccountKeeper
BankKeeper bank.Keeper
SupplyKeeper supply.Keeper
StakingKeeper staking.Keeper
SlashingKeeper slashing.Keeper
MintKeeper mint.Keeper
DistrKeeper distr.Keeper
GovKeeper gov.Keeper
CrisisKeeper crisis.Keeper
ParamsKeeper params.Keeper
EvmKeeper evm.Keeper
// the module manager
mm *module.Manager
@ -124,18 +134,25 @@ type EthermintApp struct {
// in a sovereign zone and as an application running with a shared security model.
// For now, it will support only running as a sovereign application.
func NewEthermintApp(
logger tmlog.Logger, db dbm.DB, loadLatest bool,
invCheckPeriod uint, baseAppOptions ...func(*bam.BaseApp)) *EthermintApp {
logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
invCheckPeriod uint, baseAppOptions ...func(*bam.BaseApp),
) *EthermintApp {
cdc := MakeCodec()
bApp := bam.NewBaseApp(appName, logger, db, evmtypes.TxDecoder(cdc), baseAppOptions...)
// use custom Ethermint transaction decoder
bApp := bam.NewBaseApp(appName, logger, db, evm.TxDecoder(cdc), baseAppOptions...)
bApp.SetCommitMultiStoreTracer(traceStore)
bApp.SetAppVersion(version.Version)
keys := sdk.NewKVStoreKeys(bam.MainStoreKey, auth.StoreKey, staking.StoreKey,
keys := sdk.NewKVStoreKeys(
bam.MainStoreKey, auth.StoreKey, staking.StoreKey,
supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey,
gov.StoreKey, params.StoreKey, evmtypes.EvmStoreKey, evmtypes.EvmCodeKey)
blockKey := sdk.NewKVStoreKey(evmtypes.EvmBlockKey)
tkeys := sdk.NewTransientStoreKeys(staking.TStoreKey, params.TStoreKey)
gov.StoreKey, params.StoreKey, evm.CodeKey, evm.StoreKey,
)
blockKey := sdk.NewKVStoreKey(evm.BlockKey)
tkeys := sdk.NewTransientStoreKeys(params.TStoreKey)
app := &EthermintApp{
BaseApp: bApp,
@ -143,89 +160,118 @@ func NewEthermintApp(
invCheckPeriod: invCheckPeriod,
keys: keys,
tkeys: tkeys,
subspaces: make(map[string]params.Subspace),
}
// init params keeper and subspaces
app.paramsKeeper = params.NewKeeper(app.cdc, keys[params.StoreKey], tkeys[params.TStoreKey], params.DefaultCodespace)
authSubspace := app.paramsKeeper.Subspace(auth.DefaultParamspace)
bankSubspace := app.paramsKeeper.Subspace(bank.DefaultParamspace)
stakingSubspace := app.paramsKeeper.Subspace(staking.DefaultParamspace)
mintSubspace := app.paramsKeeper.Subspace(mint.DefaultParamspace)
distrSubspace := app.paramsKeeper.Subspace(distr.DefaultParamspace)
slashingSubspace := app.paramsKeeper.Subspace(slashing.DefaultParamspace)
govSubspace := app.paramsKeeper.Subspace(gov.DefaultParamspace).WithKeyTable(gov.ParamKeyTable())
crisisSubspace := app.paramsKeeper.Subspace(crisis.DefaultParamspace)
app.ParamsKeeper = params.NewKeeper(app.cdc, keys[params.StoreKey], tkeys[params.TStoreKey], params.DefaultCodespace)
app.subspaces[auth.ModuleName] = app.ParamsKeeper.Subspace(auth.DefaultParamspace)
app.subspaces[bank.ModuleName] = app.ParamsKeeper.Subspace(bank.DefaultParamspace)
app.subspaces[staking.ModuleName] = app.ParamsKeeper.Subspace(staking.DefaultParamspace)
app.subspaces[mint.ModuleName] = app.ParamsKeeper.Subspace(mint.DefaultParamspace)
app.subspaces[distr.ModuleName] = app.ParamsKeeper.Subspace(distr.DefaultParamspace)
app.subspaces[slashing.ModuleName] = app.ParamsKeeper.Subspace(slashing.DefaultParamspace)
app.subspaces[gov.ModuleName] = app.ParamsKeeper.Subspace(gov.DefaultParamspace).WithKeyTable(gov.ParamKeyTable())
app.subspaces[crisis.ModuleName] = app.ParamsKeeper.Subspace(crisis.DefaultParamspace)
// add keepers
app.accountKeeper = auth.NewAccountKeeper(app.cdc, keys[auth.StoreKey], authSubspace, eminttypes.ProtoBaseAccount)
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper, bankSubspace, bank.DefaultCodespace, app.ModuleAccountAddrs())
app.supplyKeeper = supply.NewKeeper(app.cdc, keys[supply.StoreKey], app.accountKeeper, app.bankKeeper, maccPerms)
stakingKeeper := staking.NewKeeper(app.cdc, keys[staking.StoreKey],
app.supplyKeeper, stakingSubspace, staking.DefaultCodespace)
app.mintKeeper = mint.NewKeeper(app.cdc, keys[mint.StoreKey], mintSubspace, &stakingKeeper, app.supplyKeeper, auth.FeeCollectorName)
app.distrKeeper = distr.NewKeeper(app.cdc, keys[distr.StoreKey], distrSubspace, &stakingKeeper,
app.supplyKeeper, distr.DefaultCodespace, auth.FeeCollectorName, app.ModuleAccountAddrs())
app.slashingKeeper = slashing.NewKeeper(app.cdc, keys[slashing.StoreKey], &stakingKeeper,
slashingSubspace, slashing.DefaultCodespace)
app.crisisKeeper = crisis.NewKeeper(crisisSubspace, invCheckPeriod, app.supplyKeeper, auth.FeeCollectorName)
app.evmKeeper = evm.NewKeeper(app.accountKeeper, keys[evmtypes.EvmStoreKey], keys[evmtypes.EvmCodeKey], blockKey, cdc)
// use custom Ethermint account for contracts
app.AccountKeeper = auth.NewAccountKeeper(
app.cdc, keys[auth.StoreKey], app.subspaces[auth.ModuleName], eminttypes.ProtoBaseAccount,
)
app.BankKeeper = bank.NewBaseKeeper(
app.AccountKeeper, app.subspaces[bank.ModuleName], bank.DefaultCodespace, app.ModuleAccountAddrs(),
)
app.SupplyKeeper = supply.NewKeeper(
app.cdc, keys[supply.StoreKey], app.AccountKeeper, app.BankKeeper, maccPerms,
)
stakingKeeper := staking.NewKeeper(
app.cdc, keys[staking.StoreKey], app.SupplyKeeper, app.subspaces[staking.ModuleName],
staking.DefaultCodespace,
)
app.MintKeeper = mint.NewKeeper(
app.cdc, keys[mint.StoreKey], app.subspaces[mint.ModuleName], &stakingKeeper,
app.SupplyKeeper, auth.FeeCollectorName,
)
app.DistrKeeper = distr.NewKeeper(
app.cdc, keys[distr.StoreKey], app.subspaces[distr.ModuleName], &stakingKeeper,
app.SupplyKeeper, distr.DefaultCodespace, auth.FeeCollectorName, app.ModuleAccountAddrs(),
)
app.SlashingKeeper = slashing.NewKeeper(
app.cdc, keys[slashing.StoreKey], &stakingKeeper, app.subspaces[slashing.ModuleName],
slashing.DefaultCodespace,
)
app.CrisisKeeper = crisis.NewKeeper(
app.subspaces[crisis.ModuleName], invCheckPeriod, app.SupplyKeeper, auth.FeeCollectorName,
)
app.EvmKeeper = evm.NewKeeper(
app.cdc, blockKey, keys[evm.CodeKey], keys[evm.StoreKey], app.AccountKeeper,
)
// register the proposal types
govRouter := gov.NewRouter()
govRouter.AddRoute(gov.RouterKey, gov.ProposalHandler).
AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.paramsKeeper)).
AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper))
app.govKeeper = gov.NewKeeper(app.cdc, keys[gov.StoreKey], govSubspace,
app.supplyKeeper, &stakingKeeper, gov.DefaultCodespace, govRouter)
AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.ParamsKeeper)).
AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.DistrKeeper))
app.GovKeeper = gov.NewKeeper(
app.cdc, keys[gov.StoreKey], app.subspaces[gov.ModuleName], app.SupplyKeeper,
&stakingKeeper, gov.DefaultCodespace, govRouter,
)
// register the staking hooks
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
app.stakingKeeper = *stakingKeeper.SetHooks(
staking.NewMultiStakingHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks()),
app.StakingKeeper = *stakingKeeper.SetHooks(
staking.NewMultiStakingHooks(app.DistrKeeper.Hooks(), app.SlashingKeeper.Hooks()),
)
// NOTE: Any module instantiated in the module manager that is later modified
// must be passed by reference here.
app.mm = module.NewManager(
genutil.NewAppModule(app.accountKeeper, app.stakingKeeper, app.BaseApp.DeliverTx),
auth.NewAppModule(app.accountKeeper),
bank.NewAppModule(app.bankKeeper, app.accountKeeper),
crisis.NewAppModule(&app.crisisKeeper),
supply.NewAppModule(app.supplyKeeper, app.accountKeeper),
distr.NewAppModule(app.distrKeeper, app.accountKeeper, app.supplyKeeper, app.stakingKeeper),
gov.NewAppModule(app.govKeeper, app.accountKeeper, app.supplyKeeper),
mint.NewAppModule(app.mintKeeper),
slashing.NewAppModule(app.slashingKeeper, app.accountKeeper, app.stakingKeeper),
staking.NewAppModule(app.stakingKeeper, app.accountKeeper, app.supplyKeeper),
evm.NewAppModule(app.evmKeeper),
genutil.NewAppModule(app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx),
auth.NewAppModule(app.AccountKeeper),
bank.NewAppModule(app.BankKeeper, app.AccountKeeper),
crisis.NewAppModule(&app.CrisisKeeper),
supply.NewAppModule(app.SupplyKeeper, app.AccountKeeper),
gov.NewAppModule(app.GovKeeper, app.AccountKeeper, app.SupplyKeeper),
mint.NewAppModule(app.MintKeeper),
slashing.NewAppModule(app.SlashingKeeper, app.AccountKeeper, app.StakingKeeper),
distr.NewAppModule(app.DistrKeeper, app.AccountKeeper, app.SupplyKeeper, app.StakingKeeper),
staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper),
evm.NewAppModule(app.EvmKeeper),
)
// During begin block slashing happens after distr.BeginBlocker so that
// there is nothing left over in the validator fee pool, so as to keep the
// CanWithdrawInvariant invariant.
app.mm.SetOrderBeginBlockers(evmtypes.ModuleName, mint.ModuleName, distr.ModuleName, slashing.ModuleName)
app.mm.SetOrderEndBlockers(evmtypes.ModuleName, crisis.ModuleName, gov.ModuleName, staking.ModuleName)
app.mm.SetOrderBeginBlockers(
evm.ModuleName, mint.ModuleName, distr.ModuleName, slashing.ModuleName,
)
app.mm.SetOrderEndBlockers(
evm.ModuleName, crisis.ModuleName, gov.ModuleName, staking.ModuleName,
)
// NOTE: The genutils module must occur after staking so that pools are
// properly initialized with tokens from genesis accounts.
app.mm.SetOrderInitGenesis(
distr.ModuleName, staking.ModuleName,
auth.ModuleName, bank.ModuleName, slashing.ModuleName, gov.ModuleName,
mint.ModuleName, supply.ModuleName, crisis.ModuleName, genutil.ModuleName, evmtypes.ModuleName,
auth.ModuleName, distr.ModuleName, staking.ModuleName, bank.ModuleName,
slashing.ModuleName, gov.ModuleName, mint.ModuleName, supply.ModuleName,
crisis.ModuleName, genutil.ModuleName, evm.ModuleName,
)
app.mm.RegisterInvariants(&app.crisisKeeper)
app.mm.RegisterInvariants(&app.CrisisKeeper)
app.mm.RegisterRoutes(app.Router(), app.QueryRouter())
// initialize stores
app.MountKVStores(keys)
app.MountTransientStores(tkeys)
// Mount block hash mapping key as DB (no need for historical queries)
// TODO: why does this need to be always StoreTypeDB?
app.MountStore(blockKey, sdk.StoreTypeDB)
// initialize BaseApp
app.SetInitChainer(app.InitChainer)
app.SetBeginBlocker(app.BeginBlocker)
app.SetAnteHandler(NewAnteHandler(app.accountKeeper, app.supplyKeeper))
app.SetAnteHandler(ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper))
app.SetEndBlocker(app.EndBlocker)
if loadLatest {
@ -234,12 +280,12 @@ func NewEthermintApp(
cmn.Exit(err.Error())
}
}
return app
}
// GenesisState is the state of the blockchain is represented here as a map of raw json
// messages key'd by a identifier string.
type GenesisState map[string]json.RawMessage
// Name returns the name of the App
func (app *EthermintApp) Name() string { return app.BaseApp.Name() }
// BeginBlocker updates every begin block
func (app *EthermintApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
@ -253,7 +299,7 @@ func (app *EthermintApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) a
// InitChainer updates at chain initialization
func (app *EthermintApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
var genesisState GenesisState
var genesisState simapp.GenesisState
app.cdc.MustUnmarshalJSON(req.AppStateBytes, &genesisState)
return app.mm.InitGenesis(ctx, genesisState)
}
@ -267,8 +313,42 @@ func (app *EthermintApp) LoadHeight(height int64) error {
func (app *EthermintApp) ModuleAccountAddrs() map[string]bool {
modAccAddrs := make(map[string]bool)
for acc := range maccPerms {
modAccAddrs[app.supplyKeeper.GetModuleAddress(acc).String()] = true
modAccAddrs[supply.NewModuleAddress(acc).String()] = true
}
return modAccAddrs
}
// BlacklistedAccAddrs returns all the app's module account addresses black listed for receiving tokens.
func (app *EthermintApp) BlacklistedAccAddrs() map[string]bool {
blacklistedAddrs := make(map[string]bool)
for acc := range maccPerms {
blacklistedAddrs[supply.NewModuleAddress(acc).String()] = !allowedReceivingModAcc[acc]
}
return blacklistedAddrs
}
// GetKey returns the KVStoreKey for the provided store key.
//
// NOTE: This is solely to be used for testing purposes.
func (app *EthermintApp) GetKey(storeKey string) *sdk.KVStoreKey {
return app.keys[storeKey]
}
// Codec returns Ethermint's codec.
//
// NOTE: This is solely to be used for testing purposes as it may be desirable
// for modules to register their own custom testing types.
func (app *EthermintApp) Codec() *codec.Codec {
return app.cdc
}
// GetMaccPerms returns a copy of the module account permissions
func GetMaccPerms() map[string][]string {
dupMaccPerms := make(map[string][]string)
for k, v := range maccPerms {
dupMaccPerms[k] = v
}
return dupMaccPerms
}

View File

@ -15,7 +15,7 @@ import (
func TestEthermintAppExport(t *testing.T) {
db := dbm.NewMemDB()
app := NewEthermintApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, true, 0)
app := NewEthermintApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, 0)
genesisState := ModuleBasics.DefaultGenesis()
stateBytes, err := codec.MarshalJSONIndent(app.cdc, genesisState)
@ -31,7 +31,7 @@ func TestEthermintAppExport(t *testing.T) {
app.Commit()
// Making a new app object with the db, so that initchain hasn't been called
app2 := NewEthermintApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, true, 0)
app2 := NewEthermintApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, 0)
_, _, err = app2.ExportAppStateAndValidators(false, []string{})
require.NoError(t, err, "ExportAppStateAndValidators should not have an error")
}

View File

@ -35,7 +35,7 @@ func (app *EthermintApp) ExportAppStateAndValidators(
}
// Write validators to staking module to be used by TM node
validators = staking.WriteValidators(ctx, app.stakingKeeper)
validators = staking.WriteValidators(ctx, app.StakingKeeper)
return appState, validators, nil
}
@ -61,49 +61,48 @@ func (app *EthermintApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList
}
/* Just to be safe, assert the invariants on current state. */
app.crisisKeeper.AssertInvariants(ctx)
app.CrisisKeeper.AssertInvariants(ctx)
/* Handle fee distribution state. */
// withdraw all validator commission
app.stakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) {
_, _ = app.distrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator())
app.StakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) {
_, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator())
return false
})
// withdraw all delegator rewards
dels := app.stakingKeeper.GetAllDelegations(ctx)
dels := app.StakingKeeper.GetAllDelegations(ctx)
for _, delegation := range dels {
_, _ = app.distrKeeper.WithdrawDelegationRewards(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)
_, _ = app.DistrKeeper.WithdrawDelegationRewards(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)
}
// clear validator slash events
app.distrKeeper.DeleteAllValidatorSlashEvents(ctx)
app.DistrKeeper.DeleteAllValidatorSlashEvents(ctx)
// clear validator historical rewards
app.distrKeeper.DeleteAllValidatorHistoricalRewards(ctx)
app.DistrKeeper.DeleteAllValidatorHistoricalRewards(ctx)
// set context height to zero
height := ctx.BlockHeight()
ctx = ctx.WithBlockHeight(0)
// reinitialize all validators
app.stakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) {
app.StakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) {
// donate any unwithdrawn outstanding reward fraction tokens to the community pool
scraps := app.distrKeeper.GetValidatorOutstandingRewards(ctx, val.GetOperator())
feePool := app.distrKeeper.GetFeePool(ctx)
scraps := app.DistrKeeper.GetValidatorOutstandingRewards(ctx, val.GetOperator())
feePool := app.DistrKeeper.GetFeePool(ctx)
feePool.CommunityPool = feePool.CommunityPool.Add(scraps)
app.distrKeeper.SetFeePool(ctx, feePool)
app.DistrKeeper.SetFeePool(ctx, feePool)
app.distrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator())
app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator())
return false
})
// reinitialize all delegations
for _, del := range dels {
app.distrKeeper.Hooks().BeforeDelegationCreated(ctx, del.DelegatorAddress, del.ValidatorAddress)
app.distrKeeper.Hooks().AfterDelegationModified(ctx, del.DelegatorAddress, del.ValidatorAddress)
app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, del.DelegatorAddress, del.ValidatorAddress)
app.DistrKeeper.Hooks().AfterDelegationModified(ctx, del.DelegatorAddress, del.ValidatorAddress)
}
// reset context height
@ -112,20 +111,20 @@ func (app *EthermintApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList
/* Handle staking state. */
// iterate through redelegations, reset creation height
app.stakingKeeper.IterateRedelegations(ctx, func(_ int64, red staking.Redelegation) (stop bool) {
app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red staking.Redelegation) (stop bool) {
for i := range red.Entries {
red.Entries[i].CreationHeight = 0
}
app.stakingKeeper.SetRedelegation(ctx, red)
app.StakingKeeper.SetRedelegation(ctx, red)
return false
})
// iterate through unbonding delegations, reset creation height
app.stakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd staking.UnbondingDelegation) (stop bool) {
app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd staking.UnbondingDelegation) (stop bool) {
for i := range ubd.Entries {
ubd.Entries[i].CreationHeight = 0
}
app.stakingKeeper.SetUnbondingDelegation(ctx, ubd)
app.StakingKeeper.SetUnbondingDelegation(ctx, ubd)
return false
})
@ -137,7 +136,7 @@ func (app *EthermintApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList
for ; iter.Valid(); iter.Next() {
addr := sdk.ValAddress(iter.Key()[1:])
validator, found := app.stakingKeeper.GetValidator(ctx, addr)
validator, found := app.StakingKeeper.GetValidator(ctx, addr)
if !found {
panic("expected validator, not found")
}
@ -147,22 +146,22 @@ func (app *EthermintApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList
validator.Jailed = true
}
app.stakingKeeper.SetValidator(ctx, validator)
app.StakingKeeper.SetValidator(ctx, validator)
counter++
}
iter.Close()
_ = app.stakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx)
_ = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx)
/* Handle slashing state. */
// reset start height on signing infos
app.slashingKeeper.IterateValidatorSigningInfos(
app.SlashingKeeper.IterateValidatorSigningInfos(
ctx,
func(addr sdk.ConsAddress, info slashing.ValidatorSigningInfo) (stop bool) {
info.StartHeight = 0
app.slashingKeeper.SetValidatorSigningInfo(ctx, addr, info)
app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info)
return false
},
)

34
app/test_helpers.go Normal file
View 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
}

View File

@ -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
}

View File

@ -14,14 +14,15 @@ import (
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/input"
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
emintcrypto "github.com/cosmos/ethermint/crypto"
)
func exportEthKeyCommand() *cobra.Command {
func unsafeExportEthKeyCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "export-eth-key <name>",
Short: "Export an Ethereum private key",
Long: `Export an Ethereum private key unencrypted to use in dev tooling **UNSAFE**`,
Use: "unsafe-export-eth-key [name]",
Short: "**UNSAFE** Export an Ethereum private key",
Long: `**UNSAFE** Export an Ethereum private key unencrypted to use in dev tooling`,
Args: cobra.ExactArgs(1),
RunE: runExportCmd,
}
@ -29,12 +30,13 @@ func exportEthKeyCommand() *cobra.Command {
}
func runExportCmd(cmd *cobra.Command, args []string) error {
inBuf := bufio.NewReader(cmd.InOrStdin())
kb, err := clientkeys.NewKeyringFromHomeFlag(cmd.InOrStdin())
if err != nil {
return err
}
buf := bufio.NewReader(cmd.InOrStdin())
decryptPassword := ""
conf := true
keyringBackend := viper.GetString(flags.FlagKeyringBackend)
@ -42,11 +44,11 @@ func runExportCmd(cmd *cobra.Command, args []string) error {
case flags.KeyringBackendFile:
decryptPassword, err = input.GetPassword(
"**WARNING this is an unsafe way to export your unencrypted private key**\nEnter key password:",
buf)
inBuf)
case flags.KeyringBackendOS:
conf, err = input.GetConfirmation(
"**WARNING** this is an unsafe way to export your unencrypted private key, are you sure?",
buf)
inBuf)
}
if err != nil || !conf {
return err

View File

@ -4,12 +4,13 @@ import (
"bufio"
"io"
tmcrypto "github.com/tendermint/tendermint/crypto"
"github.com/cosmos/cosmos-sdk/client/flags"
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/crypto/keys"
emintCrypto "github.com/cosmos/ethermint/crypto"
tmcrypto "github.com/tendermint/tendermint/crypto"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@ -46,7 +47,7 @@ func keyCommands() *cobra.Command {
clientkeys.ParseKeyStringCommand(),
clientkeys.MigrateCommand(),
flags.LineBreak,
exportEthKeyCommand(),
unsafeExportEthKeyCommand(),
)
return cmd
}

View File

@ -1,34 +1,38 @@
package main
import (
"fmt"
"os"
"path"
emintapp "github.com/cosmos/ethermint/app"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/ethermint/app"
emintcrypto "github.com/cosmos/ethermint/crypto"
"github.com/cosmos/ethermint/rpc"
"github.com/tendermint/go-amino"
tmamino "github.com/tendermint/tendermint/crypto/encoding/amino"
"github.com/tendermint/tendermint/libs/cli"
"github.com/cosmos/cosmos-sdk/client"
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
sdkrpc "github.com/cosmos/cosmos-sdk/client/rpc"
clientrpc "github.com/cosmos/cosmos-sdk/client/rpc"
cryptokeys "github.com/cosmos/cosmos-sdk/crypto/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/x/auth"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
"github.com/cosmos/cosmos-sdk/x/bank"
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli"
"github.com/spf13/cobra"
"github.com/spf13/viper"
tmamino "github.com/tendermint/tendermint/crypto/encoding/amino"
"github.com/tendermint/tendermint/libs/cli"
)
func main() {
// Configure cobra to sort commands
cobra.EnableCommandSorting = false
cdc := emintapp.MakeCodec()
cdc := app.MakeCodec()
tmamino.RegisterKeyType(emintcrypto.PubKeySecp256k1{}, emintcrypto.PubKeyAminoName)
tmamino.RegisterKeyType(emintcrypto.PrivKeySecp256k1{}, emintcrypto.PrivKeyAminoName)
@ -45,7 +49,7 @@ func main() {
rootCmd := &cobra.Command{
Use: "emintcli",
Short: "Ethermint Client",
Short: "Command line interface for interacting with emintd",
}
// Add --chain-id to persistent flags and mark it required
@ -56,20 +60,24 @@ func main() {
// Construct Root Command
rootCmd.AddCommand(
sdkrpc.StatusCommand(),
client.ConfigCmd(emintapp.DefaultCLIHome),
clientrpc.StatusCommand(),
client.ConfigCmd(app.DefaultCLIHome),
queryCmd(cdc),
txCmd(cdc),
rpc.EmintServeCmd(cdc),
client.LineBreak,
keyCommands(),
client.LineBreak,
version.Cmd,
client.NewCompletionCmd(rootCmd, true),
)
executor := cli.PrepareMainCmd(rootCmd, "EM", emintapp.DefaultCLIHome)
// Add flags and prefix all env exposed with EM
executor := cli.PrepareMainCmd(rootCmd, "EM", app.DefaultCLIHome)
err := executor.Execute()
if err != nil {
panic(err)
panic(fmt.Errorf("failed executing CLI command: %w", err))
}
}
@ -89,7 +97,7 @@ func queryCmd(cdc *amino.Codec) *cobra.Command {
)
// add modules' query commands
emintapp.ModuleBasics.AddQueryCommands(queryCmd, cdc)
app.ModuleBasics.AddQueryCommands(queryCmd, cdc)
return queryCmd
}
@ -104,14 +112,27 @@ func txCmd(cdc *amino.Codec) *cobra.Command {
bankcmd.SendTxCmd(cdc),
client.LineBreak,
authcmd.GetSignCommand(cdc),
authcmd.GetMultiSignCommand(cdc),
client.LineBreak,
authcmd.GetBroadcastCommand(cdc),
authcmd.GetEncodeCommand(cdc),
authcmd.GetDecodeCommand(cdc),
client.LineBreak,
)
// add modules' tx commands
emintapp.ModuleBasics.AddTxCommands(txCmd, cdc)
app.ModuleBasics.AddTxCommands(txCmd, cdc)
// remove auth and bank commands as they're mounted under the root tx command
var cmdsToRemove []*cobra.Command
for _, cmd := range txCmd.Commands() {
if cmd.Use == auth.ModuleName || cmd.Use == bank.ModuleName {
cmdsToRemove = append(cmdsToRemove, cmd)
}
}
txCmd.RemoveCommand(cmdsToRemove...)
return txCmd
}

View File

@ -1,4 +1,4 @@
package genaccounts
package main
import (
"bufio"
@ -19,6 +19,8 @@ import (
authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting"
"github.com/cosmos/cosmos-sdk/x/genutil"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
ethermint "github.com/cosmos/ethermint/types"
)
@ -96,7 +98,10 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa
return errors.New("invalid vesting parameters; must supply start and end time or end time")
}
} else {
genAccount = ethermint.Account{BaseAccount: baseAccount}
genAccount = ethermint.Account{
BaseAccount: baseAccount,
CodeHash: ethcrypto.Keccak256(nil),
}
}
if err := genAccount.Validate(); err != nil {

View File

@ -23,17 +23,20 @@ import (
"github.com/spf13/viper"
"github.com/cosmos/ethermint/app"
"github.com/cosmos/ethermint/client/genaccounts"
emintcrypto "github.com/cosmos/ethermint/crypto"
abci "github.com/tendermint/tendermint/abci/types"
tmamino "github.com/tendermint/tendermint/crypto/encoding/amino"
"github.com/tendermint/tendermint/libs/cli"
tmlog "github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/libs/log"
tmtypes "github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tm-db"
)
const flagInvCheckPeriod = "inv-check-period"
var invCheckPeriod uint
func main() {
cobra.EnableCommandSorting = false
@ -65,12 +68,14 @@ func main() {
withChainIDValidation(genutilcli.InitCmd(ctx, cdc, app.ModuleBasics, app.DefaultNodeHome)),
genutilcli.CollectGenTxsCmd(ctx, cdc, auth.GenesisAccountIterator{}, app.DefaultNodeHome),
genutilcli.GenTxCmd(
ctx, cdc, app.ModuleBasics, staking.AppModuleBasic{}, auth.GenesisAccountIterator{}, app.DefaultNodeHome, app.DefaultCLIHome,
ctx, cdc, app.ModuleBasics, staking.AppModuleBasic{}, auth.GenesisAccountIterator{},
app.DefaultNodeHome, app.DefaultCLIHome,
),
genutilcli.ValidateGenesisCmd(ctx, cdc, app.ModuleBasics),
// AddGenesisAccountCmd allows users to add accounts to the genesis file
genaccounts.AddGenesisAccountCmd(ctx, cdc, app.DefaultNodeHome, app.DefaultCLIHome),
AddGenesisAccountCmd(ctx, cdc, app.DefaultNodeHome, app.DefaultCLIHome),
client.NewCompletionCmd(rootCmd, true),
)
// Tendermint node base commands
@ -78,23 +83,25 @@ func main() {
// prepare and add flags
executor := cli.PrepareBaseCmd(rootCmd, "EM", app.DefaultNodeHome)
rootCmd.PersistentFlags().UintVar(&invCheckPeriod, flagInvCheckPeriod,
0, "Assert registered invariants every N blocks")
err := executor.Execute()
if err != nil {
panic(err)
}
}
func newApp(logger tmlog.Logger, db dbm.DB, traceStore io.Writer) abci.Application {
return app.NewEthermintApp(logger, db, true, 0,
func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application {
return app.NewEthermintApp(logger, db, traceStore, true, 0,
baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning"))))
}
func exportAppStateAndTMValidators(
logger tmlog.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailWhiteList []string,
logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailWhiteList []string,
) (json.RawMessage, []tmtypes.GenesisValidator, error) {
if height != -1 {
emintApp := app.NewEthermintApp(logger, db, true, 0)
emintApp := app.NewEthermintApp(logger, db, traceStore, true, 0)
err := emintApp.LoadHeight(height)
if err != nil {
return nil, nil, err
@ -102,7 +109,7 @@ func exportAppStateAndTMValidators(
return emintApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
}
emintApp := app.NewEthermintApp(logger, db, true, 0)
emintApp := app.NewEthermintApp(logger, db, traceStore, true, 0)
return emintApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
}
@ -119,8 +126,7 @@ func withChainIDValidation(baseCmd *cobra.Command) *cobra.Command {
// Verify that the chain-id entered is a base 10 integer
_, ok := new(big.Int).SetString(chainIDFlag, 10)
if !ok {
return fmt.Errorf(
fmt.Sprintf("invalid chainID: %s, must be base-10 integer format", chainIDFlag))
return fmt.Errorf("invalid chainID: %s, must be base-10 integer format", chainIDFlag)
}
return baseRunE(cmd, args)

2
go.mod
View File

@ -53,3 +53,5 @@ require (
gopkg.in/urfave/cli.v1 v1.20.0 // indirect
gopkg.in/yaml.v2 v2.2.8
)
replace github.com/cosmos/cosmos-sdk => github.com/cosmos/cosmos-sdk v0.34.4-0.20191213112149-d7b0f4b9b4fb

2
go.sum
View File

@ -314,8 +314,6 @@ github.com/spf13/cobra v0.0.1 h1:zZh3X5aZbdnoj+4XkaBxKfhO4ot82icYdhhREIAXIj8=
github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs=
github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/cobra v0.0.7 h1:FfTH+vuMXOas8jmfb5/M7dzEYx7LpcLb7a0LPe34uOU=
github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=

View File

@ -44,14 +44,11 @@ var (
flagBlockchain string
flagCPUProfile string
// miner501 = ethcmn.HexToAddress("0x35e8e5dC5FBd97c5b421A80B596C030a2Be2A04D")
genInvestor = ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0")
// paramsKey = sdk.NewKVStoreKey("params")
// tParamsKey = sdk.NewTransientStoreKey("transient_params")
accKey = sdk.NewKVStoreKey("acc")
storageKey = sdk.NewKVStoreKey(evmtypes.EvmStoreKey)
codeKey = sdk.NewKVStoreKey(evmtypes.EvmCodeKey)
storageKey = sdk.NewKVStoreKey(evmtypes.StoreKey)
codeKey = sdk.NewKVStoreKey(evmtypes.CodeKey)
logger = tmlog.NewNopLogger()
@ -104,7 +101,7 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, ak auth.Accoun
ms := cms.CacheMultiStore()
ctx := sdk.NewContext(ms, abci.Header{}, false, logger)
stateDB := evmtypes.NewCommitStateDB(ctx, ak, storageKey, codeKey)
stateDB := evmtypes.NewCommitStateDB(ctx, codeKey, storageKey, ak)
// sort the addresses and insertion of key/value pairs matters
genAddrs := make([]string, len(genBlock.Alloc))
@ -270,7 +267,7 @@ func TestImportBlocks(t *testing.T) {
}
func createStateDB(ctx sdk.Context, ak auth.AccountKeeper) *evmtypes.CommitStateDB {
stateDB := evmtypes.NewCommitStateDB(ctx, ak, storageKey, codeKey)
stateDB := evmtypes.NewCommitStateDB(ctx, codeKey, storageKey, ak)
return stateDB
}
@ -346,12 +343,18 @@ func applyTransaction(config *ethparams.ChainConfig, bc ethcore.ChainContext, au
return nil, 0, err
}
// Update the state with pending changes
var root []byte
var intRoot ethcmn.Hash
if config.IsByzantium(header.Number) {
statedb.Finalise(true)
err = statedb.Finalise(true)
} else {
root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
intRoot, err = statedb.IntermediateRoot(config.IsEIP158(header.Number))
}
if err != nil {
return nil, gas, err
}
root := intRoot.Bytes()
*usedGas += gas
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
@ -364,7 +367,7 @@ func applyTransaction(config *ethparams.ChainConfig, bc ethcore.ChainContext, au
receipt.ContractAddress = ethcrypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
}
// Set the receipt logs and create a bloom for filtering
receipt.Logs = statedb.GetLogs(tx.Hash())
receipt.Logs, err = statedb.GetLogs(tx.Hash())
receipt.Bloom = ethtypes.CreateBloom(ethtypes.Receipts{receipt})
receipt.BlockHash = statedb.BlockHash()
receipt.BlockNumber = header.Number

View File

@ -2,13 +2,15 @@ package rpc
import (
"bytes"
"errors"
"fmt"
"log"
"math/big"
"strconv"
"sync"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/spf13/viper"
emintcrypto "github.com/cosmos/ethermint/crypto"
params "github.com/cosmos/ethermint/rpc/args"
emint "github.com/cosmos/ethermint/types"
@ -29,10 +31,10 @@ import (
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
authutils "github.com/cosmos/cosmos-sdk/x/auth/client/utils"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/spf13/viper"
)
// PublicEthAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec.
@ -210,7 +212,7 @@ func (e *PublicEthAPI) getBlockTransactionCountByNumber(number int64) *hexutil.U
return nil
}
n := hexutil.Uint(block.Block.NumTxs)
n := hexutil.Uint(len(block.Block.Txs))
return &n
}
@ -277,8 +279,7 @@ func (e *PublicEthAPI) SendTransaction(args params.SendTxArgs) (common.Hash, err
// parse the chainID from a string to a base-10 integer
intChainID, ok := new(big.Int).SetString(chainID, 10)
if !ok {
return common.Hash{}, fmt.Errorf(
fmt.Sprintf("invalid chainID: %s, must be integer format", chainID))
return common.Hash{}, fmt.Errorf("invalid chainID: %s, must be integer format", chainID)
}
// Sign transaction
@ -306,7 +307,7 @@ func (e *PublicEthAPI) SendTransaction(args params.SendTxArgs) (common.Hash, err
// SendRawTransaction send a raw Ethereum transaction.
func (e *PublicEthAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error) {
tx := new(types.EthereumTxMsg)
tx := new(types.MsgEthereumTx)
// RLP decode raw transaction bytes
if err := rlp.DecodeBytes(data, tx); err != nil {
@ -351,9 +352,12 @@ func (e *PublicEthAPI) Call(args CallArgs, blockNr rpc.BlockNumber, overrides *m
return []byte{}, err
}
_, _, ret, err := types.DecodeReturnData(result.Data)
data, err := types.DecodeResultData(result.Data)
if err != nil {
return []byte{}, err
}
return (hexutil.Bytes)(ret), err
return (hexutil.Bytes)(data.Ret), nil
}
// account indicates the overriding fields of account during the execution of
@ -370,8 +374,9 @@ type account struct {
StateDiff *map[common.Hash]common.Hash `json:"stateDiff"`
}
// DoCall performs a simulated call operation through the evm
func (e *PublicEthAPI) doCall(args CallArgs, blockNr rpc.BlockNumber, globalGasCap *big.Int) (sdk.Result, error) {
// DoCall performs a simulated call operation through the evm. It returns the
// estimated gas used on the operation or an error if fails.
func (e *PublicEthAPI) doCall(args CallArgs, blockNr rpc.BlockNumber, globalGasCap *big.Int) (*sdk.Result, error) {
// Set height for historical queries
ctx := e.cliCtx
if blockNr.Int64() != 0 {
@ -425,7 +430,7 @@ func (e *PublicEthAPI) doCall(args CallArgs, blockNr rpc.BlockNumber, globalGasC
}
// Create new call message
msg := types.NewEmintMsg(0, &toAddr, sdk.NewIntFromBigInt(value), gas,
msg := types.NewMsgEthermint(0, &toAddr, sdk.NewIntFromBigInt(value), gas,
sdk.NewIntFromBigInt(gasPrice), data, sdk.AccAddress(addr.Bytes()))
// Generate tx to be used to simulate (signature isn't needed)
@ -436,24 +441,26 @@ func (e *PublicEthAPI) doCall(args CallArgs, blockNr rpc.BlockNumber, globalGasC
txEncoder := authutils.GetTxEncoder(ctx.Codec)
txBytes, err := txEncoder(tx)
if err != nil {
return sdk.Result{}, err
return nil, err
}
// Transaction simulation through query
res, _, err := ctx.QueryWithData("app/simulate", txBytes)
if err != nil {
return sdk.Result{}, err
return nil, err
}
var simResult sdk.Result
if err = e.cliCtx.Codec.UnmarshalBinaryLengthPrefixed(res, &simResult); err != nil {
return sdk.Result{}, err
return nil, err
}
return simResult, nil
return &simResult, nil
}
// EstimateGas estimates gas usage for the given smart contract call.
// EstimateGas returns an estimate of gas usage for the given smart contract call.
// It adds 1,000 gas to the returned value instead of using the gas adjustment
// param from the SDK.
func (e *PublicEthAPI) EstimateGas(args CallArgs) (hexutil.Uint64, error) {
result, err := e.doCall(args, 0, big.NewInt(emint.DefaultRPCGasLimit))
if err != nil {
@ -482,31 +489,37 @@ func (e *PublicEthAPI) GetBlockByNumber(blockNum BlockNumber, fullTx bool) (map[
return e.getEthBlockByNumber(value, fullTx)
}
func (e *PublicEthAPI) getEthBlockByNumber(value int64, fullTx bool) (map[string]interface{}, error) {
func (e *PublicEthAPI) getEthBlockByNumber(height int64, fullTx bool) (map[string]interface{}, error) {
// Remove this check when 0 query is fixed ref: (https://github.com/tendermint/tendermint/issues/4014)
var blkNumPtr *int64
if value != 0 {
blkNumPtr = &value
if height != 0 {
blkNumPtr = &height
}
block, err := e.cliCtx.Client.Block(blkNumPtr)
if err != nil {
return nil, err
}
header := block.BlockMeta.Header
header := block.Block.Header
gasLimit, err := e.getGasLimit()
if err != nil {
return nil, err
}
var gasUsed *big.Int
var transactions []interface{}
var (
gasUsed *big.Int
transactions []interface{}
)
if fullTx {
// Populate full transaction data
transactions, gasUsed = convertTransactionsToRPC(e.cliCtx, block.Block.Txs,
common.BytesToHash(header.Hash()), uint64(header.Height))
transactions, gasUsed, err = convertTransactionsToRPC(
e.cliCtx, block.Block.Txs, common.BytesToHash(header.Hash()), uint64(header.Height),
)
if err != nil {
return nil, err
}
} else {
// TODO: Gas used not saved and cannot be calculated by hashes
// Return slice of transaction hashes
@ -553,19 +566,24 @@ func formatBlock(
}
}
func convertTransactionsToRPC(cliCtx context.CLIContext, txs []tmtypes.Tx, blockHash common.Hash, height uint64) ([]interface{}, *big.Int) {
func convertTransactionsToRPC(cliCtx context.CLIContext, txs []tmtypes.Tx, blockHash common.Hash, height uint64) ([]interface{}, *big.Int, error) {
transactions := make([]interface{}, len(txs))
gasUsed := big.NewInt(0)
for i, tx := range txs {
ethTx, err := bytesToEthTx(cliCtx, tx)
if err != nil {
continue
return nil, nil, err
}
// TODO: Remove gas usage calculation if saving gasUsed per block
gasUsed.Add(gasUsed, ethTx.Fee())
transactions[i] = newRPCTransaction(ethTx, blockHash, &height, uint64(i))
transactions[i], err = newRPCTransaction(*ethTx, blockHash, &height, uint64(i))
if err != nil {
return nil, nil, err
}
}
return transactions, gasUsed
return transactions, gasUsed, nil
}
// Transaction represents a transaction returned to RPC clients.
@ -586,23 +604,30 @@ type Transaction struct {
S *hexutil.Big `json:"s"`
}
func bytesToEthTx(cliCtx context.CLIContext, bz []byte) (*types.EthereumTxMsg, error) {
func bytesToEthTx(cliCtx context.CLIContext, bz []byte) (*types.MsgEthereumTx, error) {
var stdTx sdk.Tx
err := cliCtx.Codec.UnmarshalBinaryLengthPrefixed(bz, &stdTx)
ethTx, ok := stdTx.(*types.EthereumTxMsg)
if !ok || err != nil {
if err != nil {
return nil, err
}
ethTx, ok := stdTx.(types.MsgEthereumTx)
if !ok {
return nil, fmt.Errorf("invalid transaction type, must be an amino encoded Ethereum transaction")
}
return ethTx, nil
return &ethTx, nil
}
// newRPCTransaction returns a transaction that will serialize to the RPC
// representation, with the given location metadata set (if available).
func newRPCTransaction(tx *types.EthereumTxMsg, blockHash common.Hash, blockNumber *uint64, index uint64) *Transaction {
func newRPCTransaction(tx types.MsgEthereumTx, blockHash common.Hash, blockNumber *uint64, index uint64) (*Transaction, error) {
// Verify signature and retrieve sender address
from, _ := tx.VerifySig(tx.ChainID())
from, err := tx.VerifySig(tx.ChainID())
if err != nil {
return nil, err
}
result := &Transaction{
result := Transaction{
From: from,
Gas: hexutil.Uint64(tx.Data.GasLimit),
GasPrice: (*hexutil.Big)(tx.Data.Price),
@ -615,12 +640,14 @@ func newRPCTransaction(tx *types.EthereumTxMsg, blockHash common.Hash, blockNumb
R: (*hexutil.Big)(tx.Data.R),
S: (*hexutil.Big)(tx.Data.S),
}
if blockHash != (common.Hash{}) {
result.BlockHash = &blockHash
result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(*blockNumber))
result.TransactionIndex = (*hexutil.Uint64)(&index)
}
return result
return &result, nil
}
// GetTransactionByHash returns the transaction identified by hash.
@ -636,7 +663,7 @@ func (e *PublicEthAPI) GetTransactionByHash(hash common.Hash) (*Transaction, err
if err != nil {
return nil, err
}
blockHash := common.BytesToHash(block.BlockMeta.Header.Hash())
blockHash := common.BytesToHash(block.Block.Header.Hash())
ethTx, err := bytesToEthTx(e.cliCtx, tx.Tx)
if err != nil {
@ -644,7 +671,7 @@ func (e *PublicEthAPI) GetTransactionByHash(hash common.Hash) (*Transaction, err
}
height := uint64(tx.Height)
return newRPCTransaction(ethTx, blockHash, &height, uint64(tx.Index)), nil
return newRPCTransaction(*ethTx, blockHash, &height, uint64(tx.Index))
}
// GetTransactionByBlockHashAndIndex returns the transaction identified by hash and index.
@ -670,7 +697,7 @@ func (e *PublicEthAPI) getTransactionByBlockNumberAndIndex(number int64, idx hex
if err != nil {
return nil, err
}
header := block.BlockMeta.Header
header := block.Block.Header
txs := block.Block.Txs
if uint64(idx) >= uint64(len(txs)) {
@ -682,8 +709,7 @@ func (e *PublicEthAPI) getTransactionByBlockNumberAndIndex(number int64, idx hex
}
height := uint64(header.Height)
transaction := newRPCTransaction(ethTx, common.BytesToHash(header.Hash()), &height, uint64(idx))
return transaction, nil
return newRPCTransaction(*ethTx, common.BytesToHash(header.Hash()), &height, uint64(idx))
}
// GetTransactionReceipt returns the transaction receipt identified by hash.
@ -699,7 +725,7 @@ func (e *PublicEthAPI) GetTransactionReceipt(hash common.Hash) (map[string]inter
if err != nil {
return nil, err
}
blockHash := common.BytesToHash(block.BlockMeta.Header.Hash())
blockHash := common.BytesToHash(block.Block.Header.Hash())
// Convert tx bytes to eth transaction
ethTx, err := bytesToEthTx(e.cliCtx, tx.Tx)
@ -726,7 +752,10 @@ func (e *PublicEthAPI) GetTransactionReceipt(hash common.Hash) (map[string]inter
e.cliCtx.Codec.MustUnmarshalJSON(res, &logs)
txData := tx.TxResult.GetData()
contractAddress, bloomFilter, _, _ := types.DecodeReturnData(txData)
data, err := types.DecodeResultData(txData)
if err != nil {
return nil, err
}
fields := map[string]interface{}{
"blockHash": blockHash,
@ -739,12 +768,12 @@ func (e *PublicEthAPI) GetTransactionReceipt(hash common.Hash) (map[string]inter
"cumulativeGasUsed": nil, // ignore until needed
"contractAddress": nil,
"logs": logs.Logs,
"logsBloom": bloomFilter,
"logsBloom": data.Bloom,
"status": status,
}
if contractAddress != (common.Address{}) {
fields["contractAddress"] = contractAddress
if data.Address != (common.Address{}) {
fields["contractAddress"] = data.Address
}
return fields, nil
@ -766,7 +795,11 @@ func (e *PublicEthAPI) PendingTransactions() ([]*Transaction, error) {
}
// * Should check signer and reference against accounts the node manages in future
rpcTx := newRPCTransaction(ethTx, common.Hash{}, nil, 0)
rpcTx, err := newRPCTransaction(*ethTx, common.Hash{}, nil, 0)
if err != nil {
return nil, err
}
transactions = append(transactions, rpcTx)
}
@ -871,13 +904,14 @@ func (e *PublicEthAPI) getGasLimit() (int64, error) {
}
// generateFromArgs populates tx message with args (used in RPC API)
func (e *PublicEthAPI) generateFromArgs(args params.SendTxArgs) (msg *types.EthereumTxMsg, err error) {
var nonce uint64
var gasLimit uint64
func (e *PublicEthAPI) generateFromArgs(args params.SendTxArgs) (*types.MsgEthereumTx, error) {
var (
nonce uint64
gasLimit uint64
err error
)
amount := (*big.Int)(args.Value)
gasPrice := (*big.Int)(args.GasPrice)
if args.GasPrice == nil {
@ -899,7 +933,7 @@ func (e *PublicEthAPI) generateFromArgs(args params.SendTxArgs) (msg *types.Ethe
}
if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) {
return nil, fmt.Errorf(`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`)
return nil, errors.New(`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`)
}
// Sets input to either Input or Data, if both are set and not equal error above returns
@ -916,6 +950,7 @@ func (e *PublicEthAPI) generateFromArgs(args params.SendTxArgs) (msg *types.Ethe
return nil, fmt.Errorf("contract creation without any data provided")
}
}
if args.Gas == nil {
callArgs := CallArgs{
From: &args.From,
@ -925,11 +960,15 @@ func (e *PublicEthAPI) generateFromArgs(args params.SendTxArgs) (msg *types.Ethe
Value: args.Value,
Data: args.Data,
}
g, _ := e.EstimateGas(callArgs)
g, err := e.EstimateGas(callArgs)
if err != nil {
return nil, err
}
gasLimit = uint64(g)
} else {
gasLimit = (uint64)(*args.Gas)
}
msg := types.NewMsgEthereumTx(nonce, args.To, amount, gasLimit, gasPrice, input)
return types.NewEthereumTxMsg(nonce, args.To, amount, gasLimit, gasPrice, input), nil
return &msg, nil
}

View File

@ -18,6 +18,7 @@ import (
"github.com/cosmos/ethermint/version"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/stretchr/testify/require"
)
const (
@ -92,51 +93,34 @@ func TestEth_protocolVersion(t *testing.T) {
expectedRes := hexutil.Uint(version.ProtocolVersion)
rpcRes, err := call("eth_protocolVersion", []string{})
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
var res hexutil.Uint
err = res.UnmarshalJSON(rpcRes.Result)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
t.Logf("Got protocol version: %s\n", res.String())
if res != expectedRes {
t.Fatalf("expected: %s got: %s\n", expectedRes.String(), rpcRes.Result)
}
require.Equal(t, expectedRes, res, "expected: %s got: %s\n", expectedRes.String(), rpcRes.Result)
}
func TestEth_blockNumber(t *testing.T) {
rpcRes, err := call("eth_blockNumber", []string{})
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
var res hexutil.Uint64
err = res.UnmarshalJSON(rpcRes.Result)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
t.Logf("Got block number: %s\n", res.String())
}
func TestEth_GetBalance(t *testing.T) {
rpcRes, err := call("eth_getBalance", []string{addrA, "0x0"})
if err != nil {
t.Fatal(err)
return
}
require.NoError(t, err)
var res hexutil.Big
err = res.UnmarshalJSON(rpcRes.Result)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
t.Logf("Got balance %s for %s\n", res.String(), addrA)
@ -149,40 +133,27 @@ func TestEth_GetBalance(t *testing.T) {
func TestEth_GetStorageAt(t *testing.T) {
expectedRes := hexutil.Bytes{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
rpcRes, err := call("eth_getStorageAt", []string{addrA, string(addrAStoreKey), "0x0"})
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
var storage hexutil.Bytes
err = storage.UnmarshalJSON(rpcRes.Result)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
t.Logf("Got value [%X] for %s with key %X\n", storage, addrA, addrAStoreKey)
if !bytes.Equal(storage, expectedRes) {
t.Errorf("expected: %d (%d bytes) got: %d (%d bytes)", expectedRes, len(expectedRes), storage, len(storage))
}
require.True(t, bytes.Equal(storage, expectedRes), "expected: %d (%d bytes) got: %d (%d bytes)", expectedRes, len(expectedRes), storage, len(storage))
}
func TestEth_GetCode(t *testing.T) {
expectedRes := hexutil.Bytes{}
rpcRes, err := call("eth_getCode", []string{addrA, "0x0"})
if err != nil {
t.Error(err)
}
require.NoError(t, err)
var code hexutil.Bytes
err = code.UnmarshalJSON(rpcRes.Result)
if err != nil {
t.Fatal(err)
}
require.NoError(t, err)
t.Logf("Got code [%X] for %s\n", code, addrA)
if !bytes.Equal(expectedRes, code) {
t.Errorf("expected: %X got: %X", expectedRes, code)
}
require.True(t, bytes.Equal(expectedRes, code), "expected: %X got: %X", expectedRes, code)
}

45
x/evm/abci.go Normal file
View 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{}
}

View File

@ -5,7 +5,13 @@ import (
"github.com/cosmos/ethermint/x/evm/types"
)
// nolint
const (
ModuleName = types.ModuleName
StoreKey = types.StoreKey
CodeKey = types.StoreKey
BlockKey = types.BlockKey
RouterKey = types.RouterKey
QueryProtocolVersion = types.QueryProtocolVersion
QueryBalance = types.QueryBalance
QueryBlockNumber = types.QueryBlockNumber
@ -19,10 +25,13 @@ const (
QueryAccount = types.QueryAccount
)
// nolint
var (
NewKeeper = keeper.NewKeeper
TxDecoder = types.TxDecoder
)
//nolint
type (
Keeper = keeper.Keeper
QueryResAccount = types.QueryResAccount

View File

@ -85,7 +85,7 @@ func GetCmdGenTx(cdc *codec.Codec) *cobra.Command {
}
// TODO: Potentially allow overriding of gas price and gas limit
msg := types.NewEmintMsg(seq, &toAddr, sdk.NewInt(amount), txBldr.Gas(),
msg := types.NewMsgEthermint(seq, &toAddr, sdk.NewInt(amount), txBldr.Gas(),
sdk.NewInt(emint.DefaultGasPrice), data, from)
err = msg.ValidateBasic()
@ -137,7 +137,7 @@ func GetCmdGenCreateTx(cdc *codec.Codec) *cobra.Command {
}
// TODO: Potentially allow overriding of gas price and gas limit
msg := types.NewEmintMsg(seq, nil, sdk.NewInt(amount), txBldr.Gas(),
msg := types.NewMsgEthermint(seq, nil, sdk.NewInt(amount), txBldr.Gas(),
sdk.NewInt(emint.DefaultGasPrice), data, from)
err = msg.ValidateBasic()

View File

@ -18,23 +18,20 @@ import (
func NewHandler(k Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) {
case types.EthereumTxMsg:
return handleETHTxMsg(ctx, k, msg)
case *types.EmintMsg:
return handleEmintMsg(ctx, k, *msg)
case types.MsgEthereumTx:
return HandleMsgEthereumTx(ctx, k, msg)
case types.MsgEthermint:
return HandleMsgEthermint(ctx, k, msg)
default:
errMsg := fmt.Sprintf("Unrecognized ethermint Msg type: %v", msg.Type())
errMsg := fmt.Sprintf("unrecognized ethermint msg type: %v", msg.Type())
return sdk.ErrUnknownRequest(errMsg).Result()
}
}
}
// Handle an Ethereum specific tx
func handleETHTxMsg(ctx sdk.Context, k Keeper, msg types.EthereumTxMsg) sdk.Result {
if err := msg.ValidateBasic(); err != nil {
return err.Result()
}
// HandleMsgEthereumTx handles an Ethereum specific tx
func HandleMsgEthereumTx(ctx sdk.Context, k Keeper, msg types.MsgEthereumTx) sdk.Result {
ctx = ctx.WithEventManager(sdk.NewEventManager())
// parse the chainID from a string to a base-10 integer
intChainID, ok := new(big.Int).SetString(ctx.ChainID(), 10)
if !ok {
@ -44,14 +41,14 @@ func handleETHTxMsg(ctx sdk.Context, k Keeper, msg types.EthereumTxMsg) sdk.Resu
// Verify signature and retrieve sender address
sender, err := msg.VerifySig(intChainID)
if err != nil {
return emint.ErrInvalidSender(err.Error()).Result()
return sdk.ResultFromError(err)
}
// Encode transaction by default Tx encoder
txEncoder := authutils.GetTxEncoder(types.ModuleCdc)
txBytes, err := txEncoder(msg)
if err != nil {
return sdk.ErrInternal(err.Error()).Result()
return sdk.ResultFromError(err)
}
txHash := tmtypes.Tx(txBytes).Hash()
ethHash := common.BytesToHash(txHash)
@ -70,21 +67,53 @@ func handleETHTxMsg(ctx sdk.Context, k Keeper, msg types.EthereumTxMsg) sdk.Resu
Simulate: ctx.IsCheckTx(),
}
// Prepare db for logs
k.CommitStateDB.Prepare(ethHash, common.Hash{}, k.TxCount.Get())
k.TxCount.Increment()
k.CommitStateDB.Prepare(ethHash, common.Hash{}, k.TxCount)
k.TxCount++
bloom, res := st.TransitionCSDB(ctx)
if res.IsOK() {
k.Bloom.Or(k.Bloom, bloom)
// TODO: move to keeper
returnData, err := st.TransitionCSDB(ctx)
if err != nil {
return sdk.ResultFromError(err)
}
return res
// update block bloom filter
k.Bloom.Or(k.Bloom, returnData.Bloom)
// update transaction logs in KVStore
err = k.SetTransactionLogs(ctx, returnData.Logs, txHash)
if err != nil {
return sdk.ResultFromError(err)
}
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeEthereumTx,
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Data.Amount.String()),
),
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
sdk.NewAttribute(sdk.AttributeKeySender, sender.String()),
),
})
if msg.Data.Recipient != nil {
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeEthereumTx,
sdk.NewAttribute(types.AttributeKeyRecipient, msg.Data.Recipient.String()),
),
)
}
// set the events to the result
returnData.Result.Events = ctx.EventManager().Events()
return *returnData.Result
}
func handleEmintMsg(ctx sdk.Context, k Keeper, msg types.EmintMsg) sdk.Result {
if err := msg.ValidateBasic(); err != nil {
return err.Result()
}
// HandleMsgEthermint handles a MsgEthermint
func HandleMsgEthermint(ctx sdk.Context, k Keeper, msg types.MsgEthermint) sdk.Result {
ctx = ctx.WithEventManager(sdk.NewEventManager())
// parse the chainID from a string to a base-10 integer
intChainID, ok := new(big.Int).SetString(ctx.ChainID(), 10)
if !ok {
@ -109,9 +138,36 @@ func handleEmintMsg(ctx sdk.Context, k Keeper, msg types.EmintMsg) sdk.Result {
}
// Prepare db for logs
k.CommitStateDB.Prepare(common.Hash{}, common.Hash{}, k.TxCount.Get()) // Cannot provide tx hash
k.TxCount.Increment()
k.CommitStateDB.Prepare(common.Hash{}, common.Hash{}, k.TxCount) // Cannot provide tx hash
k.TxCount++
_, res := st.TransitionCSDB(ctx)
return res
returnData, err := st.TransitionCSDB(ctx)
if err != nil {
return sdk.ResultFromError(err)
}
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeEthermint,
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
),
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
sdk.NewAttribute(sdk.AttributeKeySender, msg.From.String()),
),
})
if msg.Recipient != nil {
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeEthermint,
sdk.NewAttribute(types.AttributeKeyRecipient, msg.Recipient.String()),
),
)
}
// set the events to the result
returnData.Result.Events = ctx.EventManager().Events()
return *returnData.Result
}

89
x/evm/handler_test.go Normal file
View 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)
}

View File

@ -2,6 +2,8 @@ package keeper
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"github.com/cosmos/cosmos-sdk/codec"
@ -9,7 +11,6 @@ import (
ethvm "github.com/ethereum/go-ethereum/core/vm"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/ethermint/x/evm/types"
ethstate "github.com/ethereum/go-ethereum/core/state"
ethtypes "github.com/ethereum/go-ethereum/core/types"
@ -24,35 +25,22 @@ type Keeper struct {
cdc *codec.Codec
// Store key required to update the block bloom filter mappings needed for the
// Web3 API
storeKey sdk.StoreKey
blockKey sdk.StoreKey
CommitStateDB *types.CommitStateDB
TxCount *count
TxCount int
Bloom *big.Int
}
// TODO: move to types
type count int
func (c *count) Get() int {
return (int)(*c)
}
func (c *count) Increment() {
*c++
}
func (c *count) Reset() {
*c = 0
}
// NewKeeper generates new evm module keeper
func NewKeeper(ak auth.AccountKeeper, storageKey, codeKey,
storeKey sdk.StoreKey, cdc *codec.Codec) Keeper {
func NewKeeper(
cdc *codec.Codec, blockKey, codeKey, storeKey sdk.StoreKey,
ak types.AccountKeeper,
) Keeper {
return Keeper{
cdc: cdc,
storeKey: storeKey,
CommitStateDB: types.NewCommitStateDB(sdk.Context{}, ak, storageKey, codeKey),
TxCount: new(count),
blockKey: blockKey,
CommitStateDB: types.NewCommitStateDB(sdk.Context{}, codeKey, storeKey, ak),
TxCount: 0,
Bloom: big.NewInt(0),
}
}
@ -64,21 +52,23 @@ func NewKeeper(ak auth.AccountKeeper, storageKey, codeKey,
// SetBlockHashMapping sets the mapping from block consensus hash to block height
func (k *Keeper) SetBlockHashMapping(ctx sdk.Context, hash []byte, height int64) {
store := ctx.KVStore(k.storeKey)
store := ctx.KVStore(k.blockKey)
if !bytes.Equal(hash, []byte{}) {
store.Set(hash, k.cdc.MustMarshalBinaryLengthPrefixed(height))
bz := sdk.Uint64ToBigEndian(uint64(height))
store.Set(hash, bz)
}
}
// GetBlockHashMapping gets block height from block consensus hash
func (k *Keeper) GetBlockHashMapping(ctx sdk.Context, hash []byte) (height int64) {
store := ctx.KVStore(k.storeKey)
store := ctx.KVStore(k.blockKey)
bz := store.Get(hash)
if bytes.Equal(bz, []byte{}) {
panic(fmt.Errorf("block with hash %s not found", ethcmn.BytesToHash(hash)))
}
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &height)
return
height = int64(binary.BigEndian.Uint64(bz))
return height
}
// ----------------------------------------------------------------------------
@ -87,23 +77,54 @@ func (k *Keeper) GetBlockHashMapping(ctx sdk.Context, hash []byte) (height int64
// ----------------------------------------------------------------------------
// SetBlockBloomMapping sets the mapping from block height to bloom bits
func (k *Keeper) SetBlockBloomMapping(ctx sdk.Context, bloom ethtypes.Bloom, height int64) {
store := ctx.KVStore(k.storeKey)
heightHash := k.cdc.MustMarshalBinaryLengthPrefixed(height)
if !bytes.Equal(heightHash, []byte{}) {
store.Set(heightHash, bloom.Bytes())
func (k *Keeper) SetBlockBloomMapping(ctx sdk.Context, bloom ethtypes.Bloom, height int64) error {
store := ctx.KVStore(k.blockKey)
bz := sdk.Uint64ToBigEndian(uint64(height))
if len(bz) == 0 {
return fmt.Errorf("block with bloombits %v not found", bloom)
}
store.Set(types.BloomKey(bz), bloom.Bytes())
return nil
}
// GetBlockBloomMapping gets bloombits from block height
func (k *Keeper) GetBlockBloomMapping(ctx sdk.Context, height int64) ethtypes.Bloom {
store := ctx.KVStore(k.storeKey)
heightHash := k.cdc.MustMarshalBinaryLengthPrefixed(height)
bloom := store.Get(heightHash)
if bytes.Equal(heightHash, []byte{}) {
panic(fmt.Errorf("block with bloombits %s not found", bloom))
func (k *Keeper) GetBlockBloomMapping(ctx sdk.Context, height int64) (ethtypes.Bloom, error) {
store := ctx.KVStore(k.blockKey)
bz := sdk.Uint64ToBigEndian(uint64(height))
if len(bz) == 0 {
return ethtypes.BytesToBloom([]byte{}), fmt.Errorf("block with height %d not found", height)
}
return ethtypes.BytesToBloom(bloom)
bloom := store.Get(types.BloomKey(bz))
if len(bloom) == 0 {
return ethtypes.BytesToBloom([]byte{}), fmt.Errorf("block with bloombits %v not found", bloom)
}
return ethtypes.BytesToBloom(bloom), nil
}
// SetBlockLogs sets the transaction's logs in the KVStore
func (k *Keeper) SetTransactionLogs(ctx sdk.Context, logs []*ethtypes.Log, hash []byte) error {
store := ctx.KVStore(k.blockKey)
encLogs, err := types.EncodeLogs(logs)
if err != nil {
return err
}
store.Set(types.LogsKey(hash), encLogs)
return nil
}
// GetBlockLogs gets the logs for a transaction from the KVStore
func (k *Keeper) GetTransactionLogs(ctx sdk.Context, hash []byte) ([]*ethtypes.Log, error) {
store := ctx.KVStore(k.blockKey)
encLogs := store.Get(types.LogsKey(hash))
if len(encLogs) == 0 {
return nil, errors.New("cannot get transaction logs")
}
return types.DecodeLogs(encLogs)
}
// ----------------------------------------------------------------------------
@ -225,13 +246,18 @@ func (k *Keeper) GetCommittedState(ctx sdk.Context, addr ethcmn.Address, hash et
}
// GetLogs calls CommitStateDB.GetLogs using the passed in context
func (k *Keeper) GetLogs(ctx sdk.Context, hash ethcmn.Hash) []*ethtypes.Log {
return k.CommitStateDB.WithContext(ctx).GetLogs(hash)
func (k *Keeper) GetLogs(ctx sdk.Context, hash ethcmn.Hash) ([]*ethtypes.Log, error) {
logs, err := k.CommitStateDB.WithContext(ctx).GetLogs(hash)
if err != nil {
return nil, err
}
return logs, nil
}
// Logs calls CommitStateDB.Logs using the passed in context
func (k *Keeper) Logs(ctx sdk.Context) []*ethtypes.Log {
return k.CommitStateDB.WithContext(ctx).Logs()
// AllLogs calls CommitStateDB.AllLogs using the passed in context
func (k *Keeper) AllLogs(ctx sdk.Context) []*ethtypes.Log {
return k.CommitStateDB.WithContext(ctx).AllLogs()
}
// GetRefund calls CommitStateDB.GetRefund using the passed in context
@ -264,13 +290,17 @@ func (k *Keeper) Commit(ctx sdk.Context, deleteEmptyObjects bool) (root ethcmn.H
}
// Finalise calls CommitStateDB.Finalise using the passed in context
func (k *Keeper) Finalise(ctx sdk.Context, deleteEmptyObjects bool) {
k.CommitStateDB.WithContext(ctx).Finalise(deleteEmptyObjects)
func (k *Keeper) Finalise(ctx sdk.Context, deleteEmptyObjects bool) error {
return k.CommitStateDB.WithContext(ctx).Finalise(deleteEmptyObjects)
}
// IntermediateRoot calls CommitStateDB.IntermediateRoot using the passed in context
func (k *Keeper) IntermediateRoot(ctx sdk.Context, deleteEmptyObjects bool) {
k.CommitStateDB.WithContext(ctx).IntermediateRoot(deleteEmptyObjects)
func (k *Keeper) IntermediateRoot(ctx sdk.Context, deleteEmptyObjects bool) error {
_, err := k.CommitStateDB.WithContext(ctx).IntermediateRoot(deleteEmptyObjects)
if err != nil {
return err
}
return nil
}
// ----------------------------------------------------------------------------

View File

@ -1,110 +1,78 @@
package keeper
package keeper_test
import (
"math/big"
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/ethermint/types"
evmtypes "github.com/cosmos/ethermint/x/evm/types"
"github.com/cosmos/ethermint/app"
"github.com/cosmos/ethermint/x/evm/keeper"
ethcmn "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
tmlog "github.com/tendermint/tendermint/libs/log"
dbm "github.com/tendermint/tm-db"
)
var (
address = ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b4c1")
var address = ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b4c1")
accKey = sdk.NewKVStoreKey("acc")
storageKey = sdk.NewKVStoreKey(evmtypes.EvmStoreKey)
codeKey = sdk.NewKVStoreKey(evmtypes.EvmCodeKey)
blockKey = sdk.NewKVStoreKey(evmtypes.EvmBlockKey)
type KeeperTestSuite struct {
suite.Suite
logger = tmlog.NewNopLogger()
)
func newTestCodec() *codec.Codec {
cdc := codec.New()
evmtypes.RegisterCodec(cdc)
types.RegisterCodec(cdc)
auth.RegisterCodec(cdc)
sdk.RegisterCodec(cdc)
codec.RegisterCrypto(cdc)
return cdc
ctx sdk.Context
querier sdk.Querier
app *app.EthermintApp
}
func TestDBStorage(t *testing.T) {
// create logger, codec and root multi-store
cdc := newTestCodec()
func (suite *KeeperTestSuite) SetupTest() {
checkTx := false
// The ParamsKeeper handles parameter storage for the application
keyParams := sdk.NewKVStoreKey(params.StoreKey)
tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey)
paramsKeeper := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace)
// Set specific supspaces
authSubspace := paramsKeeper.Subspace(auth.DefaultParamspace)
ak := auth.NewAccountKeeper(cdc, accKey, authSubspace, types.ProtoBaseAccount)
ek := NewKeeper(ak, storageKey, codeKey, blockKey, cdc)
suite.app = app.Setup(checkTx)
suite.ctx = suite.app.BaseApp.NewContext(checkTx, abci.Header{Height: 1, ChainID: "3", Time: time.Now().UTC()})
suite.querier = keeper.NewQuerier(suite.app.EvmKeeper)
}
db := dbm.NewMemDB()
cms := store.NewCommitMultiStore(db)
// mount stores
keys := []*sdk.KVStoreKey{accKey, storageKey, codeKey, blockKey}
for _, key := range keys {
cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, nil)
}
// load latest version (root)
err := cms.LoadLatestVersion()
require.NoError(t, err)
// First execution
ms := cms.CacheMultiStore()
ctx := sdk.NewContext(ms, abci.Header{}, false, logger)
ctx = ctx.WithBlockHeight(1)
func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(KeeperTestSuite))
}
func (suite *KeeperTestSuite) TestDBStorage() {
// Perform state transitions
ek.SetBalance(ctx, address, big.NewInt(5))
ek.SetNonce(ctx, address, 4)
ek.SetState(ctx, address, ethcmn.HexToHash("0x2"), ethcmn.HexToHash("0x3"))
ek.SetCode(ctx, address, []byte{0x1})
suite.app.EvmKeeper.CreateAccount(suite.ctx, address)
suite.app.EvmKeeper.SetBalance(suite.ctx, address, big.NewInt(5))
suite.app.EvmKeeper.SetNonce(suite.ctx, address, 4)
suite.app.EvmKeeper.SetState(suite.ctx, address, ethcmn.HexToHash("0x2"), ethcmn.HexToHash("0x3"))
suite.app.EvmKeeper.SetCode(suite.ctx, address, []byte{0x1})
// Test block hash mapping functionality
ek.SetBlockHashMapping(ctx, ethcmn.FromHex("0x0d87a3a5f73140f46aac1bf419263e4e94e87c292f25007700ab7f2060e2af68"), 7)
ek.SetBlockHashMapping(ctx, []byte{0x43, 0x32}, 8)
suite.app.EvmKeeper.SetBlockHashMapping(suite.ctx, ethcmn.FromHex("0x0d87a3a5f73140f46aac1bf419263e4e94e87c292f25007700ab7f2060e2af68"), 7)
suite.app.EvmKeeper.SetBlockHashMapping(suite.ctx, []byte{0x43, 0x32}, 8)
// Test block height mapping functionality
testBloom := ethtypes.BytesToBloom([]byte{0x1, 0x3})
ek.SetBlockBloomMapping(ctx, testBloom, 4)
err := suite.app.EvmKeeper.SetBlockBloomMapping(suite.ctx, testBloom, 4)
suite.Require().NoError(err, "failed to set block bloom mapping")
// Get those state transitions
require.Equal(t, ek.GetBalance(ctx, address).Cmp(big.NewInt(5)), 0)
require.Equal(t, ek.GetNonce(ctx, address), uint64(4))
require.Equal(t, ek.GetState(ctx, address, ethcmn.HexToHash("0x2")), ethcmn.HexToHash("0x3"))
require.Equal(t, ek.GetCode(ctx, address), []byte{0x1})
suite.Require().Equal(suite.app.EvmKeeper.GetBalance(suite.ctx, address).Cmp(big.NewInt(5)), 0)
suite.Require().Equal(suite.app.EvmKeeper.GetNonce(suite.ctx, address), uint64(4))
suite.Require().Equal(suite.app.EvmKeeper.GetState(suite.ctx, address, ethcmn.HexToHash("0x2")), ethcmn.HexToHash("0x3"))
suite.Require().Equal(suite.app.EvmKeeper.GetCode(suite.ctx, address), []byte{0x1})
require.Equal(t, ek.GetBlockHashMapping(ctx, ethcmn.FromHex("0x0d87a3a5f73140f46aac1bf419263e4e94e87c292f25007700ab7f2060e2af68")), int64(7))
require.Equal(t, ek.GetBlockHashMapping(ctx, []byte{0x43, 0x32}), int64(8))
require.Equal(t, ek.GetBlockBloomMapping(ctx, 4), testBloom)
suite.Require().Equal(suite.app.EvmKeeper.GetBlockHashMapping(suite.ctx, ethcmn.FromHex("0x0d87a3a5f73140f46aac1bf419263e4e94e87c292f25007700ab7f2060e2af68")), int64(7))
suite.Require().Equal(suite.app.EvmKeeper.GetBlockHashMapping(suite.ctx, []byte{0x43, 0x32}), int64(8))
bloom, err := suite.app.EvmKeeper.GetBlockBloomMapping(suite.ctx, 4)
suite.Require().NoError(err)
suite.Require().Equal(bloom, testBloom)
// commit stateDB
_, err = ek.Commit(ctx, false)
require.NoError(t, err, "failed to commit StateDB")
_, err = suite.app.EvmKeeper.Commit(suite.ctx, false)
suite.Require().NoError(err, "failed to commit StateDB")
// simulate BaseApp EndBlocker commitment
ms.Write()
cms.Commit()
suite.app.Commit()
}

View File

@ -1,6 +1,7 @@
package keeper
import (
"fmt"
"strconv"
"github.com/cosmos/cosmos-sdk/codec"
@ -15,7 +16,7 @@ import (
// NewQuerier is the module level router for state queries
func NewQuerier(keeper Keeper) sdk.Querier {
return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) {
return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, sdk.Error) {
switch path[0] {
case types.QueryProtocolVersion:
return queryProtocolVersion(keeper)
@ -48,140 +49,146 @@ func NewQuerier(keeper Keeper) sdk.Querier {
func queryProtocolVersion(keeper Keeper) ([]byte, sdk.Error) {
vers := version.ProtocolVersion
res, err := codec.MarshalJSONIndent(keeper.cdc, hexutil.Uint(vers))
bz, err := codec.MarshalJSONIndent(keeper.cdc, hexutil.Uint(vers))
if err != nil {
panic("could not marshal result to JSON")
return nil, sdk.ErrInternal(err.Error())
}
return res, nil
return bz, nil
}
func queryBalance(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
addr := ethcmn.HexToAddress(path[1])
balance := keeper.GetBalance(ctx, addr)
bRes := types.QueryResBalance{Balance: utils.MarshalBigInt(balance)}
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
res := types.QueryResBalance{Balance: utils.MarshalBigInt(balance)}
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
if err != nil {
panic("could not marshal result to JSON: " + err.Error())
return nil, sdk.ErrInternal(err.Error())
}
return res, nil
return bz, nil
}
func queryBlockNumber(ctx sdk.Context, keeper Keeper) ([]byte, sdk.Error) {
num := ctx.BlockHeight()
bnRes := types.QueryResBlockNumber{Number: num}
res, err := codec.MarshalJSONIndent(keeper.cdc, bnRes)
bz, err := codec.MarshalJSONIndent(keeper.cdc, bnRes)
if err != nil {
panic("could not marshal result to JSON: " + err.Error())
return nil, sdk.ErrInternal(err.Error())
}
return res, nil
return bz, nil
}
func queryStorage(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
addr := ethcmn.HexToAddress(path[1])
key := ethcmn.HexToHash(path[2])
val := keeper.GetState(ctx, addr, key)
bRes := types.QueryResStorage{Value: val.Bytes()}
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
res := types.QueryResStorage{Value: val.Bytes()}
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
if err != nil {
panic("could not marshal result to JSON: " + err.Error())
return nil, sdk.ErrInternal(err.Error())
}
return res, nil
return bz, nil
}
func queryCode(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
addr := ethcmn.HexToAddress(path[1])
code := keeper.GetCode(ctx, addr)
cRes := types.QueryResCode{Code: code}
res, err := codec.MarshalJSONIndent(keeper.cdc, cRes)
res := types.QueryResCode{Code: code}
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
if err != nil {
panic("could not marshal result to JSON: " + err.Error())
return nil, sdk.ErrInternal(err.Error())
}
return res, nil
return bz, nil
}
func queryNonce(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
addr := ethcmn.HexToAddress(path[1])
nonce := keeper.GetNonce(ctx, addr)
nRes := types.QueryResNonce{Nonce: nonce}
res, err := codec.MarshalJSONIndent(keeper.cdc, nRes)
bz, err := codec.MarshalJSONIndent(keeper.cdc, nRes)
if err != nil {
panic("could not marshal result to JSON: " + err.Error())
return nil, sdk.ErrInternal(err.Error())
}
return res, nil
return bz, nil
}
func queryHashToHeight(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
blockHash := ethcmn.FromHex(path[1])
blockNumber := keeper.GetBlockHashMapping(ctx, blockHash)
bRes := types.QueryResBlockNumber{Number: blockNumber}
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
res := types.QueryResBlockNumber{Number: blockNumber}
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
if err != nil {
panic("could not marshal result to JSON: " + err.Error())
return nil, sdk.ErrInternal(err.Error())
}
return res, nil
return bz, nil
}
func queryBlockLogsBloom(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
num, err := strconv.ParseInt(path[1], 10, 64)
if err != nil {
panic("could not unmarshall block number: " + err.Error())
return nil, sdk.ErrInternal(fmt.Sprintf("could not unmarshall block number: %s", err.Error()))
}
bloom := keeper.GetBlockBloomMapping(ctx, num)
bRes := types.QueryBloomFilter{Bloom: bloom}
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
bloom, err := keeper.GetBlockBloomMapping(ctx, num)
if err != nil {
panic("could not marshal result to JSON: " + err.Error())
return nil, sdk.ErrInternal(fmt.Sprintf("failed to get block bloom mapping: %s", err.Error()))
}
return res, nil
res := types.QueryBloomFilter{Bloom: bloom}
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
if err != nil {
return nil, sdk.ErrInternal(err.Error())
}
return bz, nil
}
func queryTxLogs(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
txHash := ethcmn.HexToHash(path[1])
logs := keeper.GetLogs(ctx, txHash)
bRes := types.QueryETHLogs{Logs: logs}
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
logs, err := keeper.GetLogs(ctx, txHash)
if err != nil {
panic("could not marshal result to JSON: " + err.Error())
return nil, sdk.ErrInternal(err.Error())
}
return res, nil
res := types.QueryETHLogs{Logs: logs}
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
if err != nil {
return nil, sdk.ErrInternal(err.Error())
}
return bz, nil
}
func queryLogs(ctx sdk.Context, keeper Keeper) ([]byte, sdk.Error) {
logs := keeper.Logs(ctx)
logs := keeper.AllLogs(ctx)
lRes := types.QueryETHLogs{Logs: logs}
l, err := codec.MarshalJSONIndent(keeper.cdc, lRes)
res := types.QueryETHLogs{Logs: logs}
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
if err != nil {
panic("could not marshal result to JSON: " + err.Error())
return nil, sdk.ErrInternal(err.Error())
}
return l, nil
return bz, nil
}
func queryAccount(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
addr := ethcmn.HexToAddress(path[1])
so := keeper.GetOrNewStateObject(ctx, addr)
lRes := types.QueryResAccount{
res := types.QueryResAccount{
Balance: utils.MarshalBigInt(so.Balance()),
CodeHash: so.CodeHash(),
Nonce: so.Nonce(),
}
l, err := codec.MarshalJSONIndent(keeper.cdc, lRes)
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
if err != nil {
panic("could not marshal result to JSON: " + err.Error())
return nil, sdk.ErrInternal(err.Error())
}
return l, nil
return bz, nil
}

View File

@ -2,19 +2,20 @@ package evm
import (
"encoding/json"
"math/big"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/ethermint/x/evm/client/cli"
"github.com/cosmos/ethermint/x/evm/keeper"
"github.com/cosmos/ethermint/x/evm/types"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
abci "github.com/tendermint/tendermint/abci/types"
)
var _ module.AppModuleBasic = AppModuleBasic{}
@ -107,33 +108,13 @@ func (am AppModule) NewQuerierHandler() sdk.Querier {
}
// BeginBlock function for module at start of each block
func (am AppModule) BeginBlock(ctx sdk.Context, bl abci.RequestBeginBlock) {
// Consider removing this when using evm as module without web3 API
bloom := ethtypes.BytesToBloom(am.keeper.Bloom.Bytes())
am.keeper.SetBlockBloomMapping(ctx, bloom, bl.Header.GetHeight()-1)
am.keeper.SetBlockHashMapping(ctx, bl.Header.LastBlockId.GetHash(), bl.Header.GetHeight()-1)
am.keeper.Bloom = big.NewInt(0)
am.keeper.TxCount.Reset()
func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
BeginBlock(am.keeper, ctx, req)
}
// EndBlock function for module at end of block
func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
// Gas costs are handled within msg handler so costs should be ignored
ebCtx := ctx.WithBlockGasMeter(sdk.NewInfiniteGasMeter())
// Update account balances before committing other parts of state
am.keeper.CommitStateDB.UpdateAccounts()
// Commit state objects to KV store
_, err := am.keeper.CommitStateDB.WithContext(ebCtx).Commit(true)
if err != nil {
panic(err)
}
// Clear accounts cache after account data has been committed
am.keeper.CommitStateDB.ClearStateObjects()
return []abci.ValidatorUpdate{}
func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate {
return EndBlock(am.keeper, ctx, req)
}
// InitGenesis instantiates the genesis state

View File

@ -9,14 +9,13 @@ var ModuleCdc = codec.New()
func init() {
cdc := codec.New()
codec.RegisterCrypto(cdc)
ModuleCdc = cdc.Seal()
}
// RegisterCodec registers concrete types and interfaces on the given codec.
func RegisterCodec(cdc *codec.Codec) {
cdc.RegisterConcrete(&EthereumTxMsg{}, "ethermint/MsgEthereumTx", nil)
cdc.RegisterConcrete(&EmintMsg{}, "ethermint/MsgEmint", nil)
cdc.RegisterConcrete(MsgEthereumTx{}, "ethermint/MsgEthereumTx", nil)
cdc.RegisterConcrete(MsgEthermint{}, "ethermint/MsgEthermint", nil)
cdc.RegisterConcrete(EncodableTxData{}, "ethermint/EncodableTxData", nil)
}

View File

@ -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{}
}

View File

@ -4,21 +4,22 @@ import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/ethermint/types"
ethcmn "github.com/ethereum/go-ethereum/common"
)
var (
_ sdk.Msg = EmintMsg{}
_ sdk.Msg = MsgEthermint{}
)
const (
// TypeEmintMsg defines the type string of Emint message
TypeEmintMsg = "emint_tx"
// TypeMsgEthermint defines the type string of Ethermint message
TypeMsgEthermint = "ethermint"
)
// EmintMsg implements a cosmos equivalent structure for Ethereum transactions
type EmintMsg struct {
// MsgEthermint implements a cosmos equivalent structure for Ethereum transactions
type MsgEthermint struct {
AccountNonce uint64 `json:"nonce"`
Price sdk.Int `json:"gasPrice"`
GasLimit uint64 `json:"gas"`
@ -30,12 +31,12 @@ type EmintMsg struct {
From sdk.AccAddress `json:"from"`
}
// NewEmintMsg returns a reference to a new Ethermint transaction
func NewEmintMsg(
// NewMsgEthermint returns a reference to a new Ethermint transaction
func NewMsgEthermint(
nonce uint64, to *sdk.AccAddress, amount sdk.Int,
gasLimit uint64, gasPrice sdk.Int, payload []byte, from sdk.AccAddress,
) EmintMsg {
return EmintMsg{
) MsgEthermint {
return MsgEthermint{
AccountNonce: nonce,
Price: gasPrice,
GasLimit: gasLimit,
@ -47,18 +48,18 @@ func NewEmintMsg(
}
// Route should return the name of the module
func (msg EmintMsg) Route() string { return RouterKey }
func (msg MsgEthermint) Route() string { return RouterKey }
// Type returns the action of the message
func (msg EmintMsg) Type() string { return TypeEmintMsg }
func (msg MsgEthermint) Type() string { return TypeMsgEthermint }
// GetSignBytes encodes the message for signing
func (msg EmintMsg) GetSignBytes() []byte {
func (msg MsgEthermint) GetSignBytes() []byte {
return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg))
}
// ValidateBasic runs stateless checks on the message
func (msg EmintMsg) ValidateBasic() sdk.Error {
func (msg MsgEthermint) ValidateBasic() sdk.Error {
if msg.Price.Sign() != 1 {
return types.ErrInvalidValue(fmt.Sprintf("Price must be positive: %x", msg.Price))
}
@ -72,13 +73,13 @@ func (msg EmintMsg) ValidateBasic() sdk.Error {
}
// GetSigners defines whose signature is required
func (msg EmintMsg) GetSigners() []sdk.AccAddress {
func (msg MsgEthermint) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.From}
}
// To returns the recipient address of the transaction. It returns nil if the
// transaction is a contract creation.
func (msg EmintMsg) To() *ethcmn.Address {
func (msg MsgEthermint) To() *ethcmn.Address {
if msg.Recipient == nil {
return nil
}

View File

@ -8,19 +8,19 @@ import (
"github.com/tendermint/tendermint/crypto/secp256k1"
)
func TestEmintMsg(t *testing.T) {
func TestMsgEthermint(t *testing.T) {
addr := newSdkAddress()
fromAddr := newSdkAddress()
msg := NewEmintMsg(0, &addr, sdk.NewInt(1), 100000, sdk.NewInt(2), []byte("test"), fromAddr)
msg := NewMsgEthermint(0, &addr, sdk.NewInt(1), 100000, sdk.NewInt(2), []byte("test"), fromAddr)
require.NotNil(t, msg)
require.Equal(t, msg.Recipient, &addr)
require.Equal(t, msg.Route(), RouterKey)
require.Equal(t, msg.Type(), TypeEmintMsg)
require.Equal(t, msg.Type(), TypeMsgEthermint)
}
func TestEmintMsgValidation(t *testing.T) {
func TestMsgEthermintValidation(t *testing.T) {
testCases := []struct {
nonce uint64
to *sdk.AccAddress
@ -38,7 +38,7 @@ func TestEmintMsgValidation(t *testing.T) {
}
for i, tc := range testCases {
msg := NewEmintMsg(tc.nonce, tc.to, tc.amount, tc.gasLimit, tc.gasPrice, tc.payload, tc.from)
msg := NewMsgEthermint(tc.nonce, tc.to, tc.amount, tc.gasLimit, tc.gasPrice, tc.payload, tc.from)
if tc.expectPass {
require.Nil(t, msg.ValidateBasic(), "test: %v", i)
@ -52,13 +52,13 @@ func TestEmintEncodingAndDecoding(t *testing.T) {
addr := newSdkAddress()
fromAddr := newSdkAddress()
msg := NewEmintMsg(0, &addr, sdk.NewInt(1), 100000, sdk.NewInt(2), []byte("test"), fromAddr)
msg := NewMsgEthermint(0, &addr, sdk.NewInt(1), 100000, sdk.NewInt(2), []byte("test"), fromAddr)
raw, err := cdc.MarshalBinaryBare(msg)
raw, err := ModuleCdc.MarshalBinaryBare(msg)
require.NoError(t, err)
var msg2 EmintMsg
err = cdc.UnmarshalBinaryBare(raw, &msg2)
var msg2 MsgEthermint
err = ModuleCdc.UnmarshalBinaryBare(raw, &msg2)
require.NoError(t, err)
require.Equal(t, msg.AccountNonce, msg2.AccountNonce)

11
x/evm/types/events.go Normal file
View File

@ -0,0 +1,11 @@
package types
// Evm module events
const (
EventTypeEthermint = TypeMsgEthermint
EventTypeEthereumTx = TypeMsgEthereumTx
AttributeKeyContractAddress = "contract"
AttributeKeyRecipient = "recipient"
AttributeValueCategory = ModuleName
)

View 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)
}

View File

@ -1,7 +1,7 @@
package types
import (
"fmt"
"errors"
"math/big"
"github.com/cosmos/ethermint/types"
@ -28,10 +28,10 @@ type (
func ValidateGenesis(data GenesisState) error {
for _, acct := range data.Accounts {
if len(acct.Address.Bytes()) == 0 {
return fmt.Errorf("invalid GenesisAccount Error: Missing Address")
return errors.New("invalid GenesisAccount: address cannot be empty")
}
if acct.Balance == nil {
return fmt.Errorf("invalid GenesisAccount Error: Missing Balance")
return errors.New("invalid GenesisAccount: balance cannot be empty")
}
}
return nil

View File

@ -4,13 +4,24 @@ const (
// ModuleName string name of module
ModuleName = "evm"
// EvmStoreKey key for ethereum storage data
EvmStoreKey = "evmstore"
// EvmCodeKey key for ethereum code data
EvmCodeKey = "evmcode"
// EvmBlockKey key for ethereum block data
EvmBlockKey = "evmblock"
// StoreKey key for ethereum storage data (StateDB)
StoreKey = ModuleName
// CodeKey key for ethereum code data
CodeKey = ModuleName + "code"
// BlockKey key
BlockKey = ModuleName + "block"
// RouterKey uses module name for routing
RouterKey = ModuleName
)
var bloomPrefix = []byte("bloom")
var logsPrefix = []byte("logs")
func BloomKey(key []byte) []byte {
return append(bloomPrefix, key...)
}
func LogsKey(key []byte) []byte {
return append(logsPrefix, key...)
}

View File

@ -19,21 +19,20 @@ import (
)
var (
_ sdk.Msg = EthereumTxMsg{}
_ sdk.Tx = EthereumTxMsg{}
_ sdk.Msg = MsgEthereumTx{}
_ sdk.Tx = MsgEthereumTx{}
)
var big8 = big.NewInt(8)
// message type and route constants
const (
TypeEthereumTxMsg = "ethereum_tx"
RouteEthereumTxMsg = RouterKey
TypeMsgEthereumTx = "ethereum"
)
// EthereumTxMsg encapsulates an Ethereum transaction as an SDK message.
// MsgEthereumTx encapsulates an Ethereum transaction as an SDK message.
type (
EthereumTxMsg struct {
MsgEthereumTx struct {
Data TxData
// caches
@ -69,28 +68,28 @@ type (
}
)
// NewEthereumTxMsg returns a reference to a new Ethereum transaction message.
func NewEthereumTxMsg(
// NewMsgEthereumTx returns a reference to a new Ethereum transaction message.
func NewMsgEthereumTx(
nonce uint64, to *ethcmn.Address, amount *big.Int,
gasLimit uint64, gasPrice *big.Int, payload []byte,
) *EthereumTxMsg {
) MsgEthereumTx {
return newEthereumTxMsg(nonce, to, amount, gasLimit, gasPrice, payload)
return newMsgEthereumTx(nonce, to, amount, gasLimit, gasPrice, payload)
}
// NewEthereumTxMsgContract returns a reference to a new Ethereum transaction
// NewMsgEthereumTxContract returns a reference to a new Ethereum transaction
// message designated for contract creation.
func NewEthereumTxMsgContract(
func NewMsgEthereumTxContract(
nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, payload []byte,
) *EthereumTxMsg {
) MsgEthereumTx {
return newEthereumTxMsg(nonce, nil, amount, gasLimit, gasPrice, payload)
return newMsgEthereumTx(nonce, nil, amount, gasLimit, gasPrice, payload)
}
func newEthereumTxMsg(
func newMsgEthereumTx(
nonce uint64, to *ethcmn.Address, amount *big.Int,
gasLimit uint64, gasPrice *big.Int, payload []byte,
) *EthereumTxMsg {
) MsgEthereumTx {
if len(payload) > 0 {
payload = ethcmn.CopyBytes(payload)
@ -115,20 +114,20 @@ func newEthereumTxMsg(
txData.Price.Set(gasPrice)
}
return &EthereumTxMsg{Data: txData}
return MsgEthereumTx{Data: txData}
}
// Route returns the route value of an EthereumTxMsg.
func (msg EthereumTxMsg) Route() string { return RouteEthereumTxMsg }
// Route returns the route value of an MsgEthereumTx.
func (msg MsgEthereumTx) Route() string { return RouterKey }
// Type returns the type value of an EthereumTxMsg.
func (msg EthereumTxMsg) Type() string { return TypeEthereumTxMsg }
// Type returns the type value of an MsgEthereumTx.
func (msg MsgEthereumTx) Type() string { return TypeMsgEthereumTx }
// ValidateBasic implements the sdk.Msg interface. It performs basic validation
// checks of a Transaction. If returns an sdk.Error if validation fails.
func (msg EthereumTxMsg) ValidateBasic() sdk.Error {
// checks of a Transaction. If returns an error if validation fails.
func (msg MsgEthereumTx) ValidateBasic() sdk.Error {
if msg.Data.Price.Sign() != 1 {
return types.ErrInvalidValue(fmt.Sprintf("Price must be positive: %x", msg.Data.Price))
return types.ErrInvalidValue(fmt.Sprintf("price must be positive: %x", msg.Data.Price))
}
// Amount can be 0
@ -141,16 +140,12 @@ func (msg EthereumTxMsg) ValidateBasic() sdk.Error {
// To returns the recipient address of the transaction. It returns nil if the
// transaction is a contract creation.
func (msg EthereumTxMsg) To() *ethcmn.Address {
if msg.Data.Recipient == nil {
return nil
}
func (msg MsgEthereumTx) To() *ethcmn.Address {
return msg.Data.Recipient
}
// GetMsgs returns a single EthereumTxMsg as an sdk.Msg.
func (msg EthereumTxMsg) GetMsgs() []sdk.Msg {
// GetMsgs returns a single MsgEthereumTx as an sdk.Msg.
func (msg MsgEthereumTx) GetMsgs() []sdk.Msg {
return []sdk.Msg{msg}
}
@ -159,7 +154,7 @@ func (msg EthereumTxMsg) GetMsgs() []sdk.Msg {
//
// NOTE: This method cannot be used as a chain ID is needed to recover the signer
// from the signature. Use 'VerifySig' instead.
func (msg EthereumTxMsg) GetSigners() []sdk.AccAddress {
func (msg MsgEthereumTx) GetSigners() []sdk.AccAddress {
panic("must use 'VerifySig' with a chain ID to get the signer")
}
@ -168,13 +163,13 @@ func (msg EthereumTxMsg) GetSigners() []sdk.AccAddress {
//
// NOTE: This method cannot be used as a chain ID is needed to create valid bytes
// to sign over. Use 'RLPSignBytes' instead.
func (msg EthereumTxMsg) GetSignBytes() []byte {
func (msg MsgEthereumTx) GetSignBytes() []byte {
panic("must use 'RLPSignBytes' with a chain ID to get the valid bytes to sign")
}
// RLPSignBytes returns the RLP hash of an Ethereum transaction message with a
// given chainID used for signing.
func (msg EthereumTxMsg) RLPSignBytes(chainID *big.Int) ethcmn.Hash {
func (msg MsgEthereumTx) RLPSignBytes(chainID *big.Int) ethcmn.Hash {
return rlpHash([]interface{}{
msg.Data.AccountNonce,
msg.Data.Price,
@ -187,12 +182,12 @@ func (msg EthereumTxMsg) RLPSignBytes(chainID *big.Int) ethcmn.Hash {
}
// EncodeRLP implements the rlp.Encoder interface.
func (msg *EthereumTxMsg) EncodeRLP(w io.Writer) error {
func (msg *MsgEthereumTx) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, &msg.Data)
}
// DecodeRLP implements the rlp.Decoder interface.
func (msg *EthereumTxMsg) DecodeRLP(s *rlp.Stream) error {
func (msg *MsgEthereumTx) DecodeRLP(s *rlp.Stream) error {
_, size, _ := s.Kind()
err := s.Decode(&msg.Data)
@ -204,7 +199,7 @@ func (msg *EthereumTxMsg) DecodeRLP(s *rlp.Stream) error {
}
// Hash hashes the RLP encoding of a transaction.
func (msg *EthereumTxMsg) Hash() ethcmn.Hash {
func (msg *MsgEthereumTx) Hash() ethcmn.Hash {
if hash := msg.hash.Load(); hash != nil {
return hash.(ethcmn.Hash)
}
@ -219,7 +214,7 @@ func (msg *EthereumTxMsg) Hash() ethcmn.Hash {
// takes a private key and chainID to sign an Ethereum transaction according to
// EIP155 standard. It mutates the transaction as it populates the V, R, S
// fields of the Transaction's Signature.
func (msg *EthereumTxMsg) Sign(chainID *big.Int, priv *ecdsa.PrivateKey) {
func (msg *MsgEthereumTx) Sign(chainID *big.Int, priv *ecdsa.PrivateKey) {
txHash := msg.RLPSignBytes(chainID)
sig, err := ethcrypto.Sign(txHash[:], priv)
@ -252,7 +247,7 @@ func (msg *EthereumTxMsg) Sign(chainID *big.Int, priv *ecdsa.PrivateKey) {
// VerifySig attempts to verify a Transaction's signature for a given chainID.
// A derived address is returned upon success or an error if recovery fails.
func (msg *EthereumTxMsg) VerifySig(chainID *big.Int) (ethcmn.Address, error) {
func (msg *MsgEthereumTx) VerifySig(chainID *big.Int) (ethcmn.Address, error) {
signer := ethtypes.NewEIP155Signer(chainID)
if sc := msg.from.Load(); sc != nil {
@ -284,19 +279,19 @@ func (msg *EthereumTxMsg) VerifySig(chainID *big.Int) (ethcmn.Address, error) {
}
// Cost returns amount + gasprice * gaslimit.
func (msg EthereumTxMsg) Cost() *big.Int {
func (msg MsgEthereumTx) Cost() *big.Int {
total := msg.Fee()
total.Add(total, msg.Data.Amount)
return total
}
// Fee returns gasprice * gaslimit.
func (msg EthereumTxMsg) Fee() *big.Int {
func (msg MsgEthereumTx) Fee() *big.Int {
return new(big.Int).Mul(msg.Data.Price, new(big.Int).SetUint64(msg.Data.GasLimit))
}
// ChainID returns which chain id this transaction was signed for (if at all)
func (msg *EthereumTxMsg) ChainID() *big.Int {
func (msg *MsgEthereumTx) ChainID() *big.Int {
return deriveChainID(msg.Data.V)
}
@ -317,7 +312,7 @@ func deriveChainID(v *big.Int) *big.Int {
// Auxiliary
// TxDecoder returns an sdk.TxDecoder that can decode both auth.StdTx and
// EthereumTxMsg transactions.
// MsgEthereumTx transactions.
func TxDecoder(cdc *codec.Codec) sdk.TxDecoder {
return func(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx sdk.Tx
@ -328,7 +323,6 @@ func TxDecoder(cdc *codec.Codec) sdk.TxDecoder {
err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
if err != nil {
fmt.Println(err.Error())
return nil, sdk.ErrTxDecode("failed to decode tx").TraceSDK(err.Error())
}

View File

@ -1,23 +1,11 @@
package types
import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/ethermint/utils"
ethcmn "github.com/ethereum/go-ethereum/common"
)
var cdc = codec.New()
func init() {
RegisterAmino(cdc)
}
// RegisterAmino registers all crypto related types in the given (amino) codec.
func RegisterAmino(cdc *codec.Codec) {
cdc.RegisterConcrete(EncodableTxData{}, "ethermint/EncodedMessage", nil)
}
// EncodableTxData implements the Ethereum transaction data structure. It is used
// solely as intended in Ethereum abiding by the protocol.
type EncodableTxData struct {
@ -38,12 +26,12 @@ type EncodableTxData struct {
}
func marshalAmino(td EncodableTxData) (string, error) {
bz, err := cdc.MarshalBinaryBare(td)
bz, err := ModuleCdc.MarshalBinaryBare(td)
return string(bz), err
}
func unmarshalAmino(td *EncodableTxData, text string) error {
return cdc.UnmarshalBinaryBare([]byte(text), td)
return ModuleCdc.UnmarshalBinaryBare([]byte(text), td)
}
// MarshalAmino defines custom encoding scheme for TxData

View File

@ -18,17 +18,17 @@ import (
func TestMsgEthereumTx(t *testing.T) {
addr := GenerateEthAddress()
msg1 := NewEthereumTxMsg(0, &addr, nil, 100000, nil, []byte("test"))
msg1 := NewMsgEthereumTx(0, &addr, nil, 100000, nil, []byte("test"))
require.NotNil(t, msg1)
require.Equal(t, *msg1.Data.Recipient, addr)
msg2 := NewEthereumTxMsgContract(0, nil, 100000, nil, []byte("test"))
msg2 := NewMsgEthereumTxContract(0, nil, 100000, nil, []byte("test"))
require.NotNil(t, msg2)
require.Nil(t, msg2.Data.Recipient)
msg3 := NewEthereumTxMsg(0, &addr, nil, 100000, nil, []byte("test"))
require.Equal(t, msg3.Route(), RouteEthereumTxMsg)
require.Equal(t, msg3.Type(), TypeEthereumTxMsg)
msg3 := NewMsgEthereumTx(0, &addr, nil, 100000, nil, []byte("test"))
require.Equal(t, msg3.Route(), RouterKey)
require.Equal(t, msg3.Type(), TypeMsgEthereumTx)
require.Panics(t, func() { msg3.GetSigners() })
require.Panics(t, func() { msg3.GetSignBytes() })
}
@ -49,7 +49,7 @@ func TestMsgEthereumTxValidation(t *testing.T) {
}
for i, tc := range testCases {
msg := NewEthereumTxMsg(tc.nonce, &tc.to, tc.amount, tc.gasLimit, tc.gasPrice, tc.payload)
msg := NewMsgEthereumTx(tc.nonce, &tc.to, tc.amount, tc.gasLimit, tc.gasPrice, tc.payload)
if tc.expectPass {
require.Nil(t, msg.ValidateBasic(), "test: %v", i)
@ -63,26 +63,26 @@ func TestMsgEthereumTxRLPSignBytes(t *testing.T) {
addr := ethcmn.BytesToAddress([]byte("test_address"))
chainID := big.NewInt(3)
msg := NewEthereumTxMsg(0, &addr, nil, 100000, nil, []byte("test"))
msg := NewMsgEthereumTx(0, &addr, nil, 100000, nil, []byte("test"))
hash := msg.RLPSignBytes(chainID)
require.Equal(t, "5BD30E35AD27449390B14C91E6BCFDCAADF8FE44EF33680E3BC200FC0DC083C7", fmt.Sprintf("%X", hash))
}
func TestMsgEthereumTxRLPEncode(t *testing.T) {
addr := ethcmn.BytesToAddress([]byte("test_address"))
msg := NewEthereumTxMsg(0, &addr, nil, 100000, nil, []byte("test"))
msg := NewMsgEthereumTx(0, &addr, nil, 100000, nil, []byte("test"))
raw, err := rlp.EncodeToBytes(msg)
raw, err := rlp.EncodeToBytes(&msg)
require.NoError(t, err)
require.Equal(t, ethcmn.FromHex("E48080830186A0940000000000000000746573745F61646472657373808474657374808080"), raw)
}
func TestMsgEthereumTxRLPDecode(t *testing.T) {
var msg EthereumTxMsg
var msg MsgEthereumTx
raw := ethcmn.FromHex("E48080830186A0940000000000000000746573745F61646472657373808474657374808080")
addr := ethcmn.BytesToAddress([]byte("test_address"))
expectedMsg := NewEthereumTxMsg(0, &addr, nil, 100000, nil, []byte("test"))
expectedMsg := NewMsgEthereumTx(0, &addr, nil, 100000, nil, []byte("test"))
err := rlp.Decode(bytes.NewReader(raw), &msg)
require.NoError(t, err)
@ -91,7 +91,7 @@ func TestMsgEthereumTxRLPDecode(t *testing.T) {
func TestMsgEthereumTxHash(t *testing.T) {
addr := ethcmn.BytesToAddress([]byte("test_address"))
msg := NewEthereumTxMsg(0, &addr, nil, 100000, nil, []byte("test"))
msg := NewMsgEthereumTx(0, &addr, nil, 100000, nil, []byte("test"))
hash := msg.Hash()
require.Equal(t, "E2AA2E68E7586AE9700F1D3D643330866B6AC2B6CA4C804F7C85ECB11D0B0B29", fmt.Sprintf("%X", hash))
@ -106,7 +106,7 @@ func TestMsgEthereumTxSig(t *testing.T) {
addr2 := ethcmn.BytesToAddress(priv2.PubKey().Address().Bytes())
// require valid signature passes validation
msg := NewEthereumTxMsg(0, &addr1, nil, 100000, nil, []byte("test"))
msg := NewMsgEthereumTx(0, &addr1, nil, 100000, nil, []byte("test"))
msg.Sign(chainID, priv1.ToECDSA())
signer, err := msg.VerifySig(chainID)
@ -115,7 +115,7 @@ func TestMsgEthereumTxSig(t *testing.T) {
require.NotEqual(t, addr2, signer)
// require invalid chain ID fail validation
msg = NewEthereumTxMsg(0, &addr1, nil, 100000, nil, []byte("test"))
msg = NewMsgEthereumTx(0, &addr1, nil, 100000, nil, []byte("test"))
msg.Sign(chainID, priv1.ToECDSA())
signer, err = msg.VerifySig(big.NewInt(4))
@ -125,7 +125,7 @@ func TestMsgEthereumTxSig(t *testing.T) {
func TestMsgEthereumTxAmino(t *testing.T) {
addr := GenerateEthAddress()
msg := NewEthereumTxMsg(5, &addr, big.NewInt(1), 100000, big.NewInt(3), []byte("test"))
msg := NewMsgEthereumTx(5, &addr, big.NewInt(1), 100000, big.NewInt(3), []byte("test"))
msg.Data.V = big.NewInt(1)
msg.Data.R = big.NewInt(2)
@ -134,7 +134,7 @@ func TestMsgEthereumTxAmino(t *testing.T) {
raw, err := ModuleCdc.MarshalBinaryBare(msg)
require.NoError(t, err)
var msg2 EthereumTxMsg
var msg2 MsgEthereumTx
err = ModuleCdc.UnmarshalBinaryBare(raw, &msg2)
require.NoError(t, err)

View File

@ -73,20 +73,21 @@ type (
}
)
func newObject(db *CommitStateDB, accProto authexported.Account) *stateObject {
acc, ok := accProto.(*types.Account)
func newStateObject(db *CommitStateDB, accProto authexported.Account) *stateObject {
ethermintAccount, ok := accProto.(*types.Account)
if !ok {
panic(fmt.Sprintf("invalid account type for state object: %T", accProto))
}
if acc.CodeHash == nil {
acc.CodeHash = emptyCodeHash
// set empty code hash
if ethermintAccount.CodeHash == nil {
ethermintAccount.CodeHash = emptyCodeHash
}
return &stateObject{
stateDB: db,
account: acc,
address: ethcmn.BytesToAddress(acc.Address.Bytes()),
account: ethermintAccount,
address: ethcmn.BytesToAddress(ethermintAccount.GetAddress().Bytes()),
originStorage: make(types.Storage),
dirtyStorage: make(types.Storage),
}
@ -188,7 +189,7 @@ func (so *stateObject) setBalance(amount sdk.Int) {
so.account.SetBalance(amount)
}
// SetNonce sets the state object's nonce (sequence number).
// SetNonce sets the state object's nonce (i.e sequence number of the account).
func (so *stateObject) SetNonce(nonce uint64) {
so.stateDB.journal.append(nonceChange{
account: &so.address,
@ -199,6 +200,9 @@ func (so *stateObject) SetNonce(nonce uint64) {
}
func (so *stateObject) setNonce(nonce uint64) {
if so.account == nil {
panic("state object account is empty")
}
so.account.Sequence = nonce
}
@ -216,7 +220,7 @@ func (so *stateObject) markSuicided() {
// commitState commits all dirty storage to a KVStore.
func (so *stateObject) commitState() {
ctx := so.stateDB.ctx
store := ctx.KVStore(so.stateDB.storageKey)
store := ctx.KVStore(so.stateDB.storeKey)
for key, value := range so.dirtyStorage {
delete(so.dirtyStorage, key)
@ -258,22 +262,32 @@ func (so stateObject) Address() ethcmn.Address {
// Balance returns the state object's current balance.
func (so *stateObject) Balance() *big.Int {
return so.account.Balance().BigInt()
balance := so.account.Balance().BigInt()
if balance == nil {
return zeroBalance
}
return balance
}
// CodeHash returns the state object's code hash.
func (so *stateObject) CodeHash() []byte {
if so.account == nil || len(so.account.CodeHash) == 0 {
return emptyCodeHash
}
return so.account.CodeHash
}
// Nonce returns the state object's current nonce (sequence number).
func (so *stateObject) Nonce() uint64 {
if so.account == nil {
return 0
}
return so.account.Sequence
}
// Code returns the contract code associated with this object, if any.
func (so *stateObject) Code(_ ethstate.Database) []byte {
if so.code != nil {
if len(so.code) > 0 {
return so.code
}
@ -286,10 +300,9 @@ func (so *stateObject) Code(_ ethstate.Database) []byte {
code := store.Get(so.CodeHash())
if len(code) == 0 {
so.setError(fmt.Errorf("failed to get code hash %x for address: %x", so.CodeHash(), so.Address()))
so.setError(fmt.Errorf("failed to get code hash %x for address %s", so.CodeHash(), so.Address().String()))
}
so.code = code
return code
}
@ -321,7 +334,7 @@ func (so *stateObject) GetCommittedState(_ ethstate.Database, key ethcmn.Hash) e
// otherwise load the value from the KVStore
ctx := so.stateDB.ctx
store := ctx.KVStore(so.stateDB.storageKey)
store := ctx.KVStore(so.stateDB.storeKey)
rawValue := store.Get(prefixKey.Bytes())
if len(rawValue) > 0 {
@ -341,7 +354,7 @@ func (so *stateObject) GetCommittedState(_ ethstate.Database, key ethcmn.Hash) e
func (so *stateObject) ReturnGas(gas *big.Int) {}
func (so *stateObject) deepCopy(db *CommitStateDB) *stateObject {
newStateObj := newObject(db, so.account)
newStateObj := newStateObject(db, so.account)
newStateObj.code = so.code
newStateObj.dirtyStorage = so.dirtyStorage.Copy()
@ -355,9 +368,11 @@ func (so *stateObject) deepCopy(db *CommitStateDB) *stateObject {
// empty returns whether the account is considered empty.
func (so *stateObject) empty() bool {
return so.account.Sequence == 0 &&
so.account.Balance().Sign() == 0 &&
bytes.Equal(so.account.CodeHash, emptyCodeHash)
return so.account == nil ||
(so.account != nil &&
so.account.Sequence == 0 &&
so.account.Balance().Sign() == 0 &&
bytes.Equal(so.account.CodeHash, emptyCodeHash))
}
// EncodeRLP implements rlp.Encoder.

View File

@ -1,6 +1,7 @@
package types
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
@ -27,14 +28,24 @@ type StateTransition struct {
Simulate bool
}
// ReturnData represents what's returned from a transition
type ReturnData struct {
Logs []*ethtypes.Log
Bloom *big.Int
Result *sdk.Result
}
// TODO: move to keeper
// TransitionCSDB performs an evm state transition from a transaction
func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*big.Int, sdk.Result) {
// TODO: update godoc, it doesn't explain what it does in depth.
func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*ReturnData, error) {
returnData := new(ReturnData)
contractCreation := st.Recipient == nil
cost, err := core.IntrinsicGas(st.Payload, contractCreation, true)
if err != nil {
return nil, sdk.ErrOutOfGas("invalid intrinsic gas for transaction").Result()
return nil, fmt.Errorf("invalid intrinsic gas for transaction: %s", err.Error())
}
// This gas limit the the transaction gas limit with intrinsic gas subtracted
@ -68,21 +79,20 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*big.Int, sdk.Result)
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
Origin: st.Sender,
Coinbase: common.Address{},
Coinbase: common.Address{}, // TODO: explain why this is empty
BlockNumber: big.NewInt(ctx.BlockHeight()),
Time: big.NewInt(ctx.BlockHeader().Time.Unix()),
Difficulty: big.NewInt(0x30000), // unused
Difficulty: big.NewInt(0), // unused. Only required in PoW context
GasLimit: gasLimit,
GasPrice: ctx.MinGasPrices().AmountOf(emint.DenomDefault).Int,
}
vmenv := vm.NewEVM(context, csdb, GenerateChainConfig(st.ChainID), vm.Config{})
evm := vm.NewEVM(context, csdb, GenerateChainConfig(st.ChainID), vm.Config{})
var (
ret []byte
leftOverGas uint64
addr common.Address
vmerr error
senderRef = vm.AccountRef(st.Sender)
)
@ -91,52 +101,77 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*big.Int, sdk.Result)
// Set nonce of sender account before evm state transition for usage in generating Create address
st.Csdb.SetNonce(st.Sender, st.AccountNonce)
if contractCreation {
ret, addr, leftOverGas, vmerr = vmenv.Create(senderRef, st.Payload, gasLimit, st.Amount)
} else {
switch contractCreation {
case true:
ret, addr, leftOverGas, err = evm.Create(senderRef, st.Payload, gasLimit, st.Amount)
default:
// Increment the nonce for the next transaction (just for evm state transition)
csdb.SetNonce(st.Sender, csdb.GetNonce(st.Sender)+1)
ret, leftOverGas, vmerr = vmenv.Call(senderRef, *st.Recipient, st.Payload, gasLimit, st.Amount)
ret, leftOverGas, err = evm.Call(senderRef, *st.Recipient, st.Payload, gasLimit, st.Amount)
}
if err != nil {
return nil, err
}
gasConsumed := gasLimit - leftOverGas
// Resets nonce to value pre state transition
st.Csdb.SetNonce(st.Sender, currentNonce)
// Generate bloom filter to be saved in tx receipt data
bloomInt := big.NewInt(0)
var bloomFilter ethtypes.Bloom
var logs []*ethtypes.Log
if st.THash != nil && !st.Simulate {
logs := csdb.GetLogs(*st.THash)
logs, err = csdb.GetLogs(*st.THash)
if err != nil {
return nil, err
}
bloomInt = ethtypes.LogsBloom(logs)
bloomFilter = ethtypes.BytesToBloom(bloomInt.Bytes())
}
// Encode all necessary data into slice of bytes to return in sdk result
returnData := EncodeReturnData(addr, bloomFilter, ret)
res := &ResultData{
Address: addr,
Bloom: bloomFilter,
Logs: logs,
Ret: ret,
}
resultData, err := EncodeResultData(res)
if err != nil {
return nil, err
}
// handle errors
if vmerr != nil {
res := emint.ErrVMExecution(vmerr.Error()).Result()
if vmerr == vm.ErrOutOfGas || vmerr == vm.ErrCodeStoreOutOfGas {
res = sdk.ErrOutOfGas("EVM execution went out of gas").Result()
if err != nil {
if err == vm.ErrOutOfGas || err == vm.ErrCodeStoreOutOfGas {
return nil, fmt.Errorf("evm execution went out of gas: %s", err.Error())
}
res.Data = returnData
// Consume gas before returning
ctx.GasMeter().ConsumeGas(gasLimit-leftOverGas, "EVM execution consumption")
return nil, res
ctx.GasMeter().ConsumeGas(gasConsumed, "EVM execution consumption")
return nil, err
}
// TODO: Refund unused gas here, if intended in future
if !st.Simulate {
// Finalise state if not a simulated transaction
st.Csdb.Finalise(true) // Change to depend on config
// TODO: change to depend on config
if err := st.Csdb.Finalise(true); err != nil {
return nil, err
}
}
// Consume gas from evm execution
// Out of gas check does not need to be done here since it is done within the EVM execution
ctx.WithGasMeter(currentGasMeter).GasMeter().ConsumeGas(gasLimit-leftOverGas, "EVM execution consumption")
ctx.WithGasMeter(currentGasMeter).GasMeter().ConsumeGas(gasConsumed, "EVM execution consumption")
return bloomInt, sdk.Result{Data: returnData, GasUsed: st.GasLimit - leftOverGas}
returnData.Logs = logs
returnData.Bloom = bloomInt
returnData.Result = &sdk.Result{Data: resultData, GasUsed: gasConsumed}
return returnData, nil
}

View File

@ -7,8 +7,6 @@ import (
"sync"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
emint "github.com/cosmos/ethermint/types"
ethcmn "github.com/ethereum/go-ethereum/common"
@ -41,9 +39,9 @@ type CommitStateDB struct {
// StateDB interface. Perhaps there is a better way.
ctx sdk.Context
ak auth.AccountKeeper
storageKey sdk.StoreKey
codeKey sdk.StoreKey
codeKey sdk.StoreKey
storeKey sdk.StoreKey // i.e storage key
accountKeeper AccountKeeper
// maps that hold 'live' objects, which will get modified while processing a
// state transition
@ -84,12 +82,14 @@ type CommitStateDB struct {
//
// CONTRACT: Stores used for state must be cache-wrapped as the ordering of the
// key/value space matters in determining the merkle root.
func NewCommitStateDB(ctx sdk.Context, ak auth.AccountKeeper, storageKey, codeKey sdk.StoreKey) *CommitStateDB {
func NewCommitStateDB(
ctx sdk.Context, codeKey, storeKey sdk.StoreKey, ak AccountKeeper,
) *CommitStateDB {
return &CommitStateDB{
ctx: ctx,
ak: ak,
storageKey: storageKey,
codeKey: codeKey,
storeKey: storeKey,
accountKeeper: ak,
stateObjects: make(map[ethcmn.Address]*stateObject),
stateObjectsDirty: make(map[ethcmn.Address]struct{}),
logs: make(map[ethcmn.Hash][]*ethtypes.Log),
@ -288,12 +288,28 @@ func (csdb *CommitStateDB) GetCommittedState(addr ethcmn.Address, hash ethcmn.Ha
}
// GetLogs returns the current logs for a given hash in the state.
func (csdb *CommitStateDB) GetLogs(hash ethcmn.Hash) []*ethtypes.Log {
return csdb.logs[hash]
func (csdb *CommitStateDB) GetLogs(hash ethcmn.Hash) ([]*ethtypes.Log, error) {
if csdb.logs[hash] != nil {
return csdb.logs[hash], nil
}
store := csdb.ctx.KVStore(csdb.storeKey)
encLogs := store.Get(LogsKey(hash[:]))
if len(encLogs) == 0 {
return []*ethtypes.Log{}, nil
}
logs, err := DecodeLogs(encLogs)
if err != nil {
return []*ethtypes.Log{}, err
}
return logs, nil
}
// Logs returns all the current logs in the state.
func (csdb *CommitStateDB) Logs() []*ethtypes.Log {
func (csdb *CommitStateDB) AllLogs() []*ethtypes.Log {
// nolint: prealloc
var logs []*ethtypes.Log
for _, lgs := range csdb.logs {
@ -364,7 +380,9 @@ func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (ethcmn.Hash, error)
}
// update the object in the KVStore
csdb.updateStateObject(so)
if err := csdb.updateStateObject(so); err != nil {
return ethcmn.Hash{}, err
}
}
delete(csdb.stateObjectsDirty, addr)
@ -379,7 +397,7 @@ func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (ethcmn.Hash, error)
// Finalise finalizes the state objects (accounts) state by setting their state,
// removing the csdb destructed objects and clearing the journal as well as the
// refunds.
func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) {
func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) error {
for addr := range csdb.journal.dirties {
so, exist := csdb.stateObjects[addr]
if !exist {
@ -401,7 +419,9 @@ func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) {
// Set all the dirty state storage items for the state object in the
// KVStore and finally set the account in the account mapper.
so.commitState()
csdb.updateStateObject(so)
if err := csdb.updateStateObject(so); err != nil {
return err
}
}
csdb.stateObjectsDirty[addr] = struct{}{}
@ -409,6 +429,7 @@ func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) {
// invalidate journal because reverting across transactions is not allowed
csdb.clearJournalAndRefund()
return nil
}
// IntermediateRoot returns the current root hash of the state. It is called in
@ -418,21 +439,24 @@ func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) {
// NOTE: The SDK has not concept or method of getting any intermediate merkle
// root as commitment of the merkle-ized tree doesn't happen until the
// BaseApps' EndBlocker.
func (csdb *CommitStateDB) IntermediateRoot(deleteEmptyObjects bool) ethcmn.Hash {
csdb.Finalise(deleteEmptyObjects)
func (csdb *CommitStateDB) IntermediateRoot(deleteEmptyObjects bool) (ethcmn.Hash, error) {
if err := csdb.Finalise(deleteEmptyObjects); err != nil {
return ethcmn.Hash{}, err
}
return ethcmn.Hash{}
return ethcmn.Hash{}, nil
}
// updateStateObject writes the given state object to the store.
func (csdb *CommitStateDB) updateStateObject(so *stateObject) {
csdb.ak.SetAccount(csdb.ctx, so.account)
func (csdb *CommitStateDB) updateStateObject(so *stateObject) error {
csdb.accountKeeper.SetAccount(csdb.ctx, so.account)
return nil
}
// deleteStateObject removes the given state object from the state store.
func (csdb *CommitStateDB) deleteStateObject(so *stateObject) {
so.deleted = true
csdb.ak.RemoveAccount(csdb.ctx, so.account)
csdb.accountKeeper.RemoveAccount(csdb.ctx, so.account)
}
// ----------------------------------------------------------------------------
@ -543,10 +567,10 @@ func (csdb *CommitStateDB) Reset(_ ethcmn.Hash) error {
// UpdateAccounts updates the nonce and coin balances of accounts
func (csdb *CommitStateDB) UpdateAccounts() {
for addr, so := range csdb.stateObjects {
currAcc := csdb.ak.GetAccount(csdb.ctx, sdk.AccAddress(addr.Bytes()))
currAcc := csdb.accountKeeper.GetAccount(csdb.ctx, sdk.AccAddress(addr.Bytes()))
emintAcc, ok := currAcc.(*emint.Account)
if ok {
if (so.Balance() != emintAcc.Balance().BigInt()) || (so.Nonce() != emintAcc.GetSequence()) {
if so.Balance() != emintAcc.Balance().BigInt() || so.Nonce() != emintAcc.GetSequence() {
// If queried account's balance or nonce are invalid, update the account pointer
so.account = emintAcc
}
@ -602,9 +626,9 @@ func (csdb *CommitStateDB) Copy() *CommitStateDB {
// copy all the basic fields, initialize the memory ones
state := &CommitStateDB{
ctx: csdb.ctx,
ak: csdb.ak,
storageKey: csdb.storageKey,
codeKey: csdb.codeKey,
storeKey: csdb.storeKey,
accountKeeper: csdb.accountKeeper,
stateObjects: make(map[ethcmn.Address]*stateObject, len(csdb.journal.dirties)),
stateObjectsDirty: make(map[ethcmn.Address]struct{}, len(csdb.journal.dirties)),
refund: csdb.refund,
@ -663,8 +687,9 @@ func (csdb *CommitStateDB) ForEachStorage(addr ethcmn.Address, cb func(key, valu
return nil
}
store := csdb.ctx.KVStore(csdb.storageKey)
store := csdb.ctx.KVStore(csdb.storeKey)
iter := sdk.KVStorePrefixIterator(store, so.Address().Bytes())
defer iter.Close()
for ; iter.Valid(); iter.Next() {
key := ethcmn.BytesToHash(iter.Key())
@ -678,7 +703,6 @@ func (csdb *CommitStateDB) ForEachStorage(addr ethcmn.Address, cb func(key, valu
cb(key, ethcmn.BytesToHash(value))
}
iter.Close()
return nil
}
@ -698,8 +722,9 @@ func (csdb *CommitStateDB) GetOrNewStateObject(addr ethcmn.Address) StateObject
func (csdb *CommitStateDB) createObject(addr ethcmn.Address) (newObj, prevObj *stateObject) {
prevObj = csdb.getStateObject(addr)
acc := csdb.ak.NewAccountWithAddress(csdb.ctx, sdk.AccAddress(addr.Bytes()))
newObj = newObject(csdb, acc)
acc := csdb.accountKeeper.NewAccountWithAddress(csdb.ctx, sdk.AccAddress(addr.Bytes()))
newObj = newStateObject(csdb, acc)
newObj.setNonce(0) // sets the object to dirty
if prevObj == nil {
@ -732,14 +757,14 @@ func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *sta
}
// otherwise, attempt to fetch the account from the account mapper
acc := csdb.ak.GetAccount(csdb.ctx, addr.Bytes())
acc := csdb.accountKeeper.GetAccount(csdb.ctx, addr.Bytes())
if acc == nil {
csdb.setError(fmt.Errorf("no account found for address: %X", addr.Bytes()))
return nil
}
// insert the state object into the live set
so := newObject(csdb, acc)
so := newStateObject(csdb, acc)
csdb.setStateObject(so)
return so
@ -748,3 +773,10 @@ func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *sta
func (csdb *CommitStateDB) setStateObject(so *stateObject) {
csdb.stateObjects[so.Address()] = so
}
// RawDump returns a raw state dump.
//
// TODO: Implement if we need it, especially for the RPC API.
func (csdb *CommitStateDB) RawDump() ethstate.Dump {
return ethstate.Dump{}
}

View File

@ -1,80 +1,40 @@
package types
package types_test
import (
"math/big"
"testing"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store"
sdkstore "github.com/cosmos/cosmos-sdk/store/types"
"github.com/stretchr/testify/suite"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/stretchr/testify/require"
ethcmn "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/cosmos/ethermint/types"
"github.com/cosmos/ethermint/app"
"github.com/cosmos/ethermint/x/evm/keeper"
abci "github.com/tendermint/tendermint/abci/types"
tmlog "github.com/tendermint/tendermint/libs/log"
dbm "github.com/tendermint/tm-db"
)
func newTestCodec() *codec.Codec {
cdc := codec.New()
// nolint: unused
type StateDBTestSuite struct {
suite.Suite
RegisterCodec(cdc)
types.RegisterCodec(cdc)
auth.RegisterCodec(cdc)
sdk.RegisterCodec(cdc)
codec.RegisterCrypto(cdc)
return cdc
ctx sdk.Context
querier sdk.Querier
app *app.EthermintApp
}
func setupStateDB() (*CommitStateDB, error) {
accKey := sdk.NewKVStoreKey("acc")
storageKey := sdk.NewKVStoreKey(EvmStoreKey)
codeKey := sdk.NewKVStoreKey(EvmCodeKey)
logger := tmlog.NewNopLogger()
func (suite *StateDBTestSuite) SetupTest() {
checkTx := false
db := dbm.NewMemDB()
// create logger, codec and root multi-store
cdc := newTestCodec()
cms := store.NewCommitMultiStore(db)
// The ParamsKeeper handles parameter storage for the application
keyParams := sdk.NewKVStoreKey(params.StoreKey)
tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey)
paramsKeeper := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace)
// Set specific supspaces
authSubspace := paramsKeeper.Subspace(auth.DefaultParamspace)
ak := auth.NewAccountKeeper(cdc, accKey, authSubspace, types.ProtoBaseAccount)
// mount stores
keys := []*sdk.KVStoreKey{accKey, storageKey, codeKey}
for _, key := range keys {
cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, nil)
}
cms.SetPruning(sdkstore.PruneNothing)
// load latest version (root)
if err := cms.LoadLatestVersion(); err != nil {
return nil, err
}
ms := cms.CacheMultiStore()
ctx := sdk.NewContext(ms, abci.Header{}, false, logger)
return NewCommitStateDB(ctx, ak, storageKey, codeKey), nil
suite.app = app.Setup(checkTx)
suite.ctx = suite.app.BaseApp.NewContext(checkTx, abci.Header{Height: 1})
suite.querier = keeper.NewQuerier(suite.app.EvmKeeper)
}
func TestBloomFilter(t *testing.T) {
stateDB, err := setupStateDB()
require.NoError(t, err)
func (suite *StateDBTestSuite) TestBloomFilter() {
stateDB := suite.app.EvmKeeper.CommitStateDB
// Prepare db for logs
tHash := ethcmn.BytesToHash([]byte{0x1})
@ -87,14 +47,15 @@ func TestBloomFilter(t *testing.T) {
stateDB.AddLog(&log)
// Get log from db
logs := stateDB.GetLogs(tHash)
require.Equal(t, len(logs), 1)
logs, err := stateDB.GetLogs(tHash)
suite.Require().NoError(err)
suite.Require().Equal(len(logs), 1)
// get logs bloom from the log
bloomInt := ethtypes.LogsBloom(logs)
bloomFilter := ethtypes.BytesToBloom(bloomInt.Bytes())
// Check to make sure bloom filter will succeed on
require.True(t, ethtypes.BloomLookup(bloomFilter, contractAddress))
require.False(t, ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2))))
suite.Require().True(ethtypes.BloomLookup(bloomFilter, contractAddress))
suite.Require().False(ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2))))
}

View File

@ -13,11 +13,6 @@ import (
"golang.org/x/crypto/sha3"
)
const (
bloomIdx = ethcmn.AddressLength
returnIdx = bloomIdx + ethtypes.BloomByteLength
)
// GenerateEthAddress generates an Ethereum address.
func GenerateEthAddress() ethcmn.Address {
priv, err := crypto.GenerateKey()
@ -52,23 +47,41 @@ func rlpHash(x interface{}) (hash ethcmn.Hash) {
return hash
}
// ResultData represents the data returned in an sdk.Result
type ResultData struct {
Address ethcmn.Address
Bloom ethtypes.Bloom
Logs []*ethtypes.Log
Ret []byte
}
// EncodeReturnData takes all of the necessary data from the EVM execution
// and returns the data as a byte slice
func EncodeReturnData(addr ethcmn.Address, bloom ethtypes.Bloom, evmRet []byte) []byte {
// Append address, bloom, evm return bytes in that order
returnData := append(addr.Bytes(), bloom.Bytes()...)
return append(returnData, evmRet...)
// and returns the data as a byte slice encoded with amino
func EncodeResultData(data *ResultData) ([]byte, error) {
return ModuleCdc.MarshalBinaryLengthPrefixed(data)
}
// DecodeReturnData decodes the byte slice of values to their respective types
func DecodeReturnData(bytes []byte) (addr ethcmn.Address, bloom ethtypes.Bloom, ret []byte, err error) {
if len(bytes) >= returnIdx {
addr = ethcmn.BytesToAddress(bytes[:bloomIdx])
bloom = ethtypes.BytesToBloom(bytes[bloomIdx:returnIdx])
ret = bytes[returnIdx:]
} else {
err = fmt.Errorf("invalid format for encoded data, message must be an EVM state transition")
// DecodeResultData decodes an amino-encoded byte slice into ReturnData
func DecodeResultData(in []byte) (ResultData, error) {
data := new(ResultData)
err := ModuleCdc.UnmarshalBinaryLengthPrefixed(in, data)
if err != nil {
return ResultData{}, err
}
return
return *data, nil
}
// EncodeLogs encodes an array of logs using amino
func EncodeLogs(logs []*ethtypes.Log) ([]byte, error) {
return ModuleCdc.MarshalBinaryLengthPrefixed(logs)
}
// DecodeLogs decodes an amino-encoded byte array into an array of logs
func DecodeLogs(in []byte) ([]*ethtypes.Log, error) {
logs := []*ethtypes.Log{}
err := ModuleCdc.UnmarshalBinaryLengthPrefixed(in, &logs)
if err != nil {
return nil, err
}
return logs, nil
}

View File

@ -13,12 +13,23 @@ func TestEvmDataEncoding(t *testing.T) {
bloom := ethtypes.BytesToBloom([]byte{0x1, 0x3})
ret := []byte{0x5, 0x8}
encoded := EncodeReturnData(addr, bloom, ret)
decAddr, decBloom, decRet, err := DecodeReturnData(encoded)
data := &ResultData{
Address: addr,
Bloom: bloom,
Logs: []*ethtypes.Log{{
Data: []byte{1, 2, 3, 4},
BlockNumber: 17,
}},
Ret: ret,
}
enc, err := EncodeResultData(data)
require.NoError(t, err)
require.Equal(t, addr, decAddr)
require.Equal(t, bloom, decBloom)
require.Equal(t, ret, decRet)
res, err := DecodeResultData(enc)
require.NoError(t, err)
require.Equal(t, addr, res.Address)
require.Equal(t, bloom, res.Bloom)
require.Equal(t, data.Logs, res.Logs)
require.Equal(t, ret, res.Ret)
}