Skip to content
This repository has been archived by the owner on Nov 30, 2021. It is now read-only.

ante: add message validation decorator #525

Merged
merged 9 commits into from
Sep 23, 2020
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

### Bug Fixes

* (ante) [\#525](https://github.com/ChainSafe/ethermint/pull/525) Add message validation decorator to `AnteHandler` for `MsgEthereumTx`.
* (types) [\#507](https://github.com/ChainSafe/ethermint/pull/507) Fix hardcoded `aphoton` on `EthAccount` balance getter and setter.
* (`x/evm`) [\#496](https://github.com/ChainSafe/ethermint/pull/496) Fix bugs on `journal.revert` and `CommitStateDB.Copy`.
* (types) [\#480](https://github.com/ChainSafe/ethermint/pull/480) Update [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) coin type to `60` to satisfy [EIP84](https://github.com/ethereum/EIPs/issues/84).
Expand Down
73 changes: 21 additions & 52 deletions app/ante/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/auth"
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
"github.com/cosmos/cosmos-sdk/x/auth/keeper"
"github.com/cosmos/cosmos-sdk/x/auth/types"

"github.com/cosmos/ethermint/crypto"
Expand Down Expand Up @@ -54,6 +55,7 @@ func NewAnteHandler(ak auth.AccountKeeper, evmKeeper EVMKeeper, sk types.SupplyK
anteHandler = sdk.ChainAnteDecorators(
NewEthSetupContextDecorator(), // outermost AnteDecorator. EthSetUpContext must be called first
NewEthMempoolFeeDecorator(evmKeeper),
authante.NewValidateBasicDecorator(),
NewEthSigVerificationDecorator(),
NewAccountVerificationDecorator(ak, evmKeeper),
NewNonceVerificationDecorator(ak),
Expand All @@ -71,7 +73,7 @@ func NewAnteHandler(ak auth.AccountKeeper, evmKeeper EVMKeeper, sk types.SupplyK
// sigGasConsumer overrides the DefaultSigVerificationGasConsumer from the x/auth
// module on the SDK. It doesn't allow ed25519 nor multisig thresholds.
func sigGasConsumer(
meter sdk.GasMeter, sig []byte, pubkey tmcrypto.PubKey, params types.Params,
meter sdk.GasMeter, _ []byte, pubkey tmcrypto.PubKey, _ types.Params,
) error {
switch pubkey.(type) {
case crypto.PubKeySecp256k1:
Expand All @@ -85,75 +87,42 @@ func sigGasConsumer(
}
}

// IncrementSequenceDecorator handles incrementing sequences of all signers.
// Use the IncrementSequenceDecorator decorator to prevent replay attacks. Note,
// there is no need to execute IncrementSequenceDecorator on RecheckTX since
// CheckTx would already bump the sequence number.
//
// NOTE: Since CheckTx and DeliverTx state are managed separately, subsequent and
// sequential txs orginating from the same account cannot be handled correctly in
// a reliable way unless sequence numbers are managed and tracked manually by a
// client. It is recommended to instead use multiple messages in a tx.
type IncrementSequenceDecorator struct {
ak auth.AccountKeeper
}

func NewIncrementSequenceDecorator(ak auth.AccountKeeper) IncrementSequenceDecorator {
return IncrementSequenceDecorator{
ak: ak,
}
}

func (isd IncrementSequenceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
// no need to increment sequence on RecheckTx
if ctx.IsReCheckTx() && !simulate {
return next(ctx, tx, simulate)
}

sigTx, ok := tx.(authante.SigVerifiableTx)
if !ok {
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type")
}

// increment sequence of all signers
for _, addr := range sigTx.GetSigners() {
acc := isd.ak.GetAccount(ctx, addr)
if err := acc.SetSequence(acc.GetSequence() + 1); err != nil {
panic(err)
}

isd.ak.SetAccount(ctx, acc)
}

return next(ctx, tx, simulate)
}

// AccountSetupDecorator sets an account to state if it's not stored already. This only applies for MsgEthermint.
type AccountSetupDecorator struct {
ak auth.AccountKeeper
}

// NewAccountSetupDecorator creates a new AccountSetupDecorator instance
func NewAccountSetupDecorator(ak auth.AccountKeeper) AccountSetupDecorator {
return AccountSetupDecorator{
ak: ak,
}
}

// AnteHandle sets an account for MsgEthermint (evm) if the sender is registered.
// NOTE: Since the account is set without any funds, the message execution will
// fail if the validator requires a minimum fee > 0.
func (asd AccountSetupDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
msgs := tx.GetMsgs()
if len(msgs) == 0 {
return ctx, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "no messages included in transaction")
}

msg, ok := msgs[0].(evmtypes.MsgEthermint)
if !ok {
return next(ctx, tx, simulate)
for _, msg := range msgs {
if msgEthermint, ok := msg.(evmtypes.MsgEthermint); ok {
setupAccount(asd.ak, ctx, msgEthermint.From)
}
}

acc := asd.ak.GetAccount(ctx, msg.From)
if acc == nil {
info := asd.ak.NewAccountWithAddress(ctx, msg.From)
asd.ak.SetAccount(ctx, info)
return next(ctx, tx, simulate)
}

func setupAccount(ak keeper.AccountKeeper, ctx sdk.Context, addr sdk.AccAddress) {
acc := ak.GetAccount(ctx, addr)
if acc != nil {
return
}

return next(ctx, tx, simulate)
acc = ak.NewAccountWithAddress(ctx, addr)
ak.SetAccount(ctx, acc)
}
26 changes: 16 additions & 10 deletions app/ante/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,10 @@ func (esvd EthSigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, s
return ctx, sdkerrors.Wrap(emint.ErrInvalidChainID, ctx.ChainID())
}

// validate sender/signature
// validate sender/signature and cache the address
_, err = msgEthTx.VerifySig(chainID)
if err != nil {
return ctx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, fmt.Sprintf("signature verification failed: %s", err.Error()))
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "signature verification failed: %s", err.Error())
}

// NOTE: when signature verification succeeds, a non-empty signer address can be
Expand Down Expand Up @@ -217,7 +217,7 @@ func (avd AccountVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, s
return next(ctx, tx, simulate)
}

// NonceVerificationDecorator that the account nonce from the transaction matches
// NonceVerificationDecorator checks that the account nonce from the transaction matches
// the sender account sequence.
type NonceVerificationDecorator struct {
ak auth.AccountKeeper
Expand Down Expand Up @@ -246,14 +246,17 @@ func (nvd NonceVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, sim

acc := nvd.ak.GetAccount(ctx, address)
if acc == nil {
return ctx, fmt.Errorf("account %s (%s) is nil", common.BytesToAddress(address.Bytes()), address)
return ctx, sdkerrors.Wrapf(
sdkerrors.ErrUnknownAddress,
"account %s (%s) is nil", common.BytesToAddress(address.Bytes()), address,
)
}

seq := acc.GetSequence()
if msgEthTx.Data.AccountNonce != seq {
return ctx, sdkerrors.Wrap(
return ctx, sdkerrors.Wrapf(
sdkerrors.ErrInvalidSequence,
fmt.Sprintf("invalid nonce; got %d, expected %d", msgEthTx.Data.AccountNonce, seq),
"invalid nonce; got %d, expected %d", msgEthTx.Data.AccountNonce, seq,
)
}

Expand Down Expand Up @@ -290,20 +293,23 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx)
}

// sender address should be in the tx cache
// sender address should be in the tx cache from the previous AnteHandle call
address := msgEthTx.From()
if address.Empty() {
panic("sender address cannot be empty")
}

// Fetch sender account from signature
// fetch sender account from signature
senderAcc, err := auth.GetSignerAcc(ctx, egcd.ak, address)
if err != nil {
return ctx, err
}

if senderAcc == nil {
return ctx, fmt.Errorf("sender account %s (%s) is nil", common.BytesToAddress(address.Bytes()), address)
return ctx, sdkerrors.Wrapf(
sdkerrors.ErrUnknownAddress,
"sender account %s (%s) is nil", common.BytesToAddress(address.Bytes()), address,
)
}

gasLimit := msgEthTx.GetGas()
Expand All @@ -314,7 +320,7 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula

// intrinsic gas verification during CheckTx
if ctx.IsCheckTx() && gasLimit < gas {
return ctx, fmt.Errorf("intrinsic gas too low: %d < %d", gasLimit, gas)
return ctx, sdkerrors.Wrapf(sdkerrors.ErrOutOfGas, "intrinsic gas too low: %d < %d", gasLimit, gas)
}

// Charge sender for gas up to limit
Expand Down