diff --git a/app/ante_handler.go b/app/ante_handler.go index 3b55b7f9..5bca6498 100644 --- a/app/ante_handler.go +++ b/app/ante_handler.go @@ -2,16 +2,22 @@ package app import ( errorsmod "cosmossdk.io/errors" + globalfeeante "github.com/OmniFlix/omniflixhub/v2/x/globalfee/ante" + globalfeekeeper "github.com/OmniFlix/omniflixhub/v2/x/globalfee/keeper" "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/ante" govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" ibcante "github.com/cosmos/ibc-go/v7/modules/core/ante" ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" ) +// Lower back to 1 mil after https://github.com/cosmos/relayer/issues/1255 +const maxBypassMinFeeMsgGasUsage = 2_000_000 + // HandlerOptions extend the SDK's AnteHandler options by requiring the IBC // channel keeper. type HandlerOptions struct { @@ -21,6 +27,11 @@ type HandlerOptions struct { IBCKeeper *ibckeeper.Keeper TxCounterStoreKey storetypes.StoreKey Codec codec.BinaryCodec + + BypassMinFeeMsgTypes []string + + GlobalFeeKeeper globalfeekeeper.Keeper + StakingKeeper stakingkeeper.Keeper } func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { @@ -46,6 +57,12 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { ante.NewTxTimeoutHeightDecorator(), ante.NewValidateMemoDecorator(options.AccountKeeper), ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), + globalfeeante.NewFeeDecorator( + options.BypassMinFeeMsgTypes, + options.GlobalFeeKeeper, + options.StakingKeeper, + maxBypassMinFeeMsgGasUsage, + ), ante.NewDeductFeeDecorator( options.AccountKeeper, options.BankKeeper, diff --git a/app/app.go b/app/app.go index a7bf7230..f7e379be 100644 --- a/app/app.go +++ b/app/app.go @@ -9,6 +9,7 @@ import ( autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" + "github.com/OmniFlix/omniflixhub/v2/app/openapiconsole" appparams "github.com/OmniFlix/omniflixhub/v2/app/params" "github.com/OmniFlix/omniflixhub/v2/docs" @@ -39,9 +40,13 @@ import ( paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" - ibcclientclient "github.com/cosmos/ibc-go/v7/modules/core/02-client/client" "github.com/spf13/cast" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + ibcclientclient "github.com/cosmos/ibc-go/v7/modules/core/02-client/client" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + "github.com/OmniFlix/omniflixhub/v2/app/keepers" "github.com/OmniFlix/omniflixhub/v2/app/upgrades" v012 "github.com/OmniFlix/omniflixhub/v2/app/upgrades/v012" @@ -211,6 +216,10 @@ func NewOmniFlixApp( GovKeeper: app.GovKeeper, IBCKeeper: app.IBCKeeper, Codec: appCodec, + + BypassMinFeeMsgTypes: GetDefaultBypassFeeMessages(), + GlobalFeeKeeper: app.GlobalFeeKeeper, + StakingKeeper: *app.StakingKeeper, }, ) if err != nil { @@ -394,3 +403,22 @@ func GetMaccPerms() map[string][]string { } return dupMaccPerms } + +func GetDefaultBypassFeeMessages() []string { + return []string{ + // IBC messages + sdk.MsgTypeURL(&ibcchanneltypes.MsgRecvPacket{}), + sdk.MsgTypeURL(&ibcchanneltypes.MsgAcknowledgement{}), + sdk.MsgTypeURL(&ibcclienttypes.MsgUpdateClient{}), + sdk.MsgTypeURL(&ibctransfertypes.MsgTransfer{}), + sdk.MsgTypeURL(&ibcchanneltypes.MsgTimeout{}), + sdk.MsgTypeURL(&ibcchanneltypes.MsgTimeoutOnClose{}), + sdk.MsgTypeURL(&ibcchanneltypes.MsgChannelOpenTry{}), + sdk.MsgTypeURL(&ibcchanneltypes.MsgChannelOpenConfirm{}), + sdk.MsgTypeURL(&ibcchanneltypes.MsgChannelOpenAck{}), + } +} + +func (app *OmniFlixApp) GetChainBondDenom() string { + return "uflix" +} diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 4fc6787e..fdfda755 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -1,6 +1,7 @@ package keepers import ( + globalfeetypes "github.com/OmniFlix/omniflixhub/v2/x/globalfee/types" "github.com/cometbft/cometbft/libs/log" tmos "github.com/cometbft/cometbft/libs/os" "github.com/cosmos/cosmos-sdk/baseapp" @@ -40,6 +41,9 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/OmniFlix/omniflixhub/v2/x/globalfee" + globalfeekeeper "github.com/OmniFlix/omniflixhub/v2/x/globalfee/keeper" + mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" @@ -116,6 +120,7 @@ type AppKeepers struct { FeeGrantKeeper feegrantkeeper.Keeper AuthzKeeper authzkeeper.Keeper ConsensusParamsKeeper consensusparamkeeper.Keeper + GlobalFeeKeeper globalfeekeeper.Keeper // make scoped keepers public for test purposes ScopedIBCKeeper capabilitykeeper.ScopedKeeper @@ -217,6 +222,7 @@ func NewAppKeeper( keys[feegrant.StoreKey], appKeepers.AccountKeeper, ) + appKeepers.StakingKeeper = stakingkeeper.NewKeeper( appCodec, keys[stakingtypes.StoreKey], @@ -357,6 +363,12 @@ func NewAppKeeper( ) icaHostIBCModule := icahost.NewIBCModule(appKeepers.ICAHostKeeper) + appKeepers.GlobalFeeKeeper = globalfeekeeper.NewKeeper( + appCodec, + keys[globalfeetypes.StoreKey], + govModAddress, + ) + appKeepers.AllocKeeper = *allockeeper.NewKeeper( appCodec, appKeepers.keys[alloctypes.StoreKey], @@ -450,6 +462,7 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino paramsKeeper.Subspace(ibcexported.ModuleName) paramsKeeper.Subspace(icahosttypes.SubModuleName) paramsKeeper.Subspace(packetforwardtypes.ModuleName) + paramsKeeper.Subspace(globalfee.ModuleName) paramsKeeper.Subspace(alloctypes.ModuleName) paramsKeeper.Subspace(onfttypes.ModuleName) paramsKeeper.Subspace(marketplacetypes.ModuleName) diff --git a/app/keepers/keys.go b/app/keepers/keys.go index 7ab61e72..93d5f3a5 100644 --- a/app/keepers/keys.go +++ b/app/keepers/keys.go @@ -2,6 +2,7 @@ package keepers import ( alloctypes "github.com/OmniFlix/omniflixhub/v2/x/alloc/types" + globalfeetypes "github.com/OmniFlix/omniflixhub/v2/x/globalfee/types" itctypes "github.com/OmniFlix/omniflixhub/v2/x/itc/types" marketplacetypes "github.com/OmniFlix/omniflixhub/v2/x/marketplace/types" onfttypes "github.com/OmniFlix/onft/types" @@ -49,6 +50,7 @@ func (appKeepers *AppKeepers) GenerateKeys() { capabilitytypes.StoreKey, crisistypes.StoreKey, feegrant.StoreKey, + globalfeetypes.StoreKey, authzkeeper.StoreKey, alloctypes.StoreKey, onfttypes.StoreKey, diff --git a/app/modules.go b/app/modules.go index 7f4dd3a4..015f7a42 100644 --- a/app/modules.go +++ b/app/modules.go @@ -2,6 +2,7 @@ package app import ( appparams "github.com/OmniFlix/omniflixhub/v2/app/params" + "github.com/OmniFlix/omniflixhub/v2/x/globalfee" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/auth" authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation" @@ -107,6 +108,7 @@ var ( evidence.AppModuleBasic{}, transfer.AppModuleBasic{}, vesting.AppModuleBasic{}, + globalfee.AppModuleBasic{}, alloc.AppModuleBasic{}, onft.AppModuleBasic{}, @@ -126,6 +128,7 @@ var ( govtypes.ModuleName: {authtypes.Burner}, ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, icatypes.ModuleName: nil, + globalfee.ModuleName: nil, alloctypes.ModuleName: {authtypes.Minter, authtypes.Burner, authtypes.Staking}, onfttypes.ModuleName: nil, marketplacetypes.ModuleName: nil, @@ -140,6 +143,7 @@ func appModules( skipGenesisInvariants bool, ) []module.AppModule { appCodec := encodingConfig.Marshaler + bondDenom := app.GetChainBondDenom() return []module.AppModule{ genutil.NewAppModule( app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx, @@ -191,6 +195,7 @@ func appModules( transfer.NewAppModule(app.TransferKeeper), ica.NewAppModule(nil, &app.ICAHostKeeper), packetforward.NewAppModule(app.PacketForwardKeeper), + globalfee.NewAppModule(appCodec, app.GlobalFeeKeeper, bondDenom), alloc.NewAppModule(appCodec, app.AllocKeeper, app.GetSubspace(alloctypes.ModuleName)), onft.NewAppModule( appCodec, @@ -272,6 +277,7 @@ func orderBeginBlockers() []string { authtypes.ModuleName, crisistypes.ModuleName, feegrant.ModuleName, + globalfee.ModuleName, onfttypes.ModuleName, marketplacetypes.ModuleName, streampaytypes.ModuleName, @@ -301,6 +307,7 @@ func orderEndBlockers() []string { distrtypes.ModuleName, ibcexported.ModuleName, feegrant.ModuleName, + globalfee.ModuleName, authz.ModuleName, alloctypes.ModuleName, onfttypes.ModuleName, @@ -338,6 +345,7 @@ func orderInitGenesis() []string { upgradetypes.ModuleName, vestingtypes.ModuleName, feegrant.ModuleName, + globalfee.ModuleName, ibcexported.ModuleName, ibctransfertypes.ModuleName, icatypes.ModuleName, diff --git a/go.mod b/go.mod index 62552d52..f2963c0a 100644 --- a/go.mod +++ b/go.mod @@ -26,18 +26,20 @@ require ( google.golang.org/protobuf v1.31.0 ) +require github.com/gogo/protobuf v1.3.2 // indirect + require ( - cosmossdk.io/api v0.3.1 - cosmossdk.io/errors v1.0.0 - cosmossdk.io/math v1.1.2 cloud.google.com/go v0.110.4 // indirect cloud.google.com/go/compute v1.20.1 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.0 // indirect cloud.google.com/go/storage v1.30.1 // indirect + cosmossdk.io/api v0.3.1 cosmossdk.io/core v0.5.1 // indirect cosmossdk.io/depinject v1.0.0-alpha.4 // indirect + cosmossdk.io/errors v1.0.0 cosmossdk.io/log v1.2.1 // indirect + cosmossdk.io/math v1.1.2 cosmossdk.io/tools/rosetta v0.2.1 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect @@ -84,7 +86,6 @@ require ( github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/googleapis v1.4.1 // indirect - github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/glog v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/mock v1.6.0 // indirect diff --git a/proto/OmniFlix/globalfee/v1beta1/genesis.proto b/proto/OmniFlix/globalfee/v1beta1/genesis.proto new file mode 100644 index 00000000..a02eb2df --- /dev/null +++ b/proto/OmniFlix/globalfee/v1beta1/genesis.proto @@ -0,0 +1,43 @@ +syntax = "proto3"; +package OmniFlix.globalfee.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; + +option go_package = "github.com/OmniFlix/omniflixhub/v2/x/globalfee/types"; + +// GenesisState - initial state of module +message GenesisState { + // Params of this module + Params params = 1 [ + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "params,omitempty" + ]; +} + +// Params defines the set of module parameters. +message Params { + // minimum_gas_prices stores the minimum gas price(s) for all TX on the chain. + // When multiple coins are defined then they are accepted alternatively. + // The list must be sorted by denoms asc. No duplicate denoms or zero amount + // values allowed. For more information see + // https://docs.cosmos.network/main/modules/auth#concepts + repeated cosmos.base.v1beta1.DecCoin minimum_gas_prices = 1 [ + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "minimum_gas_prices,omitempty", + (gogoproto.moretags) = "yaml:\"minimum_gas_prices\"", + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins" + ]; + + // bypass_min_fee_msg_types defines a list of message type urls + // that are free of fee charge. + repeated string bypass_min_fee_msg_types = 2 [ + (gogoproto.jsontag) = "bypass_min_fee_msg_types,omitempty", + (gogoproto.moretags) = "yaml:\"bypass_min_fee_msg_types\"" + ]; + + // max_total_bypass_min_fee_msg_gas_usage defines the total maximum gas usage + // allowed for a transaction containing only messages of types in bypass_min_fee_msg_types + // to bypass fee charge. + uint64 max_total_bypass_min_fee_msg_gas_usage = 3; +} \ No newline at end of file diff --git a/proto/OmniFlix/globalfee/v1beta1/query.proto b/proto/OmniFlix/globalfee/v1beta1/query.proto new file mode 100644 index 00000000..3f65e4ed --- /dev/null +++ b/proto/OmniFlix/globalfee/v1beta1/query.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; +package OmniFlix.globalfee.v1beta1; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "OmniFlix/globalfee/v1beta1/genesis.proto"; + +option go_package = "github.com/OmniFlix/omniflixhub/v2/x/globalfee/types"; + +// Query defines the gRPC querier service. +service Query { + rpc Params(QueryParamsRequest) + returns (QueryParamsResponse) { + option (google.api.http).get = + "/omniflix/globalfee/v1beta1/params"; + } +} + +// QueryMinimumGasPricesRequest is the request type for the +// Query/MinimumGasPrices RPC method. +message QueryParamsRequest {} + +// QueryMinimumGasPricesResponse is the response type for the +// Query/MinimumGasPrices RPC method. +message QueryParamsResponse { + Params params = 1 [(gogoproto.nullable) = false]; +} \ No newline at end of file diff --git a/proto/OmniFlix/globalfee/v1beta1/tx.proto b/proto/OmniFlix/globalfee/v1beta1/tx.proto new file mode 100644 index 00000000..4aa87b27 --- /dev/null +++ b/proto/OmniFlix/globalfee/v1beta1/tx.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; +package OmniFlix.globalfee.v1beta1; + + +import "cosmos/msg/v1/msg.proto"; +import "OmniFlix/globalfee/v1beta1/genesis.proto"; +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; + +option go_package = "github.com/OmniFlix/omniflixhub/v2/x/globalfee/types"; + +// Msg defines the x/globalfee Msg service. +service Msg { + // UpdateParams defines a governance operation for updating the x/globalfee module + // parameters. The authority is hard-coded to the x/gov module account. + // + // Since: cosmos-sdk 0.47 + rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); +} + +// MsgUpdateParams is the Msg/UpdateParams request type. +// +// Since: cosmos-sdk 0.47 +message MsgUpdateParams { + option (cosmos.msg.v1.signer) = "authority"; + + // authority is the address of the governance account. + string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + + // params defines the x/mint parameters to update. + // + // NOTE: All parameters must be supplied. + Params params = 2 [(gogoproto.nullable) = false]; +} + +// MsgUpdateParamsResponse defines the response structure for executing a +// MsgUpdateParams message. +// +// Since: cosmos-sdk 0.47 +message MsgUpdateParamsResponse {} \ No newline at end of file diff --git a/x/globalfee/README.md b/x/globalfee/README.md new file mode 100644 index 00000000..d9eccb73 --- /dev/null +++ b/x/globalfee/README.md @@ -0,0 +1,6 @@ +# Global fee module + +The Global fee module was supplied by the great folks at [TGrade](https://github.com/confio/tgrade) 👋, minor modifications done by [cosmoshub](https://github.com/cosmos/gaia/tree/main/x/globalfee) team. +All credits and big thanks go to the original authors and cosmoshub team. + +More information about global fee system please check [here](../../docs/modules/globalfee.md). diff --git a/x/globalfee/alias.go b/x/globalfee/alias.go new file mode 100644 index 00000000..eda7c90f --- /dev/null +++ b/x/globalfee/alias.go @@ -0,0 +1,9 @@ +package globalfee + +import ( + "github.com/OmniFlix/omniflixhub/v2/x/globalfee/types" +) + +const ( + ModuleName = types.ModuleName +) diff --git a/x/globalfee/ante/fee.go b/x/globalfee/ante/fee.go new file mode 100644 index 00000000..4f090e53 --- /dev/null +++ b/x/globalfee/ante/fee.go @@ -0,0 +1,215 @@ +package ante + +import ( + "errors" + + tmstrings "github.com/cometbft/cometbft/libs/strings" + + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + + globalfeekeeper "github.com/OmniFlix/omniflixhub/v2/x/globalfee/keeper" +) + +// FeeWithBypassDecorator checks if the transaction's fee is at least as large +// as the local validator's minimum gasFee (defined in validator config) and global fee, and the fee denom should be in the global fees' denoms. +// +// If fee is too low, decorator returns error and tx is rejected from mempool. +// Note this only applies when ctx.CheckTx = true. If fee is high enough or not +// CheckTx, then call next AnteHandler. +// +// CONTRACT: Tx must implement FeeTx to use FeeDecorator +// If the tx msg type is one of the bypass msg types, the tx is valid even if the min fee is lower than normally required. +// If the bypass tx still carries fees, the fee denom should be the same as global fee required. + +var _ sdk.AnteDecorator = FeeDecorator{} + +type FeeDecorator struct { + BypassMinFeeMsgTypes []string + GlobalFeeKeeper globalfeekeeper.Keeper + StakingKeeper stakingkeeper.Keeper + MaxTotalBypassMinFeeMsgGasUsage uint64 +} + +func NewFeeDecorator(bypassMsgTypes []string, gfk globalfeekeeper.Keeper, sk stakingkeeper.Keeper, maxTotalBypassMinFeeMsgGasUsage uint64) FeeDecorator { + return FeeDecorator{ + BypassMinFeeMsgTypes: bypassMsgTypes, + GlobalFeeKeeper: gfk, + StakingKeeper: sk, + MaxTotalBypassMinFeeMsgGasUsage: maxTotalBypassMinFeeMsgGasUsage, + } +} + +// AnteHandle implements the AnteDecorator interface +func (mfd FeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + feeTx, ok := tx.(sdk.FeeTx) + if !ok { + return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must implement the sdk.FeeTx interface") + } + + // Only check for minimum fees and global fee if the execution mode is CheckTx + if !ctx.IsCheckTx() || simulate { + return next(ctx, tx, simulate) + } + + // Sort fee tx's coins, zero coins in feeCoins are already removed + feeCoins := feeTx.GetFee().Sort() + gas := feeTx.GetGas() + msgs := feeTx.GetMsgs() + + // Get required Global Fee + requiredGlobalFees, err := mfd.GetGlobalFee(ctx, feeTx) + if err != nil { + return ctx, err + } + + // Get local minimum-gas-prices + localFees := GetMinGasPrice(ctx, int64(feeTx.GetGas())) + + // CombinedFeeRequirement should never be empty since + // global fee is set to its default value, i.e. 0uatom, if empty + combinedFeeRequirement := CombinedFeeRequirement(requiredGlobalFees, localFees) + if len(combinedFeeRequirement) == 0 { + return ctx, errorsmod.Wrapf(sdkerrors.ErrNotFound, "required fees are not setup.") + } + + nonZeroCoinFeesReq, zeroCoinFeesDenomReq := getNonZeroFees(combinedFeeRequirement) + + // feeCoinsNonZeroDenom contains non-zero denominations from the combinedFeeRequirement + // + // feeCoinsNoZeroDenom is used to check if the fees meets the requirement imposed by nonZeroCoinFeesReq + // when feeCoins does not contain zero coins' denoms in combinedFeeRequirement + feeCoinsNonZeroDenom, feeCoinsZeroDenom := splitCoinsByDenoms(feeCoins, zeroCoinFeesDenomReq) + + // Check that the fees are in expected denominations. + // if feeCoinsNoZeroDenom=[], DenomsSubsetOf returns true + // if feeCoinsNoZeroDenom is not empty, but nonZeroCoinFeesReq empty, return false + if !feeCoinsNonZeroDenom.DenomsSubsetOf(nonZeroCoinFeesReq) { + return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "this fee denom is not accepted; got %s, one is required: %s", feeCoins, PrettyPrint(combinedFeeRequirement)) + } + + // Accept zero fee transactions only if both of the following statements are true: + // + // - the tx contains only message types that can bypass the minimum fee, + // see BypassMinFeeMsgTypes; + // - the total gas limit per message does not exceed MaxTotalBypassMinFeeMsgGasUsage, + // i.e., totalGas <= MaxTotalBypassMinFeeMsgGasUsage + // + // Otherwise, minimum fees and global fees are checked to prevent spam. + doesNotExceedMaxGasUsage := gas <= mfd.MaxTotalBypassMinFeeMsgGasUsage + allowedToBypassMinFee := mfd.ContainsOnlyBypassMinFeeMsgs(msgs) && doesNotExceedMaxGasUsage + + // Either the transaction contains at least one message of a type + // that cannot bypass the minimum fee or the total gas limit exceeds + // the imposed threshold. As a result, besides check the fees are in + // expected denominations, check the amounts are greater or equal than + // the expected amounts. + + // only check feeCoinsNoZeroDenom has coins IsAnyGTE than nonZeroCoinFeesReq + // when feeCoins does not contain denoms of zero denoms in combinedFeeRequirement + if !allowedToBypassMinFee && len(feeCoinsZeroDenom) == 0 { + // special case: when feeCoins=[] and there is zero coin in fee requirement + if len(feeCoins) == 0 && len(zeroCoinFeesDenomReq) != 0 { + return next(ctx, tx, simulate) + } + + // Check that the amounts of the fees are greater or equal than + // the expected amounts, i.e., at least one feeCoin amount must + // be greater or equal to one of the combined required fees. + + // if feeCoinsNoZeroDenom=[], return false + // if nonZeroCoinFeesReq=[], return false (this situation should not happen + // because when nonZeroCoinFeesReq empty, and DenomsSubsetOf check passed, + // the tx should already passed before) + if !feeCoinsNonZeroDenom.IsAnyGTE(nonZeroCoinFeesReq) { + if len(feeCoins) == 0 { + return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "no fees were specified; one fee must be provided %s", PrettyPrint(combinedFeeRequirement)) + } + + return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; only got: %s. one is required: %s. ", feeCoins, PrettyPrint(combinedFeeRequirement)) + } + } + + return next(ctx, tx, simulate) +} + +// GetGlobalFee returns the global fees for a given fee tx's gas +// (might also return 0denom if globalMinGasPrice is 0) +// sorted in ascending order. +// Note that ParamStoreKeyMinGasPrices type requires coins sorted. +func (mfd FeeDecorator) GetGlobalFee(ctx sdk.Context, feeTx sdk.FeeTx) (sdk.Coins, error) { + var ( + globalMinGasPrices sdk.DecCoins + err error + ) + + globalMinGasPrices = mfd.GlobalFeeKeeper.GetParams(ctx).MinimumGasPrices + + // global fee is empty set, set global fee to 0uatom + if len(globalMinGasPrices) == 0 { + globalMinGasPrices, err = mfd.DefaultZeroGlobalFee(ctx) + if err != nil { + return sdk.Coins{}, err + } + } + requiredGlobalFees := make(sdk.Coins, len(globalMinGasPrices)) + // Determine the required fees by multiplying each required minimum gas + // price by the gas limit, where fee = ceil(minGasPrice * gasLimit). + glDec := sdk.NewDec(int64(feeTx.GetGas())) + for i, gp := range globalMinGasPrices { + fee := gp.Amount.Mul(glDec) + requiredGlobalFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt()) + } + + return requiredGlobalFees.Sort(), nil +} + +func (mfd FeeDecorator) DefaultZeroGlobalFee(ctx sdk.Context) ([]sdk.DecCoin, error) { + bondDenom := mfd.getBondDenom(ctx) + if bondDenom == "" { + return nil, errors.New("empty staking bond denomination") + } + + return []sdk.DecCoin{sdk.NewDecCoinFromDec(bondDenom, sdk.NewDec(0))}, nil +} + +func (mfd FeeDecorator) getBondDenom(ctx sdk.Context) string { + return mfd.StakingKeeper.BondDenom(ctx) +} + +// ContainsOnlyBypassMinFeeMsgs returns true if all the given msgs type are listed +// in the BypassMinFeeMsgTypes of the FeeDecorator. +func (mfd FeeDecorator) ContainsOnlyBypassMinFeeMsgs(msgs []sdk.Msg) bool { + for _, msg := range msgs { + if tmstrings.StringInSlice(sdk.MsgTypeURL(msg), mfd.BypassMinFeeMsgTypes) { + continue + } + return false + } + + return true +} + +// GetMinGasPrice returns the validator's minimum gas prices +// fees given a gas limit +func GetMinGasPrice(ctx sdk.Context, gasLimit int64) sdk.Coins { + minGasPrices := ctx.MinGasPrices() + // special case: if minGasPrices=[], requiredFees=[] + if minGasPrices.IsZero() { + return sdk.Coins{} + } + + requiredFees := make(sdk.Coins, len(minGasPrices)) + // Determine the required fees by multiplying each required minimum gas + // price by the gas limit, where fee = ceil(minGasPrice * gasLimit). + glDec := sdk.NewDec(gasLimit) + for i, gp := range minGasPrices { + fee := gp.Amount.Mul(glDec) + requiredFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt()) + } + + return requiredFees.Sort() +} diff --git a/x/globalfee/ante/fee_utils.go b/x/globalfee/ante/fee_utils.go new file mode 100644 index 00000000..6b70add3 --- /dev/null +++ b/x/globalfee/ante/fee_utils.go @@ -0,0 +1,128 @@ +package ante + +import ( + "encoding/json" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// ContainZeroCoins returns true if the given coins are empty or contain zero coins, +// Note that the coins denoms must be validated, see sdk.ValidateDenom +func ContainZeroCoins(coins sdk.Coins) bool { + if len(coins) == 0 { + return true + } + for _, coin := range coins { + if coin.IsZero() { + return true + } + } + + return false +} + +// PrettyPrint returns a pretty printed representation of the given coins. +func PrettyPrint(coins sdk.Coins) string { + arr := make([]string, len(coins)) + for idx, coin := range coins { + arr[idx] = fmt.Sprintf("%s%s", coin.Amount.String(), coin.Denom) + } + + bz, err := json.MarshalIndent(arr, "", " ") + if err != nil { + return "" + } + + return string(bz) +} + +// CombinedFeeRequirement returns the global fee and min_gas_price combined and sorted. +// Both globalFees and minGasPrices must be valid, but CombinedFeeRequirement +// does not validate them, so it may return 0denom. +// if globalfee is empty, CombinedFeeRequirement return sdk.Coins{} +func CombinedFeeRequirement(globalFees, minGasPrices sdk.Coins) sdk.Coins { + // empty min_gas_price + if len(minGasPrices) == 0 { + return globalFees + } + // empty global fee is not possible if we set default global fee + if len(globalFees) == 0 && len(minGasPrices) != 0 { + return sdk.Coins{} + } + + // if min_gas_price denom is in globalfee, and the amount is higher than globalfee, add min_gas_price to allFees + var allFees sdk.Coins + for _, fee := range globalFees { + // min_gas_price denom in global fee + ok, c := Find(minGasPrices, fee.Denom) + if ok && c.Amount.GT(fee.Amount) { + allFees = append(allFees, c) + } else { + allFees = append(allFees, fee) + } + } + + return allFees.Sort() +} + +// Find replaces the functionality of Coins.Find from SDK v0.46.x +func Find(coins sdk.Coins, denom string) (bool, sdk.Coin) { + switch len(coins) { + case 0: + return false, sdk.Coin{} + + case 1: + coin := coins[0] + if coin.Denom == denom { + return true, coin + } + return false, sdk.Coin{} + + default: + midIdx := len(coins) / 2 // 2:1, 3:1, 4:2 + coin := coins[midIdx] + switch { + case denom < coin.Denom: + return Find(coins[:midIdx], denom) + case denom == coin.Denom: + return true, coin + default: + return Find(coins[midIdx+1:], denom) + } + } +} + +// splitCoinsByDenoms returns the given coins split in two whether +// their demon is or isn't found in the given denom map. +func splitCoinsByDenoms(feeCoins sdk.Coins, denomMap map[string]bool) (sdk.Coins, sdk.Coins) { + feeCoinsNonZeroDenom, feeCoinsZeroDenom := sdk.Coins{}, sdk.Coins{} + + for _, fc := range feeCoins { + _, found := denomMap[fc.Denom] + if found { + feeCoinsZeroDenom = append(feeCoinsZeroDenom, fc) + } else { + feeCoinsNonZeroDenom = append(feeCoinsNonZeroDenom, fc) + } + } + + return feeCoinsNonZeroDenom.Sort(), feeCoinsZeroDenom.Sort() +} + +// getNonZeroFees returns the given fees nonzero coins +// and a map storing the zero coins's denoms +func getNonZeroFees(fees sdk.Coins) (sdk.Coins, map[string]bool) { + requiredFeesNonZero := sdk.Coins{} + requiredFeesZeroDenom := map[string]bool{} + + for _, gf := range fees { + if gf.IsZero() { + requiredFeesZeroDenom[gf.Denom] = true + } else { + requiredFeesNonZero = append(requiredFeesNonZero, gf) + } + } + + return requiredFeesNonZero.Sort(), requiredFeesZeroDenom +} diff --git a/x/globalfee/ante/fee_utils_test.go b/x/globalfee/ante/fee_utils_test.go new file mode 100644 index 00000000..85552046 --- /dev/null +++ b/x/globalfee/ante/fee_utils_test.go @@ -0,0 +1,299 @@ +package ante + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestContainZeroCoins(t *testing.T) { + zeroCoin1 := sdk.NewCoin("photon", sdk.ZeroInt()) + zeroCoin2 := sdk.NewCoin("uflix", sdk.ZeroInt()) + coin1 := sdk.NewCoin("photon", sdk.NewInt(1)) + coin2 := sdk.NewCoin("uflix", sdk.NewInt(2)) + coin3 := sdk.NewCoin("quark", sdk.NewInt(3)) + // coins must be valid !!! + coinsEmpty := sdk.Coins{} + coinsNonEmpty := sdk.Coins{coin1, coin2} + coinsCointainZero := sdk.Coins{coin1, zeroCoin2} + coinsCointainTwoZero := sdk.Coins{zeroCoin1, zeroCoin2, coin3} + coinsAllZero := sdk.Coins{zeroCoin1, zeroCoin2} + + tests := []struct { + c sdk.Coins + ok bool + }{ + { + coinsEmpty, + true, + }, + { + coinsNonEmpty, + false, + }, + { + coinsCointainZero, + true, + }, + { + coinsCointainTwoZero, + true, + }, + { + coinsAllZero, + true, + }, + } + + for _, test := range tests { + ok := ContainZeroCoins(test.c) + require.Equal(t, test.ok, ok) + } +} + +// Note that in a real Gaia deployment all zero coins can be removed from minGasPrice. +// This sanitizing happens when the minGasPrice is set into the context. +// (see baseapp.SetMinGasPrices in gaia/cmd/root.go line 221) +func TestCombinedFeeRequirement(t *testing.T) { + zeroCoin1 := sdk.NewCoin("photon", sdk.ZeroInt()) + zeroCoin2 := sdk.NewCoin("stake", sdk.ZeroInt()) + zeroCoin3 := sdk.NewCoin("quark", sdk.ZeroInt()) + coin1 := sdk.NewCoin("photon", sdk.NewInt(1)) + coin2 := sdk.NewCoin("stake", sdk.NewInt(2)) + coin1High := sdk.NewCoin("photon", sdk.NewInt(10)) + coin2High := sdk.NewCoin("stake", sdk.NewInt(20)) + coinNewDenom1 := sdk.NewCoin("Newphoton", sdk.NewInt(1)) + coinNewDenom2 := sdk.NewCoin("Newstake", sdk.NewInt(1)) + // coins must be valid !!! and sorted!!! + coinsEmpty := sdk.Coins{} + coinsNonEmpty := sdk.Coins{coin1, coin2}.Sort() + coinsNonEmptyHigh := sdk.Coins{coin1High, coin2High}.Sort() + coinsNonEmptyOneHigh := sdk.Coins{coin1High, coin2}.Sort() + coinsNewDenom := sdk.Coins{coinNewDenom1, coinNewDenom2}.Sort() + coinsNewOldDenom := sdk.Coins{coin1, coinNewDenom1}.Sort() + coinsNewOldDenomHigh := sdk.Coins{coin1High, coinNewDenom1}.Sort() + coinsCointainZero := sdk.Coins{coin1, zeroCoin2}.Sort() + coinsCointainZeroNewDenom := sdk.Coins{coin1, zeroCoin3}.Sort() + coinsAllZero := sdk.Coins{zeroCoin1, zeroCoin2}.Sort() + tests := map[string]struct { + cGlobal sdk.Coins + c sdk.Coins + combined sdk.Coins + }{ + "global fee empty, min fee empty, combined fee empty": { + cGlobal: coinsEmpty, + c: coinsEmpty, + combined: coinsEmpty, + }, + "global fee empty, min fee nonempty, combined fee empty": { + cGlobal: coinsEmpty, + c: coinsNonEmpty, + combined: coinsEmpty, + }, + "global fee nonempty, min fee empty, combined fee = global fee": { + cGlobal: coinsNonEmpty, + c: coinsNonEmpty, + combined: coinsNonEmpty, + }, + "global fee and min fee have overlapping denom, min fees amounts are all higher": { + cGlobal: coinsNonEmpty, + c: coinsNonEmptyHigh, + combined: coinsNonEmptyHigh, + }, + "global fee and min fee have overlapping denom, one of min fees amounts is higher": { + cGlobal: coinsNonEmpty, + c: coinsNonEmptyOneHigh, + combined: coinsNonEmptyOneHigh, + }, + "global fee and min fee have no overlapping denom, combined fee = global fee": { + cGlobal: coinsNonEmpty, + c: coinsNewDenom, + combined: coinsNonEmpty, + }, + "global fees and min fees have partial overlapping denom, min fee amount <= global fee amount, combined fees = global fees": { + cGlobal: coinsNonEmpty, + c: coinsNewOldDenom, + combined: coinsNonEmpty, + }, + "global fees and min fees have partial overlapping denom, one min fee amount > global fee amount, combined fee = overlapping highest": { + cGlobal: coinsNonEmpty, + c: coinsNewOldDenomHigh, + combined: sdk.Coins{coin1High, coin2}, + }, + "global fees have zero fees, min fees have overlapping non-zero fees, combined fees = overlapping highest": { + cGlobal: coinsCointainZero, + c: coinsNonEmpty, + combined: sdk.Coins{coin1, coin2}, + }, + "global fees have zero fees, min fees have overlapping zero fees": { + cGlobal: coinsCointainZero, + c: coinsCointainZero, + combined: coinsCointainZero, + }, + "global fees have zero fees, min fees have non-overlapping zero fees": { + cGlobal: coinsCointainZero, + c: coinsCointainZeroNewDenom, + combined: coinsCointainZero, + }, + "global fees are all zero fees, min fees have overlapping zero fees": { + cGlobal: coinsAllZero, + c: coinsAllZero, + combined: coinsAllZero, + }, + "global fees are all zero fees, min fees have overlapping non-zero fees, combined fee = overlapping highest": { + cGlobal: coinsAllZero, + c: coinsCointainZeroNewDenom, + combined: sdk.Coins{coin1, zeroCoin2}, + }, + "global fees are all zero fees, fees have one overlapping non-zero fee": { + cGlobal: coinsAllZero, + c: coinsCointainZero, + combined: coinsCointainZero, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + allFees := CombinedFeeRequirement(test.cGlobal, test.c) + require.Equal(t, test.combined, allFees) + }) + } +} + +func TestSplitCoinsByDenoms(t *testing.T) { + zeroGlobalFeesDenom0 := map[string]bool{} + zeroGlobalFeesDenom1 := map[string]bool{ + "uatom": true, + "photon": true, + } + zeroGlobalFeesDenom2 := map[string]bool{ + "uatom": true, + } + zeroGlobalFeesDenom3 := map[string]bool{ + "stake": true, + } + + photon := sdk.NewCoin("photon", sdk.OneInt()) + uatom := sdk.NewCoin("uatom", sdk.OneInt()) + feeCoins := sdk.NewCoins(photon, uatom) + + tests := map[string]struct { + feeCoins sdk.Coins + zeroGlobalFeesDenom map[string]bool + expectedNonZeroCoins sdk.Coins + expectedZeroCoins sdk.Coins + }{ + "no zero coins in global fees": { + feeCoins: feeCoins, + zeroGlobalFeesDenom: zeroGlobalFeesDenom0, + expectedNonZeroCoins: feeCoins, + expectedZeroCoins: sdk.Coins{}, + }, + "no split of fee coins": { + feeCoins: feeCoins, + zeroGlobalFeesDenom: zeroGlobalFeesDenom3, + expectedNonZeroCoins: feeCoins, + expectedZeroCoins: sdk.Coins{}, + }, + "split the fee coins": { + feeCoins: feeCoins, + zeroGlobalFeesDenom: zeroGlobalFeesDenom2, + expectedNonZeroCoins: sdk.NewCoins(photon), + expectedZeroCoins: sdk.NewCoins(uatom), + }, + "remove all of the fee coins": { + feeCoins: feeCoins, + zeroGlobalFeesDenom: zeroGlobalFeesDenom1, + expectedNonZeroCoins: sdk.Coins{}, + expectedZeroCoins: feeCoins, + }, + "fee coins are empty": { + feeCoins: sdk.Coins{}, + zeroGlobalFeesDenom: zeroGlobalFeesDenom1, + expectedNonZeroCoins: sdk.Coins{}, + expectedZeroCoins: sdk.Coins{}, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + feeCoinsNoZeroDenoms, feeCoinsZeroDenoms := splitCoinsByDenoms(test.feeCoins, test.zeroGlobalFeesDenom) + require.Equal(t, test.expectedNonZeroCoins, feeCoinsNoZeroDenoms) + require.Equal(t, test.expectedZeroCoins, feeCoinsZeroDenoms) + }) + } +} + +func TestSplitGlobalFees(t *testing.T) { + photon0 := sdk.NewCoin("photon", sdk.ZeroInt()) + uatom0 := sdk.NewCoin("uatom", sdk.ZeroInt()) + photon1 := sdk.NewCoin("photon", sdk.OneInt()) + uatom1 := sdk.NewCoin("uatom", sdk.OneInt()) + + globalFeesEmpty := sdk.Coins{} + globalFees := sdk.Coins{photon1, uatom1}.Sort() + globalFeesZeroCoins := sdk.Coins{photon0, uatom0}.Sort() + globalFeesMix := sdk.Coins{photon0, uatom1}.Sort() + + tests := map[string]struct { + globalfees sdk.Coins + zeroGlobalFeesDenom map[string]bool + globalfeesNonZero sdk.Coins + }{ + "empty global fees": { + globalfees: globalFeesEmpty, + zeroGlobalFeesDenom: map[string]bool{}, + globalfeesNonZero: sdk.Coins{}, + }, + "nonzero coins global fees": { + globalfees: globalFees, + zeroGlobalFeesDenom: map[string]bool{}, + globalfeesNonZero: globalFees, + }, + "zero coins global fees": { + globalfees: globalFeesZeroCoins, + zeroGlobalFeesDenom: map[string]bool{ + "photon": true, + "uatom": true, + }, + globalfeesNonZero: sdk.Coins{}, + }, + "mix zero, nonzero coins global fees": { + globalfees: globalFeesMix, + zeroGlobalFeesDenom: map[string]bool{ + "photon": true, + }, + globalfeesNonZero: sdk.NewCoins(uatom1), + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + nonZeroCoins, zeroCoinsMap := getNonZeroFees(test.globalfees) + require.True(t, nonZeroCoins.IsEqual(test.globalfeesNonZero)) + require.True(t, equalMap(zeroCoinsMap, test.zeroGlobalFeesDenom)) + }) + } +} + +func equalMap(a, b map[string]bool) bool { + if len(a) != len(b) { + return false + } + if len(a) == 0 && len(b) == 0 { + return true + } + if len(a) == 0 { + return false + } + + for k := range a { + if _, ok := b[k]; !ok { + return false + } + } + + return true +} diff --git a/x/globalfee/client/cli/query.go b/x/globalfee/client/cli/query.go new file mode 100644 index 00000000..31c6d858 --- /dev/null +++ b/x/globalfee/client/cli/query.go @@ -0,0 +1,49 @@ +package cli + +import ( + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + + "github.com/OmniFlix/omniflixhub/v2/x/globalfee/types" +) + +func GetQueryCmd() *cobra.Command { + queryCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the global fee module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + queryCmd.AddCommand( + GetCmdShowGlobalFeeParams(), + ) + return queryCmd +} + +func GetCmdShowGlobalFeeParams() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "Show globalfee params", + Long: "Show all globalfee params", + Aliases: []string{"min"}, + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + res, err := queryClient.Params(cmd.Context(), &types.QueryParamsRequest{}) + if err != nil { + return err + } + return clientCtx.PrintProto(res) + }, + } + flags.AddQueryFlagsToCmd(cmd) + return cmd +} diff --git a/x/globalfee/genesis_test.go b/x/globalfee/genesis_test.go new file mode 100644 index 00000000..185b514f --- /dev/null +++ b/x/globalfee/genesis_test.go @@ -0,0 +1,148 @@ +package globalfee + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + dbm "github.com/cometbft/cometbft-db" + "github.com/cometbft/cometbft/libs/log" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + appparams "github.com/OmniFlix/omniflixhub/v2/app/params" + globalfeekeeper "github.com/OmniFlix/omniflixhub/v2/x/globalfee/keeper" + "github.com/OmniFlix/omniflixhub/v2/x/globalfee/types" +) + +func TestDefaultGenesis(t *testing.T) { + encCfg := appparams.MakeEncodingConfig() + gotJSON := AppModuleBasic{}.DefaultGenesis(encCfg.Marshaler) + assert.JSONEq(t, `{"params":{"minimum_gas_prices":[],"bypass_min_fee_msg_types":[],"max_total_bypass_min_fee_msg_gas_usage":"2000000"}}`, string(gotJSON), string(gotJSON)) +} + +func TestValidateGenesis(t *testing.T) { + encCfg := appparams.MakeEncodingConfig() + specs := map[string]struct { + src string + expErr bool + }{ + "all good": { + src: `{"params":{"minimum_gas_prices":[{"denom":"ALX", "amount":"1"}]}}`, + }, + "empty minimum": { + src: `{"params":{"minimum_gas_prices":[]}}`, + }, + "minimum not set": { + src: `{"params":{}}`, + }, + "zero amount allowed": { + src: `{"params":{"minimum_gas_prices":[{"denom":"ALX", "amount":"0"}]}}`, + expErr: false, + }, + "duplicate denoms not allowed": { + src: `{"params":{"minimum_gas_prices":[{"denom":"ALX", "amount":"1"},{"denom":"ALX", "amount":"2"}]}}`, + expErr: true, + }, + "negative amounts not allowed": { + src: `{"params":{"minimum_gas_prices":[{"denom":"ALX", "amount":"-1"}]}}`, + expErr: true, + }, + "denom must be sorted": { + src: `{"params":{"minimum_gas_prices":[{"denom":"ZLX", "amount":"1"},{"denom":"ALX", "amount":"2"}]}}`, + expErr: true, + }, + "sorted denoms is allowed": { + src: `{"params":{"minimum_gas_prices":[{"denom":"ALX", "amount":"1"},{"denom":"ZLX", "amount":"2"}]}}`, + expErr: false, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + gotErr := AppModuleBasic{}.ValidateGenesis(encCfg.Marshaler, nil, []byte(spec.src)) + if spec.expErr { + require.Error(t, gotErr) + return + } + require.NoError(t, gotErr) + }) + } +} + +func TestInitExportGenesis(t *testing.T) { + specs := map[string]struct { + src string + exp types.GenesisState + }{ + "single fee": { + src: `{"params":{"minimum_gas_prices":[{"denom":"ALX", "amount":"1"}],"bypass_min_fee_msg_types":[],"max_total_bypass_min_fee_msg_gas_usage":"0"}}`, + exp: types.GenesisState{ + Params: types.Params{ + MinimumGasPrices: sdk.NewDecCoins(sdk.NewDecCoin("ALX", sdk.NewInt(1))), + BypassMinFeeMsgTypes: []string{}, + MaxTotalBypassMinFeeMsgGasUsage: uint64(0), + }, + }, + }, + "multiple fee options": { + src: `{"params":{"minimum_gas_prices":[{"denom":"ALX", "amount":"1"}, {"denom":"BLX", "amount":"0.001"}],"bypass_min_fee_msg_types":[],"max_total_bypass_min_fee_msg_gas_usage":"0"}}`, + exp: types.GenesisState{ + Params: types.Params{ + MinimumGasPrices: sdk.NewDecCoins( + sdk.NewDecCoin("ALX", sdk.NewInt(1)), + sdk.NewDecCoinFromDec("BLX", sdk.NewDecWithPrec(1, 3)), + ), + BypassMinFeeMsgTypes: []string{}, + MaxTotalBypassMinFeeMsgGasUsage: uint64(0), + }, + }, + }, + "no fee set": { + src: `{"params":{"minimum_gas_prices":[],"bypass_min_fee_msg_types":[],"max_total_bypass_min_fee_msg_gas_usage":"0"}}`, + exp: types.GenesisState{ + Params: types.Params{ + MinimumGasPrices: sdk.DecCoins{}, + BypassMinFeeMsgTypes: []string{}, + MaxTotalBypassMinFeeMsgGasUsage: uint64(0), + }, + }, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + ctx, encCfg, keeper := setupTestStore(t) + m := NewAppModule(encCfg.Marshaler, keeper, "uflix") + m.InitGenesis(ctx, encCfg.Marshaler, []byte(spec.src)) + gotJSON := m.ExportGenesis(ctx, encCfg.Marshaler) + var got types.GenesisState + t.Log(got) + require.NoError(t, encCfg.Marshaler.UnmarshalJSON(gotJSON, &got)) + assert.Equal(t, spec.exp, got, string(gotJSON)) + }) + } +} + +func setupTestStore(t *testing.T) (sdk.Context, appparams.EncodingConfig, globalfeekeeper.Keeper) { + t.Helper() + db := dbm.NewMemDB() + ms := store.NewCommitMultiStore(db) + encCfg := appparams.MakeEncodingConfig() + keyParams := sdk.NewKVStoreKey(types.StoreKey) + ms.MountStoreWithDB(keyParams, storetypes.StoreTypeIAVL, db) + require.NoError(t, ms.LoadLatestVersion()) + + globalfeeKeeper := globalfeekeeper.NewKeeper(encCfg.Marshaler, keyParams, "omniflix1llyd96levrglxhw6sczgk9wn48t64zkhv4fq0r") + + ctx := sdk.NewContext(ms, tmproto.Header{ + Height: 1234567, + Time: time.Date(2020, time.April, 22, 12, 0, 0, 0, time.UTC), + ChainID: "testing", + }, false, log.NewNopLogger()) + + return ctx, encCfg, globalfeeKeeper +} diff --git a/x/globalfee/keeper/grpc_query.go b/x/globalfee/keeper/grpc_query.go new file mode 100644 index 00000000..a18000f7 --- /dev/null +++ b/x/globalfee/keeper/grpc_query.go @@ -0,0 +1,31 @@ +package keeper + +import ( + "context" + + "github.com/OmniFlix/omniflixhub/v2/x/globalfee/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var _ types.QueryServer = &GrpcQuerier{} + +type GrpcQuerier struct { + keeper Keeper +} + +func NewGrpcQuerier(k Keeper) GrpcQuerier { + return GrpcQuerier{ + keeper: k, + } +} + +// Params return global-fee params +func (g GrpcQuerier) Params(stdCtx context.Context, _ *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + ctx := sdk.UnwrapSDKContext(stdCtx) + + p := g.keeper.GetParams(ctx) + + return &types.QueryParamsResponse{ + Params: p, + }, nil +} diff --git a/x/globalfee/keeper/grpc_query_test.go b/x/globalfee/keeper/grpc_query_test.go new file mode 100644 index 00000000..86938ab4 --- /dev/null +++ b/x/globalfee/keeper/grpc_query_test.go @@ -0,0 +1,60 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + + globalfeekeeper "github.com/OmniFlix/omniflixhub/v2/x/globalfee/keeper" + "github.com/OmniFlix/omniflixhub/v2/x/globalfee/types" +) + +func TestQueryGlobalFeeParamMinGasPrices(t *testing.T) { + specs := map[string]struct { + setupStore func(ctx sdk.Context, k globalfeekeeper.Keeper) + expMin sdk.DecCoins + }{ + "single fee coin": { + setupStore: func(ctx sdk.Context, k globalfeekeeper.Keeper) { + err := k.SetParams(ctx, types.Params{ + MinimumGasPrices: sdk.NewDecCoins(sdk.NewDecCoin("uflix", sdk.OneInt())), + }) + require.NoError(t, err) + }, + expMin: sdk.NewDecCoins(sdk.NewDecCoin("uflix", sdk.OneInt())), + }, + "multiple fee coins": { + setupStore: func(ctx sdk.Context, k globalfeekeeper.Keeper) { + err := k.SetParams(ctx, types.Params{ + MinimumGasPrices: sdk.NewDecCoins(sdk.NewDecCoin("uflix", sdk.OneInt()), sdk.NewDecCoin("test", sdk.NewInt(2))), + }) + require.NoError(t, err) + }, + expMin: sdk.NewDecCoins(sdk.NewDecCoin("uflix", sdk.OneInt()), sdk.NewDecCoin("test", sdk.NewInt(2))), + }, + "no coins": { + setupStore: func(ctx sdk.Context, k globalfeekeeper.Keeper) { + err := k.SetParams(ctx, types.Params{}) + require.NoError(t, err) + }, + }, + "no param set": { + setupStore: func(ctx sdk.Context, k globalfeekeeper.Keeper) { + }, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + ctx, _, keeper := setupTestStore(t) + spec.setupStore(ctx, keeper) + q := globalfeekeeper.NewGrpcQuerier(keeper) + gotResp, gotErr := q.Params(sdk.WrapSDKContext(ctx), nil) + require.NoError(t, gotErr) + require.NotNil(t, gotResp) + assert.Equal(t, spec.expMin, gotResp.Params.MinimumGasPrices) + }) + } +} diff --git a/x/globalfee/keeper/keeper.go b/x/globalfee/keeper/keeper.go new file mode 100644 index 00000000..9b11aacc --- /dev/null +++ b/x/globalfee/keeper/keeper.go @@ -0,0 +1,61 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/OmniFlix/omniflixhub/v2/x/globalfee/types" +) + +// Keeper of the globalfee store +type Keeper struct { + cdc codec.BinaryCodec + storeKey storetypes.StoreKey + + // the address capable of executing a MsgUpdateParams message. Typically, this + // should be the x/gov module account. + authority string +} + +func NewKeeper( + cdc codec.BinaryCodec, + key storetypes.StoreKey, + authority string, +) Keeper { + return Keeper{ + cdc: cdc, + storeKey: key, + authority: authority, + } +} + +// GetAuthority returns the x/globalfee module's authority. +func (k Keeper) GetAuthority() string { + return k.authority +} + +// SetParams sets the x/globalfee module parameters. +func (k Keeper) SetParams(ctx sdk.Context, p types.Params) error { + if err := p.Validate(); err != nil { + return err + } + + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshal(&p) + store.Set(types.ParamsKey, bz) + + return nil +} + +// GetParams returns the current x/globalfee module parameters. +func (k Keeper) GetParams(ctx sdk.Context) (p types.Params) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.ParamsKey) + if bz == nil { + return p + } + + k.cdc.MustUnmarshal(bz, &p) + return p +} diff --git a/x/globalfee/keeper/keeper_test.go b/x/globalfee/keeper/keeper_test.go new file mode 100644 index 00000000..0dfb225f --- /dev/null +++ b/x/globalfee/keeper/keeper_test.go @@ -0,0 +1,37 @@ +package keeper_test + +import ( + "testing" + "time" + + appparams "github.com/OmniFlix/omniflixhub/v2/app/params" + globalfeekeeper "github.com/OmniFlix/omniflixhub/v2/x/globalfee/keeper" + "github.com/OmniFlix/omniflixhub/v2/x/globalfee/types" + dbm "github.com/cometbft/cometbft-db" + "github.com/cometbft/cometbft/libs/log" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +func setupTestStore(t *testing.T) (sdk.Context, appparams.EncodingConfig, globalfeekeeper.Keeper) { + t.Helper() + db := dbm.NewMemDB() + ms := store.NewCommitMultiStore(db) + encCfg := appparams.MakeEncodingConfig() + keyParams := sdk.NewKVStoreKey(types.StoreKey) + ms.MountStoreWithDB(keyParams, storetypes.StoreTypeIAVL, db) + require.NoError(t, ms.LoadLatestVersion()) + + globalFeeKeeper := globalfeekeeper.NewKeeper(encCfg.Marshaler, keyParams, "omniflix1llyd96levrglxhw6sczgk9wn48t64zkhv4fq0r") + + ctx := sdk.NewContext(ms, tmproto.Header{ + Height: 1234567, + Time: time.Date(2020, time.April, 22, 12, 0, 0, 0, time.UTC), + ChainID: "omniflixhub-test", + }, false, log.NewNopLogger()) + + return ctx, encCfg, globalFeeKeeper +} diff --git a/x/globalfee/keeper/msg_server.go b/x/globalfee/keeper/msg_server.go new file mode 100644 index 00000000..7e91b7d0 --- /dev/null +++ b/x/globalfee/keeper/msg_server.go @@ -0,0 +1,38 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + + "github.com/OmniFlix/omniflixhub/v2/x/globalfee/types" +) + +var _ types.MsgServer = msgServer{} + +type msgServer struct { + Keeper +} + +// NewMsgServerImpl returns an implementation of the x/mint MsgServer interface. +func NewMsgServerImpl(k Keeper) types.MsgServer { + return &msgServer{ + Keeper: k, + } +} + +func (ms msgServer) UpdateParams(goCtx context.Context, req *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) { + if ms.authority != req.Authority { + return nil, errorsmod.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", ms.authority, req.Authority) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + if err := ms.SetParams(ctx, req.Params); err != nil { + return nil, err + } + + return &types.MsgUpdateParamsResponse{}, nil +} diff --git a/x/globalfee/module.go b/x/globalfee/module.go new file mode 100644 index 00000000..69ec4985 --- /dev/null +++ b/x/globalfee/module.go @@ -0,0 +1,145 @@ +package globalfee + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + + abci "github.com/cometbft/cometbft/abci/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + + "github.com/OmniFlix/omniflixhub/v2/x/globalfee/client/cli" + "github.com/OmniFlix/omniflixhub/v2/x/globalfee/keeper" + "github.com/OmniFlix/omniflixhub/v2/x/globalfee/types" +) + +var ( + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleGenesis = AppModule{} + _ module.AppModule = AppModule{} +) + +// ConsensusVersion defines the current x/globalfee module consensus version. +const ConsensusVersion = 2 + +// AppModuleBasic defines the basic application module used by the wasm module. +type AppModuleBasic struct { + cdc codec.Codec +} + +func (am AppModuleBasic) Name() string { + return types.ModuleName +} + +func (am AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) +} + +func (am AppModuleBasic) ValidateGenesis(marshaler codec.JSONCodec, _ client.TxEncodingConfig, message json.RawMessage) error { + var data types.GenesisState + err := marshaler.UnmarshalJSON(message, &data) + if err != nil { + return err + } + if err := types.ValidateGenesis(data); err != nil { + return err + } + return nil +} + +func (am AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) + if err != nil { + panic(err) + } +} + +func (am AppModuleBasic) GetTxCmd() *cobra.Command { + return nil +} + +func (am AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) +} + +func (am AppModuleBasic) RegisterInterfaces(r codectypes.InterfaceRegistry) { + types.RegisterInterfaces(r) +} + +type AppModule struct { + AppModuleBasic + + keeper keeper.Keeper + + // bondDenom is used solely for migration off of x/params + bondDenom string +} + +// NewAppModule constructor +func NewAppModule( + cdc codec.Codec, + keeper keeper.Keeper, + debondDenom string, +) *AppModule { + return &AppModule{ + AppModuleBasic: AppModuleBasic{cdc: cdc}, + keeper: keeper, + bondDenom: debondDenom, + } +} + +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate { + var genesisState types.GenesisState + cdc.MustUnmarshalJSON(gs, &genesisState) + fmt.Println(genesisState) + if err := am.keeper.SetParams(ctx, genesisState.Params); err != nil { + panic(err) + } + return nil +} + +func (am AppModule) ExportGenesis(ctx sdk.Context, marshaller codec.JSONCodec) json.RawMessage { + genState := &types.GenesisState{ + Params: am.keeper.GetParams(ctx), + } + return marshaller.MustMarshalJSON(genState) +} + +func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) { +} + +func (am AppModule) QuerierRoute() string { + return types.QuerierRoute +} + +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) + types.RegisterQueryServer(cfg.QueryServer(), keeper.NewGrpcQuerier(am.keeper)) +} + +func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) { +} + +func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return nil +} + +// ConsensusVersion is a sequence number for state-breaking change of the +// module. It should be incremented on each consensus-breaking change +// introduced by the module. To avoid wrong/empty versions, the initial version +// should be set to 1. +func (am AppModule) ConsensusVersion() uint64 { + return ConsensusVersion +} diff --git a/x/globalfee/types/codec.go b/x/globalfee/types/codec.go new file mode 100644 index 00000000..93c4d5be --- /dev/null +++ b/x/globalfee/types/codec.go @@ -0,0 +1,44 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/legacy" + "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" + govcodec "github.com/cosmos/cosmos-sdk/x/gov/codec" +) + +var ( + amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewAminoCodec(amino) +) + +func init() { + RegisterLegacyAminoCodec(amino) + cryptocodec.RegisterCrypto(amino) + sdk.RegisterLegacyAminoCodec(amino) + + // Register all Amino interfaces and concrete types on the authz Amino codec + // so that this can later be used to properly serialize MsgGrant and MsgExec + // instances. + RegisterLegacyAminoCodec(authzcodec.Amino) + RegisterLegacyAminoCodec(govcodec.Amino) +} + +// RegisterLegacyAminoCodec registers concrete types on the LegacyAmino codec +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(Params{}, "OmniFlix/globalfee/Params", nil) + legacy.RegisterAminoMsg(cdc, &MsgUpdateParams{}, "OmniFlix/globalfee/MsgUpdateParams") +} + +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgUpdateParams{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} diff --git a/x/globalfee/types/genesis.go b/x/globalfee/types/genesis.go new file mode 100644 index 00000000..ffaccae1 --- /dev/null +++ b/x/globalfee/types/genesis.go @@ -0,0 +1,41 @@ +package types + +import ( + "encoding/json" + + errorsmod "cosmossdk.io/errors" + + "github.com/cosmos/cosmos-sdk/codec" +) + +// NewGenesisState - Create a new genesis state +func NewGenesisState(params Params) *GenesisState { + return &GenesisState{ + Params: params, + } +} + +// DefaultGenesisState - Return a default genesis state +func DefaultGenesisState() *GenesisState { + return NewGenesisState(DefaultParams()) +} + +// GetGenesisStateFromAppState returns x/auth GenesisState given raw application +// genesis state. +func GetGenesisStateFromAppState(cdc codec.Codec, appState map[string]json.RawMessage) *GenesisState { + var genesisState GenesisState + + if appState[ModuleName] != nil { + cdc.MustUnmarshalJSON(appState[ModuleName], &genesisState) + } + + return &genesisState +} + +func ValidateGenesis(data GenesisState) error { + if err := data.Params.Validate(); err != nil { + return errorsmod.Wrap(err, "invalid globalfee params") + } + + return nil +} diff --git a/x/globalfee/types/genesis.pb.go b/x/globalfee/types/genesis.pb.go new file mode 100644 index 00000000..3ebb6b42 --- /dev/null +++ b/x/globalfee/types/genesis.pb.go @@ -0,0 +1,623 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: OmniFlix/globalfee/v1beta1/genesis.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// GenesisState - initial state of module +type GenesisState struct { + // Params of this module + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params,omitempty"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_ede90870a7dc1a24, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// Params defines the set of module parameters. +type Params struct { + // minimum_gas_prices stores the minimum gas price(s) for all TX on the chain. + // When multiple coins are defined then they are accepted alternatively. + // The list must be sorted by denoms asc. No duplicate denoms or zero amount + // values allowed. For more information see + // https://docs.cosmos.network/main/modules/auth#concepts + MinimumGasPrices github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,1,rep,name=minimum_gas_prices,json=minimumGasPrices,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"minimum_gas_prices,omitempty" yaml:"minimum_gas_prices"` + // bypass_min_fee_msg_types defines a list of message type urls + // that are free of fee charge. + BypassMinFeeMsgTypes []string `protobuf:"bytes,2,rep,name=bypass_min_fee_msg_types,json=bypassMinFeeMsgTypes,proto3" json:"bypass_min_fee_msg_types,omitempty" yaml:"bypass_min_fee_msg_types"` + // max_total_bypass_min_fee_msg_gas_usage defines the total maximum gas usage + // allowed for a transaction containing only messages of types in bypass_min_fee_msg_types + // to bypass fee charge. + MaxTotalBypassMinFeeMsgGasUsage uint64 `protobuf:"varint,3,opt,name=max_total_bypass_min_fee_msg_gas_usage,json=maxTotalBypassMinFeeMsgGasUsage,proto3" json:"max_total_bypass_min_fee_msg_gas_usage,omitempty"` +} + +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_ede90870a7dc1a24, []int{1} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetMinimumGasPrices() github_com_cosmos_cosmos_sdk_types.DecCoins { + if m != nil { + return m.MinimumGasPrices + } + return nil +} + +func (m *Params) GetBypassMinFeeMsgTypes() []string { + if m != nil { + return m.BypassMinFeeMsgTypes + } + return nil +} + +func (m *Params) GetMaxTotalBypassMinFeeMsgGasUsage() uint64 { + if m != nil { + return m.MaxTotalBypassMinFeeMsgGasUsage + } + return 0 +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "OmniFlix.globalfee.v1beta1.GenesisState") + proto.RegisterType((*Params)(nil), "OmniFlix.globalfee.v1beta1.Params") +} + +func init() { + proto.RegisterFile("OmniFlix/globalfee/v1beta1/genesis.proto", fileDescriptor_ede90870a7dc1a24) +} + +var fileDescriptor_ede90870a7dc1a24 = []byte{ + // 444 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xbf, 0x6e, 0xd3, 0x40, + 0x1c, 0xc7, 0x73, 0x04, 0x45, 0xc2, 0x65, 0xa8, 0xac, 0x0e, 0x26, 0xaa, 0xec, 0xc8, 0x03, 0xb2, + 0x04, 0xdc, 0xa9, 0x81, 0x89, 0xd1, 0xa0, 0x46, 0x0c, 0xa5, 0x55, 0x08, 0x0b, 0xcb, 0xe9, 0xec, + 0x5e, 0xae, 0x27, 0x7c, 0x77, 0x56, 0x7e, 0x97, 0x2a, 0x19, 0x79, 0x03, 0x1e, 0x80, 0x27, 0xe0, + 0x19, 0x78, 0x80, 0x8e, 0x1d, 0x99, 0x0c, 0x4a, 0xb6, 0x8e, 0x3c, 0x01, 0xb2, 0xcf, 0x4d, 0x5b, + 0xb5, 0x99, 0x6c, 0xf9, 0xf7, 0xf9, 0xfe, 0xf1, 0x4f, 0x3f, 0x2f, 0x39, 0x56, 0x5a, 0x1e, 0x16, + 0x72, 0x41, 0x44, 0x61, 0x32, 0x56, 0x4c, 0x39, 0x27, 0xe7, 0x07, 0x19, 0xb7, 0xec, 0x80, 0x08, + 0xae, 0x39, 0x48, 0xc0, 0xe5, 0xcc, 0x58, 0xe3, 0xf7, 0xaf, 0x49, 0xbc, 0x21, 0x71, 0x4b, 0xf6, + 0xf7, 0x84, 0x11, 0xa6, 0xc1, 0x48, 0xfd, 0xe6, 0x14, 0xfd, 0x30, 0x37, 0xa0, 0x0c, 0x90, 0x8c, + 0xc1, 0x8d, 0x69, 0x6e, 0xa4, 0x76, 0xf3, 0xf8, 0xd4, 0x7b, 0x3a, 0x72, 0x11, 0x9f, 0x2c, 0xb3, + 0xdc, 0x9f, 0x78, 0xbd, 0x92, 0xcd, 0x98, 0x82, 0x00, 0x0d, 0x50, 0xb2, 0x33, 0x8c, 0xf1, 0xf6, + 0x48, 0x7c, 0xd2, 0x90, 0x69, 0x70, 0x51, 0x45, 0x9d, 0xab, 0x2a, 0xda, 0x75, 0xca, 0x97, 0x46, + 0x49, 0xcb, 0x55, 0x69, 0x97, 0xe3, 0xd6, 0x2b, 0xfe, 0xd1, 0xf5, 0x7a, 0x0e, 0xf6, 0x7f, 0x21, + 0xcf, 0x57, 0x52, 0x4b, 0x35, 0x57, 0x54, 0x30, 0xa0, 0xe5, 0x4c, 0xe6, 0xbc, 0x4e, 0xeb, 0x26, + 0x3b, 0xc3, 0x7d, 0xec, 0xea, 0xe2, 0xba, 0xee, 0x26, 0xe6, 0x3d, 0xcf, 0xdf, 0x19, 0xa9, 0xd3, + 0xb2, 0xcd, 0xd9, 0xbf, 0xaf, 0xbf, 0xc9, 0xfc, 0x57, 0x45, 0xcf, 0x96, 0x4c, 0x15, 0x6f, 0xe3, + 0xfb, 0x54, 0xfc, 0xf3, 0x4f, 0xf4, 0x42, 0x48, 0x7b, 0x36, 0xcf, 0x70, 0x6e, 0x14, 0x69, 0x77, + 0xe3, 0x1e, 0xaf, 0xe0, 0xf4, 0x2b, 0xb1, 0xcb, 0x92, 0xc3, 0x75, 0x20, 0x8c, 0x77, 0x5b, 0x8f, + 0x11, 0x83, 0x93, 0xc6, 0xc1, 0xff, 0x86, 0xbc, 0x20, 0x5b, 0x96, 0x0c, 0x80, 0x2a, 0xa9, 0xe9, + 0x94, 0x73, 0xaa, 0x40, 0xd0, 0x46, 0x17, 0x3c, 0x1a, 0x74, 0x93, 0x27, 0xe9, 0x87, 0xab, 0x2a, + 0x8a, 0xb7, 0x31, 0x77, 0x8a, 0x46, 0xae, 0xe8, 0x36, 0x36, 0x1e, 0xef, 0xb9, 0xd1, 0x91, 0xd4, + 0x87, 0x9c, 0x1f, 0x81, 0x98, 0xd4, 0x9f, 0xfd, 0x63, 0xef, 0xb9, 0x62, 0x0b, 0x6a, 0x8d, 0x65, + 0x05, 0x7d, 0x40, 0x5c, 0xff, 0xf0, 0x1c, 0x98, 0xe0, 0x41, 0x77, 0x80, 0x92, 0xc7, 0xe3, 0x48, + 0xb1, 0xc5, 0xa4, 0x86, 0xd3, 0xbb, 0x6e, 0x23, 0x06, 0x9f, 0x6b, 0x2c, 0xfd, 0x78, 0xb1, 0x0a, + 0xd1, 0xe5, 0x2a, 0x44, 0x7f, 0x57, 0x21, 0xfa, 0xbe, 0x0e, 0x3b, 0x97, 0xeb, 0xb0, 0xf3, 0x7b, + 0x1d, 0x76, 0xbe, 0xbc, 0xb9, 0xb5, 0xad, 0xcd, 0x95, 0x1a, 0xa5, 0xe5, 0xb4, 0x90, 0x8b, 0xb3, + 0x79, 0x46, 0xce, 0x87, 0xe4, 0xf6, 0xd9, 0x36, 0xbd, 0xb3, 0x5e, 0x73, 0x5b, 0xaf, 0xff, 0x07, + 0x00, 0x00, 0xff, 0xff, 0xf8, 0x6f, 0x4d, 0x08, 0xd9, 0x02, 0x00, 0x00, +} + +func (m *GenesisState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *Params) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.MaxTotalBypassMinFeeMsgGasUsage != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.MaxTotalBypassMinFeeMsgGasUsage)) + i-- + dAtA[i] = 0x18 + } + if len(m.BypassMinFeeMsgTypes) > 0 { + for iNdEx := len(m.BypassMinFeeMsgTypes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.BypassMinFeeMsgTypes[iNdEx]) + copy(dAtA[i:], m.BypassMinFeeMsgTypes[iNdEx]) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.BypassMinFeeMsgTypes[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.MinimumGasPrices) > 0 { + for iNdEx := len(m.MinimumGasPrices) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.MinimumGasPrices[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.MinimumGasPrices) > 0 { + for _, e := range m.MinimumGasPrices { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.BypassMinFeeMsgTypes) > 0 { + for _, s := range m.BypassMinFeeMsgTypes { + l = len(s) + n += 1 + l + sovGenesis(uint64(l)) + } + } + if m.MaxTotalBypassMinFeeMsgGasUsage != 0 { + n += 1 + sovGenesis(uint64(m.MaxTotalBypassMinFeeMsgGasUsage)) + } + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Params) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MinimumGasPrices", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MinimumGasPrices = append(m.MinimumGasPrices, types.DecCoin{}) + if err := m.MinimumGasPrices[len(m.MinimumGasPrices)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BypassMinFeeMsgTypes", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BypassMinFeeMsgTypes = append(m.BypassMinFeeMsgTypes, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxTotalBypassMinFeeMsgGasUsage", wireType) + } + m.MaxTotalBypassMinFeeMsgGasUsage = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxTotalBypassMinFeeMsgGasUsage |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/globalfee/types/keys.go b/x/globalfee/types/keys.go new file mode 100644 index 00000000..8489c40a --- /dev/null +++ b/x/globalfee/types/keys.go @@ -0,0 +1,12 @@ +package types + +var ParamsKey = []byte{0x00} + +const ( + // ModuleName is the name of the module + ModuleName = "globalfee" + + StoreKey = ModuleName + + QuerierRoute = ModuleName +) diff --git a/x/globalfee/types/msgs.go b/x/globalfee/types/msgs.go new file mode 100644 index 00000000..e0ce8a24 --- /dev/null +++ b/x/globalfee/types/msgs.go @@ -0,0 +1,29 @@ +package types + +import ( + "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var _ sdk.Msg = &MsgUpdateParams{} + +// GetSignBytes implements the LegacyMsg interface. +func (m MsgUpdateParams) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&m)) +} + +// GetSigners returns the expected signers for a MsgUpdateParams message. +func (m *MsgUpdateParams) GetSigners() []sdk.AccAddress { + addr, _ := sdk.AccAddressFromBech32(m.Authority) + return []sdk.AccAddress{addr} +} + +// ValidateBasic does a sanity check on the provided data. +func (m *MsgUpdateParams) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(m.Authority); err != nil { + return errors.Wrap(err, "invalid authority address") + } + + return m.Params.Validate() +} diff --git a/x/globalfee/types/params.go b/x/globalfee/types/params.go new file mode 100644 index 00000000..5db865f2 --- /dev/null +++ b/x/globalfee/types/params.go @@ -0,0 +1,72 @@ +package types + +import ( + "fmt" + + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +var maxTotalBypassMinFeeMsgGasUsage = 2_000_000 + +// DefaultParams returns default parameters +func DefaultParams() Params { + return Params{ + MinimumGasPrices: sdk.DecCoins(nil), + BypassMinFeeMsgTypes: []string{}, + MaxTotalBypassMinFeeMsgGasUsage: uint64(maxTotalBypassMinFeeMsgGasUsage), + } +} + +// Validate performs basic validation. +func (p Params) Validate() error { + return validateMinimumGasPrices(p.MinimumGasPrices) +} + +// this requires the fee non-negative +func validateMinimumGasPrices(i interface{}) error { + v, ok := i.(sdk.DecCoins) + if !ok { + return errorsmod.Wrapf(sdkerrors.ErrInvalidType, "type: %T, expected sdk.DecCoins", i) + } + + dec := DecCoins(v) + return dec.Validate() +} + +type DecCoins sdk.DecCoins + +// Validate checks that the DecCoins are sorted, have non-negtive amount, with a valid and unique +// denomination (i.e no duplicates). Otherwise, it returns an error. +func (coins DecCoins) Validate() error { + if len(coins) == 0 { + return nil + } + + lowDenom := "" + seenDenoms := make(map[string]bool) + + for i, coin := range coins { + if seenDenoms[coin.Denom] { + return fmt.Errorf("duplicate denomination %s", coin.Denom) + } + if err := sdk.ValidateDenom(coin.Denom); err != nil { + return err + } + // skip the denom order check for the first denom in the coins list + if i != 0 && coin.Denom <= lowDenom { + return fmt.Errorf("denomination %s is not sorted", coin.Denom) + } + if coin.IsNegative() { + return fmt.Errorf("coin %s amount is negative", coin.Amount) + } + + // we compare each coin against the last denom + lowDenom = coin.Denom + seenDenoms[coin.Denom] = true + } + + return nil +} diff --git a/x/globalfee/types/params_test.go b/x/globalfee/types/params_test.go new file mode 100644 index 00000000..f03b3bfa --- /dev/null +++ b/x/globalfee/types/params_test.go @@ -0,0 +1,74 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestDefaultParams(t *testing.T) { + p := DefaultParams() + require.EqualValues(t, p.MinimumGasPrices, sdk.DecCoins(nil)) +} + +func Test_validateParams(t *testing.T) { + tests := map[string]struct { + coins interface{} // not sdk.DeCoins, but Decoins defined in glboalfee + expectErr bool + }{ + "DefaultParams, pass": { + DefaultParams().MinimumGasPrices, + false, + }, + "DecCoins conversion fails, fail": { + sdk.Coins{sdk.NewCoin("photon", sdk.OneInt())}, + true, + }, + "coins amounts are zero, pass": { + sdk.DecCoins{ + sdk.NewDecCoin("atom", sdk.ZeroInt()), + sdk.NewDecCoin("photon", sdk.ZeroInt()), + }, + false, + }, + "duplicate coins denoms, fail": { + sdk.DecCoins{ + sdk.NewDecCoin("photon", sdk.OneInt()), + sdk.NewDecCoin("photon", sdk.OneInt()), + }, + true, + }, + "coins are not sorted by denom alphabetically, fail": { + sdk.DecCoins{ + sdk.NewDecCoin("photon", sdk.OneInt()), + sdk.NewDecCoin("atom", sdk.OneInt()), + }, + true, + }, + "negative amount, fail": { + sdk.DecCoins{ + sdk.DecCoin{Denom: "photon", Amount: sdk.OneDec().Neg()}, + }, + true, + }, + "invalid denom, fail": { + sdk.DecCoins{ + sdk.DecCoin{Denom: "photon!", Amount: sdk.OneDec().Neg()}, + }, + true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + err := validateMinimumGasPrices(test.coins) + if test.expectErr { + require.Error(t, err) + return + } + require.NoError(t, err) + }) + } +} diff --git a/x/globalfee/types/query.pb.go b/x/globalfee/types/query.pb.go new file mode 100644 index 00000000..b626fcef --- /dev/null +++ b/x/globalfee/types/query.pb.go @@ -0,0 +1,537 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: OmniFlix/globalfee/v1beta1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// QueryMinimumGasPricesRequest is the request type for the +// Query/MinimumGasPrices RPC method. +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_0456bf7c6f56dff0, []int{0} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryMinimumGasPricesResponse is the response type for the +// Query/MinimumGasPrices RPC method. +type QueryParamsResponse struct { + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_0456bf7c6f56dff0, []int{1} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func init() { + proto.RegisterType((*QueryParamsRequest)(nil), "OmniFlix.globalfee.v1beta1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "OmniFlix.globalfee.v1beta1.QueryParamsResponse") +} + +func init() { + proto.RegisterFile("OmniFlix/globalfee/v1beta1/query.proto", fileDescriptor_0456bf7c6f56dff0) +} + +var fileDescriptor_0456bf7c6f56dff0 = []byte{ + // 301 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xf3, 0xcf, 0xcd, 0xcb, + 0x74, 0xcb, 0xc9, 0xac, 0xd0, 0x4f, 0xcf, 0xc9, 0x4f, 0x4a, 0xcc, 0x49, 0x4b, 0x4d, 0xd5, 0x2f, + 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, 0x2f, 0x2c, 0x4d, 0x2d, 0xaa, 0xd4, 0x2b, 0x28, 0xca, + 0x2f, 0xc9, 0x17, 0x92, 0x82, 0xa9, 0xd3, 0x83, 0xab, 0xd3, 0x83, 0xaa, 0x93, 0x12, 0x49, 0xcf, + 0x4f, 0xcf, 0x07, 0x2b, 0xd3, 0x07, 0xb1, 0x20, 0x3a, 0xa4, 0x64, 0xd2, 0xf3, 0xf3, 0xd3, 0x73, + 0x52, 0xf5, 0x13, 0x0b, 0x32, 0xf5, 0x13, 0xf3, 0xf2, 0xf2, 0x4b, 0x12, 0x4b, 0x32, 0xf3, 0xf3, + 0x8a, 0xa1, 0xb2, 0x1a, 0x78, 0xec, 0x4d, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x84, 0xaa, 0x54, 0x12, + 0xe1, 0x12, 0x0a, 0x04, 0x39, 0x24, 0x20, 0xb1, 0x28, 0x31, 0xb7, 0x38, 0x28, 0xb5, 0xb0, 0x34, + 0xb5, 0xb8, 0x44, 0x29, 0x9c, 0x4b, 0x18, 0x45, 0xb4, 0xb8, 0x20, 0x3f, 0xaf, 0x38, 0x55, 0xc8, + 0x81, 0x8b, 0xad, 0x00, 0x2c, 0x22, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x6d, 0xa4, 0xa4, 0x87, 0xdb, + 0xdd, 0x7a, 0x10, 0xbd, 0x4e, 0x2c, 0x27, 0xee, 0xc9, 0x33, 0x04, 0x41, 0xf5, 0x19, 0xcd, 0x67, + 0xe4, 0x62, 0x05, 0x9b, 0x2c, 0x34, 0x95, 0x91, 0x8b, 0x0d, 0xa2, 0x44, 0x48, 0x0f, 0x9f, 0x31, + 0x98, 0xae, 0x93, 0xd2, 0x27, 0x5a, 0x3d, 0xc4, 0xdd, 0x4a, 0x5a, 0x4d, 0x97, 0x9f, 0x4c, 0x66, + 0x52, 0x11, 0x52, 0xd2, 0xcf, 0xcf, 0xcd, 0xcb, 0x4c, 0xc3, 0x1e, 0x2e, 0x10, 0x17, 0x3a, 0xf9, + 0x9d, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, + 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x49, 0x7a, 0x66, 0x49, 0x46, + 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x3e, 0x3c, 0x7c, 0x61, 0x06, 0x66, 0x94, 0x26, 0xe9, 0x97, + 0x19, 0xe9, 0x23, 0x1b, 0x5c, 0x52, 0x59, 0x90, 0x5a, 0x9c, 0xc4, 0x06, 0x0e, 0x67, 0x63, 0x40, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xe5, 0x52, 0xde, 0xcc, 0x0b, 0x02, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// QueryClient is the client API for Query service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type QueryClient interface { + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/OmniFlix.globalfee.v1beta1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/OmniFlix.globalfee.v1beta1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "OmniFlix.globalfee.v1beta1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "OmniFlix/globalfee/v1beta1/query.proto", +} + +func (m *QueryParamsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQuery(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/globalfee/types/query.pb.gw.go b/x/globalfee/types/query.pb.gw.go new file mode 100644 index 00000000..9b6ee4ec --- /dev/null +++ b/x/globalfee/types/query.pb.gw.go @@ -0,0 +1,153 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: OmniFlix/globalfee/v1beta1/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage +var _ = metadata.Join + +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "QueryClient" to call the correct interceptors. +func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Params_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"omniflix", "globalfee", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Query_Params_0 = runtime.ForwardResponseMessage +) diff --git a/x/globalfee/types/tx.pb.go b/x/globalfee/types/tx.pb.go new file mode 100644 index 00000000..b277aff6 --- /dev/null +++ b/x/globalfee/types/tx.pb.go @@ -0,0 +1,607 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: OmniFlix/globalfee/v1beta1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + _ "github.com/cosmos/cosmos-sdk/types/msgservice" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// MsgUpdateParams is the Msg/UpdateParams request type. +// +// Since: cosmos-sdk 0.47 +type MsgUpdateParams struct { + // authority is the address of the governance account. + Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` + // params defines the x/mint parameters to update. + // + // NOTE: All parameters must be supplied. + Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"` +} + +func (m *MsgUpdateParams) Reset() { *m = MsgUpdateParams{} } +func (m *MsgUpdateParams) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateParams) ProtoMessage() {} +func (*MsgUpdateParams) Descriptor() ([]byte, []int) { + return fileDescriptor_faa4e91f0e3593b9, []int{0} +} +func (m *MsgUpdateParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateParams.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUpdateParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateParams.Merge(m, src) +} +func (m *MsgUpdateParams) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateParams) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateParams.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateParams proto.InternalMessageInfo + +func (m *MsgUpdateParams) GetAuthority() string { + if m != nil { + return m.Authority + } + return "" +} + +func (m *MsgUpdateParams) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// MsgUpdateParamsResponse defines the response structure for executing a +// MsgUpdateParams message. +// +// Since: cosmos-sdk 0.47 +type MsgUpdateParamsResponse struct { +} + +func (m *MsgUpdateParamsResponse) Reset() { *m = MsgUpdateParamsResponse{} } +func (m *MsgUpdateParamsResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateParamsResponse) ProtoMessage() {} +func (*MsgUpdateParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_faa4e91f0e3593b9, []int{1} +} +func (m *MsgUpdateParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUpdateParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateParamsResponse.Merge(m, src) +} +func (m *MsgUpdateParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateParamsResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgUpdateParams)(nil), "OmniFlix.globalfee.v1beta1.MsgUpdateParams") + proto.RegisterType((*MsgUpdateParamsResponse)(nil), "OmniFlix.globalfee.v1beta1.MsgUpdateParamsResponse") +} + +func init() { + proto.RegisterFile("OmniFlix/globalfee/v1beta1/tx.proto", fileDescriptor_faa4e91f0e3593b9) +} + +var fileDescriptor_faa4e91f0e3593b9 = []byte{ + // 337 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xf6, 0xcf, 0xcd, 0xcb, + 0x74, 0xcb, 0xc9, 0xac, 0xd0, 0x4f, 0xcf, 0xc9, 0x4f, 0x4a, 0xcc, 0x49, 0x4b, 0x4d, 0xd5, 0x2f, + 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, 0x2f, 0xa9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, + 0x92, 0x82, 0x29, 0xd2, 0x83, 0x2b, 0xd2, 0x83, 0x2a, 0x92, 0x12, 0x4f, 0xce, 0x2f, 0xce, 0xcd, + 0x2f, 0xd6, 0xcf, 0x2d, 0x4e, 0xd7, 0x2f, 0x33, 0x04, 0x51, 0x10, 0x4d, 0x52, 0x1a, 0x78, 0x4c, + 0x4e, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0x86, 0xaa, 0x14, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x33, + 0xf5, 0x41, 0x2c, 0xa8, 0xa8, 0x24, 0xc4, 0xe0, 0x78, 0x88, 0x04, 0x84, 0x03, 0x91, 0x52, 0x9a, + 0xcd, 0xc8, 0xc5, 0xef, 0x5b, 0x9c, 0x1e, 0x5a, 0x90, 0x92, 0x58, 0x92, 0x1a, 0x90, 0x58, 0x94, + 0x98, 0x5b, 0x2c, 0x64, 0xc6, 0xc5, 0x99, 0x58, 0x5a, 0x92, 0x91, 0x5f, 0x94, 0x59, 0x52, 0x29, + 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0xe9, 0x24, 0x71, 0x69, 0x8b, 0xae, 0x08, 0x54, 0xa3, 0x63, 0x4a, + 0x4a, 0x51, 0x6a, 0x71, 0x71, 0x70, 0x49, 0x51, 0x66, 0x5e, 0x7a, 0x10, 0x42, 0xa9, 0x90, 0x03, + 0x17, 0x5b, 0x01, 0xd8, 0x04, 0x09, 0x26, 0x05, 0x46, 0x0d, 0x6e, 0x23, 0x25, 0x3d, 0xdc, 0x9e, + 0xd5, 0x83, 0xd8, 0xe5, 0xc4, 0x72, 0xe2, 0x9e, 0x3c, 0x43, 0x10, 0x54, 0x9f, 0x15, 0x5f, 0xd3, + 0xf3, 0x0d, 0x5a, 0x08, 0x13, 0x95, 0x24, 0xb9, 0xc4, 0xd1, 0x1c, 0x17, 0x94, 0x5a, 0x5c, 0x90, + 0x9f, 0x57, 0x9c, 0x6a, 0x54, 0xce, 0xc5, 0xec, 0x5b, 0x9c, 0x2e, 0x54, 0xc0, 0xc5, 0x83, 0xe2, + 0x76, 0x6d, 0x7c, 0x76, 0xa2, 0x99, 0x25, 0x65, 0x4c, 0x82, 0x62, 0x98, 0xc5, 0x4e, 0x7e, 0x27, + 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, + 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0x65, 0x92, 0x9e, 0x59, 0x92, 0x51, 0x9a, + 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x0f, 0x8f, 0xb1, 0xfc, 0xdc, 0xbc, 0xcc, 0xb4, 0x9c, 0xcc, 0x8a, + 0x8c, 0xd2, 0x24, 0xfd, 0x32, 0x23, 0x7d, 0xe4, 0x28, 0x2c, 0xa9, 0x2c, 0x48, 0x2d, 0x4e, 0x62, + 0x03, 0x47, 0x84, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x85, 0x57, 0xc4, 0x78, 0x3f, 0x02, 0x00, + 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + // UpdateParams defines a governance operation for updating the x/globalfee module + // parameters. The authority is hard-coded to the x/gov module account. + // + // Since: cosmos-sdk 0.47 + UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) { + out := new(MsgUpdateParamsResponse) + err := c.cc.Invoke(ctx, "/OmniFlix.globalfee.v1beta1.Msg/UpdateParams", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // UpdateParams defines a governance operation for updating the x/globalfee module + // parameters. The authority is hard-coded to the x/gov module account. + // + // Since: cosmos-sdk 0.47 + UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) UpdateParams(ctx context.Context, req *MsgUpdateParams) (*MsgUpdateParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_UpdateParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUpdateParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).UpdateParams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/OmniFlix.globalfee.v1beta1.Msg/UpdateParams", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).UpdateParams(ctx, req.(*MsgUpdateParams)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "OmniFlix.globalfee.v1beta1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "UpdateParams", + Handler: _Msg_UpdateParams_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "OmniFlix/globalfee/v1beta1/tx.proto", +} + +func (m *MsgUpdateParams) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUpdateParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Authority) > 0 { + i -= len(m.Authority) + copy(dAtA[i:], m.Authority) + i = encodeVarintTx(dAtA, i, uint64(len(m.Authority))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgUpdateParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUpdateParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgUpdateParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Authority) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Params.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgUpdateParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgUpdateParams) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUpdateParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Authority = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgUpdateParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUpdateParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +)