Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: init custom fee antehandler and posthandler #27

Merged
merged 69 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
2975617
go mod
Nov 15, 2023
7727eee
add ante
Nov 15, 2023
028f302
set antehandler
Nov 15, 2023
560d930
cute
Nov 15, 2023
fb7f63a
Merge branch 'main' into aljo242/ante
Nov 15, 2023
5789e63
lint fix
Nov 15, 2023
0a841b9
Merge branch 'main' into aljo242/ante
Nov 15, 2023
ed24377
setup and wire
Nov 15, 2023
5aec66a
wire fmk
Nov 15, 2023
5b1e5bf
clean
Nov 15, 2023
e2c15bf
format
Nov 15, 2023
12457ce
todo
Nov 15, 2023
a76474c
lint fix
Nov 15, 2023
402b9b1
rename
Nov 15, 2023
2f464f4
wip
Nov 15, 2023
9f4d21b
proto
Nov 15, 2023
364aaa1
add param
Nov 15, 2023
018da67
add get
Nov 15, 2023
81ed0fb
test
Nov 15, 2023
7549ced
format
Nov 15, 2023
f98ba34
Merge branch 'aljo242/denom-param' into aljo242/fee-antehandler-func
Nov 15, 2023
cb00ab4
re-wire
Nov 15, 2023
36ebe05
add ak
Nov 15, 2023
099fc86
fee decorator
Nov 15, 2023
a2ddad4
Merge branch 'main' into aljo242/ante
Nov 15, 2023
7bd86e1
Merge branch 'aljo242/ante' into aljo242/fee-antehandler
Nov 15, 2023
8730cb1
Merge branch 'aljo242/fee-antehandler' into aljo242/fee-antehandler-func
Nov 15, 2023
0614980
mock
Nov 15, 2023
8364cbd
always check and use fee
Nov 15, 2023
477dcdd
extend with tip
Nov 15, 2023
87a6b82
Merge branch 'main' into aljo242/ante
Nov 16, 2023
3d74a44
Merge branch 'aljo242/ante' into aljo242/fee-antehandler
Nov 16, 2023
8dcfd60
Merge branch 'aljo242/fee-antehandler' into aljo242/fee-antehandler-func
Nov 16, 2023
d0bad79
Merge branch 'main' into aljo242/fee-antehandler
Nov 20, 2023
265b9bd
Merge branch 'aljo242/fee-antehandler' into aljo242/fee-antehandler-func
Nov 20, 2023
7f50ce6
fix lint
Nov 20, 2023
37344a1
refactor
Nov 20, 2023
47a2c2b
utd
Nov 20, 2023
53437a2
utd
Nov 20, 2023
22735e4
setup test
Nov 20, 2023
b62d82c
attempt test
Nov 20, 2023
dcd7461
fmt
Nov 21, 2023
96433e4
clean
Nov 21, 2023
ef715a0
add
Nov 21, 2023
6eb04d6
fix
Nov 21, 2023
8629aef
Merge branch 'main' into aljo242/fee-antehandler
Nov 27, 2023
4e131c9
rename
Nov 27, 2023
a05dac8
set posthandler
Nov 27, 2023
12282aa
set posthandler
Nov 27, 2023
5e7c47e
setup post
Nov 27, 2023
d5a1c91
finalize
Nov 27, 2023
34b43b8
add options
Nov 27, 2023
d9ddd15
clean
Nov 27, 2023
e62a188
comments
Nov 27, 2023
e4b6d3e
clean
Nov 27, 2023
5189858
rename
Nov 27, 2023
265fd57
use names
Nov 27, 2023
85cdd4f
Merge branch 'main' into aljo242/fee-antehandler
davidterpay Nov 28, 2023
bda2901
Update x/feemarket/types/expected_keepers.go
Nov 28, 2023
5d9c9eb
Update x/feemarket/post/fee.go
Nov 28, 2023
b09b25a
Update x/feemarket/ante/fee.go
Nov 28, 2023
6992e7d
fix
Nov 28, 2023
ceec81f
use feeTx
Nov 28, 2023
ff44b2b
fix
Nov 28, 2023
134a4c0
separate
Nov 28, 2023
e7357bf
refactor tip
Nov 28, 2023
50260da
Merge branch 'main' into aljo242/fee-antehandler
Nov 28, 2023
8fa833b
fix
Nov 28, 2023
c65be63
use update
Nov 28, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
cosmossdk.io/api v0.7.2
cosmossdk.io/core v0.11.0
cosmossdk.io/depinject v1.0.0-alpha.4
cosmossdk.io/errors v1.0.0
cosmossdk.io/log v1.2.1
cosmossdk.io/math v1.1.3-rc.1
github.com/client9/misspell v0.3.4
Expand Down Expand Up @@ -35,7 +36,6 @@ require (
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.1.4 // indirect
cloud.google.com/go/storage v1.30.1 // indirect
cosmossdk.io/errors v1.0.0 // indirect
cosmossdk.io/tools/rosetta v0.2.1 // indirect
filippo.io/edwards25519 v1.0.0 // indirect
github.com/4meepo/tagalign v1.3.3 // indirect
Expand Down
36 changes: 23 additions & 13 deletions tests/simapp/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"

feemarketante "github.com/skip-mev/feemarket/x/feemarket/ante"
)

// HandlerOptions are the options required for constructing an SDK AnteHandler with the fee market injected.
type HandlerOptions struct {
BaseOptions authante.HandlerOptions
// AnteHandlerOptions are the options required for constructing an SDK AnteHandler with the fee market injected.
type AnteHandlerOptions struct {
BaseOptions authante.HandlerOptions
AccountKeeper feemarketante.AccountKeeper
FeeMarketKeeper feemarketante.FeeMarketKeeper
}

// NewAnteHandler returns an AnteHandler that checks and increments sequence
// numbers, checks signatures & account numbers, and deducts fees from the first
// signer.
func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
if options.BaseOptions.AccountKeeper == nil {
func NewAnteHandler(options AnteHandlerOptions) (sdk.AnteHandler, error) {
if options.AccountKeeper == nil {
return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "account keeper is required for ante builder")
}

Expand All @@ -29,19 +33,25 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "sign mode handler is required for ante builder")
}

if options.FeeMarketKeeper == nil {
return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "feemarket keeper is required for ante builder")
}

anteDecorators := []sdk.AnteDecorator{
authante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first
authante.NewExtensionOptionsDecorator(options.BaseOptions.ExtensionOptionChecker),
authante.NewValidateBasicDecorator(),
authante.NewTxTimeoutHeightDecorator(),
authante.NewValidateMemoDecorator(options.BaseOptions.AccountKeeper),
authante.NewConsumeGasForTxSizeDecorator(options.BaseOptions.AccountKeeper),
authante.NewDeductFeeDecorator(options.BaseOptions.AccountKeeper, options.BaseOptions.BankKeeper, options.BaseOptions.FeegrantKeeper, options.BaseOptions.TxFeeChecker),
authante.NewSetPubKeyDecorator(options.BaseOptions.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators
authante.NewValidateSigCountDecorator(options.BaseOptions.AccountKeeper),
authante.NewSigGasConsumeDecorator(options.BaseOptions.AccountKeeper, options.BaseOptions.SigGasConsumer),
authante.NewSigVerificationDecorator(options.BaseOptions.AccountKeeper, options.BaseOptions.SignModeHandler),
authante.NewIncrementSequenceDecorator(options.BaseOptions.AccountKeeper),
authante.NewValidateMemoDecorator(options.AccountKeeper),
authante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
feemarketante.NewFeeMarketCheckDecorator( // fee market check replaces fee deduct decorator
options.FeeMarketKeeper,
), // fees are deducted in the fee market deduct post handler
authante.NewSetPubKeyDecorator(options.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators
authante.NewValidateSigCountDecorator(options.AccountKeeper),
authante.NewSigGasConsumeDecorator(options.AccountKeeper, options.BaseOptions.SigGasConsumer),
authante.NewSigVerificationDecorator(options.AccountKeeper, options.BaseOptions.SignModeHandler),
authante.NewIncrementSequenceDecorator(options.AccountKeeper),
}

return sdk.ChainAnteDecorators(anteDecorators...), nil
Expand Down
28 changes: 22 additions & 6 deletions tests/simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ type SimApp struct {
GroupKeeper groupkeeper.Keeper
NFTKeeper nftkeeper.Keeper
ConsensusParamsKeeper consensuskeeper.Keeper
FeeMarketKeeper feemarketkeeper.Keeper
FeeMarketKeeper *feemarketkeeper.Keeper

// simulation manager
sm *module.SimulationManager
Expand Down Expand Up @@ -263,21 +263,37 @@ func NewSimApp(
// ------------------------- Begin Custom Code -------------------------------- //
// ---------------------------------------------------------------------------- //

handlerOptions := ante.HandlerOptions{
AccountKeeper: app.AccountKeeper,
anteHandlerOptions := ante.HandlerOptions{
BankKeeper: app.BankKeeper,
FeegrantKeeper: app.FeeGrantKeeper,
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
SignModeHandler: app.txConfig.SignModeHandler(),
}
options := HandlerOptions{
BaseOptions: handlerOptions,

anteOptions := AnteHandlerOptions{
BaseOptions: anteHandlerOptions,
AccountKeeper: app.AccountKeeper,
FeeMarketKeeper: app.FeeMarketKeeper,
}
anteHandler, err := NewAnteHandler(options)
anteHandler, err := NewAnteHandler(anteOptions)
if err != nil {
panic(err)
}

postHandlerOptions := PostHandlerOptions{
AccountKeeper: app.AccountKeeper,
BankKeeper: app.BankKeeper,
FeeGrantKeeper: app.FeeGrantKeeper,
FeeMarketKeeper: app.FeeMarketKeeper,
}
postHandler, err := NewPostHandler(postHandlerOptions)
if err != nil {
panic(err)
}

// set ante and post handlers
app.App.SetAnteHandler(anteHandler)
app.App.SetPostHandler(postHandler)

/**** Module Options ****/

Expand Down
2 changes: 2 additions & 0 deletions tests/simapp/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ var (

// module account permissions
moduleAccPerms = []*authmodulev1.ModuleAccountPermission{
{Account: feemarkettypes.FeeCollectorName, Permissions: []string{authtypes.Burner}}, // allow fee market to burn
{Account: authtypes.FeeCollectorName},
{Account: distrtypes.ModuleName},
{Account: feemarkettypes.ModuleName},
Expand All @@ -99,6 +100,7 @@ var (
minttypes.ModuleName,
stakingtypes.BondedPoolName,
stakingtypes.NotBondedPoolName,
feemarkettypes.FeeCollectorName,
// We allow the following module accounts to receive funds:
// govtypes.ModuleName
}
Expand Down
43 changes: 43 additions & 0 deletions tests/simapp/post.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package simapp

import (
errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

feemarketpost "github.com/skip-mev/feemarket/x/feemarket/post"
)

// PostHandlerOptions are the options required for constructing a FeeMarket PostHandler.
type PostHandlerOptions struct {
AccountKeeper feemarketpost.AccountKeeper
BankKeeper feemarketpost.BankKeeper
FeeMarketKeeper feemarketpost.FeeMarketKeeper
FeeGrantKeeper feemarketpost.FeeGrantKeeper
}

// NewPostHandler returns a PostHandler chain with the fee deduct decorator.
func NewPostHandler(options PostHandlerOptions) (sdk.PostHandler, error) {
if options.AccountKeeper == nil {
return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "account keeper is required for post builder")
}

if options.BankKeeper == nil {
return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "bank keeper is required for post builder")
}

if options.FeeMarketKeeper == nil {
return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "feemarket keeper is required for post builder")
}

postDecorators := []sdk.PostDecorator{
feemarketpost.NewFeeMarketDeductDecorator(
options.AccountKeeper,
options.BankKeeper,
options.FeeGrantKeeper,
options.FeeMarketKeeper,
),
}

return sdk.ChainPostDecorators(postDecorators...), nil
}
13 changes: 8 additions & 5 deletions testutils/testutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package testutils
import (
"math/rand"

authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
Expand All @@ -28,21 +30,22 @@ type EncodingConfig struct {
}

func CreateTestEncodingConfig() EncodingConfig {
cdc := codec.NewLegacyAmino()
amino := codec.NewLegacyAmino()
interfaceRegistry := types.NewInterfaceRegistry()

banktypes.RegisterInterfaces(interfaceRegistry)
authtypes.RegisterInterfaces(interfaceRegistry)
cryptocodec.RegisterInterfaces(interfaceRegistry)
feemarkettypes.RegisterInterfaces(interfaceRegistry)
stakingtypes.RegisterInterfaces(interfaceRegistry)

codec := codec.NewProtoCodec(interfaceRegistry)
cdc := codec.NewProtoCodec(interfaceRegistry)

return EncodingConfig{
InterfaceRegistry: interfaceRegistry,
Codec: codec,
TxConfig: tx.NewTxConfig(codec, tx.DefaultSignModes),
Amino: cdc,
Codec: cdc,
TxConfig: tx.NewTxConfig(cdc, tx.DefaultSignModes),
Amino: amino,
}
}

Expand Down
32 changes: 25 additions & 7 deletions x/feemarket/ante/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,44 @@ package ante

import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"

feemarkettypes "github.com/skip-mev/feemarket/x/feemarket/types"
)

// AccountKeeper defines the contract needed for AccountKeeper related APIs.
// Interface provides support to use non-sdk AccountKeeper for AnteHandler's decorators.
//
//go:generate mockery --name AccountKeeper --filename mock_account_keeper.go
type AccountKeeper interface {
GetParams(ctx sdk.Context) (params types.Params)
GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI
SetAccount(ctx sdk.Context, acc types.AccountI)
GetParams(ctx sdk.Context) (params authtypes.Params)
GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI
SetAccount(ctx sdk.Context, acc authtypes.AccountI)
GetModuleAddress(moduleName string) sdk.AccAddress
GetModuleAccount(ctx sdk.Context, name string) authtypes.ModuleAccountI
NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI
}

// FeegrantKeeper defines the expected feegrant keeper.
type FeegrantKeeper interface {
// FeeGrantKeeper defines the expected feegrant keeper.
//
//go:generate mockery --name FeeGrantKeeper --filename mock_feegrant_keeper.go
type FeeGrantKeeper interface {
UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins, msgs []sdk.Msg) error
}

// BankKeeper defines the contract needed for supply related APIs (noalias)
// BankKeeper defines the contract needed for supply related APIs.
//
//go:generate mockery --name BankKeeper --filename mock_bank_keeper.go
type BankKeeper interface {
IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error
SendCoins(ctx sdk.Context, from, to sdk.AccAddress, amt sdk.Coins) error
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
}

// FeeMarketKeeper defines the expected feemarket keeper.
//
//go:generate mockery --name FeeMarketKeeper --filename mock_feemarket_keeper.go
type FeeMarketKeeper interface {
GetState(ctx sdk.Context) (feemarkettypes.State, error)
GetMinGasPrices(ctx sdk.Context) (sdk.Coins, error)
}
113 changes: 113 additions & 0 deletions x/feemarket/ante/fee.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package ante
aljo242 marked this conversation as resolved.
Show resolved Hide resolved

import (
"math"

errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"

sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

// FeeMarketCheckDecorator checks sufficient fees from the fee payer based off of the current
// state of the feemarket.
// If the fee payer does not have the funds to pay for the fees, return an InsufficientFunds error.
// Call next AnteHandler if fees successfully checked.
// CONTRACT: Tx must implement FeeTx interface
type FeeMarketCheckDecorator struct {
feemarketKeeper FeeMarketKeeper
}

func NewFeeMarketCheckDecorator(fmk FeeMarketKeeper) FeeMarketCheckDecorator {
return FeeMarketCheckDecorator{
feemarketKeeper: fmk,
}
}

// AnteHandle checks if the tx provides sufficient fee to cover the required fee from the fee market.
func (dfd FeeMarketCheckDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
feeTx, ok := tx.(sdk.FeeTx)
if !ok {
return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
}

if !simulate && ctx.BlockHeight() > 0 && feeTx.GetGas() == 0 {
return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidGasLimit, "must provide positive gas")
}

minGasPrices, err := dfd.feemarketKeeper.GetMinGasPrices(ctx)
if err != nil {
return ctx, errorsmod.Wrapf(err, "unable to get fee market state")
}

fee := feeTx.GetFee()
gas := feeTx.GetGas() // use provided gas limit

if !simulate {
fee, _, err = CheckTxFees(minGasPrices, tx, gas)
if err != nil {
return ctx, err
}
}

minGasPricesDecCoins := sdk.NewDecCoinsFromCoins(minGasPrices...)
newCtx := ctx.WithPriority(getTxPriority(fee, int64(gas))).WithMinGasPrices(minGasPricesDecCoins)
aljo242 marked this conversation as resolved.
Show resolved Hide resolved
return next(newCtx, tx, simulate)
}

// CheckTxFees implements the logic for the fee market to check if a Tx has provided suffucient
aljo242 marked this conversation as resolved.
Show resolved Hide resolved
// fees given the current state of the fee market. Returns an error if insufficient fees.
func CheckTxFees(minFees sdk.Coins, tx sdk.Tx, gas uint64) (feeCoins sdk.Coins, tip sdk.Coins, err error) {
feeTx, ok := tx.(sdk.FeeTx)
if !ok {
return nil, nil, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
}
aljo242 marked this conversation as resolved.
Show resolved Hide resolved

feesDec := sdk.NewDecCoinsFromCoins(minFees...)

feeCoins = feeTx.GetFee()

// Ensure that the provided fees meet the minimum
minGasPrices := feesDec
if !minGasPrices.IsZero() {
requiredFees := make(sdk.Coins, len(minGasPrices))

// Determine the required fees by multiplying each required minimum gas
// price by the gas, where fee = ceil(minGasPrice * gas).
glDec := sdkmath.LegacyNewDec(int64(gas))
for i, gp := range minGasPrices {
fee := gp.Amount.Mul(glDec)
requiredFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt())
}

if !feeCoins.IsAnyGTE(requiredFees) {
return nil, nil, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, requiredFees)
}

tip = feeCoins.Sub(minFees...) // tip is the difference between feeCoins and the min fees
feeCoins = requiredFees // set fee coins to be ONLY the required amount
}

return feeCoins, tip, nil
}

// getTxPriority returns a naive tx priority based on the amount of the smallest denomination of the gas price
// provided in a transaction.
// NOTE: This implementation should be used with a great consideration as it opens potential attack vectors
// where txs with multiple coins could not be prioritized as expected.
func getTxPriority(fee sdk.Coins, gas int64) int64 {
var priority int64
for _, c := range fee {
p := int64(math.MaxInt64)
gasPrice := c.Amount.QuoRaw(gas)
if gasPrice.IsInt64() {
p = gasPrice.Int64()
}
if priority == 0 || p < priority {
priority = p
}
}

return priority
}
Loading