ante: add message validation decorator (#525)

* ante: add message validation decorator

* changelog

* fix lint

* fix lint

* rename
This commit is contained in:
Federico Kunze 2020-09-23 17:49:20 +03:00 committed by GitHub
parent 291dfcafbf
commit 1f4f7fb017
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 39 additions and 63 deletions

View File

@ -55,6 +55,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Bug Fixes ### 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. * (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`. * (`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). * (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).

View File

@ -5,6 +5,7 @@ import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
authante "github.com/cosmos/cosmos-sdk/x/auth/ante" 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/cosmos-sdk/x/auth/types"
"github.com/cosmos/ethermint/crypto" "github.com/cosmos/ethermint/crypto"
@ -54,6 +55,7 @@ func NewAnteHandler(ak auth.AccountKeeper, evmKeeper EVMKeeper, sk types.SupplyK
anteHandler = sdk.ChainAnteDecorators( anteHandler = sdk.ChainAnteDecorators(
NewEthSetupContextDecorator(), // outermost AnteDecorator. EthSetUpContext must be called first NewEthSetupContextDecorator(), // outermost AnteDecorator. EthSetUpContext must be called first
NewEthMempoolFeeDecorator(evmKeeper), NewEthMempoolFeeDecorator(evmKeeper),
authante.NewValidateBasicDecorator(),
NewEthSigVerificationDecorator(), NewEthSigVerificationDecorator(),
NewAccountVerificationDecorator(ak, evmKeeper), NewAccountVerificationDecorator(ak, evmKeeper),
NewNonceVerificationDecorator(ak), NewNonceVerificationDecorator(ak),
@ -71,7 +73,7 @@ func NewAnteHandler(ak auth.AccountKeeper, evmKeeper EVMKeeper, sk types.SupplyK
// sigGasConsumer overrides the DefaultSigVerificationGasConsumer from the x/auth // sigGasConsumer overrides the DefaultSigVerificationGasConsumer from the x/auth
// module on the SDK. It doesn't allow ed25519 nor multisig thresholds. // module on the SDK. It doesn't allow ed25519 nor multisig thresholds.
func sigGasConsumer( func sigGasConsumer(
meter sdk.GasMeter, sig []byte, pubkey tmcrypto.PubKey, params types.Params, meter sdk.GasMeter, _ []byte, pubkey tmcrypto.PubKey, _ types.Params,
) error { ) error {
switch pubkey.(type) { switch pubkey.(type) {
case crypto.PubKeySecp256k1: case crypto.PubKeySecp256k1:
@ -85,75 +87,42 @@ func sigGasConsumer(
} }
} }
// IncrementSequenceDecorator handles incrementing sequences of all signers. // AccountSetupDecorator sets an account to state if it's not stored already. This only applies for MsgEthermint.
// 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)
}
type AccountSetupDecorator struct { type AccountSetupDecorator struct {
ak auth.AccountKeeper ak auth.AccountKeeper
} }
// NewAccountSetupDecorator creates a new AccountSetupDecorator instance
func NewAccountSetupDecorator(ak auth.AccountKeeper) AccountSetupDecorator { func NewAccountSetupDecorator(ak auth.AccountKeeper) AccountSetupDecorator {
return AccountSetupDecorator{ return AccountSetupDecorator{
ak: ak, 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) { func (asd AccountSetupDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
msgs := tx.GetMsgs() msgs := tx.GetMsgs()
if len(msgs) == 0 { if len(msgs) == 0 {
return ctx, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "no messages included in transaction") return ctx, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "no messages included in transaction")
} }
msg, ok := msgs[0].(evmtypes.MsgEthermint) for _, msg := range msgs {
if !ok { if msgEthermint, ok := msg.(evmtypes.MsgEthermint); ok {
return next(ctx, tx, simulate) 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) 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
}
acc = ak.NewAccountWithAddress(ctx, addr)
ak.SetAccount(ctx, acc)
}

View File

@ -146,10 +146,10 @@ func (esvd EthSigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, s
return ctx, sdkerrors.Wrap(emint.ErrInvalidChainID, ctx.ChainID()) return ctx, sdkerrors.Wrap(emint.ErrInvalidChainID, ctx.ChainID())
} }
// validate sender/signature // validate sender/signature and cache the address
_, err = msgEthTx.VerifySig(chainID) _, err = msgEthTx.VerifySig(chainID)
if err != nil { 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 // NOTE: when signature verification succeeds, a non-empty signer address can be
@ -217,7 +217,7 @@ func (avd AccountVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, s
return next(ctx, tx, simulate) 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. // the sender account sequence.
type NonceVerificationDecorator struct { type NonceVerificationDecorator struct {
ak auth.AccountKeeper ak auth.AccountKeeper
@ -246,14 +246,17 @@ func (nvd NonceVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, sim
acc := nvd.ak.GetAccount(ctx, address) acc := nvd.ak.GetAccount(ctx, address)
if acc == nil { 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() seq := acc.GetSequence()
if msgEthTx.Data.AccountNonce != seq { if msgEthTx.Data.AccountNonce != seq {
return ctx, sdkerrors.Wrap( return ctx, sdkerrors.Wrapf(
sdkerrors.ErrInvalidSequence, sdkerrors.ErrInvalidSequence,
fmt.Sprintf("invalid nonce; got %d, expected %d", msgEthTx.Data.AccountNonce, seq), "invalid nonce; got %d, expected %d", msgEthTx.Data.AccountNonce, seq,
) )
} }
@ -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) 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() address := msgEthTx.From()
if address.Empty() { if address.Empty() {
panic("sender address cannot be 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) senderAcc, err := auth.GetSignerAcc(ctx, egcd.ak, address)
if err != nil { if err != nil {
return ctx, err return ctx, err
} }
if senderAcc == nil { 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() gasLimit := msgEthTx.GetGas()
@ -314,7 +320,7 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula
// intrinsic gas verification during CheckTx // intrinsic gas verification during CheckTx
if ctx.IsCheckTx() && gasLimit < gas { 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 // Charge sender for gas up to limit