From 1829bc42cab97e706126a9883ea9a1c468fd0391 Mon Sep 17 00:00:00 2001 From: Oleg Nikonychev Date: Tue, 1 Oct 2024 18:05:18 +0400 Subject: [PATCH] feat(evm): ante handler to prohibit authz grant evm messages (#2032) * feat(evm): ante handler to prohibit authz grant evm messages * feat(authz): rejecting authz exec messages with MsgEthereumTx inside * chore: lint * chore: typo fix --------- Co-authored-by: Unique Divine <51418232+Unique-Divine@users.noreply.github.com> Co-authored-by: Kevin Yang <5478483+k-yang@users.noreply.github.com> --- CHANGELOG.md | 1 + app/ante.go | 1 + app/ante/auth_grard_test.go | 138 ++++++++++++++++++++++++++++++++++++ app/ante/authz_guard.go | 72 +++++++++++++++++-- 4 files changed, 205 insertions(+), 7 deletions(-) create mode 100644 app/ante/auth_grard_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 57d38bc11..d0590d8a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -119,6 +119,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#2023](https://github.com/NibiruChain/nibiru/pull/2023) - fix(evm)!: adjusted generation and parsing of the block bloom events - [#2030](https://github.com/NibiruChain/nibiru/pull/2030) - refactor(eth/rpc): Delete unused code and improve logging in the eth and debug namespaces - [#2031](https://github.com/NibiruChain/nibiru/pull/2031) - fix(evm): debug calls with custom tracer and tracer options +- [#2032](https://github.com/NibiruChain/nibiru/pull/2032) - feat(evm): ante handler to prohibit authz grant evm messages - [#2039](https://github.com/NibiruChain/nibiru/pull/2039) - refactor(rpc-backend): remove unnecessary interface code - [#2044](https://github.com/NibiruChain/nibiru/pull/2044) - feat(evm): evm tx indexer service implemented - [#2045](https://github.com/NibiruChain/nibiru/pull/2045) - test(evm): backend tests with test network and real txs diff --git a/app/ante.go b/app/ante.go index f8c661163..79c2d5adf 100644 --- a/app/ante.go +++ b/app/ante.go @@ -62,6 +62,7 @@ func NewAnteHandlerNonEVM( ) sdk.AnteHandler { return sdk.ChainAnteDecorators( ante.AnteDecoratorPreventEtheruemTxMsgs{}, // reject MsgEthereumTxs + ante.AnteDecoratorAuthzGuard{}, // disable certain messages in authz grant "generic" authante.NewSetUpContextDecorator(), wasmkeeper.NewLimitSimulationGasDecorator(opts.WasmConfig.SimulationGasLimit), wasmkeeper.NewCountTXDecorator(opts.TxCounterStoreKey), diff --git a/app/ante/auth_grard_test.go b/app/ante/auth_grard_test.go new file mode 100644 index 000000000..838ed804e --- /dev/null +++ b/app/ante/auth_grard_test.go @@ -0,0 +1,138 @@ +package ante_test + +import ( + "time" + + sdkclienttx "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/NibiruChain/nibiru/v2/app" + "github.com/NibiruChain/nibiru/v2/app/ante" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" +) + +func (s *AnteTestSuite) TestAnteDecoratorAuthzGuard() { + testCases := []struct { + name string + txMsg func() sdk.Msg + wantErr string + }{ + { + name: "sad: authz generic grant with evm message", + txMsg: func() sdk.Msg { + someTime := time.Now() + expiryTime := someTime.Add(time.Hour) + genericGrant, err := authz.NewGrant( + someTime, + authz.NewGenericAuthorization(sdk.MsgTypeURL(&evm.MsgEthereumTx{})), &expiryTime, + ) + s.Require().NoError(err) + return &authz.MsgGrant{Grant: genericGrant} + }, + wantErr: "not allowed", + }, + { + name: "happy: authz generic grant with non evm message", + txMsg: func() sdk.Msg { + someTime := time.Now() + expiryTime := someTime.Add(time.Hour) + genericGrant, err := authz.NewGrant( + someTime, + authz.NewGenericAuthorization(sdk.MsgTypeURL(&stakingtypes.MsgCreateValidator{})), &expiryTime, + ) + s.Require().NoError(err) + return &authz.MsgGrant{Grant: genericGrant} + }, + wantErr: "", + }, + { + name: "happy: authz non generic grant", + txMsg: func() sdk.Msg { + someTime := time.Now() + expiryTime := someTime.Add(time.Hour) + genericGrant, err := authz.NewGrant( + someTime, + &banktypes.SendAuthorization{}, + &expiryTime, + ) + s.Require().NoError(err) + return &authz.MsgGrant{Grant: genericGrant} + }, + wantErr: "", + }, + { + name: "happy: non authz message", + txMsg: func() sdk.Msg { + return &evm.MsgEthereumTx{} + }, + wantErr: "", + }, + { + name: "sad: authz exec with a single evm message", + txMsg: func() sdk.Msg { + msgExec := authz.NewMsgExec( + sdk.AccAddress("nibiuser"), + []sdk.Msg{ + &evm.MsgEthereumTx{}, + }, + ) + return &msgExec + }, + wantErr: "ExtensionOptionsEthereumTx", + }, + { + name: "sad: authz exec with evm message and non evm message", + txMsg: func() sdk.Msg { + msgExec := authz.NewMsgExec( + sdk.AccAddress("nibiuser"), + []sdk.Msg{ + &banktypes.MsgSend{}, + &evm.MsgEthereumTx{}, + }, + ) + return &msgExec + }, + wantErr: "ExtensionOptionsEthereumTx", + }, + { + name: "happy: authz exec without evm messages", + txMsg: func() sdk.Msg { + msgExec := authz.NewMsgExec( + sdk.AccAddress("nibiuser"), + []sdk.Msg{ + &banktypes.MsgSend{}, + }, + ) + return &msgExec + }, + wantErr: "", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + deps := evmtest.NewTestDeps() + anteDec := ante.AnteDecoratorAuthzGuard{} + + encCfg := app.MakeEncodingConfig() + txBuilder, err := sdkclienttx.Factory{}. + WithChainID(s.ctx.ChainID()). + WithTxConfig(encCfg.TxConfig). + BuildUnsignedTx(tc.txMsg()) + s.Require().NoError(err) + + _, err = anteDec.AnteHandle( + deps.Ctx, txBuilder.GetTx(), false, evmtest.NextNoOpAnteHandler, + ) + if tc.wantErr != "" { + s.Require().ErrorContains(err, tc.wantErr) + return + } + s.Require().NoError(err) + }) + } +} diff --git a/app/ante/authz_guard.go b/app/ante/authz_guard.go index 9ceb8b240..c1cbb8f8d 100644 --- a/app/ante/authz_guard.go +++ b/app/ante/authz_guard.go @@ -1,9 +1,67 @@ +// Copyright (c) 2023-2024 Nibi, Inc. package ante -// TODO: https://github.com/NibiruChain/nibiru/issues/1915 -// feat(ante): Add an authz guard to disable authz Ethereum txs and provide -// additional security around the default functionality exposed by the module. -// -// Implemenetation Notes -// UD-NOTE - IsAuthzMessage fn. Use authz import with module name -// UD-NOTE - Define set of disabled txMsgs +import ( + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/authz" + + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +// AnteDecoratorAuthzGuard filters autz messages +type AnteDecoratorAuthzGuard struct{} + +// AnteHandle rejects "authz grant generic --msg-type '/eth.evm.v1.MsgEthereumTx'" +// Also rejects authz exec tx.json with any MsgEthereumTx inside +func (rmd AnteDecoratorAuthzGuard) AnteHandle( + ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler, +) (newCtx sdk.Context, err error) { + for _, msg := range tx.GetMsgs() { + // Do not allow grant for MsgEthereumTx + if msgGrant, ok := msg.(*authz.MsgGrant); ok { + if msgGrant.Grant.Authorization == nil { + return ctx, errors.Wrapf( + errortypes.ErrInvalidType, + "grant authorization is missing", + ) + } + authorization, err := msgGrant.Grant.GetAuthorization() + if err != nil { + return ctx, errors.Wrapf( + errortypes.ErrInvalidType, + "failed unmarshaling generic authorization %s", err, + ) + } + if genericAuth, ok := authorization.(*authz.GenericAuthorization); ok { + if genericAuth.MsgTypeURL() == sdk.MsgTypeURL(&evm.MsgEthereumTx{}) { + return ctx, errors.Wrapf( + errortypes.ErrNotSupported, + "authz grant generic for msg type %s is not allowed", + genericAuth.MsgTypeURL(), + ) + } + } + } + // Also reject MsgEthereumTx in exec + if msgExec, ok := msg.(*authz.MsgExec); ok { + msgsInExec, err := msgExec.GetMessages() + if err != nil { + return ctx, errors.Wrapf( + errortypes.ErrInvalidType, + "failed getting exec messages %s", err, + ) + } + for _, msgInExec := range msgsInExec { + if _, ok := msgInExec.(*evm.MsgEthereumTx); ok { + return ctx, errors.Wrapf( + errortypes.ErrInvalidType, + "MsgEthereumTx needs to be contained within a tx with 'ExtensionOptionsEthereumTx' option", + ) + } + } + } + } + return next(ctx, tx, simulate) +}