From abce938f217bc06aed7155741acab0c69ef0a3a3 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 16 Oct 2019 10:20:24 +0200 Subject: [PATCH 01/77] Add docs --- x/delegation/README.md | 121 +++++++++++++++++++++++++++++++++++++++++ x/delegation/doc.go | 32 +++++++++++ 2 files changed, 153 insertions(+) create mode 100644 x/delegation/README.md create mode 100644 x/delegation/doc.go diff --git a/x/delegation/README.md b/x/delegation/README.md new file mode 100644 index 000000000000..6a1cb65ca3e3 --- /dev/null +++ b/x/delegation/README.md @@ -0,0 +1,121 @@ +## Fee Delegation Work + +Much of this is ported from an older implementation of fee delegation from aaronc. + +To take a look at this (which had many changes to auth as well, which has been heavily refactored upstream): + +``` +git remote add keys https://github.com/cosmos-cg-key-management/cosmos-sdk.git +git fetch keys +git diff --compact-summary f1b08b85f 980d713f +``` + +This is based on https://gist.github.com/aaronc/b60628017352df5983791cad30babe56#fee-delegation + +In particular the following parts: + +-------------------- + +The `delegation` module also allows for fee delegation via some +changes to the `AnteHandler` and `StdTx`. The behavior is similar +to that described above for `Msg` delegations except using +the interface `FeeAllowance` instead of `Capability`: + +```go +// FeeAllowance defines a permission for one account to use another account's balance +// to pay fees +type FeeAllowance interface { + // Accept checks whether this allowance allows the provided fees to be spent, + // and optionally updates the allowance or deletes it entirely + Accept(fee sdk.Coins, block abci.Header) (allow bool, updated FeeAllowance, delete bool) +} +``` + +An example `FeeAllowance` could be created that simply sets a `SpendLimit`: + +```go +type BasicFeeAllowance struct { + // SpendLimit specifies the maximum amount of tokens that can be spent + // by this capability and will be updated as tokens are spent. If it is + // empty, there is no spend limit and any amount of coins can be spent. + SpendLimit sdk.Coins +} + +func (cap BasicFeeAllowance) Accept(fee sdk.Coins, block abci.Header) (allow bool, updated FeeAllowance, delete bool) { + left, invalid := cap.SpendLimit.SafeSub(fee) + if invalid { + return false, nil, false + } + if left.IsZero() { + return true, nil, true + } + return true, BasicFeeAllowance{SpendLimit: left}, false +} + +``` + +Other `FeeAllowance` types could be created such as a daily spend limit. + +## `StdTx` and `AnteHandler` changes + +In order to support delegated fees `StdTx` and the `AnteHandler` needed to be changed. + +The field `FeeAccount` was added to `StdTx`. + +```go +type StdTx struct { + Msgs []sdk.Msg `json:"msg"` + Fee StdFee `json:"fee"` + Signatures []StdSignature `json:"signatures"` + Memo string `json:"memo"` + // FeeAccount is an optional account that fees can be spent from if such + // delegation is enabled + FeeAccount sdk.AccAddress `json:"fee_account"` +} +``` + +An interface `FeeDelegationHandler` (which is implemented by the `delegation` module) was created and a parameter for it was added to the default `AnteHandler`: + +```go +type FeeDelegationHandler interface { + // AllowDelegatedFees checks if the grantee can use the granter's account to spend the specified fees, updating + // any fee allowance in accordance with the provided fees + AllowDelegatedFees(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, fee sdk.Coins) bool +} + +// NewAnteHandler returns an AnteHandler that checks and increments sequence +// numbers, checks signatures & account numbers, and deducts fees from the first +// signer. +func NewAnteHandler(ak AccountKeeper, fck FeeCollectionKeeper, feeDelegationHandler FeeDelegationHandler, sigGasConsumer SignatureVerificationGasConsumer) sdk.AnteHandler { +``` + +Basically if someone sets `FeeAccount` on `StdTx`, the `AnteHandler` will call into the `delegation` module via its `FeeDelegationHandler` and check if the tx's fees have been delegated by that `FeeAccount` to the key actually signing the transaction. + +## Core `FeeAllowance` types + +```go +type BasicFeeAllowance struct { + // SpendLimit specifies the maximum amount of tokens that can be spent + // by this capability and will be updated as tokens are spent. If it is + // empty, there is no spend limit and any amount of coins can be spent. + SpendLimit sdk.Coins + // Expiration specifies an optional time when this allowance expires + Expiration time.Time +} + +type PeriodicFeeAllowance struct { + BasicFeeAllowance + // Period specifies the time duration in which PeriodSpendLimit coins can + // be spent before that allowance is reset + Period time.Duration + // PeriodSpendLimit specifies the maximum number of coins that can be spent + // in the Period + PeriodSpendLimit sdk.Coins + // PeriodCanSpend is the number of coins left to be spend before the PeriodReset time + PeriodCanSpend sdk.Coins + // PeriodReset is the time at which this period resets and a new one begins, + // it is calculated from the start time of the first transaction after the + // last period ended + PeriodReset time.Time +} +``` \ No newline at end of file diff --git a/x/delegation/doc.go b/x/delegation/doc.go new file mode 100644 index 000000000000..8d9a71fa04b7 --- /dev/null +++ b/x/delegation/doc.go @@ -0,0 +1,32 @@ +/* +Package delegation provides functionality for delegating sub-permissions +from one account to another account. + +The first implementation allows the delegation for the payment of transaction fees. +Effectively, this allows for a user to pay fees using the balance of an account +different from their own. Example use cases would be allowing a key on a device to +pay for fees using a master wallet, or a third party service allowing users to +pay for transactions without ever really holding their own tokens. This package +provides ways for specifying fee allowances such that delegating fees +to another account can be done with clear and safe restrictions. + +A user would delegate fees to a user using MsgDelegateFeeAllowance and revoke +that delegation using MsgRevokeFeeAllowance. In both cases Granter is the one +who is delegating fees and Grantee is the one who is receiving the delegation. +So grantee would correspond to the one who is signing a transaction and the +granter would be the address they place in StdTx.FeeAccount. + +The fee allowance that a grantee receives is specified by an implementation of +the FeeAllowance interface. Two FeeAllowance implementations are provided in +this package: BasicFeeAllowance and PeriodicFeeAllowance. + +TODO: update if needed with new modular ante handler + +In order to integrate this into an application, the "ante handler" which deducts +fees must call Keeper.AllowDelegatedFees to check if +the provided StdTx.Fee can be delegated from the Std.TxFeeAccount address +to the first signer of the transaction. An example usage would be: + +allow := feeDelegationKeeper.AllowDelegatedFees(ctx, signers[0], stdTx.FeeAccount, stdTx.Fee.Amount) +*/ +package delegation From ea6fe7d851123fac3667a3332b1e28ef76aed672 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 16 Oct 2019 10:45:17 +0200 Subject: [PATCH 02/77] Add BasicFeeAllowance implementation --- x/delegation/exported/fees.go | 19 ++++++++++++ x/delegation/internal/types/basic_fee.go | 38 ++++++++++++++++++++++++ x/delegation/internal/types/codec.go | 22 ++++++++++++++ x/delegation/internal/types/errors.go | 23 ++++++++++++++ x/delegation/internal/types/key.go | 15 ++++++++++ 5 files changed, 117 insertions(+) create mode 100644 x/delegation/exported/fees.go create mode 100644 x/delegation/internal/types/basic_fee.go create mode 100644 x/delegation/internal/types/codec.go create mode 100644 x/delegation/internal/types/errors.go create mode 100644 x/delegation/internal/types/key.go diff --git a/x/delegation/exported/fees.go b/x/delegation/exported/fees.go new file mode 100644 index 000000000000..b18c1d51354e --- /dev/null +++ b/x/delegation/exported/fees.go @@ -0,0 +1,19 @@ +package exported + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + abci "github.com/tendermint/tendermint/abci/types" +) + +// FeeAllowance implementations are tied to a given delegator and delegatee, +// and are used to enforce limits on this payment. +type FeeAllowance interface { + // Accept can use fee payment requested as well as timestamp/height of the current block + // to determine whether or not to process this. + // If it returns an error, the fee payment is rejected, otherwise it is accepted. + // The FeeAllowance implementation is expected to update it's internal state + // and will be saved again after an acceptance. + // If remove is true (regardless of the error), the FeeAllowance will be deleted from storage + // (eg. when it expires) + Accept(fee sdk.Coins, block abci.Header) (remove bool, err error) +} diff --git a/x/delegation/internal/types/basic_fee.go b/x/delegation/internal/types/basic_fee.go new file mode 100644 index 000000000000..195df50869ea --- /dev/null +++ b/x/delegation/internal/types/basic_fee.go @@ -0,0 +1,38 @@ +package types + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/delegation/exported" + abci "github.com/tendermint/tendermint/abci/types" +) + +// BasicFeeAllowance implements FeeAllowance with a one-time grant of tokens +// that optionally expires. The delegatee can use up to SpendLimit to cover fees. +type BasicFeeAllowance struct { + // SpendLimit is the maximum amount of tokens to be spent + SpendLimit sdk.Coins + + // TODO: make this time or height + // Expiration specifies an optional time when this allowance expires + // is Expiration.IsZero() then it never expires + Expiration time.Time +} + +var _ exported.FeeAllowance = (*BasicFeeAllowance)(nil) + +// Accept implements FeeAllowance and deducts the fees from the SpendLimit if possible +func (a *BasicFeeAllowance) Accept(fee sdk.Coins, block abci.Header) (remove bool, err error) { + // TODO: handle expiry + + left, invalid := a.SpendLimit.SafeSub(fee) + if invalid { + return false, ErrFeeLimitExceeded() + } + if left.IsZero() { + return true, nil + } + a.SpendLimit = left + return false, nil +} diff --git a/x/delegation/internal/types/codec.go b/x/delegation/internal/types/codec.go new file mode 100644 index 000000000000..e0836dacbb4d --- /dev/null +++ b/x/delegation/internal/types/codec.go @@ -0,0 +1,22 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/delegation/exported" +) + +// RegisterCodec registers the account types and interface +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterInterface((*exported.FeeAllowance)(nil), nil) + cdc.RegisterConcrete(&BasicFeeAllowance{}, "cosmos-sdk/BasicFeeAllowance", nil) +} + +// ModuleCdc generic sealed codec to be used throughout module +var ModuleCdc *codec.Codec + +func init() { + cdc := codec.New() + RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + ModuleCdc = cdc.Seal() +} diff --git a/x/delegation/internal/types/errors.go b/x/delegation/internal/types/errors.go new file mode 100644 index 000000000000..778c7d131d54 --- /dev/null +++ b/x/delegation/internal/types/errors.go @@ -0,0 +1,23 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Codes for governance errors +const ( + DefaultCodespace sdk.CodespaceType = ModuleName + + CodeFeeLimitExceeded sdk.CodeType = 1 + CodeFeeLimitExpired sdk.CodeType = 2 +) + +// ErrFeeLimitExceeded error if there are not enough allowance to cover the fees +func ErrFeeLimitExceeded() sdk.Error { + return sdk.NewError(DefaultCodespace, CodeFeeLimitExceeded, "fee limit exceeded") +} + +// ErrFeeLimitExpired error if the allowance has expired +func ErrFeeLimitExpired() sdk.Error { + return sdk.NewError(DefaultCodespace, CodeFeeLimitExpired, "fee limit expired") +} diff --git a/x/delegation/internal/types/key.go b/x/delegation/internal/types/key.go new file mode 100644 index 000000000000..1e4528e100c4 --- /dev/null +++ b/x/delegation/internal/types/key.go @@ -0,0 +1,15 @@ +package types + +const ( + // ModuleName is the module name constant used in many places + ModuleName = "delegation" + + // StoreKey is the store key string for supply + StoreKey = ModuleName + + // RouterKey is the message route for supply + RouterKey = ModuleName + + // QuerierRoute is the querier route for supply + QuerierRoute = ModuleName +) From c46153cf0bea71f0b32c3d66fafd7a8a47bfb0bf Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 16 Oct 2019 11:26:37 +0200 Subject: [PATCH 03/77] Add expiration structs and complete basic fee --- x/delegation/exported/fees.go | 5 +- x/delegation/internal/types/basic_fee.go | 27 ++--- x/delegation/internal/types/errors.go | 12 +++ x/delegation/internal/types/expiration.go | 115 ++++++++++++++++++++++ 4 files changed, 146 insertions(+), 13 deletions(-) create mode 100644 x/delegation/internal/types/expiration.go diff --git a/x/delegation/exported/fees.go b/x/delegation/exported/fees.go index b18c1d51354e..a757073c05ac 100644 --- a/x/delegation/exported/fees.go +++ b/x/delegation/exported/fees.go @@ -1,8 +1,9 @@ package exported import ( + "time" + sdk "github.com/cosmos/cosmos-sdk/types" - abci "github.com/tendermint/tendermint/abci/types" ) // FeeAllowance implementations are tied to a given delegator and delegatee, @@ -15,5 +16,5 @@ type FeeAllowance interface { // and will be saved again after an acceptance. // If remove is true (regardless of the error), the FeeAllowance will be deleted from storage // (eg. when it expires) - Accept(fee sdk.Coins, block abci.Header) (remove bool, err error) + Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (remove bool, err error) } diff --git a/x/delegation/internal/types/basic_fee.go b/x/delegation/internal/types/basic_fee.go index 195df50869ea..322ce457d602 100644 --- a/x/delegation/internal/types/basic_fee.go +++ b/x/delegation/internal/types/basic_fee.go @@ -5,7 +5,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/delegation/exported" - abci "github.com/tendermint/tendermint/abci/types" ) // BasicFeeAllowance implements FeeAllowance with a one-time grant of tokens @@ -14,25 +13,31 @@ type BasicFeeAllowance struct { // SpendLimit is the maximum amount of tokens to be spent SpendLimit sdk.Coins - // TODO: make this time or height - // Expiration specifies an optional time when this allowance expires - // is Expiration.IsZero() then it never expires - Expiration time.Time + // Expiration specifies an optional time or height when this allowance expires. + // If Expiration.IsZero() then it never expires + Expiration ExpiresAt } var _ exported.FeeAllowance = (*BasicFeeAllowance)(nil) // Accept implements FeeAllowance and deducts the fees from the SpendLimit if possible -func (a *BasicFeeAllowance) Accept(fee sdk.Coins, block abci.Header) (remove bool, err error) { - // TODO: handle expiry +func (a *BasicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (remove bool, err error) { + if a.Expiration.IsExpired(blockTime, blockHeight) { + return true, ErrFeeLimitExpired() + } left, invalid := a.SpendLimit.SafeSub(fee) if invalid { return false, ErrFeeLimitExceeded() } - if left.IsZero() { - return true, nil - } + a.SpendLimit = left - return false, nil + return left.IsZero(), nil +} + +func (a BasicFeeAllowance) ValidateBasic() error { + if a.SpendLimit.Empty() || !a.SpendLimit.IsAllPositive() { + return ErrNonPositiveCoins() + } + return a.Expiration.ValidateBasic() } diff --git a/x/delegation/internal/types/errors.go b/x/delegation/internal/types/errors.go index 778c7d131d54..320f13e50402 100644 --- a/x/delegation/internal/types/errors.go +++ b/x/delegation/internal/types/errors.go @@ -10,6 +10,8 @@ const ( CodeFeeLimitExceeded sdk.CodeType = 1 CodeFeeLimitExpired sdk.CodeType = 2 + CodeInvalidPeriod sdk.CodeType = 3 + CodeNonPositiveCoins sdk.CodeType = 4 ) // ErrFeeLimitExceeded error if there are not enough allowance to cover the fees @@ -21,3 +23,13 @@ func ErrFeeLimitExceeded() sdk.Error { func ErrFeeLimitExpired() sdk.Error { return sdk.NewError(DefaultCodespace, CodeFeeLimitExpired, "fee limit expired") } + +// ErrInvalidPeriod error if the period is invalid or doesn't match the expiration +func ErrInvalidPeriod(reason string) sdk.Error { + return sdk.NewError(DefaultCodespace, CodeInvalidPeriod, reason) +} + +// ErrNonPositiveCoins error if some fees or allowance are non positive +func ErrNonPositiveCoins() sdk.Error { + return sdk.NewError(DefaultCodespace, CodeNonPositiveCoins, "non positive coin amount") +} diff --git a/x/delegation/internal/types/expiration.go b/x/delegation/internal/types/expiration.go new file mode 100644 index 000000000000..7eb75654d652 --- /dev/null +++ b/x/delegation/internal/types/expiration.go @@ -0,0 +1,115 @@ +package types + +import "time" + +// ExpiresAt is a point in time where something expires. +// It may be *either* block time or block height +type ExpiresAt struct { + Time time.Time `json:"time" yaml:"time"` + Height int64 `json:"height" yaml:"height"` +} + +// ExpiresAtTime creates an expiration at the given time +func ExpiresAtTime(t time.Time) ExpiresAt { + return ExpiresAt{Time: t} +} + +// ExpiresAtHeight creates an expiration at the given height +func ExpiresAtHeight(h int64) ExpiresAt { + return ExpiresAt{Height: h} +} + +// ValidateBasic performs basic sanity checks. +// Note that empty expiration is allowed +func (e ExpiresAt) ValidateBasic() error { + if !e.Time.IsZero() && e.Height != 0 { + return ErrInvalidPeriod("both time and height are set") + } + if e.Height < 0 { + return ErrInvalidPeriod("negative height") + } + return nil +} + +// IsZero returns true for an uninitialized struct +func (e ExpiresAt) IsZero() bool { + return e.Time.IsZero() && e.Height == 0 +} + +// IsExpired returns if the time or height is *equal to* or greater +// than the defined expiration point. Note that it is expired upon +// an exact match. +// +// Note a "zero" ExpiresAt is never expired +func (e ExpiresAt) IsExpired(t time.Time, h int64) bool { + if !e.Time.IsZero() && !t.Before(e.Time) { + return true + } + return e.Height != 0 && h >= e.Height +} + +// IsCompatible returns true iff the two use the same units. +// If false, they cannot be added. +func (e ExpiresAt) IsCompatible(p Period) bool { + if !e.Time.IsZero() { + return p.Clock > 0 + } + return p.Block > 0 +} + +// Step will increase the expiration point by one period +// It returns an error if the period is incompatible +func (e *ExpiresAt) Step(p Period) error { + if !e.IsCompatible(p) { + return ErrInvalidPeriod("expires_at and period have different units") + } + if !e.Time.IsZero() { + e.Time = e.Time.Add(p.Clock) + } else { + e.Height = e.Height + p.Block + } + return nil +} + +// Period is a repeating unit of either clock time or number of blocks. +// This is designed to be added to an ExpiresAt struct. +type Period struct { + Clock time.Duration `json:"clock" yaml:"clock"` + Block int64 `json:"block" yaml:"block"` +} + +// ClockPeriod creates an period by clock time +func ClockPeriod(d time.Duration) Period { + // assert nothing negative + if d < 0 { + panic("Cannot use a negative duration") + } + return Period{Clock: d} +} + +// BlockPeriod creates an period by block height +func BlockPeriod(h int64) Period { + // assert nothing negative + if h < 0 { + panic("Cannot use a negative block step") + } + return Period{Block: h} +} + +// ValidateBasic performs basic sanity checks +// Note that exactly one must be set and it must be positive +func (p Period) ValidateBasic() error { + if p.Block == 0 && p.Clock == 0 { + return ErrInvalidPeriod("neither time and height are set") + } + if p.Block != 0 && p.Clock != 0 { + return ErrInvalidPeriod("both time and height are set") + } + if p.Block < 0 { + return ErrInvalidPeriod("negative block step") + } + if p.Clock < 0 { + return ErrInvalidPeriod("negative clock step") + } + return nil +} From 42c17f5815c27625375bc7667f6ec1dc66b61a4f Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 16 Oct 2019 11:46:11 +0200 Subject: [PATCH 04/77] Add delegation messages, add validation logic --- types/errors.go | 13 ++++ x/delegation/exported/fees.go | 4 ++ x/delegation/internal/types/basic_fee.go | 8 ++- x/delegation/internal/types/codec.go | 2 +- x/delegation/internal/types/msgs.go | 91 ++++++++++++++++++++++++ 5 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 x/delegation/internal/types/msgs.go diff --git a/types/errors.go b/types/errors.go index 1447a21b4cd3..d23d2e9bb322 100644 --- a/types/errors.go +++ b/types/errors.go @@ -309,6 +309,19 @@ func ResultFromError(err error) Result { } } +// ConvertError will take a standard error (from types/error) and +// convert it to an sdk.Error +func ConvertError(err error) Error { + if err == nil { + return nil + } + if sdk, ok := err.(Error); ok { + return sdk + } + space, code, log := sdkerrors.ABCIInfo(err, false) + return NewError(CodespaceType(space), CodeType(code), log) +} + //---------------------------------------- // REST error utilities diff --git a/x/delegation/exported/fees.go b/x/delegation/exported/fees.go index a757073c05ac..a9c57de6c823 100644 --- a/x/delegation/exported/fees.go +++ b/x/delegation/exported/fees.go @@ -17,4 +17,8 @@ type FeeAllowance interface { // If remove is true (regardless of the error), the FeeAllowance will be deleted from storage // (eg. when it expires) Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (remove bool, err error) + + // ValidateBasic should evaluate this FeeAllowance for internal consistency. + // Don't allow negative amounts, or negative periods for example. + ValidateBasic() error } diff --git a/x/delegation/internal/types/basic_fee.go b/x/delegation/internal/types/basic_fee.go index 322ce457d602..01b23be9d286 100644 --- a/x/delegation/internal/types/basic_fee.go +++ b/x/delegation/internal/types/basic_fee.go @@ -35,9 +35,13 @@ func (a *BasicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockHeig return left.IsZero(), nil } +// ValidateBasic implements FeeAllowance and enforces basic sanity checks func (a BasicFeeAllowance) ValidateBasic() error { - if a.SpendLimit.Empty() || !a.SpendLimit.IsAllPositive() { - return ErrNonPositiveCoins() + if !a.SpendLimit.IsValid() { + return sdk.ErrInvalidCoins("send amount is invalid: " + a.SpendLimit.String()) + } + if !a.SpendLimit.IsAllPositive() { + return sdk.ErrInvalidCoins("spend limit must be positive") } return a.Expiration.ValidateBasic() } diff --git a/x/delegation/internal/types/codec.go b/x/delegation/internal/types/codec.go index e0836dacbb4d..b7fc25bb04d7 100644 --- a/x/delegation/internal/types/codec.go +++ b/x/delegation/internal/types/codec.go @@ -8,7 +8,7 @@ import ( // RegisterCodec registers the account types and interface func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*exported.FeeAllowance)(nil), nil) - cdc.RegisterConcrete(&BasicFeeAllowance{}, "cosmos-sdk/BasicFeeAllowance", nil) + cdc.RegisterConcrete(&BasicFeeAllowance{}, "delegation/BasicFeeAllowance", nil) } // ModuleCdc generic sealed codec to be used throughout module diff --git a/x/delegation/internal/types/msgs.go b/x/delegation/internal/types/msgs.go new file mode 100644 index 000000000000..b89817166a65 --- /dev/null +++ b/x/delegation/internal/types/msgs.go @@ -0,0 +1,91 @@ +package types + +import ( + "encoding/json" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/delegation/exported" +) + +// MsgDelegateFeeAllowance adds permission for Grantee to spend up to Allowance +// of fees from the account of Granter. +// If there was already an existing delegation, this overwrites it. +type MsgDelegateFeeAllowance struct { + Granter sdk.AccAddress `json:"granter" yaml:"granter"` + Grantee sdk.AccAddress `json:"grantee" yaml:"grantee"` + Allowance exported.FeeAllowance `json:"allowance" yaml:"allowance"` +} + +func NewMsgDelegateFeeAllowance(granter sdk.AccAddress, grantee sdk.AccAddress, allowance exported.FeeAllowance) MsgDelegateFeeAllowance { + return MsgDelegateFeeAllowance{Granter: granter, Grantee: grantee, Allowance: allowance} +} + +func (msg MsgDelegateFeeAllowance) Route() string { + return "delegation" +} + +func (msg MsgDelegateFeeAllowance) Type() string { + return "delegate-fee-allowance" +} + +func (msg MsgDelegateFeeAllowance) ValidateBasic() sdk.Error { + if msg.Granter.Empty() { + return sdk.ErrInvalidAddress("missing granter address") + } + if msg.Grantee.Empty() { + return sdk.ErrInvalidAddress("missing grantee address") + } + return sdk.ConvertError(msg.Allowance.ValidateBasic()) +} + +func (msg MsgDelegateFeeAllowance) GetSignBytes() []byte { + b, err := json.Marshal(msg) + if err != nil { + panic(err) + } + return sdk.MustSortJSON(b) +} + +func (msg MsgDelegateFeeAllowance) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Granter} +} + +// MsgRevokeFeeAllowance removes any existing FeeAllowance from Granter to Grantee. +type MsgRevokeFeeAllowance struct { + Granter sdk.AccAddress `json:"granter" yaml:"granter"` + Grantee sdk.AccAddress `json:"grantee" yaml:"granter"` +} + +func NewMsgRevokeFeeAllowance(granter sdk.AccAddress, grantee sdk.AccAddress) MsgRevokeFeeAllowance { + return MsgRevokeFeeAllowance{Granter: granter, Grantee: grantee} +} + +func (msg MsgRevokeFeeAllowance) Route() string { + return "delegation" +} + +func (msg MsgRevokeFeeAllowance) Type() string { + return "revoke-fee-allowance" +} + +func (msg MsgRevokeFeeAllowance) ValidateBasic() sdk.Error { + if msg.Granter.Empty() { + return sdk.ErrInvalidAddress("missing granter address") + } + if msg.Grantee.Empty() { + return sdk.ErrInvalidAddress("missing grantee address") + } + return nil +} + +func (msg MsgRevokeFeeAllowance) GetSignBytes() []byte { + b, err := json.Marshal(msg) + if err != nil { + panic(err) + } + return sdk.MustSortJSON(b) +} + +func (msg MsgRevokeFeeAllowance) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Granter} +} From 0c137cb1d0f4072db0d64e162b00c0a00c3041a6 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 16 Oct 2019 12:49:32 +0200 Subject: [PATCH 05/77] Add keeper and helper structs --- x/delegation/internal/keeper/keeper.go | 116 +++++++++++++++++++++++++ x/delegation/internal/types/errors.go | 6 -- x/delegation/internal/types/grant.go | 24 +++++ x/delegation/internal/types/key.go | 22 +++++ 4 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 x/delegation/internal/keeper/keeper.go create mode 100644 x/delegation/internal/types/grant.go diff --git a/x/delegation/internal/keeper/keeper.go b/x/delegation/internal/keeper/keeper.go new file mode 100644 index 000000000000..e7baa5ee44b7 --- /dev/null +++ b/x/delegation/internal/keeper/keeper.go @@ -0,0 +1,116 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/delegation/exported" + "github.com/cosmos/cosmos-sdk/x/delegation/internal/types" +) + +type Keeper struct { + storeKey sdk.StoreKey + cdc *codec.Codec +} + +// NewKeeper creates a DelegationKeeper +func NewKeeper(storeKey sdk.StoreKey, cdc *codec.Codec) Keeper { + return Keeper{storeKey, cdc} +} + +// DelegateFeeAllowance creates a new grant +func (k Keeper) DelegateFeeAllowance(ctx sdk.Context, grant types.FeeAllowanceGrant) error { + store := ctx.KVStore(k.storeKey) + key := types.FeeAllowanceKey(grant.Granter, grant.Grantee) + + bz, err := k.cdc.MarshalBinaryBare(grant) + if err != nil { + return err + } + store.Set(key, bz) + return nil +} + +// RevokeFeeAllowance removes an existing grant +func (k Keeper) RevokeFeeAllowance(ctx sdk.Context, granter sdk.AccAddress, grantee sdk.AccAddress) { + store := ctx.KVStore(k.storeKey) + key := types.FeeAllowanceKey(granter, grantee) + store.Delete(key) +} + +// GetFeeAllowance returns the allowance between the granter and grantee. +// If there is none, it returns nil, nil. +// Returns an error on parsing issues +func (k Keeper) GetFeeAllowance(ctx sdk.Context, granter sdk.AccAddress, grantee sdk.AccAddress) (exported.FeeAllowance, error) { + grant, err := k.GetFeeGrant(ctx, granter, grantee) + if err != nil { + return nil, err + } + return grant.Allowance, nil +} + +// GetFeeGrant returns entire grant between both accounts +func (k Keeper) GetFeeGrant(ctx sdk.Context, granter sdk.AccAddress, grantee sdk.AccAddress) (*types.FeeAllowanceGrant, error) { + store := ctx.KVStore(k.storeKey) + key := types.FeeAllowanceKey(granter, grantee) + bz := store.Get(key) + if len(bz) == 0 { + return nil, nil + } + + var grant types.FeeAllowanceGrant + err := k.cdc.UnmarshalBinaryBare(bz, &grant) + if err != nil { + return nil, err + } + return &grant, nil +} + +// GetAllFeeAllowances returns a list of all the grants from anyone to the given grantee. +func (k Keeper) GetAllFeeAllowances(ctx sdk.Context, grantee sdk.AccAddress) ([]types.FeeAllowanceGrant, error) { + store := ctx.KVStore(k.storeKey) + var grants []types.FeeAllowanceGrant + + prefix := types.FeeAllowancePrefixByGrantee(grantee) + iter := sdk.KVStorePrefixIterator(store, prefix) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + bz := iter.Value() + var grant types.FeeAllowanceGrant + err := k.cdc.UnmarshalBinaryBare(bz, &grant) + if err != nil { + return nil, err + } + grants = append(grants, grant) + } + return grants, nil +} + +// UseDelegatedFees will try to pay the given fee from the granter's account as requested by the grantee +// (true, nil) will update the allowance, and assumes the AnteHandler deducts the given fees +// (false, nil) rejects payment on behalf of grantee +// (?, err) means there was a data parsing error (abort tx and log this info) +func (k Keeper) UseDelegatedFees(ctx sdk.Context, granter sdk.AccAddress, grantee sdk.AccAddress, fee sdk.Coins) bool { + grant, err := k.GetFeeGrant(ctx, granter, grantee) + if err != nil { + // we should acknowlegde a db issue somehow (better?) + ctx.Logger().Error(err.Error()) + return false + } + + remove, err := grant.Allowance.Accept(fee, ctx.BlockTime(), ctx.BlockHeight()) + if remove { + k.RevokeFeeAllowance(ctx, granter, grantee) + return err == nil + } + if err != nil { + return false + } + + // if we accepted, store the updated state of the allowance + if err := k.DelegateFeeAllowance(ctx, *grant); err != nil { + // we should acknowlegde a db issue somehow (better?) + ctx.Logger().Error(err.Error()) + return false + } + return true +} diff --git a/x/delegation/internal/types/errors.go b/x/delegation/internal/types/errors.go index 320f13e50402..1d5c57115030 100644 --- a/x/delegation/internal/types/errors.go +++ b/x/delegation/internal/types/errors.go @@ -11,7 +11,6 @@ const ( CodeFeeLimitExceeded sdk.CodeType = 1 CodeFeeLimitExpired sdk.CodeType = 2 CodeInvalidPeriod sdk.CodeType = 3 - CodeNonPositiveCoins sdk.CodeType = 4 ) // ErrFeeLimitExceeded error if there are not enough allowance to cover the fees @@ -28,8 +27,3 @@ func ErrFeeLimitExpired() sdk.Error { func ErrInvalidPeriod(reason string) sdk.Error { return sdk.NewError(DefaultCodespace, CodeInvalidPeriod, reason) } - -// ErrNonPositiveCoins error if some fees or allowance are non positive -func ErrNonPositiveCoins() sdk.Error { - return sdk.NewError(DefaultCodespace, CodeNonPositiveCoins, "non positive coin amount") -} diff --git a/x/delegation/internal/types/grant.go b/x/delegation/internal/types/grant.go new file mode 100644 index 000000000000..33aa7e29f7df --- /dev/null +++ b/x/delegation/internal/types/grant.go @@ -0,0 +1,24 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/delegation/exported" +) + +// FeeAllowanceGrant is stored in the KVStore to record a grant with full context +type FeeAllowanceGrant struct { + Granter sdk.AccAddress `json:"granter" yaml:"granter"` + Grantee sdk.AccAddress `json:"grantee" yaml:"grantee"` + Allowance exported.FeeAllowance `json:"allowance" yaml:"allowance"` +} + +// ValidateBasic ensures that +func (a FeeAllowanceGrant) ValidateBasic() error { + if a.Granter.Empty() { + return sdk.ErrInvalidAddress("missing granter address") + } + if a.Grantee.Empty() { + return sdk.ErrInvalidAddress("missing grantee address") + } + return a.Allowance.ValidateBasic() +} diff --git a/x/delegation/internal/types/key.go b/x/delegation/internal/types/key.go index 1e4528e100c4..6e5987fa08b7 100644 --- a/x/delegation/internal/types/key.go +++ b/x/delegation/internal/types/key.go @@ -1,5 +1,11 @@ package types +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + const ( // ModuleName is the module name constant used in many places ModuleName = "delegation" @@ -13,3 +19,19 @@ const ( // QuerierRoute is the querier route for supply QuerierRoute = ModuleName ) + +var ( + // FeeAllowanceKeyPrefix is the set of the kvstore for fee allowance data + FeeAllowanceKeyPrefix = []byte{0x00} +) + +// FeeAllowanceKey is the cannonical key to store a grant from granter to grantee +// We store by grantee first to allow searching by everyone who granted to you +func FeeAllowanceKey(granter sdk.AccAddress, grantee sdk.AccAddress) []byte { + return append(FeeAllowanceKeyPrefix, []byte(fmt.Sprintf("%s/%s", grantee, granter))...) +} + +// FeeAllowancePrefixByGrantee returns a prefix to scan for all grants to this given address. +func FeeAllowancePrefixByGrantee(grantee sdk.AccAddress) []byte { + return append(FeeAllowanceKeyPrefix, []byte(fmt.Sprintf("%s/", grantee))...) +} From 67cb830c81aa6e51d9ff75a80889482e3a52e78b Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 16 Oct 2019 12:54:00 +0200 Subject: [PATCH 06/77] Add alias and handler to top level --- x/delegation/alias.go | 53 +++++++++++++++++++++++++++++++++++++++++ x/delegation/handler.go | 24 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 x/delegation/alias.go create mode 100644 x/delegation/handler.go diff --git a/x/delegation/alias.go b/x/delegation/alias.go new file mode 100644 index 000000000000..8a2eda474911 --- /dev/null +++ b/x/delegation/alias.go @@ -0,0 +1,53 @@ +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/delegation/internal/types +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/delegation/internal/keeper +package delegation + +import ( + "github.com/cosmos/cosmos-sdk/x/delegation/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/delegation/internal/types" +) + +const ( + DefaultCodespace = types.DefaultCodespace + CodeFeeLimitExceeded = types.CodeFeeLimitExceeded + CodeFeeLimitExpired = types.CodeFeeLimitExpired + CodeInvalidPeriod = types.CodeInvalidPeriod + ModuleName = types.ModuleName + StoreKey = types.StoreKey + RouterKey = types.RouterKey + QuerierRoute = types.QuerierRoute +) + +var ( + // functions aliases + RegisterCodec = types.RegisterCodec + ErrFeeLimitExceeded = types.ErrFeeLimitExceeded + ErrFeeLimitExpired = types.ErrFeeLimitExpired + ErrInvalidPeriod = types.ErrInvalidPeriod + ExpiresAtTime = types.ExpiresAtTime + ExpiresAtHeight = types.ExpiresAtHeight + ClockPeriod = types.ClockPeriod + BlockPeriod = types.BlockPeriod + FeeAllowanceKey = types.FeeAllowanceKey + FeeAllowancePrefixByGrantee = types.FeeAllowancePrefixByGrantee + NewMsgDelegateFeeAllowance = types.NewMsgDelegateFeeAllowance + NewMsgRevokeFeeAllowance = types.NewMsgRevokeFeeAllowance + NewKeeper = keeper.NewKeeper + + // variable aliases + ModuleCdc = types.ModuleCdc + FeeAllowanceKeyPrefix = types.FeeAllowanceKeyPrefix +) + +type ( + BasicFeeAllowance = types.BasicFeeAllowance + ExpiresAt = types.ExpiresAt + Period = types.Period + FeeAllowanceGrant = types.FeeAllowanceGrant + MsgDelegateFeeAllowance = types.MsgDelegateFeeAllowance + MsgRevokeFeeAllowance = types.MsgRevokeFeeAllowance + Keeper = keeper.Keeper +) diff --git a/x/delegation/handler.go b/x/delegation/handler.go new file mode 100644 index 000000000000..070673fceb3a --- /dev/null +++ b/x/delegation/handler.go @@ -0,0 +1,24 @@ +package delegation + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func NewHandler(k Keeper) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + switch msg := msg.(type) { + case MsgDelegateFeeAllowance: + grant := FeeAllowanceGrant{Granter: msg.Granter, Grantee: msg.Grantee, Allowance: msg.Allowance} + k.DelegateFeeAllowance(ctx, grant) + return sdk.Result{} + case MsgRevokeFeeAllowance: + k.RevokeFeeAllowance(ctx, msg.Granter, msg.Grantee) + return sdk.Result{} + default: + errMsg := fmt.Sprintf("Unrecognized data Msg type: %v", msg.Type()) + return sdk.ErrUnknownRequest(errMsg).Result() + } + } +} From f6ba9bcdfe756bda1c340167faab86b3f3313d9a Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 16 Oct 2019 14:07:09 +0200 Subject: [PATCH 07/77] Add delegation module --- x/delegation/module.go | 154 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 x/delegation/module.go diff --git a/x/delegation/module.go b/x/delegation/module.go new file mode 100644 index 000000000000..a491b39b3ea9 --- /dev/null +++ b/x/delegation/module.go @@ -0,0 +1,154 @@ +package delegation + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" +) + +// TODO: +// * genesis +// * querier +// * cli +// * rest +// * ante handler +// -> changes to auth, etc + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// AppModuleBasic defines the basic application module used by the delegation module. +type AppModuleBasic struct{} + +// Name returns the delegation module's name. +func (AppModuleBasic) Name() string { + return ModuleName +} + +// RegisterCodec registers the delegation module's types for the given codec. +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + RegisterCodec(cdc) +} + +// DefaultGenesis returns default genesis state as raw bytes for the delegation +// module. +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + panic("not implemented!") + // return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) +} + +// ValidateGenesis performs genesis state validation for the delegation module. +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + // TODO + return nil + // var data GenesisState + // err := ModuleCdc.UnmarshalJSON(bz, &data) + // if err != nil { + // return err + // } + // return ValidateGenesis(data) +} + +// RegisterRESTRoutes registers the REST routes for the delegation module. +func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { + // TODO + // rest.RegisterRoutes(ctx, rtr) +} + +// GetTxCmd returns the root tx command for the delegation module. +func (AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { + // TODO + return nil +} + +// GetQueryCmd returns no root query command for the delegation module. +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + // TODO + return nil + // return cli.GetQueryCmd(cdc) +} + +//____________________________________________________________________________ + +// AppModule implements an application module for the delegation module. +type AppModule struct { + AppModuleBasic + keeper Keeper +} + +// NewAppModule creates a new AppModule object +func NewAppModule(keeper Keeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + keeper: keeper, + } +} + +// Name returns the delegation module's name. +func (AppModule) Name() string { + return ModuleName +} + +// RegisterInvariants registers the delegation module invariants. +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + // TODO? + // RegisterInvariants(ir, am.keeper) +} + +// Route returns the message routing key for the delegation module. +func (AppModule) Route() string { + return RouterKey +} + +// NewHandler returns an sdk.Handler for the delegation module. +func (am AppModule) NewHandler() sdk.Handler { + return NewHandler(am.keeper) +} + +// QuerierRoute returns the delegation module's querier route name. +func (AppModule) QuerierRoute() string { + return QuerierRoute +} + +// NewQuerierHandler returns the delegation module sdk.Querier. +func (am AppModule) NewQuerierHandler() sdk.Querier { + panic("not implemented!") + // return NewQuerier(am.keeper) +} + +// InitGenesis performs genesis initialization for the delegation module. It returns +// no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + // TODO + // var genesisState GenesisState + // ModuleCdc.MustUnmarshalJSON(data, &genesisState) + // InitGenesis(ctx, am.keeper, am.ak, genesisState) + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the exported genesis state as raw bytes for the delegation +// module. +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + panic("not implemented!") + // gs := ExportGenesis(ctx, am.keeper) + // return ModuleCdc.MustMarshalJSON(gs) +} + +// BeginBlock returns the begin blocker for the delegation module. +func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} + +// EndBlock returns the end blocker for the delegation module. It returns no validator +// updates. +func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} From a00236646cdd19d9faad32f8898eac85e07e370e Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 16 Oct 2019 14:13:20 +0200 Subject: [PATCH 08/77] Add basic querier --- x/delegation/internal/keeper/querier.go | 44 +++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 x/delegation/internal/keeper/querier.go diff --git a/x/delegation/internal/keeper/querier.go b/x/delegation/internal/keeper/querier.go new file mode 100644 index 000000000000..ab12c5ecb804 --- /dev/null +++ b/x/delegation/internal/keeper/querier.go @@ -0,0 +1,44 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + abci "github.com/tendermint/tendermint/abci/types" +) + +const ( + QueryGetFeeAllowances = "fees" +) + +// NewQuerier creates a new querier +func NewQuerier(keeper Keeper) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, sdk.Error) { + switch path[0] { + case QueryGetFeeAllowances: + return queryGetFeeAllowances(ctx, path[1:], keeper) + default: + return nil, sdk.ErrUnknownRequest("Unknown package delegation query endpoint") + } + } +} + +func queryGetFeeAllowances(ctx sdk.Context, args []string, keeper Keeper) ([]byte, sdk.Error) { + grantee := args[0] + granteeAddr, err := sdk.AccAddressFromBech32(grantee) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("invalid address", err.Error())) + } + + fees, err := keeper.GetAllFeeAllowances(ctx, granteeAddr) + if err != nil { + return nil, sdk.ConvertError(err) + } + if fees == nil { + return []byte("[]"), nil + } + + bz, err := keeper.cdc.MarshalJSON(fees) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil +} From 15c63619efc08270ac1adc1dd0180983d3751ec1 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 16 Oct 2019 14:43:08 +0200 Subject: [PATCH 09/77] Add types tests --- x/delegation/internal/types/expiration.go | 8 - .../internal/types/expiration_test.go | 169 ++++++++++++++++++ 2 files changed, 169 insertions(+), 8 deletions(-) create mode 100644 x/delegation/internal/types/expiration_test.go diff --git a/x/delegation/internal/types/expiration.go b/x/delegation/internal/types/expiration.go index 7eb75654d652..f82af4d84721 100644 --- a/x/delegation/internal/types/expiration.go +++ b/x/delegation/internal/types/expiration.go @@ -80,19 +80,11 @@ type Period struct { // ClockPeriod creates an period by clock time func ClockPeriod(d time.Duration) Period { - // assert nothing negative - if d < 0 { - panic("Cannot use a negative duration") - } return Period{Clock: d} } // BlockPeriod creates an period by block height func BlockPeriod(h int64) Period { - // assert nothing negative - if h < 0 { - panic("Cannot use a negative block step") - } return Period{Block: h} } diff --git a/x/delegation/internal/types/expiration_test.go b/x/delegation/internal/types/expiration_test.go new file mode 100644 index 000000000000..5b032d204a08 --- /dev/null +++ b/x/delegation/internal/types/expiration_test.go @@ -0,0 +1,169 @@ +package types + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestExpiresAt(t *testing.T) { + now := time.Now() + + cases := map[string]struct { + example ExpiresAt + valid bool + zero bool + before *ExpiresAt + after *ExpiresAt + }{ + "basic": { + example: ExpiresAtHeight(100), + valid: true, + before: &ExpiresAt{Height: 50, Time: now}, + after: &ExpiresAt{Height: 122, Time: now}, + }, + "zero": { + example: ExpiresAt{}, + zero: true, + valid: true, + before: &ExpiresAt{Height: 1}, + }, + "double": { + example: ExpiresAt{Height: 100, Time: now}, + valid: false, + }, + "match height": { + example: ExpiresAtHeight(1000), + valid: true, + before: &ExpiresAt{Height: 999, Time: now}, + after: &ExpiresAt{Height: 1000, Time: now}, + }, + "match time": { + example: ExpiresAtTime(now), + valid: true, + before: &ExpiresAt{Height: 43, Time: now.Add(-1 * time.Second)}, + after: &ExpiresAt{Height: 76, Time: now}, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + err := tc.example.ValidateBasic() + assert.Equal(t, tc.zero, tc.example.IsZero()) + if !tc.valid { + require.Error(t, err) + return + } + require.NoError(t, err) + + if tc.before != nil { + assert.Equal(t, false, tc.example.IsExpired(tc.before.Time, tc.before.Height)) + } + if tc.after != nil { + assert.Equal(t, true, tc.example.IsExpired(tc.after.Time, tc.after.Height)) + } + }) + } +} + +func TestPeriodValid(t *testing.T) { + now := time.Now() + + cases := map[string]struct { + period Period + valid bool + compatible ExpiresAt + incompatible ExpiresAt + }{ + "basic height": { + period: BlockPeriod(100), + valid: true, + compatible: ExpiresAtHeight(50), + incompatible: ExpiresAtTime(now), + }, + "basic time": { + period: ClockPeriod(time.Hour), + valid: true, + compatible: ExpiresAtTime(now), + incompatible: ExpiresAtHeight(50), + }, + "zero": { + period: Period{}, + valid: false, + }, + "double": { + period: Period{Block: 100, Clock: time.Hour}, + valid: false, + }, + "negative clock": { + period: ClockPeriod(-1 * time.Hour), + valid: false, + }, + "negative block": { + period: BlockPeriod(-5), + valid: false, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + err := tc.period.ValidateBasic() + if !tc.valid { + require.Error(t, err) + return + } + require.NoError(t, err) + + assert.Equal(t, true, tc.compatible.IsCompatible(tc.period)) + assert.Equal(t, false, tc.incompatible.IsCompatible(tc.period)) + }) + } +} + +func TestPeriodStep(t *testing.T) { + now := time.Now() + + cases := map[string]struct { + expires ExpiresAt + period Period + valid bool + result ExpiresAt + }{ + "add height": { + expires: ExpiresAtHeight(789), + period: BlockPeriod(100), + valid: true, + result: ExpiresAtHeight(889), + }, + "add time": { + expires: ExpiresAtTime(now), + period: ClockPeriod(time.Hour), + valid: true, + result: ExpiresAtTime(now.Add(time.Hour)), + }, + "mismatch": { + expires: ExpiresAtHeight(789), + period: ClockPeriod(time.Hour), + valid: false, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + err := tc.period.ValidateBasic() + require.NoError(t, err) + err = tc.expires.ValidateBasic() + require.NoError(t, err) + + err = tc.expires.Step(tc.period) + if !tc.valid { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, tc.result, tc.expires) + }) + } +} From 410d1f19ca56f07ae7e01563c2fae9186f99b017 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 16 Oct 2019 14:55:36 +0200 Subject: [PATCH 10/77] Add types tests --- x/delegation/internal/types/basic_fee_test.go | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 x/delegation/internal/types/basic_fee_test.go diff --git a/x/delegation/internal/types/basic_fee_test.go b/x/delegation/internal/types/basic_fee_test.go new file mode 100644 index 000000000000..4e7c0e726dc3 --- /dev/null +++ b/x/delegation/internal/types/basic_fee_test.go @@ -0,0 +1,108 @@ +package types + +import ( + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestBasicFeeValidAllow(t *testing.T) { + eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 10)) + atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) + smallAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 43)) + leftAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 512)) + + cases := map[string]struct { + allow BasicFeeAllowance + valid bool + // all below checks are ignored if invalid + fee sdk.Coins + blockTime time.Time + blockHeight int64 + accept bool + remove bool + remains sdk.Coins + }{ + "empty": { + allow: BasicFeeAllowance{}, + valid: false, + }, + "small fee": { + allow: BasicFeeAllowance{ + SpendLimit: atom, + }, + valid: true, + fee: smallAtom, + accept: true, + remove: false, + remains: leftAtom, + }, + "all fee": { + allow: BasicFeeAllowance{ + SpendLimit: smallAtom, + }, + valid: true, + fee: smallAtom, + accept: true, + remove: true, + }, + "wrong fee": { + allow: BasicFeeAllowance{ + SpendLimit: smallAtom, + }, + valid: true, + fee: eth, + accept: false, + }, + "non-expired": { + allow: BasicFeeAllowance{ + SpendLimit: atom, + Expiration: ExpiresAtHeight(100), + }, + valid: true, + fee: smallAtom, + blockHeight: 85, + accept: true, + remove: false, + remains: leftAtom, + }, + "expired": { + allow: BasicFeeAllowance{ + SpendLimit: atom, + Expiration: ExpiresAtHeight(100), + }, + valid: true, + fee: smallAtom, + blockHeight: 121, + accept: false, + remove: true, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + err := tc.allow.ValidateBasic() + if !tc.valid { + require.Error(t, err) + return + } + require.NoError(t, err) + + // now try to deduct + remove, err := tc.allow.Accept(tc.fee, tc.blockTime, tc.blockHeight) + if !tc.accept { + require.Error(t, err) + return + } + require.NoError(t, err) + + require.Equal(t, tc.remove, remove) + if !remove { + assert.Equal(t, tc.allow.SpendLimit, tc.remains) + } + }) + } +} From f65db09669fb73cc8716d55dbfae37476558b78d Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 16 Oct 2019 15:36:37 +0200 Subject: [PATCH 11/77] More internal test coverage --- x/delegation/internal/keeper/keeper.go | 4 +- x/delegation/internal/keeper/keeper_test.go | 186 ++++++++++++++++++++ x/delegation/internal/types/grant.go | 3 + x/delegation/internal/types/grant_test.go | 98 +++++++++++ 4 files changed, 289 insertions(+), 2 deletions(-) create mode 100644 x/delegation/internal/keeper/keeper_test.go create mode 100644 x/delegation/internal/types/grant_test.go diff --git a/x/delegation/internal/keeper/keeper.go b/x/delegation/internal/keeper/keeper.go index e7baa5ee44b7..9654c28c24e0 100644 --- a/x/delegation/internal/keeper/keeper.go +++ b/x/delegation/internal/keeper/keeper.go @@ -42,10 +42,10 @@ func (k Keeper) RevokeFeeAllowance(ctx sdk.Context, granter sdk.AccAddress, gran // Returns an error on parsing issues func (k Keeper) GetFeeAllowance(ctx sdk.Context, granter sdk.AccAddress, grantee sdk.AccAddress) (exported.FeeAllowance, error) { grant, err := k.GetFeeGrant(ctx, granter, grantee) - if err != nil { + if grant == nil { return nil, err } - return grant.Allowance, nil + return grant.Allowance, err } // GetFeeGrant returns entire grant between both accounts diff --git a/x/delegation/internal/keeper/keeper_test.go b/x/delegation/internal/keeper/keeper_test.go new file mode 100644 index 000000000000..c85eb1012d13 --- /dev/null +++ b/x/delegation/internal/keeper/keeper_test.go @@ -0,0 +1,186 @@ +package keeper_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" + + codec "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/delegation/exported" + "github.com/cosmos/cosmos-sdk/x/delegation/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/delegation/internal/types" +) + +type testInput struct { + cdc *codec.Codec + ctx sdk.Context + dk keeper.Keeper +} + +func setupTestInput() testInput { + db := dbm.NewMemDB() + + cdc := codec.New() + types.RegisterCodec(cdc) + sdk.RegisterCodec(cdc) + + delCapKey := sdk.NewKVStoreKey("delKey") + + ms := store.NewCommitMultiStore(db) + ms.MountStoreWithDB(delCapKey, sdk.StoreTypeIAVL, db) + ms.LoadLatestVersion() + + dk := keeper.NewKeeper(delCapKey, cdc) + + ctx := sdk.NewContext(ms, abci.Header{ChainID: "test-chain-id", Time: time.Now().UTC(), Height: 1234}, false, log.NewNopLogger()) + return testInput{cdc: cdc, ctx: ctx, dk: dk} +} + +var ( + // some valid cosmos keys.... + addr = mustAddr("cosmos157ez5zlaq0scm9aycwphhqhmg3kws4qusmekll") + addr2 = mustAddr("cosmos1rjxwm0rwyuldsg00qf5lt26wxzzppjzxs2efdw") + addr3 = mustAddr("cosmos1qk93t4j0yyzgqgt6k5qf8deh8fq6smpn3ntu3x") + addr4 = mustAddr("cosmos1p9qh4ldfd6n0qehujsal4k7g0e37kel90rc4ts") +) + +func mustAddr(acc string) sdk.AccAddress { + addr, err := sdk.AccAddressFromBech32(acc) + if err != nil { + panic(err) + } + return addr +} + +func TestKeeperCrud(t *testing.T) { + input := setupTestInput() + ctx := input.ctx + k := input.dk + + // some helpers + atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) + eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123)) + basic := types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(334455), + } + basic2 := types.BasicFeeAllowance{ + SpendLimit: eth, + Expiration: types.ExpiresAtHeight(172436), + } + + // let's set up some initial state here + err := k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + Granter: addr, Grantee: addr2, Allowance: &basic, + }) + require.NoError(t, err) + err = k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + Granter: addr, Grantee: addr3, Allowance: &basic2, + }) + require.NoError(t, err) + err = k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + Granter: addr2, Grantee: addr3, Allowance: &basic, + }) + require.NoError(t, err) + err = k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + Granter: addr2, Grantee: addr4, Allowance: &basic, + }) + require.NoError(t, err) + err = k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + Granter: addr4, Grantee: addr, Allowance: &basic2, + }) + require.NoError(t, err) + + // remove some, overwrite other + k.RevokeFeeAllowance(ctx, addr, addr2) + k.RevokeFeeAllowance(ctx, addr, addr3) + err = k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + Granter: addr, Grantee: addr3, Allowance: &basic, + }) + require.NoError(t, err) + err = k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + Granter: addr2, Grantee: addr3, Allowance: &basic2, + }) + require.NoError(t, err) + + // end state: + // addr -> addr3 (basic) + // addr2 -> addr3 (basic2), addr4(basic) + // addr4 -> addr (basic2) + + // then lots of queries + cases := map[string]struct { + grantee sdk.AccAddress + granter sdk.AccAddress + allowance exported.FeeAllowance + }{ + "addr revoked": { + granter: addr, + grantee: addr2, + }, + "addr revoked and added": { + granter: addr, + grantee: addr3, + allowance: &basic, + }, + "addr never there": { + granter: addr, + grantee: addr4, + }, + "addr modified": { + granter: addr2, + grantee: addr3, + allowance: &basic2, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + allow, err := k.GetFeeAllowance(ctx, tc.granter, tc.grantee) + require.NoError(t, err) + if tc.allowance == nil { + require.Nil(t, allow) + return + } + require.NotNil(t, allow) + b, ok := allow.(*types.BasicFeeAllowance) + require.True(t, ok) + require.Equal(t, tc.allowance, b) + }) + } + + allCases := map[string]struct { + grantee sdk.AccAddress + grants []types.FeeAllowanceGrant + }{ + "addr2 has none": { + grantee: addr2, + }, + "addr has one": { + grantee: addr, + grants: []types.FeeAllowanceGrant{{Granter: addr4, Grantee: addr, Allowance: &basic2}}, + }, + "addr3 has two": { + grantee: addr3, + grants: []types.FeeAllowanceGrant{ + {Granter: addr, Grantee: addr3, Allowance: &basic}, + {Granter: addr2, Grantee: addr3, Allowance: &basic2}, + }, + }, + } + + for name, tc := range allCases { + t.Run(name, func(t *testing.T) { + grants, err := k.GetAllFeeAllowances(ctx, tc.grantee) + require.NoError(t, err) + assert.Equal(t, tc.grants, grants) + }) + } +} diff --git a/x/delegation/internal/types/grant.go b/x/delegation/internal/types/grant.go index 33aa7e29f7df..c7bed78b6e30 100644 --- a/x/delegation/internal/types/grant.go +++ b/x/delegation/internal/types/grant.go @@ -20,5 +20,8 @@ func (a FeeAllowanceGrant) ValidateBasic() error { if a.Grantee.Empty() { return sdk.ErrInvalidAddress("missing grantee address") } + if a.Grantee.Equals(a.Granter) { + return sdk.ErrInvalidAddress("cannot self-grant fees") + } return a.Allowance.ValidateBasic() } diff --git a/x/delegation/internal/types/grant_test.go b/x/delegation/internal/types/grant_test.go new file mode 100644 index 000000000000..527806f1bccd --- /dev/null +++ b/x/delegation/internal/types/grant_test.go @@ -0,0 +1,98 @@ +package types + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGrant(t *testing.T) { + addr, err := sdk.AccAddressFromBech32("cosmos1qk93t4j0yyzgqgt6k5qf8deh8fq6smpn3ntu3x") + require.NoError(t, err) + addr2, err := sdk.AccAddressFromBech32("cosmos1p9qh4ldfd6n0qehujsal4k7g0e37kel90rc4ts") + require.NoError(t, err) + atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) + + cdc := codec.New() + RegisterCodec(cdc) + + cases := map[string]struct { + grant FeeAllowanceGrant + valid bool + }{ + "good": { + grant: FeeAllowanceGrant{ + Grantee: addr, + Granter: addr2, + Allowance: &BasicFeeAllowance{ + SpendLimit: atom, + Expiration: ExpiresAtHeight(100), + }, + }, + valid: true, + }, + "no grantee": { + grant: FeeAllowanceGrant{ + Granter: addr2, + Allowance: &BasicFeeAllowance{ + SpendLimit: atom, + Expiration: ExpiresAtHeight(100), + }, + }, + }, + "no granter": { + grant: FeeAllowanceGrant{ + Grantee: addr2, + Allowance: &BasicFeeAllowance{ + SpendLimit: atom, + Expiration: ExpiresAtHeight(100), + }, + }, + }, + "self-grant": { + grant: FeeAllowanceGrant{ + Grantee: addr2, + Granter: addr2, + Allowance: &BasicFeeAllowance{ + SpendLimit: atom, + Expiration: ExpiresAtHeight(100), + }, + }, + }, + "bad allowance": { + grant: FeeAllowanceGrant{ + Grantee: addr, + Granter: addr2, + Allowance: &BasicFeeAllowance{ + Expiration: ExpiresAtHeight(100), + }, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + err := tc.grant.ValidateBasic() + if !tc.valid { + require.Error(t, err) + return + } + require.NoError(t, err) + + // if it is valid, let's try to serialize, deserialize, and make sure it matches + bz, err := cdc.MarshalBinaryBare(tc.grant) + require.NoError(t, err) + var loaded FeeAllowanceGrant + err = cdc.UnmarshalBinaryBare(bz, &loaded) + require.NoError(t, err) + + err = tc.grant.ValidateBasic() + require.NoError(t, err) + assert.Equal(t, tc.grant, loaded) + }) + } + +} From e0f632780a4a91253ebf5823d9c4e70f87fa2eac Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 16 Oct 2019 16:05:28 +0200 Subject: [PATCH 12/77] Solid internal test coverage --- x/delegation/internal/keeper/keeper_test.go | 91 +++++++++++++++++++- x/delegation/internal/keeper/querier_test.go | 88 +++++++++++++++++++ x/delegation/module.go | 3 +- 3 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 x/delegation/internal/keeper/querier_test.go diff --git a/x/delegation/internal/keeper/keeper_test.go b/x/delegation/internal/keeper/keeper_test.go index c85eb1012d13..649e0c639b28 100644 --- a/x/delegation/internal/keeper/keeper_test.go +++ b/x/delegation/internal/keeper/keeper_test.go @@ -150,9 +150,7 @@ func TestKeeperCrud(t *testing.T) { return } require.NotNil(t, allow) - b, ok := allow.(*types.BasicFeeAllowance) - require.True(t, ok) - require.Equal(t, tc.allowance, b) + require.Equal(t, tc.allowance, allow) }) } @@ -184,3 +182,90 @@ func TestKeeperCrud(t *testing.T) { }) } } + +func TestUseDelegatedFee(t *testing.T) { + input := setupTestInput() + ctx := input.ctx + k := input.dk + + // some helpers + atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) + eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123)) + future := types.BasicFeeAllowance{ + SpendLimit: atom, + Expiration: types.ExpiresAtHeight(5678), + } + expired := types.BasicFeeAllowance{ + SpendLimit: eth, + Expiration: types.ExpiresAtHeight(55), + } + + // for testing limits of the contract + hugeAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 9999)) + smallAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 1)) + futureAfterSmall := types.BasicFeeAllowance{ + SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 554)), + Expiration: types.ExpiresAtHeight(5678), + } + + // then lots of queries + cases := map[string]struct { + grantee sdk.AccAddress + granter sdk.AccAddress + fee sdk.Coins + allowed bool + final exported.FeeAllowance + }{ + "use entire pot": { + granter: addr, + grantee: addr2, + fee: atom, + allowed: true, + final: nil, + }, + "expired and removed": { + granter: addr, + grantee: addr3, + fee: eth, + allowed: false, + final: nil, + }, + "too high": { + granter: addr, + grantee: addr2, + fee: hugeAtom, + allowed: false, + final: &future, + }, + "use a little": { + granter: addr, + grantee: addr2, + fee: smallAtom, + allowed: true, + final: &futureAfterSmall, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + // let's set up some initial state here + // addr -> addr2 (future) + // addr -> addr3 (expired) + err := k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + Granter: addr, Grantee: addr2, Allowance: &future, + }) + require.NoError(t, err) + err = k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + Granter: addr, Grantee: addr3, Allowance: &expired, + }) + require.NoError(t, err) + + allowed := k.UseDelegatedFees(ctx, tc.granter, tc.grantee, tc.fee) + require.Equal(t, tc.allowed, allowed) + + loaded, err := k.GetFeeAllowance(ctx, tc.granter, tc.grantee) + require.NoError(t, err) + require.Equal(t, tc.final, loaded) + }) + } +} diff --git a/x/delegation/internal/keeper/querier_test.go b/x/delegation/internal/keeper/querier_test.go new file mode 100644 index 000000000000..7cec96217859 --- /dev/null +++ b/x/delegation/internal/keeper/querier_test.go @@ -0,0 +1,88 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + + codec "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/delegation/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/delegation/internal/types" +) + +func TestQuery(t *testing.T) { + input := setupTestInput() + ctx := input.ctx + k := input.dk + + cdc := codec.New() + types.RegisterCodec(cdc) + + // some helpers + grant1 := types.FeeAllowanceGrant{ + Granter: addr, + Grantee: addr3, + Allowance: &types.BasicFeeAllowance{ + SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 555)), + Expiration: types.ExpiresAtHeight(334455), + }, + } + grant2 := types.FeeAllowanceGrant{ + Granter: addr2, + Grantee: addr3, + Allowance: &types.BasicFeeAllowance{ + SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("eth", 123)), + Expiration: types.ExpiresAtHeight(334455), + }, + } + + // let's set up some initial state here + err := k.DelegateFeeAllowance(ctx, grant1) + require.NoError(t, err) + err = k.DelegateFeeAllowance(ctx, grant2) + require.NoError(t, err) + + // now try some queries + cases := map[string]struct { + path []string + valid bool + res []types.FeeAllowanceGrant + }{ + "bad path": { + path: []string{"foo", "bar"}, + }, + "no data": { + // addr in bech32 + path: []string{"fees", "cosmos157ez5zlaq0scm9aycwphhqhmg3kws4qusmekll"}, + valid: true, + }, + "two grants": { + // addr3 in bech32 + path: []string{"fees", "cosmos1qk93t4j0yyzgqgt6k5qf8deh8fq6smpn3ntu3x"}, + valid: true, + res: []types.FeeAllowanceGrant{grant1, grant2}, + }, + } + + querier := keeper.NewQuerier(k) + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + bz, err := querier(ctx, tc.path, abci.RequestQuery{}) + if !tc.valid { + require.Error(t, err) + return + } + require.NoError(t, err) + + var grants []types.FeeAllowanceGrant + serr := cdc.UnmarshalJSON(bz, &grants) + require.NoError(t, serr) + + assert.Equal(t, tc.res, grants) + }) + } + +} diff --git a/x/delegation/module.go b/x/delegation/module.go index a491b39b3ea9..8736a1ae9976 100644 --- a/x/delegation/module.go +++ b/x/delegation/module.go @@ -15,11 +15,10 @@ import ( ) // TODO: +// * ante handler // * genesis -// * querier // * cli // * rest -// * ante handler // -> changes to auth, etc var ( From 93c9b7e99d229ca0d57a656561fbe5417dc3988a Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 16 Oct 2019 18:39:22 +0200 Subject: [PATCH 13/77] Expose Querier to top level module --- x/delegation/alias.go | 18 ++++++++++-------- x/delegation/module.go | 3 +-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/x/delegation/alias.go b/x/delegation/alias.go index 8a2eda474911..7bfc3f7d5fbf 100644 --- a/x/delegation/alias.go +++ b/x/delegation/alias.go @@ -11,14 +11,15 @@ import ( ) const ( - DefaultCodespace = types.DefaultCodespace - CodeFeeLimitExceeded = types.CodeFeeLimitExceeded - CodeFeeLimitExpired = types.CodeFeeLimitExpired - CodeInvalidPeriod = types.CodeInvalidPeriod - ModuleName = types.ModuleName - StoreKey = types.StoreKey - RouterKey = types.RouterKey - QuerierRoute = types.QuerierRoute + DefaultCodespace = types.DefaultCodespace + CodeFeeLimitExceeded = types.CodeFeeLimitExceeded + CodeFeeLimitExpired = types.CodeFeeLimitExpired + CodeInvalidPeriod = types.CodeInvalidPeriod + ModuleName = types.ModuleName + StoreKey = types.StoreKey + RouterKey = types.RouterKey + QuerierRoute = types.QuerierRoute + QueryGetFeeAllowances = keeper.QueryGetFeeAllowances ) var ( @@ -36,6 +37,7 @@ var ( NewMsgDelegateFeeAllowance = types.NewMsgDelegateFeeAllowance NewMsgRevokeFeeAllowance = types.NewMsgRevokeFeeAllowance NewKeeper = keeper.NewKeeper + NewQuerier = keeper.NewQuerier // variable aliases ModuleCdc = types.ModuleCdc diff --git a/x/delegation/module.go b/x/delegation/module.go index 8736a1ae9976..04adfb0f7e9b 100644 --- a/x/delegation/module.go +++ b/x/delegation/module.go @@ -121,8 +121,7 @@ func (AppModule) QuerierRoute() string { // NewQuerierHandler returns the delegation module sdk.Querier. func (am AppModule) NewQuerierHandler() sdk.Querier { - panic("not implemented!") - // return NewQuerier(am.keeper) + return NewQuerier(am.keeper) } // InitGenesis performs genesis initialization for the delegation module. It returns From 7968441a4d1add9b7844320ad9c45892a09a9467 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 16 Oct 2019 18:59:53 +0200 Subject: [PATCH 14/77] Add FeeAccount to auth/types, like StdTx, SignDoc --- x/auth/types/stdsignmsg.go | 15 ++++++++------- x/auth/types/stdtx.go | 18 +++++++++++++----- x/auth/types/stdtx_test.go | 23 ++++++++++++----------- x/auth/types/test_common.go | 27 ++++++++++++++++++++++----- x/auth/types/txbuilder.go | 21 +++++++++++++++++---- x/auth/types/txbuilder_test.go | 4 +++- 6 files changed, 75 insertions(+), 33 deletions(-) diff --git a/x/auth/types/stdsignmsg.go b/x/auth/types/stdsignmsg.go index e018dac40048..f27b00291fb8 100644 --- a/x/auth/types/stdsignmsg.go +++ b/x/auth/types/stdsignmsg.go @@ -8,15 +8,16 @@ import ( // a Msg with the other requirements for a StdSignDoc before // it is signed. For use in the CLI. type StdSignMsg struct { - ChainID string `json:"chain_id" yaml:"chain_id"` - AccountNumber uint64 `json:"account_number" yaml:"account_number"` - Sequence uint64 `json:"sequence" yaml:"sequence"` - Fee StdFee `json:"fee" yaml:"fee"` - Msgs []sdk.Msg `json:"msgs" yaml:"msgs"` - Memo string `json:"memo" yaml:"memo"` + ChainID string `json:"chain_id" yaml:"chain_id"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` + Fee StdFee `json:"fee" yaml:"fee"` + Msgs []sdk.Msg `json:"msgs" yaml:"msgs"` + Memo string `json:"memo" yaml:"memo"` + FeeAccount sdk.AccAddress `json:"fee_account" yaml:"fee_account"` } // get message bytes func (msg StdSignMsg) Bytes() []byte { - return StdSignBytes(msg.ChainID, msg.AccountNumber, msg.Sequence, msg.Fee, msg.Msgs, msg.Memo) + return StdSignBytes(msg.ChainID, msg.AccountNumber, msg.Sequence, msg.Fee, msg.Msgs, msg.Memo, msg.FeeAccount) } diff --git a/x/auth/types/stdtx.go b/x/auth/types/stdtx.go index fa8b5d100d5f..713539d2ca2b 100644 --- a/x/auth/types/stdtx.go +++ b/x/auth/types/stdtx.go @@ -26,14 +26,16 @@ type StdTx struct { Fee StdFee `json:"fee" yaml:"fee"` Signatures []StdSignature `json:"signatures" yaml:"signatures"` Memo string `json:"memo" yaml:"memo"` + FeeAccount sdk.AccAddress `json:"fee_account" yaml:"fee_account"` } -func NewStdTx(msgs []sdk.Msg, fee StdFee, sigs []StdSignature, memo string) StdTx { +func NewStdTx(msgs []sdk.Msg, fee StdFee, sigs []StdSignature, memo string, feeAccount sdk.AccAddress) StdTx { return StdTx{ Msgs: msgs, Fee: fee, Signatures: sigs, Memo: memo, + FeeAccount: feeAccount, } } @@ -98,6 +100,9 @@ func (tx StdTx) GetSigners() []sdk.AccAddress { // GetMemo returns the memo func (tx StdTx) GetMemo() string { return tx.Memo } +// GetFeeAccount returns the account paying the fees (often nil, default ot first signer) +func (tx StdTx) GetFeeAccount() sdk.AccAddress { return tx.FeeAccount } + // GetSignatures returns the signature of signers who signed the Msg. // CONTRACT: Length returned is same as length of // pubkeys returned from MsgKeySigners, and the order @@ -133,7 +138,7 @@ func (tx StdTx) GetSignBytes(ctx sdk.Context, acc exported.Account) []byte { } return StdSignBytes( - chainID, accNum, acc.GetSequence(), tx.Fee, tx.Msgs, tx.Memo, + chainID, accNum, acc.GetSequence(), tx.Fee, tx.Msgs, tx.Memo, tx.FeeAccount, ) } @@ -210,10 +215,11 @@ type StdSignDoc struct { Memo string `json:"memo" yaml:"memo"` Msgs []json.RawMessage `json:"msgs" yaml:"msgs"` Sequence uint64 `json:"sequence" yaml:"sequence"` + FeeAccount sdk.AccAddress `json:"fee_account" yaml:"fee_account"` } // StdSignBytes returns the bytes to sign for a transaction. -func StdSignBytes(chainID string, accnum uint64, sequence uint64, fee StdFee, msgs []sdk.Msg, memo string) []byte { +func StdSignBytes(chainID string, accnum uint64, sequence uint64, fee StdFee, msgs []sdk.Msg, memo string, feeAccount sdk.AccAddress) []byte { msgsBytes := make([]json.RawMessage, 0, len(msgs)) for _, msg := range msgs { msgsBytes = append(msgsBytes, json.RawMessage(msg.GetSignBytes())) @@ -225,6 +231,7 @@ func StdSignBytes(chainID string, accnum uint64, sequence uint64, fee StdFee, ms Memo: memo, Msgs: msgsBytes, Sequence: sequence, + FeeAccount: feeAccount, }) if err != nil { panic(err) @@ -234,8 +241,9 @@ func StdSignBytes(chainID string, accnum uint64, sequence uint64, fee StdFee, ms // StdSignature represents a sig type StdSignature struct { - crypto.PubKey `json:"pub_key" yaml:"pub_key"` // optional - Signature []byte `json:"signature" yaml:"signature"` + // Pubkey is optional + crypto.PubKey `json:"pub_key" yaml:"pub_key"` + Signature []byte `json:"signature" yaml:"signature"` } // DefaultTxDecoder logic for standard transaction decoding diff --git a/x/auth/types/stdtx_test.go b/x/auth/types/stdtx_test.go index 375a15c49cc4..c731b595cff2 100644 --- a/x/auth/types/stdtx_test.go +++ b/x/auth/types/stdtx_test.go @@ -25,7 +25,7 @@ func TestStdTx(t *testing.T) { fee := NewTestStdFee() sigs := []StdSignature{} - tx := NewStdTx(msgs, fee, sigs, "") + tx := NewStdTx(msgs, fee, sigs, "", nil) require.Equal(t, msgs, tx.GetMsgs()) require.Equal(t, sigs, tx.Signatures) @@ -35,12 +35,13 @@ func TestStdTx(t *testing.T) { func TestStdSignBytes(t *testing.T) { type args struct { - chainID string - accnum uint64 - sequence uint64 - fee StdFee - msgs []sdk.Msg - memo string + chainID string + accnum uint64 + sequence uint64 + fee StdFee + msgs []sdk.Msg + memo string + feeAccount sdk.AccAddress } defaultFee := NewTestStdFee() tests := []struct { @@ -48,12 +49,12 @@ func TestStdSignBytes(t *testing.T) { want string }{ { - args{"1234", 3, 6, defaultFee, []sdk.Msg{sdk.NewTestMsg(addr)}, "memo"}, - fmt.Sprintf("{\"account_number\":\"3\",\"chain_id\":\"1234\",\"fee\":{\"amount\":[{\"amount\":\"150\",\"denom\":\"atom\"}],\"gas\":\"100000\"},\"memo\":\"memo\",\"msgs\":[[\"%s\"]],\"sequence\":\"6\"}", addr), + args{"1234", 3, 6, defaultFee, []sdk.Msg{sdk.NewTestMsg(addr)}, "memo", nil}, + fmt.Sprintf(`{"account_number":"3","chain_id":"1234","fee":{"amount":[{"amount":"150","denom":"atom"}],"gas":"100000"},"fee_account":"","memo":"memo","msgs":[["%s"]],"sequence":"6"}`, addr), }, } for i, tc := range tests { - got := string(StdSignBytes(tc.args.chainID, tc.args.accnum, tc.args.sequence, tc.args.fee, tc.args.msgs, tc.args.memo)) + got := string(StdSignBytes(tc.args.chainID, tc.args.accnum, tc.args.sequence, tc.args.fee, tc.args.msgs, tc.args.memo, tc.args.feeAccount)) require.Equal(t, tc.want, got, "Got unexpected result on test case i: %d", i) } } @@ -124,7 +125,7 @@ func TestDefaultTxEncoder(t *testing.T) { fee := NewTestStdFee() sigs := []StdSignature{} - tx := NewStdTx(msgs, fee, sigs, "") + tx := NewStdTx(msgs, fee, sigs, "", nil) cdcBytes, err := cdc.MarshalBinaryLengthPrefixed(tx) diff --git a/x/auth/types/test_common.go b/x/auth/types/test_common.go index 9e2ba20e98d2..447ae77d1be3 100644 --- a/x/auth/types/test_common.go +++ b/x/auth/types/test_common.go @@ -35,7 +35,7 @@ func KeyTestPubAddr() (crypto.PrivKey, crypto.PubKey, sdk.AccAddress) { func NewTestTx(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums []uint64, seqs []uint64, fee StdFee) sdk.Tx { sigs := make([]StdSignature, len(privs)) for i, priv := range privs { - signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, "") + signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, "", nil) sig, err := priv.Sign(signBytes) if err != nil { @@ -45,14 +45,14 @@ func NewTestTx(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: sig} } - tx := NewStdTx(msgs, fee, sigs, "") + tx := NewStdTx(msgs, fee, sigs, "", nil) return tx } func NewTestTxWithMemo(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums []uint64, seqs []uint64, fee StdFee, memo string) sdk.Tx { sigs := make([]StdSignature, len(privs)) for i, priv := range privs { - signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, memo) + signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, memo, nil) sig, err := priv.Sign(signBytes) if err != nil { @@ -62,7 +62,24 @@ func NewTestTxWithMemo(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: sig} } - tx := NewStdTx(msgs, fee, sigs, memo) + tx := NewStdTx(msgs, fee, sigs, memo, nil) + return tx +} + +func NewTestTxWithFeeAccount(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums []uint64, seqs []uint64, fee StdFee, feeAccount sdk.AccAddress) sdk.Tx { + sigs := make([]StdSignature, len(privs)) + for i, priv := range privs { + signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, "", feeAccount) + + sig, err := priv.Sign(signBytes) + if err != nil { + panic(err) + } + + sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: sig} + } + + tx := NewStdTx(msgs, fee, sigs, "", feeAccount) return tx } @@ -77,6 +94,6 @@ func NewTestTxWithSignBytes(msgs []sdk.Msg, privs []crypto.PrivKey, accNums []ui sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: sig} } - tx := NewStdTx(msgs, fee, sigs, memo) + tx := NewStdTx(msgs, fee, sigs, memo, nil) return tx } diff --git a/x/auth/types/txbuilder.go b/x/auth/types/txbuilder.go index 7d60086a137d..d2852a98e564 100644 --- a/x/auth/types/txbuilder.go +++ b/x/auth/types/txbuilder.go @@ -24,6 +24,7 @@ type TxBuilder struct { simulateAndExecute bool chainID string memo string + feeAccount sdk.AccAddress fees sdk.Coins gasPrices sdk.DecCoins } @@ -31,7 +32,7 @@ type TxBuilder struct { // NewTxBuilder returns a new initialized TxBuilder. func NewTxBuilder( txEncoder sdk.TxEncoder, accNumber, seq, gas uint64, gasAdj float64, - simulateAndExecute bool, chainID, memo string, fees sdk.Coins, gasPrices sdk.DecCoins, + simulateAndExecute bool, chainID, memo string, feeAccount sdk.AccAddress, fees sdk.Coins, gasPrices sdk.DecCoins, ) TxBuilder { return TxBuilder{ @@ -44,6 +45,7 @@ func NewTxBuilder( simulateAndExecute: simulateAndExecute, chainID: chainID, memo: memo, + feeAccount: feeAccount, fees: fees, gasPrices: gasPrices, } @@ -101,6 +103,9 @@ func (bldr TxBuilder) ChainID() string { return bldr.chainID } // Memo returns the memo message func (bldr TxBuilder) Memo() string { return bldr.memo } +// FeeAccount returns an alternate account to pay fees +func (bldr *TxBuilder) FeeAccount() sdk.AccAddress { return bldr.feeAccount } + // Fees returns the fees for the transaction func (bldr TxBuilder) Fees() sdk.Coins { return bldr.fees } @@ -125,6 +130,12 @@ func (bldr TxBuilder) WithGas(gas uint64) TxBuilder { return bldr } +// WithFeeAccount returns a copy of the context with an updated FeeAccount. +func (bldr TxBuilder) WithFeeAccount(feeAccount sdk.AccAddress) TxBuilder { + bldr.feeAccount = feeAccount + return bldr +} + // WithFees returns a copy of the context with an updated fee. func (bldr TxBuilder) WithFees(fees string) TxBuilder { parsedFees, err := sdk.ParseCoins(fees) @@ -201,6 +212,7 @@ func (bldr TxBuilder) BuildSignMsg(msgs []sdk.Msg) (StdSignMsg, error) { AccountNumber: bldr.accountNumber, Sequence: bldr.sequence, Memo: bldr.memo, + FeeAccount: bldr.feeAccount, Msgs: msgs, Fee: NewStdFee(bldr.gas, fees), }, nil @@ -214,7 +226,7 @@ func (bldr TxBuilder) Sign(name, passphrase string, msg StdSignMsg) ([]byte, err return nil, err } - return bldr.txEncoder(NewStdTx(msg.Msgs, msg.Fee, []StdSignature{sig}, msg.Memo)) + return bldr.txEncoder(NewStdTx(msg.Msgs, msg.Fee, []StdSignature{sig}, msg.Memo, msg.FeeAccount)) } // BuildAndSign builds a single message to be signed, and signs a transaction @@ -238,7 +250,7 @@ func (bldr TxBuilder) BuildTxForSim(msgs []sdk.Msg) ([]byte, error) { // the ante handler will populate with a sentinel pubkey sigs := []StdSignature{{}} - return bldr.txEncoder(NewStdTx(signMsg.Msgs, signMsg.Fee, sigs, signMsg.Memo)) + return bldr.txEncoder(NewStdTx(signMsg.Msgs, signMsg.Fee, sigs, signMsg.Memo, signMsg.FeeAccount)) } // SignStdTx appends a signature to a StdTx and returns a copy of it. If append @@ -255,6 +267,7 @@ func (bldr TxBuilder) SignStdTx(name, passphrase string, stdTx StdTx, appendSig Fee: stdTx.Fee, Msgs: stdTx.GetMsgs(), Memo: stdTx.GetMemo(), + FeeAccount: stdTx.FeeAccount, }) if err != nil { return @@ -266,7 +279,7 @@ func (bldr TxBuilder) SignStdTx(name, passphrase string, stdTx StdTx, appendSig } else { sigs = append(sigs, stdSignature) } - signedStdTx = NewStdTx(stdTx.GetMsgs(), stdTx.Fee, sigs, stdTx.GetMemo()) + signedStdTx = NewStdTx(stdTx.GetMsgs(), stdTx.Fee, sigs, stdTx.GetMemo(), bldr.feeAccount) return } diff --git a/x/auth/types/txbuilder_test.go b/x/auth/types/txbuilder_test.go index 063401c8a4ba..8d87c502ba0c 100644 --- a/x/auth/types/txbuilder_test.go +++ b/x/auth/types/txbuilder_test.go @@ -20,6 +20,7 @@ func TestTxBuilderBuild(t *testing.T) { SimulateGas bool ChainID string Memo string + FeeAccount sdk.AccAddress Fees sdk.Coins GasPrices sdk.DecCoins } @@ -135,7 +136,8 @@ func TestTxBuilderBuild(t *testing.T) { bldr := NewTxBuilder( tt.fields.TxEncoder, tt.fields.AccountNumber, tt.fields.Sequence, tt.fields.Gas, tt.fields.GasAdjustment, tt.fields.SimulateGas, - tt.fields.ChainID, tt.fields.Memo, tt.fields.Fees, tt.fields.GasPrices, + tt.fields.ChainID, tt.fields.Memo, tt.fields.FeeAccount, + tt.fields.Fees, tt.fields.GasPrices, ) got, err := bldr.BuildSignMsg(tt.msgs) require.Equal(t, tt.wantErr, (err != nil)) From 7e82fb88f1ac76165583f87f8dd2325d9fbb9f49 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 16 Oct 2019 19:12:20 +0200 Subject: [PATCH 15/77] Fix all tests in x/auth --- simapp/test_helpers.go | 4 ++-- types/rest/rest.go | 21 +++++++++++---------- x/auth/ante/ante_test.go | 2 +- x/auth/client/cli/tx_multisign.go | 4 ++-- x/auth/client/cli/tx_sign.go | 2 +- x/auth/client/utils/rest.go | 4 ++-- x/auth/client/utils/tx.go | 2 +- x/auth/client/utils/tx_test.go | 8 ++++++-- x/mock/app.go | 4 ++-- 9 files changed, 28 insertions(+), 23 deletions(-) diff --git a/simapp/test_helpers.go b/simapp/test_helpers.go index c108d9f9f5e1..9166d55e1b98 100644 --- a/simapp/test_helpers.go +++ b/simapp/test_helpers.go @@ -120,7 +120,7 @@ func GenTx(msgs []sdk.Msg, accnums []uint64, seq []uint64, priv ...crypto.PrivKe for i, p := range priv { // use a empty chainID for ease of testing - sig, err := p.Sign(auth.StdSignBytes("", accnums[i], seq[i], fee, msgs, memo)) + sig, err := p.Sign(auth.StdSignBytes("", accnums[i], seq[i], fee, msgs, memo, nil)) if err != nil { panic(err) } @@ -131,7 +131,7 @@ func GenTx(msgs []sdk.Msg, accnums []uint64, seq []uint64, priv ...crypto.PrivKe } } - return auth.NewStdTx(msgs, fee, sigs, memo) + return auth.NewStdTx(msgs, fee, sigs, memo, nil) } // SignCheckDeliver checks a generated signed transaction and simulates a diff --git a/types/rest/rest.go b/types/rest/rest.go index 89770c944650..7a9048522177 100644 --- a/types/rest/rest.go +++ b/types/rest/rest.go @@ -47,16 +47,17 @@ type GasEstimateResponse struct { // BaseReq defines a structure that can be embedded in other request structures // that all share common "base" fields. type BaseReq struct { - From string `json:"from"` - Memo string `json:"memo"` - ChainID string `json:"chain_id"` - AccountNumber uint64 `json:"account_number"` - Sequence uint64 `json:"sequence"` - Fees sdk.Coins `json:"fees"` - GasPrices sdk.DecCoins `json:"gas_prices"` - Gas string `json:"gas"` - GasAdjustment string `json:"gas_adjustment"` - Simulate bool `json:"simulate"` + From string `json:"from"` + Memo string `json:"memo"` + ChainID string `json:"chain_id"` + AccountNumber uint64 `json:"account_number"` + Sequence uint64 `json:"sequence"` + Fees sdk.Coins `json:"fees"` + FeeAccount sdk.AccAddress `json:"fee_account"` + GasPrices sdk.DecCoins `json:"gas_prices"` + Gas string `json:"gas"` + GasAdjustment string `json:"gas_adjustment"` + Simulate bool `json:"simulate"` } // NewBaseReq creates a new basic request instance and sanitizes its values diff --git a/x/auth/ante/ante_test.go b/x/auth/ante/ante_test.go index 60fe2149cd62..c74b2c223d5c 100644 --- a/x/auth/ante/ante_test.go +++ b/x/auth/ante/ante_test.go @@ -513,7 +513,7 @@ func TestAnteHandlerBadSignBytes(t *testing.T) { for _, cs := range cases { tx := types.NewTestTxWithSignBytes( msgs, privs, accnums, seqs, fee, - types.StdSignBytes(cs.chainID, cs.accnum, cs.seq, cs.fee, cs.msgs, ""), + types.StdSignBytes(cs.chainID, cs.accnum, cs.seq, cs.fee, cs.msgs, "", nil), "", ) checkInvalidTx(t, anteHandler, ctx, tx, false, cs.code) diff --git a/x/auth/client/cli/tx_multisign.go b/x/auth/client/cli/tx_multisign.go index e6d958a5d9cd..ae020f7fcb54 100644 --- a/x/auth/client/cli/tx_multisign.go +++ b/x/auth/client/cli/tx_multisign.go @@ -102,7 +102,7 @@ func makeMultiSignCmd(cdc *codec.Codec) func(cmd *cobra.Command, args []string) // Validate each signature sigBytes := types.StdSignBytes( txBldr.ChainID(), txBldr.AccountNumber(), txBldr.Sequence(), - stdTx.Fee, stdTx.GetMsgs(), stdTx.GetMemo(), + stdTx.Fee, stdTx.GetMsgs(), stdTx.GetMemo(), stdTx.FeeAccount, ) if ok := stdSig.PubKey.VerifyBytes(sigBytes, stdSig.Signature); !ok { return fmt.Errorf("couldn't verify signature") @@ -113,7 +113,7 @@ func makeMultiSignCmd(cdc *codec.Codec) func(cmd *cobra.Command, args []string) } newStdSig := types.StdSignature{Signature: cdc.MustMarshalBinaryBare(multisigSig), PubKey: multisigPub} - newTx := types.NewStdTx(stdTx.GetMsgs(), stdTx.Fee, []types.StdSignature{newStdSig}, stdTx.GetMemo()) + newTx := types.NewStdTx(stdTx.GetMsgs(), stdTx.Fee, []types.StdSignature{newStdSig}, stdTx.GetMemo(), stdTx.GetFeeAccount()) sigOnly := viper.GetBool(flagSigOnly) var json []byte diff --git a/x/auth/client/cli/tx_sign.go b/x/auth/client/cli/tx_sign.go index d4af880d3e91..b5fb15031038 100644 --- a/x/auth/client/cli/tx_sign.go +++ b/x/auth/client/cli/tx_sign.go @@ -230,7 +230,7 @@ func printAndValidateSigs( sigBytes := types.StdSignBytes( chainID, acc.GetAccountNumber(), acc.GetSequence(), - stdTx.Fee, stdTx.GetMsgs(), stdTx.GetMemo(), + stdTx.Fee, stdTx.GetMsgs(), stdTx.GetMemo(), stdTx.GetFeeAccount(), ) if ok := sig.VerifyBytes(sigBytes, sig.Signature); !ok { diff --git a/x/auth/client/utils/rest.go b/x/auth/client/utils/rest.go index 9423fe629ee4..dce53f67d8c3 100644 --- a/x/auth/client/utils/rest.go +++ b/x/auth/client/utils/rest.go @@ -26,7 +26,7 @@ func WriteGenerateStdTxResponse(w http.ResponseWriter, cliCtx context.CLIContext txBldr := types.NewTxBuilder( GetTxEncoder(cliCtx.Codec), br.AccountNumber, br.Sequence, gas, gasAdj, - br.Simulate, br.ChainID, br.Memo, br.Fees, br.GasPrices, + br.Simulate, br.ChainID, br.Memo, br.FeeAccount, br.Fees, br.GasPrices, ) if br.Simulate || simAndExec { @@ -53,7 +53,7 @@ func WriteGenerateStdTxResponse(w http.ResponseWriter, cliCtx context.CLIContext return } - output, err := cliCtx.Codec.MarshalJSON(types.NewStdTx(stdMsg.Msgs, stdMsg.Fee, nil, stdMsg.Memo)) + output, err := cliCtx.Codec.MarshalJSON(types.NewStdTx(stdMsg.Msgs, stdMsg.Fee, nil, stdMsg.Memo, stdMsg.FeeAccount)) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return diff --git a/x/auth/client/utils/tx.go b/x/auth/client/utils/tx.go index d2212a039ad4..a94b96717162 100644 --- a/x/auth/client/utils/tx.go +++ b/x/auth/client/utils/tx.go @@ -349,7 +349,7 @@ func buildUnsignedStdTxOffline(txBldr authtypes.TxBuilder, cliCtx context.CLICon return stdTx, nil } - return authtypes.NewStdTx(stdSignMsg.Msgs, stdSignMsg.Fee, nil, stdSignMsg.Memo), nil + return authtypes.NewStdTx(stdSignMsg.Msgs, stdSignMsg.Fee, nil, stdSignMsg.Memo, stdSignMsg.FeeAccount), nil } func isTxSigner(user sdk.AccAddress, signers []sdk.AccAddress) bool { diff --git a/x/auth/client/utils/tx_test.go b/x/auth/client/utils/tx_test.go index 3ae79f7bdc6f..4274abf78df0 100644 --- a/x/auth/client/utils/tx_test.go +++ b/x/auth/client/utils/tx_test.go @@ -98,7 +98,9 @@ func TestReadStdTxFromFile(t *testing.T) { // Build a test transaction fee := authtypes.NewStdFee(50000, sdk.Coins{sdk.NewInt64Coin("atom", 150)}) - stdTx := authtypes.NewStdTx([]sdk.Msg{}, fee, []authtypes.StdSignature{}, "foomemo") + feeAccount, err := sdk.AccAddressFromBech32("cosmos1zxcxurm8gwp43n4efqms6484gkdnnq763w03t6") + require.NoError(t, err) + stdTx := authtypes.NewStdTx([]sdk.Msg{}, fee, []authtypes.StdSignature{}, "foomemo", feeAccount) // Write it to the file encodedTx, _ := cdc.MarshalJSON(stdTx) @@ -109,11 +111,13 @@ func TestReadStdTxFromFile(t *testing.T) { decodedTx, err := ReadStdTxFromFile(cdc, jsonTxFile.Name()) require.NoError(t, err) require.Equal(t, decodedTx.Memo, "foomemo") + require.Equal(t, decodedTx.FeeAccount, feeAccount) + require.Equal(t, decodedTx.Fee, fee) } func compareEncoders(t *testing.T, expected sdk.TxEncoder, actual sdk.TxEncoder) { msgs := []sdk.Msg{sdk.NewTestMsg(addr)} - tx := authtypes.NewStdTx(msgs, authtypes.StdFee{}, []authtypes.StdSignature{}, "") + tx := authtypes.NewStdTx(msgs, authtypes.StdFee{}, []authtypes.StdSignature{}, "", nil) defaultEncoderBytes, err := expected(tx) require.NoError(t, err) diff --git a/x/mock/app.go b/x/mock/app.go index 1e59e050507b..04e4230ee411 100644 --- a/x/mock/app.go +++ b/x/mock/app.go @@ -217,7 +217,7 @@ func GenTx(msgs []sdk.Msg, accnums []uint64, seq []uint64, priv ...crypto.PrivKe memo := "testmemotestmemo" for i, p := range priv { - sig, err := p.Sign(auth.StdSignBytes(chainID, accnums[i], seq[i], fee, msgs, memo)) + sig, err := p.Sign(auth.StdSignBytes(chainID, accnums[i], seq[i], fee, msgs, memo, nil)) if err != nil { panic(err) } @@ -228,7 +228,7 @@ func GenTx(msgs []sdk.Msg, accnums []uint64, seq []uint64, priv ...crypto.PrivKe } } - return auth.NewStdTx(msgs, fee, sigs, memo) + return auth.NewStdTx(msgs, fee, sigs, memo, nil) } // GeneratePrivKeys generates a total n secp256k1 private keys. From 64d5159089b2a1c4192eeb1d8f99f5d3ee9afbf1 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 16 Oct 2019 19:15:01 +0200 Subject: [PATCH 16/77] All tests pass --- x/distribution/client/cli/tx_test.go | 1 + x/genutil/types/genesis_state_test.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/x/distribution/client/cli/tx_test.go b/x/distribution/client/cli/tx_test.go index af09d5445a89..51ae03488d8b 100644 --- a/x/distribution/client/cli/tx_test.go +++ b/x/distribution/client/cli/tx_test.go @@ -24,6 +24,7 @@ func createFakeTxBuilder() auth.TxBuilder { false, "test_chain", "hello", + nil, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))), sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.NewDecWithPrec(10000, sdk.Precision))}, ) diff --git a/x/genutil/types/genesis_state_test.go b/x/genutil/types/genesis_state_test.go index aed7cd7f311e..1f9ebf67a1e5 100644 --- a/x/genutil/types/genesis_state_test.go +++ b/x/genutil/types/genesis_state_test.go @@ -27,7 +27,7 @@ func TestValidateGenesisMultipleMessages(t *testing.T) { msg2 := stakingtypes.NewMsgCreateValidator(sdk.ValAddress(pk2.Address()), pk2, sdk.NewInt64Coin(sdk.DefaultBondDenom, 50), desc, comm, sdk.OneInt()) - genTxs := authtypes.NewStdTx([]sdk.Msg{msg1, msg2}, authtypes.StdFee{}, nil, "") + genTxs := authtypes.NewStdTx([]sdk.Msg{msg1, msg2}, authtypes.StdFee{}, nil, "", nil) genesisState := NewGenesisStateFromStdTx([]authtypes.StdTx{genTxs}) err := ValidateGenesis(genesisState) @@ -39,7 +39,7 @@ func TestValidateGenesisBadMessage(t *testing.T) { msg1 := stakingtypes.NewMsgEditValidator(sdk.ValAddress(pk1.Address()), desc, nil, nil) - genTxs := authtypes.NewStdTx([]sdk.Msg{msg1}, authtypes.StdFee{}, nil, "") + genTxs := authtypes.NewStdTx([]sdk.Msg{msg1}, authtypes.StdFee{}, nil, "", nil) genesisState := NewGenesisStateFromStdTx([]authtypes.StdTx{genTxs}) err := ValidateGenesis(genesisState) From 8b2b64b51156c010bd2bb8c888d777af7b39532b Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 16 Oct 2019 19:20:45 +0200 Subject: [PATCH 17/77] Appease the Golang Linter --- x/delegation/handler.go | 2 +- x/delegation/internal/keeper/keeper.go | 4 ++-- x/delegation/internal/types/basic_fee_test.go | 4 ++-- x/delegation/internal/types/expiration.go | 2 +- x/delegation/internal/types/key.go | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/x/delegation/handler.go b/x/delegation/handler.go index 070673fceb3a..367eb7b21523 100644 --- a/x/delegation/handler.go +++ b/x/delegation/handler.go @@ -10,7 +10,7 @@ func NewHandler(k Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { switch msg := msg.(type) { case MsgDelegateFeeAllowance: - grant := FeeAllowanceGrant{Granter: msg.Granter, Grantee: msg.Grantee, Allowance: msg.Allowance} + grant := FeeAllowanceGrant(msg) k.DelegateFeeAllowance(ctx, grant) return sdk.Result{} case MsgRevokeFeeAllowance: diff --git a/x/delegation/internal/keeper/keeper.go b/x/delegation/internal/keeper/keeper.go index 9654c28c24e0..2546b6af7b68 100644 --- a/x/delegation/internal/keeper/keeper.go +++ b/x/delegation/internal/keeper/keeper.go @@ -92,7 +92,7 @@ func (k Keeper) GetAllFeeAllowances(ctx sdk.Context, grantee sdk.AccAddress) ([] func (k Keeper) UseDelegatedFees(ctx sdk.Context, granter sdk.AccAddress, grantee sdk.AccAddress, fee sdk.Coins) bool { grant, err := k.GetFeeGrant(ctx, granter, grantee) if err != nil { - // we should acknowlegde a db issue somehow (better?) + // we should acknowledge a db issue somehow (better?) ctx.Logger().Error(err.Error()) return false } @@ -108,7 +108,7 @@ func (k Keeper) UseDelegatedFees(ctx sdk.Context, granter sdk.AccAddress, grante // if we accepted, store the updated state of the allowance if err := k.DelegateFeeAllowance(ctx, *grant); err != nil { - // we should acknowlegde a db issue somehow (better?) + // we should acknowledge a db issue somehow (better?) ctx.Logger().Error(err.Error()) return false } diff --git a/x/delegation/internal/types/basic_fee_test.go b/x/delegation/internal/types/basic_fee_test.go index 4e7c0e726dc3..857153d05cc7 100644 --- a/x/delegation/internal/types/basic_fee_test.go +++ b/x/delegation/internal/types/basic_fee_test.go @@ -17,11 +17,11 @@ func TestBasicFeeValidAllow(t *testing.T) { cases := map[string]struct { allow BasicFeeAllowance - valid bool - // all below checks are ignored if invalid + // all other checks are ignored if valid=false fee sdk.Coins blockTime time.Time blockHeight int64 + valid bool accept bool remove bool remains sdk.Coins diff --git a/x/delegation/internal/types/expiration.go b/x/delegation/internal/types/expiration.go index f82af4d84721..09ae0ec449ad 100644 --- a/x/delegation/internal/types/expiration.go +++ b/x/delegation/internal/types/expiration.go @@ -66,7 +66,7 @@ func (e *ExpiresAt) Step(p Period) error { if !e.Time.IsZero() { e.Time = e.Time.Add(p.Clock) } else { - e.Height = e.Height + p.Block + e.Height += p.Block } return nil } diff --git a/x/delegation/internal/types/key.go b/x/delegation/internal/types/key.go index 6e5987fa08b7..88b2901326c0 100644 --- a/x/delegation/internal/types/key.go +++ b/x/delegation/internal/types/key.go @@ -25,7 +25,7 @@ var ( FeeAllowanceKeyPrefix = []byte{0x00} ) -// FeeAllowanceKey is the cannonical key to store a grant from granter to grantee +// FeeAllowanceKey is the canonical key to store a grant from granter to grantee // We store by grantee first to allow searching by everyone who granted to you func FeeAllowanceKey(granter sdk.AccAddress, grantee sdk.AccAddress) []byte { return append(FeeAllowanceKeyPrefix, []byte(fmt.Sprintf("%s/%s", grantee, granter))...) From be12b4464dc9bc40bfdb33681b2a5a19ffce41d2 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 16 Oct 2019 19:25:57 +0200 Subject: [PATCH 18/77] Add fee-account command line flag --- client/flags/flags.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/flags/flags.go b/client/flags/flags.go index 62f2a8d012f9..774b3ccd8c95 100644 --- a/client/flags/flags.go +++ b/client/flags/flags.go @@ -54,6 +54,7 @@ const ( FlagRPCWriteTimeout = "write-timeout" FlagOutputDocument = "output-document" // inspired by wget -O FlagSkipConfirmation = "yes" + FlagFeeAccount = "fee-account" ) // LineBreak can be included in a command list to provide a blank line @@ -99,6 +100,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { c.Flags().Bool(FlagDryRun, false, "ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it") c.Flags().Bool(FlagGenerateOnly, false, "Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase is not accessible and the node operates offline)") c.Flags().BoolP(FlagSkipConfirmation, "y", false, "Skip tx broadcasting prompt confirmation") + c.Flags().String(FlagFeeAccount, "", "Set a fee account to pay fess with if they have been delegated by this account") // --gas can accept integers and "simulate" c.Flags().Var(&GasFlagVar, "gas", fmt.Sprintf( From c703e599fb5ded3f0650e57761744a17029699df Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 17 Oct 2019 11:46:00 +0200 Subject: [PATCH 19/77] Start on DelegatedDeductFeeDecorator --- x/delegation/internal/ante/fee.go | 81 ++++++++++++++++++++++++++ x/delegation/internal/ante/fee_test.go | 1 + 2 files changed, 82 insertions(+) create mode 100644 x/delegation/internal/ante/fee.go create mode 100644 x/delegation/internal/ante/fee_test.go diff --git a/x/delegation/internal/ante/fee.go b/x/delegation/internal/ante/fee.go new file mode 100644 index 000000000000..6f323de698a0 --- /dev/null +++ b/x/delegation/internal/ante/fee.go @@ -0,0 +1,81 @@ +package ante + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + // we depend on the auth module internals... maybe some more of this can be exported? + // but things like `x/auth/types/FeeCollectorName` are quite clearly tied to it + authAnte "github.com/cosmos/cosmos-sdk/x/auth/ante" + authKeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authTypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/delegation/internal/keeper" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +var ( + _ DelegatedFeeTx = (*authTypes.StdTx)(nil) // assert StdTx implements DelegatedFeeTx +) + +// DelegatedFeeTx defines the interface to be implemented by Tx to use the DelegatedFeeDecorator +type DelegatedFeeTx interface { + authAnte.FeeTx + GetFeeAccount() sdk.AccAddress +} + +// DeductDelegatedFeeDecorator deducts fees from the first signer of the tx +// If the first signer does not have the funds to pay for the fees, return with InsufficientFunds error +// Call next AnteHandler if fees successfully deducted +// CONTRACT: Tx must implement DelegatedFeeTx interface to use DeductDelegatedFeeDecorator +type DeductDelegatedFeeDecorator struct { + base authAnte.DeductFeeDecorator + ak authKeeper.AccountKeeper + dk keeper.Keeper + sk authTypes.SupplyKeeper +} + +func NewDeductDelegatedFeeDecorator(ak authKeeper.AccountKeeper, sk authTypes.SupplyKeeper, dk keeper.Keeper) DeductDelegatedFeeDecorator { + return DeductDelegatedFeeDecorator{ + base: authAnte.NewDeductFeeDecorator(ak, sk), + ak: ak, + dk: dk, + sk: sk, + } +} + +func (d DeductDelegatedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + // make sure there is a delegation, if not, default to the standard DeductFeeDecorator behavior + var granter sdk.AccAddress + delTx, ok := tx.(DelegatedFeeTx) + if ok { + granter = delTx.GetFeeAccount() + } + if granter == nil { + // just defer to the basic DeductFeeHandler + return d.base.AnteHandle(ctx, tx, simulate, next) + } + + // short-circuit on zero fee + fee := delTx.GetFee() + if fee.IsZero() { + return next(ctx, tx, simulate) + } + + // ensure the delegation is allowed + grantee := delTx.FeePayer() + allowed := d.dk.UseDelegatedFees(ctx, granter, grantee, fee) + if !allowed { + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s not allowed to pay fees from %s", grantee, granter) + } + + // now deduct fees from the granter + feePayerAcc := d.ak.GetAccount(ctx, granter) + if feePayerAcc == nil { + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "granter address: %s does not exist", granter) + } + err = authAnte.DeductFees(d.sk, ctx, feePayerAcc, fee) + if err != nil { + return ctx, err + } + return next(ctx, tx, simulate) +} diff --git a/x/delegation/internal/ante/fee_test.go b/x/delegation/internal/ante/fee_test.go new file mode 100644 index 000000000000..dfb29020286b --- /dev/null +++ b/x/delegation/internal/ante/fee_test.go @@ -0,0 +1 @@ +package ante \ No newline at end of file From 907776f3353565b761fa80895a693aff9b380baf Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 17 Oct 2019 11:53:52 +0200 Subject: [PATCH 20/77] Cleanup the Decorator --- x/delegation/internal/ante/fee.go | 61 ++++++++++++++++++------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/x/delegation/internal/ante/fee.go b/x/delegation/internal/ante/fee.go index 6f323de698a0..c1f988e4094c 100644 --- a/x/delegation/internal/ante/fee.go +++ b/x/delegation/internal/ante/fee.go @@ -1,6 +1,8 @@ package ante import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" // we depend on the auth module internals... maybe some more of this can be exported? @@ -28,50 +30,57 @@ type DelegatedFeeTx interface { // Call next AnteHandler if fees successfully deducted // CONTRACT: Tx must implement DelegatedFeeTx interface to use DeductDelegatedFeeDecorator type DeductDelegatedFeeDecorator struct { - base authAnte.DeductFeeDecorator - ak authKeeper.AccountKeeper - dk keeper.Keeper - sk authTypes.SupplyKeeper + ak authKeeper.AccountKeeper + dk keeper.Keeper + sk authTypes.SupplyKeeper } func NewDeductDelegatedFeeDecorator(ak authKeeper.AccountKeeper, sk authTypes.SupplyKeeper, dk keeper.Keeper) DeductDelegatedFeeDecorator { return DeductDelegatedFeeDecorator{ - base: authAnte.NewDeductFeeDecorator(ak, sk), - ak: ak, - dk: dk, - sk: sk, + ak: ak, + dk: dk, + sk: sk, } } func (d DeductDelegatedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - // make sure there is a delegation, if not, default to the standard DeductFeeDecorator behavior - var granter sdk.AccAddress - delTx, ok := tx.(DelegatedFeeTx) - if ok { - granter = delTx.GetFeeAccount() - } - if granter == nil { - // just defer to the basic DeductFeeHandler - return d.base.AnteHandle(ctx, tx, simulate, next) + feeTx, ok := tx.(authAnte.FeeTx) + if !ok { + return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") } // short-circuit on zero fee - fee := delTx.GetFee() + fee := feeTx.GetFee() if fee.IsZero() { return next(ctx, tx, simulate) } + // sanity check from DeductFeeDecorator + if addr := d.sk.GetModuleAddress(authTypes.FeeCollectorName); addr == nil { + panic(fmt.Sprintf("%s module account has not been set", authTypes.FeeCollectorName)) + } + + // see if there is a delegation + var feePayer sdk.AccAddress + if delTx, ok := tx.(DelegatedFeeTx); ok { + feePayer = delTx.GetFeeAccount() + } - // ensure the delegation is allowed - grantee := delTx.FeePayer() - allowed := d.dk.UseDelegatedFees(ctx, granter, grantee, fee) - if !allowed { - return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s not allowed to pay fees from %s", grantee, granter) + txSigner := feeTx.FeePayer() + if feePayer == nil { + // if this is not explicitly set, use the first signer as always + feePayer = txSigner + } else { + // ensure the delegation is allowed + allowed := d.dk.UseDelegatedFees(ctx, feePayer, txSigner, fee) + if !allowed { + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s not allowed to pay fees from %s", txSigner, feePayer) + } } - // now deduct fees from the granter - feePayerAcc := d.ak.GetAccount(ctx, granter) + // now, either way, we know that we are authorized to deduct the fees from the feePayer account + feePayerAcc := d.ak.GetAccount(ctx, feePayer) if feePayerAcc == nil { - return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "granter address: %s does not exist", granter) + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "fee payer address: %s does not exist", feePayer) } err = authAnte.DeductFees(d.sk, ctx, feePayerAcc, fee) if err != nil { From 005afd96e8e1858c5506be2d0cfe0a904124641b Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 17 Oct 2019 12:09:48 +0200 Subject: [PATCH 21/77] Wire up delegation module in simapp --- simapp/app.go | 27 ++++++++++++++++----------- x/delegation/module.go | 5 +++-- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/simapp/app.go b/simapp/app.go index 32501acebad8..ac0ac5c07331 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -18,6 +18,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/vesting" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/crisis" + "github.com/cosmos/cosmos-sdk/x/delegation" distr "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/genutil" "github.com/cosmos/cosmos-sdk/x/gov" @@ -90,16 +91,17 @@ type SimApp struct { tkeys map[string]*sdk.TransientStoreKey // keepers - AccountKeeper auth.AccountKeeper - BankKeeper bank.Keeper - SupplyKeeper supply.Keeper - StakingKeeper staking.Keeper - SlashingKeeper slashing.Keeper - MintKeeper mint.Keeper - DistrKeeper distr.Keeper - GovKeeper gov.Keeper - CrisisKeeper crisis.Keeper - ParamsKeeper params.Keeper + AccountKeeper auth.AccountKeeper + BankKeeper bank.Keeper + SupplyKeeper supply.Keeper + StakingKeeper staking.Keeper + SlashingKeeper slashing.Keeper + MintKeeper mint.Keeper + DistrKeeper distr.Keeper + GovKeeper gov.Keeper + CrisisKeeper crisis.Keeper + DelegationKeeper delegation.Keeper + ParamsKeeper params.Keeper // the module manager mm *module.Manager @@ -156,6 +158,7 @@ func NewSimApp( app.SlashingKeeper = slashing.NewKeeper(app.cdc, keys[slashing.StoreKey], &stakingKeeper, slashingSubspace, slashing.DefaultCodespace) app.CrisisKeeper = crisis.NewKeeper(crisisSubspace, invCheckPeriod, app.SupplyKeeper, auth.FeeCollectorName) + app.DelegationKeeper = delegation.NewKeeper(app.cdc, keys[delegation.StoreKey]) // register the proposal types govRouter := gov.NewRouter() @@ -184,6 +187,7 @@ func NewSimApp( distr.NewAppModule(app.DistrKeeper, app.SupplyKeeper), slashing.NewAppModule(app.SlashingKeeper, app.StakingKeeper), staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper), + delegation.NewAppModule(app.DelegationKeeper), ) // During begin block slashing happens after distr.BeginBlocker so that @@ -199,7 +203,7 @@ func NewSimApp( auth.ModuleName, distr.ModuleName, staking.ModuleName, bank.ModuleName, slashing.ModuleName, gov.ModuleName, mint.ModuleName, supply.ModuleName, crisis.ModuleName, - genutil.ModuleName, + genutil.ModuleName, delegation.ModuleName, ) app.mm.RegisterInvariants(&app.CrisisKeeper) @@ -218,6 +222,7 @@ func NewSimApp( distr.NewAppModule(app.DistrKeeper, app.SupplyKeeper), staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper), slashing.NewAppModule(app.SlashingKeeper, app.StakingKeeper), + // delegation.NewAppModule(app.DelegationKeeper), ) app.sm.RegisterStoreDecoders() diff --git a/x/delegation/module.go b/x/delegation/module.go index 04adfb0f7e9b..a3c09da29f8e 100644 --- a/x/delegation/module.go +++ b/x/delegation/module.go @@ -42,7 +42,7 @@ func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { // DefaultGenesis returns default genesis state as raw bytes for the delegation // module. func (AppModuleBasic) DefaultGenesis() json.RawMessage { - panic("not implemented!") + return []byte("{}") // return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) } @@ -137,7 +137,8 @@ func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.Va // ExportGenesis returns the exported genesis state as raw bytes for the delegation // module. func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { - panic("not implemented!") + // TODO + return []byte("{}") // gs := ExportGenesis(ctx, am.keeper) // return ModuleCdc.MustMarshalJSON(gs) } From 45c64d251d5e1bfece98d3df3952cab30e65c083 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 17 Oct 2019 12:10:24 +0200 Subject: [PATCH 22/77] add basic test for decorator (no delegation) --- x/delegation/internal/ante/fee_test.go | 63 ++++++++++++++++++++- x/delegation/internal/keeper/keeper.go | 6 +- x/delegation/internal/keeper/keeper_test.go | 2 +- 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/x/delegation/internal/ante/fee_test.go b/x/delegation/internal/ante/fee_test.go index dfb29020286b..e61941ff339c 100644 --- a/x/delegation/internal/ante/fee_test.go +++ b/x/delegation/internal/ante/fee_test.go @@ -1 +1,62 @@ -package ante \ No newline at end of file +package ante_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/delegation/internal/ante" + // delTypes "github.com/cosmos/cosmos-sdk/x/delegation/internal/types" +) + +func TestDeductFeesNoDelegation(t *testing.T) { + // setup + app, ctx := createTestApp(true) + + // keys and addresses + priv1, _, addr1 := authtypes.KeyTestPubAddr() + + // msg and signatures + msg1 := authtypes.NewTestMsg(addr1) + fee := authtypes.NewTestStdFee() + + msgs := []sdk.Msg{msg1} + + privs, accNums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} + tx := authtypes.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) + + // Set account with insufficient funds + acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) + acc.SetCoins([]sdk.Coin{sdk.NewCoin("atom", sdk.NewInt(10))}) + app.AccountKeeper.SetAccount(ctx, acc) + + dfd := ante.NewDeductDelegatedFeeDecorator(app.AccountKeeper, app.SupplyKeeper, app.DelegationKeeper) + antehandler := sdk.ChainAnteDecorators(dfd) + + _, err := antehandler(ctx, tx, false) + + require.NotNil(t, err, "Tx did not error when fee payer had insufficient funds") + + // Set account with sufficient funds + acc.SetCoins([]sdk.Coin{sdk.NewCoin("atom", sdk.NewInt(200))}) + app.AccountKeeper.SetAccount(ctx, acc) + + _, err = antehandler(ctx, tx, false) + + require.Nil(t, err, "Tx errored after account has been set with sufficient funds") +} + +// returns context and app with params set on account keeper +func createTestApp(isCheckTx bool) (*simapp.SimApp, sdk.Context) { + app := simapp.Setup(isCheckTx) + ctx := app.BaseApp.NewContext(isCheckTx, abci.Header{}) + app.AccountKeeper.SetParams(ctx, authtypes.DefaultParams()) + + return app, ctx +} diff --git a/x/delegation/internal/keeper/keeper.go b/x/delegation/internal/keeper/keeper.go index 2546b6af7b68..d0aac56c0726 100644 --- a/x/delegation/internal/keeper/keeper.go +++ b/x/delegation/internal/keeper/keeper.go @@ -8,13 +8,13 @@ import ( ) type Keeper struct { - storeKey sdk.StoreKey cdc *codec.Codec + storeKey sdk.StoreKey } // NewKeeper creates a DelegationKeeper -func NewKeeper(storeKey sdk.StoreKey, cdc *codec.Codec) Keeper { - return Keeper{storeKey, cdc} +func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey) Keeper { + return Keeper{cdc: cdc, storeKey: storeKey} } // DelegateFeeAllowance creates a new grant diff --git a/x/delegation/internal/keeper/keeper_test.go b/x/delegation/internal/keeper/keeper_test.go index 649e0c639b28..617623ed3dd9 100644 --- a/x/delegation/internal/keeper/keeper_test.go +++ b/x/delegation/internal/keeper/keeper_test.go @@ -37,7 +37,7 @@ func setupTestInput() testInput { ms.MountStoreWithDB(delCapKey, sdk.StoreTypeIAVL, db) ms.LoadLatestVersion() - dk := keeper.NewKeeper(delCapKey, cdc) + dk := keeper.NewKeeper(cdc, delCapKey) ctx := sdk.NewContext(ms, abci.Header{ChainID: "test-chain-id", Time: time.Now().UTC(), Height: 1234}, false, log.NewNopLogger()) return testInput{cdc: cdc, ctx: ctx, dk: dk} From 87fd0ce345ddf38e0080ff86dcc99345d5a3a526 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 17 Oct 2019 12:53:31 +0200 Subject: [PATCH 23/77] Table tests for deduct fees --- x/delegation/internal/ante/fee_test.go | 104 ++++++++++++++++++------- 1 file changed, 75 insertions(+), 29 deletions(-) diff --git a/x/delegation/internal/ante/fee_test.go b/x/delegation/internal/ante/fee_test.go index e61941ff339c..000fac453f5a 100644 --- a/x/delegation/internal/ante/fee_test.go +++ b/x/delegation/internal/ante/fee_test.go @@ -18,38 +18,84 @@ import ( func TestDeductFeesNoDelegation(t *testing.T) { // setup app, ctx := createTestApp(true) - - // keys and addresses - priv1, _, addr1 := authtypes.KeyTestPubAddr() - - // msg and signatures - msg1 := authtypes.NewTestMsg(addr1) - fee := authtypes.NewTestStdFee() - - msgs := []sdk.Msg{msg1} - - privs, accNums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx := authtypes.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) - - // Set account with insufficient funds - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) - acc.SetCoins([]sdk.Coin{sdk.NewCoin("atom", sdk.NewInt(10))}) - app.AccountKeeper.SetAccount(ctx, acc) - dfd := ante.NewDeductDelegatedFeeDecorator(app.AccountKeeper, app.SupplyKeeper, app.DelegationKeeper) antehandler := sdk.ChainAnteDecorators(dfd) - _, err := antehandler(ctx, tx, false) - - require.NotNil(t, err, "Tx did not error when fee payer had insufficient funds") - - // Set account with sufficient funds - acc.SetCoins([]sdk.Coin{sdk.NewCoin("atom", sdk.NewInt(200))}) - app.AccountKeeper.SetAccount(ctx, acc) - - _, err = antehandler(ctx, tx, false) - - require.Nil(t, err, "Tx errored after account has been set with sufficient funds") + // keys and addresses + priv1, _, addr1 := authtypes.KeyTestPubAddr() + priv2, _, addr2 := authtypes.KeyTestPubAddr() + priv3, _, addr3 := authtypes.KeyTestPubAddr() + priv4, _, addr4 := authtypes.KeyTestPubAddr() + + // Set addr1 with insufficient funds + acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) + acc1.SetCoins([]sdk.Coin{sdk.NewCoin("atom", sdk.NewInt(10))}) + app.AccountKeeper.SetAccount(ctx, acc1) + + // Set addr2 with more funds + acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) + acc2.SetCoins([]sdk.Coin{sdk.NewCoin("atom", sdk.NewInt(99999))}) + app.AccountKeeper.SetAccount(ctx, acc2) + + // Set delegation from addr2 to addr3 (plenty to pay) + + // Set delegation from addr1 to addr4 (insufficient funds) + + cases := map[string]struct { + signerKey crypto.PrivKey + signer sdk.AccAddress + feeAccount sdk.AccAddress + fee int64 + valid bool + }{ + "paying with low funds": { + signerKey: priv1, + signer: addr1, + fee: 50, + valid: false, + }, + "paying with good funds": { + signerKey: priv2, + signer: addr2, + fee: 50, + valid: true, + }, + "paying with no account": { + signerKey: priv3, + signer: addr3, + fee: 1, + valid: false, + }, + "no fee with real account": { + signerKey: priv1, + signer: addr1, + fee: 0, + valid: true, + }, + "no fee with no account": { + signerKey: priv4, + signer: addr4, + fee: 0, + valid: true, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + // msg and signatures + fee := authtypes.NewStdFee(100000, sdk.NewCoins(sdk.NewInt64Coin("atom", tc.fee))) + msgs := []sdk.Msg{sdk.NewTestMsg(tc.signer)} + privs, accNums, seqs := []crypto.PrivKey{tc.signerKey}, []uint64{0}, []uint64{0} + tx := authtypes.NewTestTxWithFeeAccount(ctx, msgs, privs, accNums, seqs, fee, tc.feeAccount) + + _, err := antehandler(ctx, tx, false) + if tc.valid { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } } // returns context and app with params set on account keeper From d03c65009f8d858b834ba164f0ab4159cc0c8cde Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 17 Oct 2019 13:04:36 +0200 Subject: [PATCH 24/77] Table tests over all conditions of delegated fee decorator --- simapp/app.go | 3 +- x/delegation/internal/ante/fee_test.go | 57 +++++++++++++++++++++++++- x/delegation/internal/keeper/keeper.go | 3 ++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/simapp/app.go b/simapp/app.go index ac0ac5c07331..fc7895c3eec0 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -54,6 +54,7 @@ var ( params.AppModuleBasic{}, crisis.AppModuleBasic{}, slashing.AppModuleBasic{}, + delegation.AppModuleBasic{}, ) // module account permissions @@ -124,7 +125,7 @@ func NewSimApp( keys := sdk.NewKVStoreKeys(bam.MainStoreKey, auth.StoreKey, staking.StoreKey, supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey, - gov.StoreKey, params.StoreKey) + gov.StoreKey, params.StoreKey, delegation.StoreKey) tkeys := sdk.NewTransientStoreKeys(params.TStoreKey) app := &SimApp{ diff --git a/x/delegation/internal/ante/fee_test.go b/x/delegation/internal/ante/fee_test.go index 000fac453f5a..7f89fc84b254 100644 --- a/x/delegation/internal/ante/fee_test.go +++ b/x/delegation/internal/ante/fee_test.go @@ -12,6 +12,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/delegation/internal/ante" + "github.com/cosmos/cosmos-sdk/x/delegation/internal/types" // delTypes "github.com/cosmos/cosmos-sdk/x/delegation/internal/types" ) @@ -38,8 +39,34 @@ func TestDeductFeesNoDelegation(t *testing.T) { app.AccountKeeper.SetAccount(ctx, acc2) // Set delegation from addr2 to addr3 (plenty to pay) + err := app.DelegationKeeper.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + Granter: addr2, + Grantee: addr3, + Allowance: &types.BasicFeeAllowance{ + SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 500)), + }, + }) + require.NoError(t, err) + + // Set low delegation from addr2 to addr4 (delegation will reject) + err = app.DelegationKeeper.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + Granter: addr2, + Grantee: addr4, + Allowance: &types.BasicFeeAllowance{ + SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 20)), + }, + }) + require.NoError(t, err) - // Set delegation from addr1 to addr4 (insufficient funds) + // Set delegation from addr1 to addr4 (cannot cover this ) + err = app.DelegationKeeper.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + Granter: addr2, + Grantee: addr3, + Allowance: &types.BasicFeeAllowance{ + SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 500)), + }, + }) + require.NoError(t, err) cases := map[string]struct { signerKey crypto.PrivKey @@ -78,6 +105,34 @@ func TestDeductFeesNoDelegation(t *testing.T) { fee: 0, valid: true, }, + "valid delegation": { + signerKey: priv3, + signer: addr3, + feeAccount: addr2, + fee: 50, + valid: true, + }, + "no delegation": { + signerKey: priv3, + signer: addr3, + feeAccount: addr1, + fee: 2, + valid: false, + }, + "allowance smaller than requested fee": { + signerKey: priv4, + signer: addr4, + feeAccount: addr2, + fee: 50, + valid: false, + }, + "granter cannot cover allowed delegation": { + signerKey: priv4, + signer: addr4, + feeAccount: addr1, + fee: 50, + valid: false, + }, } for name, tc := range cases { diff --git a/x/delegation/internal/keeper/keeper.go b/x/delegation/internal/keeper/keeper.go index d0aac56c0726..e0ba70f7524d 100644 --- a/x/delegation/internal/keeper/keeper.go +++ b/x/delegation/internal/keeper/keeper.go @@ -96,6 +96,9 @@ func (k Keeper) UseDelegatedFees(ctx sdk.Context, granter sdk.AccAddress, grante ctx.Logger().Error(err.Error()) return false } + if grant == nil || grant.Allowance == nil { + return false + } remove, err := grant.Allowance.Accept(fee, ctx.BlockTime(), ctx.BlockHeight()) if remove { From e476c030dc1c5b0a180a69abe124124a76e3c976 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 17 Oct 2019 13:19:57 +0200 Subject: [PATCH 25/77] Build full ante handler stack and test it --- x/delegation/internal/ante/ante.go | 27 ++++++ x/delegation/internal/ante/fee_test.go | 112 ++++++++++++++++++++++--- 2 files changed, 128 insertions(+), 11 deletions(-) create mode 100644 x/delegation/internal/ante/ante.go diff --git a/x/delegation/internal/ante/ante.go b/x/delegation/internal/ante/ante.go new file mode 100644 index 000000000000..e4d343a5f86d --- /dev/null +++ b/x/delegation/internal/ante/ante.go @@ -0,0 +1,27 @@ +package ante + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + authAnte "github.com/cosmos/cosmos-sdk/x/auth/ante" + authKeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authTypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/delegation/internal/keeper" +) + +// NewAnteHandler is just like auth.NewAnteHandler, except we use the DeductDelegatedFeeDecorator +// in order to allow payment of fees via a delegation. +func NewAnteHandler(ak authKeeper.AccountKeeper, supplyKeeper authTypes.SupplyKeeper, dk keeper.Keeper, sigGasConsumer authAnte.SignatureVerificationGasConsumer) sdk.AnteHandler { + return sdk.ChainAnteDecorators( + authAnte.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first + authAnte.NewMempoolFeeDecorator(), + authAnte.NewValidateBasicDecorator(), + authAnte.NewValidateMemoDecorator(ak), + authAnte.NewConsumeGasForTxSizeDecorator(ak), + authAnte.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators + authAnte.NewValidateSigCountDecorator(ak), + NewDeductDelegatedFeeDecorator(ak, supplyKeeper, dk), + authAnte.NewSigGasConsumeDecorator(ak, sigGasConsumer), + authAnte.NewSigVerificationDecorator(ak), + authAnte.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator + ) +} diff --git a/x/delegation/internal/ante/fee_test.go b/x/delegation/internal/ante/fee_test.go index 7f89fc84b254..1abc98f0617f 100644 --- a/x/delegation/internal/ante/fee_test.go +++ b/x/delegation/internal/ante/fee_test.go @@ -19,8 +19,13 @@ import ( func TestDeductFeesNoDelegation(t *testing.T) { // setup app, ctx := createTestApp(true) + + // this just tests our handler dfd := ante.NewDeductDelegatedFeeDecorator(app.AccountKeeper, app.SupplyKeeper, app.DelegationKeeper) - antehandler := sdk.ChainAnteDecorators(dfd) + ourAnteHandler := sdk.ChainAnteDecorators(dfd) + + // this tests the whole stack + anteHandlerStack := ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, app.DelegationKeeper, SigGasNoConsumer) // keys and addresses priv1, _, addr1 := authtypes.KeyTestPubAddr() @@ -72,65 +77,145 @@ func TestDeductFeesNoDelegation(t *testing.T) { signerKey crypto.PrivKey signer sdk.AccAddress feeAccount sdk.AccAddress + handler sdk.AnteHandler fee int64 valid bool }{ - "paying with low funds": { + "paying with low funds (only ours)": { signerKey: priv1, signer: addr1, fee: 50, + handler: ourAnteHandler, valid: false, }, - "paying with good funds": { + "paying with good funds (only ours)": { signerKey: priv2, signer: addr2, fee: 50, + handler: ourAnteHandler, valid: true, }, - "paying with no account": { + "paying with no account (only ours)": { signerKey: priv3, signer: addr3, fee: 1, + handler: ourAnteHandler, valid: false, }, - "no fee with real account": { + "no fee with real account (only ours)": { signerKey: priv1, signer: addr1, fee: 0, + handler: ourAnteHandler, valid: true, }, - "no fee with no account": { + "no fee with no account (only ours)": { signerKey: priv4, signer: addr4, fee: 0, + handler: ourAnteHandler, valid: true, }, - "valid delegation": { + "valid delegation (only ours)": { signerKey: priv3, signer: addr3, feeAccount: addr2, fee: 50, + handler: ourAnteHandler, valid: true, }, - "no delegation": { + "no delegation (only ours)": { signerKey: priv3, signer: addr3, feeAccount: addr1, fee: 2, + handler: ourAnteHandler, valid: false, }, - "allowance smaller than requested fee": { + "allowance smaller than requested fee (only ours)": { signerKey: priv4, signer: addr4, feeAccount: addr2, fee: 50, + handler: ourAnteHandler, valid: false, }, - "granter cannot cover allowed delegation": { + "granter cannot cover allowed delegation (only ours)": { signerKey: priv4, signer: addr4, feeAccount: addr1, fee: 50, + handler: ourAnteHandler, + valid: false, + }, + + "paying with low funds (whole stack)": { + signerKey: priv1, + signer: addr1, + fee: 50, + handler: anteHandlerStack, + valid: false, + }, + "paying with good funds (whole stack)": { + signerKey: priv2, + signer: addr2, + fee: 50, + handler: anteHandlerStack, + valid: true, + }, + "paying with no account (whole stack)": { + signerKey: priv3, + signer: addr3, + fee: 1, + handler: anteHandlerStack, + valid: false, + }, + "no fee with real account (whole stack)": { + signerKey: priv1, + signer: addr1, + fee: 0, + handler: anteHandlerStack, + valid: true, + }, + // TODO: cannot pay zero fees if account doesn't exist (is this good?) + // "no fee with no account (whole stack)": { + // signerKey: priv4, + // signer: addr4, + // fee: 0, + // handler: anteHandlerStack, + // valid: true, + // }, + // TODO: cannot delegate fees if account doesn't exist (this must change for delegation) + // "valid delegation (whole stack)": { + // signerKey: priv3, + // signer: addr3, + // feeAccount: addr2, + // fee: 50, + // handler: anteHandlerStack, + // valid: true, + // }, + "no delegation (whole stack)": { + signerKey: priv3, + signer: addr3, + feeAccount: addr1, + fee: 2, + handler: anteHandlerStack, + valid: false, + }, + "allowance smaller than requested fee (whole stack)": { + signerKey: priv4, + signer: addr4, + feeAccount: addr2, + fee: 50, + handler: anteHandlerStack, + valid: false, + }, + "granter cannot cover allowed delegation (whole stack)": { + signerKey: priv4, + signer: addr4, + feeAccount: addr1, + fee: 50, + handler: anteHandlerStack, valid: false, }, } @@ -143,7 +228,7 @@ func TestDeductFeesNoDelegation(t *testing.T) { privs, accNums, seqs := []crypto.PrivKey{tc.signerKey}, []uint64{0}, []uint64{0} tx := authtypes.NewTestTxWithFeeAccount(ctx, msgs, privs, accNums, seqs, fee, tc.feeAccount) - _, err := antehandler(ctx, tx, false) + _, err := tc.handler(ctx, tx, false) if tc.valid { require.NoError(t, err) } else { @@ -161,3 +246,8 @@ func createTestApp(isCheckTx bool) (*simapp.SimApp, sdk.Context) { return app, ctx } + +// don't cosume any gas +func SigGasNoConsumer(meter sdk.GasMeter, sig []byte, pubkey crypto.PubKey, params authtypes.Params) error { + return nil +} From 182eb3435a2e3fee7518ab22ef078b9ba6b8acb0 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 17 Oct 2019 15:01:23 +0200 Subject: [PATCH 26/77] Start genesis --- x/delegation/genesis.go | 24 +++++++++++++++++++ x/delegation/module.go | 51 +++++++++++++++++++++-------------------- 2 files changed, 50 insertions(+), 25 deletions(-) create mode 100644 x/delegation/genesis.go diff --git a/x/delegation/genesis.go b/x/delegation/genesis.go new file mode 100644 index 000000000000..e5a5117dd215 --- /dev/null +++ b/x/delegation/genesis.go @@ -0,0 +1,24 @@ +package delegation + +import sdk "github.com/cosmos/cosmos-sdk/types" + +type GenesisState []FeeAllowanceGrant + +func (g GenesisState) ValidateBasic() error { + for _, f := range g { + err := f.ValidateBasic() + if err != nil { + return err + } + } + return nil +} + +func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) { + // TODO +} + +func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState { + // TODO + return {} +} diff --git a/x/delegation/module.go b/x/delegation/module.go index a3c09da29f8e..6b91b3bda239 100644 --- a/x/delegation/module.go +++ b/x/delegation/module.go @@ -15,11 +15,11 @@ import ( ) // TODO: -// * ante handler // * genesis // * cli // * rest -// -> changes to auth, etc +// * periodic fee +// -> change StdFee instead of StdTx, etc? var ( _ module.AppModule = AppModule{} @@ -42,20 +42,25 @@ func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { // DefaultGenesis returns default genesis state as raw bytes for the delegation // module. func (AppModuleBasic) DefaultGenesis() json.RawMessage { - return []byte("{}") - // return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) + return []byte("[]") } // ValidateGenesis performs genesis state validation for the delegation module. -func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { - // TODO - return nil - // var data GenesisState - // err := ModuleCdc.UnmarshalJSON(bz, &data) - // if err != nil { - // return err - // } - // return ValidateGenesis(data) +func (a AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + _, err := a.getValidatedGenesis(bz) + return err +} + +func (a AppModuleBasic) getValidatedGenesis(bz json.RawMessage) (GenesisState, error) { + cdc := codec.New() + a.RegisterCodec(cdc) + + var data GenesisState + err := cdc.UnmarshalJSON(bz, &data) + if err != nil { + return nil, err + } + return data, data.ValidateBasic() } // RegisterRESTRoutes registers the REST routes for the delegation module. @@ -99,10 +104,7 @@ func (AppModule) Name() string { } // RegisterInvariants registers the delegation module invariants. -func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { - // TODO? - // RegisterInvariants(ir, am.keeper) -} +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} // Route returns the message routing key for the delegation module. func (AppModule) Route() string { @@ -127,20 +129,19 @@ func (am AppModule) NewQuerierHandler() sdk.Querier { // InitGenesis performs genesis initialization for the delegation module. It returns // no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { - // TODO - // var genesisState GenesisState - // ModuleCdc.MustUnmarshalJSON(data, &genesisState) - // InitGenesis(ctx, am.keeper, am.ak, genesisState) + genesisState, err := am.getValidatedGenesis(data) + if err != nil { + panic(err) + } + InitGenesis(ctx, am.keeper, genesisState) return []abci.ValidatorUpdate{} } // ExportGenesis returns the exported genesis state as raw bytes for the delegation // module. func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { - // TODO - return []byte("{}") - // gs := ExportGenesis(ctx, am.keeper) - // return ModuleCdc.MustMarshalJSON(gs) + gs := ExportGenesis(ctx, am.keeper) + return ModuleCdc.MustMarshalJSON(gs) } // BeginBlock returns the begin blocker for the delegation module. From 071e111705c70f8b5e41db305891cec0d79be8a1 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 17 Oct 2019 16:54:17 +0200 Subject: [PATCH 27/77] Implement Genesis --- x/delegation/genesis.go | 19 +++++++++++----- x/delegation/internal/keeper/keeper.go | 24 +++++++++++++++++++-- x/delegation/internal/keeper/keeper_test.go | 2 +- x/delegation/internal/keeper/querier.go | 2 +- x/delegation/module.go | 10 +++++++-- 5 files changed, 46 insertions(+), 11 deletions(-) diff --git a/x/delegation/genesis.go b/x/delegation/genesis.go index e5a5117dd215..4aae4e68433a 100644 --- a/x/delegation/genesis.go +++ b/x/delegation/genesis.go @@ -2,8 +2,10 @@ package delegation import sdk "github.com/cosmos/cosmos-sdk/types" +// GenesisState contains a set of fee allowances, persisted from the store type GenesisState []FeeAllowanceGrant +// ValidateBasic ensures all grants in the genesis state are valid func (g GenesisState) ValidateBasic() error { for _, f := range g { err := f.ValidateBasic() @@ -14,11 +16,18 @@ func (g GenesisState) ValidateBasic() error { return nil } -func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) { - // TODO +// InitGenesis will initialize the keeper from a *previously validated* GenesisState +func InitGenesis(ctx sdk.Context, k Keeper, gen GenesisState) error { + for _, f := range gen { + err := k.DelegateFeeAllowance(ctx, f) + if err != nil { + return err + } + } + return nil } -func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState { - // TODO - return {} +// ExportGenesis will dump the contents of the keeper into a serializable GenesisState +func ExportGenesis(ctx sdk.Context, k Keeper) (GenesisState, error) { + return k.GetAllFeeAllowances(ctx) } diff --git a/x/delegation/internal/keeper/keeper.go b/x/delegation/internal/keeper/keeper.go index e0ba70f7524d..670051191967 100644 --- a/x/delegation/internal/keeper/keeper.go +++ b/x/delegation/internal/keeper/keeper.go @@ -65,8 +65,8 @@ func (k Keeper) GetFeeGrant(ctx sdk.Context, granter sdk.AccAddress, grantee sdk return &grant, nil } -// GetAllFeeAllowances returns a list of all the grants from anyone to the given grantee. -func (k Keeper) GetAllFeeAllowances(ctx sdk.Context, grantee sdk.AccAddress) ([]types.FeeAllowanceGrant, error) { +// GetAllMyFeeAllowances returns a list of all the grants from anyone to the given grantee. +func (k Keeper) GetAllMyFeeAllowances(ctx sdk.Context, grantee sdk.AccAddress) ([]types.FeeAllowanceGrant, error) { store := ctx.KVStore(k.storeKey) var grants []types.FeeAllowanceGrant @@ -85,6 +85,26 @@ func (k Keeper) GetAllFeeAllowances(ctx sdk.Context, grantee sdk.AccAddress) ([] return grants, nil } +// GetAllFeeAllowances returns a list of all the grants in the store. +// This is very expensive and only designed for export genesis +func (k Keeper) GetAllFeeAllowances(ctx sdk.Context) ([]types.FeeAllowanceGrant, error) { + store := ctx.KVStore(k.storeKey) + var grants []types.FeeAllowanceGrant + + iter := sdk.KVStorePrefixIterator(store, types.FeeAllowanceKeyPrefix) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + bz := iter.Value() + var grant types.FeeAllowanceGrant + err := k.cdc.UnmarshalBinaryBare(bz, &grant) + if err != nil { + return nil, err + } + grants = append(grants, grant) + } + return grants, nil +} + // UseDelegatedFees will try to pay the given fee from the granter's account as requested by the grantee // (true, nil) will update the allowance, and assumes the AnteHandler deducts the given fees // (false, nil) rejects payment on behalf of grantee diff --git a/x/delegation/internal/keeper/keeper_test.go b/x/delegation/internal/keeper/keeper_test.go index 617623ed3dd9..5c5bd671b641 100644 --- a/x/delegation/internal/keeper/keeper_test.go +++ b/x/delegation/internal/keeper/keeper_test.go @@ -176,7 +176,7 @@ func TestKeeperCrud(t *testing.T) { for name, tc := range allCases { t.Run(name, func(t *testing.T) { - grants, err := k.GetAllFeeAllowances(ctx, tc.grantee) + grants, err := k.GetAllMyFeeAllowances(ctx, tc.grantee) require.NoError(t, err) assert.Equal(t, tc.grants, grants) }) diff --git a/x/delegation/internal/keeper/querier.go b/x/delegation/internal/keeper/querier.go index ab12c5ecb804..238d5cba428b 100644 --- a/x/delegation/internal/keeper/querier.go +++ b/x/delegation/internal/keeper/querier.go @@ -28,7 +28,7 @@ func queryGetFeeAllowances(ctx sdk.Context, args []string, keeper Keeper) ([]byt return nil, sdk.ErrInternal(sdk.AppendMsgToErr("invalid address", err.Error())) } - fees, err := keeper.GetAllFeeAllowances(ctx, granteeAddr) + fees, err := keeper.GetAllMyFeeAllowances(ctx, granteeAddr) if err != nil { return nil, sdk.ConvertError(err) } diff --git a/x/delegation/module.go b/x/delegation/module.go index 6b91b3bda239..4ba94502d1b0 100644 --- a/x/delegation/module.go +++ b/x/delegation/module.go @@ -133,14 +133,20 @@ func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.Va if err != nil { panic(err) } - InitGenesis(ctx, am.keeper, genesisState) + err = InitGenesis(ctx, am.keeper, genesisState) + if err != nil { + panic(err) + } return []abci.ValidatorUpdate{} } // ExportGenesis returns the exported genesis state as raw bytes for the delegation // module. func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { - gs := ExportGenesis(ctx, am.keeper) + gs, err := ExportGenesis(ctx, am.keeper) + if err != nil { + panic(err) + } return ModuleCdc.MustMarshalJSON(gs) } From 98b88a32a9a8acbc3a847deeec4f04208471b392 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 18 Oct 2019 10:45:28 +0200 Subject: [PATCH 28/77] Rename package delegation to subkeys --- simapp/app.go | 16 ++++++++-------- x/{delegation => subkeys}/README.md | 0 x/{delegation => subkeys}/alias.go | 10 +++++----- x/{delegation => subkeys}/doc.go | 6 +++--- x/{delegation => subkeys}/exported/fees.go | 0 x/{delegation => subkeys}/genesis.go | 8 +++++++- x/{delegation => subkeys}/handler.go | 2 +- x/{delegation => subkeys}/internal/ante/ante.go | 2 +- x/{delegation => subkeys}/internal/ante/fee.go | 2 +- .../internal/ante/fee_test.go | 6 +++--- .../internal/keeper/keeper.go | 4 ++-- .../internal/keeper/keeper_test.go | 6 +++--- .../internal/keeper/querier.go | 2 +- .../internal/keeper/querier_test.go | 4 ++-- .../internal/types/basic_fee.go | 2 +- .../internal/types/basic_fee_test.go | 0 .../internal/types/codec.go | 2 +- .../internal/types/errors.go | 0 .../internal/types/expiration.go | 0 .../internal/types/expiration_test.go | 0 .../internal/types/grant.go | 2 +- .../internal/types/grant_test.go | 0 x/{delegation => subkeys}/internal/types/key.go | 0 x/{delegation => subkeys}/internal/types/msgs.go | 2 +- x/{delegation => subkeys}/module.go | 3 +-- 25 files changed, 42 insertions(+), 37 deletions(-) rename x/{delegation => subkeys}/README.md (100%) rename x/{delegation => subkeys}/alias.go (86%) rename x/{delegation => subkeys}/doc.go (92%) rename x/{delegation => subkeys}/exported/fees.go (100%) rename x/{delegation => subkeys}/genesis.go (67%) rename x/{delegation => subkeys}/handler.go (96%) rename x/{delegation => subkeys}/internal/ante/ante.go (95%) rename x/{delegation => subkeys}/internal/ante/fee.go (97%) rename x/{delegation => subkeys}/internal/ante/fee_test.go (97%) rename x/{delegation => subkeys}/internal/keeper/keeper.go (97%) rename x/{delegation => subkeys}/internal/keeper/keeper_test.go (97%) rename x/{delegation => subkeys}/internal/keeper/querier.go (92%) rename x/{delegation => subkeys}/internal/keeper/querier_test.go (94%) rename x/{delegation => subkeys}/internal/types/basic_fee.go (96%) rename x/{delegation => subkeys}/internal/types/basic_fee_test.go (100%) rename x/{delegation => subkeys}/internal/types/codec.go (90%) rename x/{delegation => subkeys}/internal/types/errors.go (100%) rename x/{delegation => subkeys}/internal/types/expiration.go (100%) rename x/{delegation => subkeys}/internal/types/expiration_test.go (100%) rename x/{delegation => subkeys}/internal/types/grant.go (93%) rename x/{delegation => subkeys}/internal/types/grant_test.go (100%) rename x/{delegation => subkeys}/internal/types/key.go (100%) rename x/{delegation => subkeys}/internal/types/msgs.go (97%) rename x/{delegation => subkeys}/module.go (99%) diff --git a/simapp/app.go b/simapp/app.go index fc7895c3eec0..118cda2d0195 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -18,7 +18,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/vesting" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/crisis" - "github.com/cosmos/cosmos-sdk/x/delegation" + "github.com/cosmos/cosmos-sdk/x/subkeys" distr "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/genutil" "github.com/cosmos/cosmos-sdk/x/gov" @@ -54,7 +54,7 @@ var ( params.AppModuleBasic{}, crisis.AppModuleBasic{}, slashing.AppModuleBasic{}, - delegation.AppModuleBasic{}, + subkeys.AppModuleBasic{}, ) // module account permissions @@ -101,7 +101,7 @@ type SimApp struct { DistrKeeper distr.Keeper GovKeeper gov.Keeper CrisisKeeper crisis.Keeper - DelegationKeeper delegation.Keeper + DelegationKeeper subkeys.Keeper ParamsKeeper params.Keeper // the module manager @@ -125,7 +125,7 @@ func NewSimApp( keys := sdk.NewKVStoreKeys(bam.MainStoreKey, auth.StoreKey, staking.StoreKey, supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey, - gov.StoreKey, params.StoreKey, delegation.StoreKey) + gov.StoreKey, params.StoreKey, subkeys.StoreKey) tkeys := sdk.NewTransientStoreKeys(params.TStoreKey) app := &SimApp{ @@ -159,7 +159,7 @@ func NewSimApp( app.SlashingKeeper = slashing.NewKeeper(app.cdc, keys[slashing.StoreKey], &stakingKeeper, slashingSubspace, slashing.DefaultCodespace) app.CrisisKeeper = crisis.NewKeeper(crisisSubspace, invCheckPeriod, app.SupplyKeeper, auth.FeeCollectorName) - app.DelegationKeeper = delegation.NewKeeper(app.cdc, keys[delegation.StoreKey]) + app.DelegationKeeper = subkeys.NewKeeper(app.cdc, keys[subkeys.StoreKey]) // register the proposal types govRouter := gov.NewRouter() @@ -188,7 +188,7 @@ func NewSimApp( distr.NewAppModule(app.DistrKeeper, app.SupplyKeeper), slashing.NewAppModule(app.SlashingKeeper, app.StakingKeeper), staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper), - delegation.NewAppModule(app.DelegationKeeper), + subkeys.NewAppModule(app.DelegationKeeper), ) // During begin block slashing happens after distr.BeginBlocker so that @@ -204,7 +204,7 @@ func NewSimApp( auth.ModuleName, distr.ModuleName, staking.ModuleName, bank.ModuleName, slashing.ModuleName, gov.ModuleName, mint.ModuleName, supply.ModuleName, crisis.ModuleName, - genutil.ModuleName, delegation.ModuleName, + genutil.ModuleName, subkeys.ModuleName, ) app.mm.RegisterInvariants(&app.CrisisKeeper) @@ -223,7 +223,7 @@ func NewSimApp( distr.NewAppModule(app.DistrKeeper, app.SupplyKeeper), staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper), slashing.NewAppModule(app.SlashingKeeper, app.StakingKeeper), - // delegation.NewAppModule(app.DelegationKeeper), + // subkeys.NewAppModule(app.DelegationKeeper), ) app.sm.RegisterStoreDecoders() diff --git a/x/delegation/README.md b/x/subkeys/README.md similarity index 100% rename from x/delegation/README.md rename to x/subkeys/README.md diff --git a/x/delegation/alias.go b/x/subkeys/alias.go similarity index 86% rename from x/delegation/alias.go rename to x/subkeys/alias.go index 7bfc3f7d5fbf..9bdce90331f7 100644 --- a/x/delegation/alias.go +++ b/x/subkeys/alias.go @@ -1,13 +1,13 @@ // nolint // autogenerated code using github.com/rigelrozanski/multitool // aliases generated for the following subdirectories: -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/delegation/internal/types -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/delegation/internal/keeper -package delegation +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/subkeys/internal/types +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/subkeys/internal/keeper +package subkeys import ( - "github.com/cosmos/cosmos-sdk/x/delegation/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/delegation/internal/types" + "github.com/cosmos/cosmos-sdk/x/subkeys/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types" ) const ( diff --git a/x/delegation/doc.go b/x/subkeys/doc.go similarity index 92% rename from x/delegation/doc.go rename to x/subkeys/doc.go index 8d9a71fa04b7..d21ec229452e 100644 --- a/x/delegation/doc.go +++ b/x/subkeys/doc.go @@ -1,6 +1,6 @@ /* -Package delegation provides functionality for delegating sub-permissions -from one account to another account. +Package subkeys provides functionality for delegating sub-permissions +from one account (key) to another account (key). The first implementation allows the delegation for the payment of transaction fees. Effectively, this allows for a user to pay fees using the balance of an account @@ -29,4 +29,4 @@ to the first signer of the transaction. An example usage would be: allow := feeDelegationKeeper.AllowDelegatedFees(ctx, signers[0], stdTx.FeeAccount, stdTx.Fee.Amount) */ -package delegation +package subkeys diff --git a/x/delegation/exported/fees.go b/x/subkeys/exported/fees.go similarity index 100% rename from x/delegation/exported/fees.go rename to x/subkeys/exported/fees.go diff --git a/x/delegation/genesis.go b/x/subkeys/genesis.go similarity index 67% rename from x/delegation/genesis.go rename to x/subkeys/genesis.go index 4aae4e68433a..28b403921fbf 100644 --- a/x/delegation/genesis.go +++ b/x/subkeys/genesis.go @@ -1,4 +1,4 @@ -package delegation +package subkeys import sdk "github.com/cosmos/cosmos-sdk/types" @@ -29,5 +29,11 @@ func InitGenesis(ctx sdk.Context, k Keeper, gen GenesisState) error { // ExportGenesis will dump the contents of the keeper into a serializable GenesisState func ExportGenesis(ctx sdk.Context, k Keeper) (GenesisState, error) { + // TODO: all expiration heights will be thrown off if we dump state and start at a new + // chain at height 0. Maybe we need to allow the Allowances to "prepare themselves" + // for export, like if they have exiry at 5000 and current is 4000, they export with + // expiry of 1000. It would need a new method on the FeeAllowance interface. + // + // Currently, we handle expirations naively return k.GetAllFeeAllowances(ctx) } diff --git a/x/delegation/handler.go b/x/subkeys/handler.go similarity index 96% rename from x/delegation/handler.go rename to x/subkeys/handler.go index 367eb7b21523..a2438a542cf7 100644 --- a/x/delegation/handler.go +++ b/x/subkeys/handler.go @@ -1,4 +1,4 @@ -package delegation +package subkeys import ( "fmt" diff --git a/x/delegation/internal/ante/ante.go b/x/subkeys/internal/ante/ante.go similarity index 95% rename from x/delegation/internal/ante/ante.go rename to x/subkeys/internal/ante/ante.go index e4d343a5f86d..dcac1b0295f1 100644 --- a/x/delegation/internal/ante/ante.go +++ b/x/subkeys/internal/ante/ante.go @@ -5,7 +5,7 @@ import ( authAnte "github.com/cosmos/cosmos-sdk/x/auth/ante" authKeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authTypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/delegation/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/subkeys/internal/keeper" ) // NewAnteHandler is just like auth.NewAnteHandler, except we use the DeductDelegatedFeeDecorator diff --git a/x/delegation/internal/ante/fee.go b/x/subkeys/internal/ante/fee.go similarity index 97% rename from x/delegation/internal/ante/fee.go rename to x/subkeys/internal/ante/fee.go index c1f988e4094c..23f6645e86dc 100644 --- a/x/delegation/internal/ante/fee.go +++ b/x/subkeys/internal/ante/fee.go @@ -10,7 +10,7 @@ import ( authAnte "github.com/cosmos/cosmos-sdk/x/auth/ante" authKeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authTypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/delegation/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/subkeys/internal/keeper" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) diff --git a/x/delegation/internal/ante/fee_test.go b/x/subkeys/internal/ante/fee_test.go similarity index 97% rename from x/delegation/internal/ante/fee_test.go rename to x/subkeys/internal/ante/fee_test.go index 1abc98f0617f..d96a418b6fa6 100644 --- a/x/delegation/internal/ante/fee_test.go +++ b/x/subkeys/internal/ante/fee_test.go @@ -11,9 +11,9 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/delegation/internal/ante" - "github.com/cosmos/cosmos-sdk/x/delegation/internal/types" - // delTypes "github.com/cosmos/cosmos-sdk/x/delegation/internal/types" + "github.com/cosmos/cosmos-sdk/x/subkeys/internal/ante" + "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types" + // delTypes "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types" ) func TestDeductFeesNoDelegation(t *testing.T) { diff --git a/x/delegation/internal/keeper/keeper.go b/x/subkeys/internal/keeper/keeper.go similarity index 97% rename from x/delegation/internal/keeper/keeper.go rename to x/subkeys/internal/keeper/keeper.go index 670051191967..272d88296fe4 100644 --- a/x/delegation/internal/keeper/keeper.go +++ b/x/subkeys/internal/keeper/keeper.go @@ -3,8 +3,8 @@ package keeper import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/delegation/exported" - "github.com/cosmos/cosmos-sdk/x/delegation/internal/types" + "github.com/cosmos/cosmos-sdk/x/subkeys/exported" + "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types" ) type Keeper struct { diff --git a/x/delegation/internal/keeper/keeper_test.go b/x/subkeys/internal/keeper/keeper_test.go similarity index 97% rename from x/delegation/internal/keeper/keeper_test.go rename to x/subkeys/internal/keeper/keeper_test.go index 5c5bd671b641..1f923c866a2c 100644 --- a/x/delegation/internal/keeper/keeper_test.go +++ b/x/subkeys/internal/keeper/keeper_test.go @@ -13,9 +13,9 @@ import ( codec "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/delegation/exported" - "github.com/cosmos/cosmos-sdk/x/delegation/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/delegation/internal/types" + "github.com/cosmos/cosmos-sdk/x/subkeys/exported" + "github.com/cosmos/cosmos-sdk/x/subkeys/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types" ) type testInput struct { diff --git a/x/delegation/internal/keeper/querier.go b/x/subkeys/internal/keeper/querier.go similarity index 92% rename from x/delegation/internal/keeper/querier.go rename to x/subkeys/internal/keeper/querier.go index 238d5cba428b..4728c3e2bf2c 100644 --- a/x/delegation/internal/keeper/querier.go +++ b/x/subkeys/internal/keeper/querier.go @@ -16,7 +16,7 @@ func NewQuerier(keeper Keeper) sdk.Querier { case QueryGetFeeAllowances: return queryGetFeeAllowances(ctx, path[1:], keeper) default: - return nil, sdk.ErrUnknownRequest("Unknown package delegation query endpoint") + return nil, sdk.ErrUnknownRequest("Unknown package subkeys query endpoint") } } } diff --git a/x/delegation/internal/keeper/querier_test.go b/x/subkeys/internal/keeper/querier_test.go similarity index 94% rename from x/delegation/internal/keeper/querier_test.go rename to x/subkeys/internal/keeper/querier_test.go index 7cec96217859..e9bc45f68b26 100644 --- a/x/delegation/internal/keeper/querier_test.go +++ b/x/subkeys/internal/keeper/querier_test.go @@ -9,8 +9,8 @@ import ( codec "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/delegation/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/delegation/internal/types" + "github.com/cosmos/cosmos-sdk/x/subkeys/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types" ) func TestQuery(t *testing.T) { diff --git a/x/delegation/internal/types/basic_fee.go b/x/subkeys/internal/types/basic_fee.go similarity index 96% rename from x/delegation/internal/types/basic_fee.go rename to x/subkeys/internal/types/basic_fee.go index 01b23be9d286..9a110ff54f16 100644 --- a/x/delegation/internal/types/basic_fee.go +++ b/x/subkeys/internal/types/basic_fee.go @@ -4,7 +4,7 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/delegation/exported" + "github.com/cosmos/cosmos-sdk/x/subkeys/exported" ) // BasicFeeAllowance implements FeeAllowance with a one-time grant of tokens diff --git a/x/delegation/internal/types/basic_fee_test.go b/x/subkeys/internal/types/basic_fee_test.go similarity index 100% rename from x/delegation/internal/types/basic_fee_test.go rename to x/subkeys/internal/types/basic_fee_test.go diff --git a/x/delegation/internal/types/codec.go b/x/subkeys/internal/types/codec.go similarity index 90% rename from x/delegation/internal/types/codec.go rename to x/subkeys/internal/types/codec.go index b7fc25bb04d7..cbc66ed8612f 100644 --- a/x/delegation/internal/types/codec.go +++ b/x/subkeys/internal/types/codec.go @@ -2,7 +2,7 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/delegation/exported" + "github.com/cosmos/cosmos-sdk/x/subkeys/exported" ) // RegisterCodec registers the account types and interface diff --git a/x/delegation/internal/types/errors.go b/x/subkeys/internal/types/errors.go similarity index 100% rename from x/delegation/internal/types/errors.go rename to x/subkeys/internal/types/errors.go diff --git a/x/delegation/internal/types/expiration.go b/x/subkeys/internal/types/expiration.go similarity index 100% rename from x/delegation/internal/types/expiration.go rename to x/subkeys/internal/types/expiration.go diff --git a/x/delegation/internal/types/expiration_test.go b/x/subkeys/internal/types/expiration_test.go similarity index 100% rename from x/delegation/internal/types/expiration_test.go rename to x/subkeys/internal/types/expiration_test.go diff --git a/x/delegation/internal/types/grant.go b/x/subkeys/internal/types/grant.go similarity index 93% rename from x/delegation/internal/types/grant.go rename to x/subkeys/internal/types/grant.go index c7bed78b6e30..bb7006ac837f 100644 --- a/x/delegation/internal/types/grant.go +++ b/x/subkeys/internal/types/grant.go @@ -2,7 +2,7 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/delegation/exported" + "github.com/cosmos/cosmos-sdk/x/subkeys/exported" ) // FeeAllowanceGrant is stored in the KVStore to record a grant with full context diff --git a/x/delegation/internal/types/grant_test.go b/x/subkeys/internal/types/grant_test.go similarity index 100% rename from x/delegation/internal/types/grant_test.go rename to x/subkeys/internal/types/grant_test.go diff --git a/x/delegation/internal/types/key.go b/x/subkeys/internal/types/key.go similarity index 100% rename from x/delegation/internal/types/key.go rename to x/subkeys/internal/types/key.go diff --git a/x/delegation/internal/types/msgs.go b/x/subkeys/internal/types/msgs.go similarity index 97% rename from x/delegation/internal/types/msgs.go rename to x/subkeys/internal/types/msgs.go index b89817166a65..7686f95a3a5a 100644 --- a/x/delegation/internal/types/msgs.go +++ b/x/subkeys/internal/types/msgs.go @@ -4,7 +4,7 @@ import ( "encoding/json" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/delegation/exported" + "github.com/cosmos/cosmos-sdk/x/subkeys/exported" ) // MsgDelegateFeeAllowance adds permission for Grantee to spend up to Allowance diff --git a/x/delegation/module.go b/x/subkeys/module.go similarity index 99% rename from x/delegation/module.go rename to x/subkeys/module.go index 4ba94502d1b0..b0b32332ea77 100644 --- a/x/delegation/module.go +++ b/x/subkeys/module.go @@ -1,4 +1,4 @@ -package delegation +package subkeys import ( "encoding/json" @@ -15,7 +15,6 @@ import ( ) // TODO: -// * genesis // * cli // * rest // * periodic fee From 376e9cd8ee77fdb1db50642b991921796d55a902 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 18 Oct 2019 10:50:11 +0200 Subject: [PATCH 29/77] Clarify antes test cases, handle empty account w/o fees --- x/subkeys/internal/ante/fee.go | 11 +++++---- x/subkeys/internal/ante/fee_test.go | 36 ++++++++++++++--------------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/x/subkeys/internal/ante/fee.go b/x/subkeys/internal/ante/fee.go index 23f6645e86dc..be95b3c4fcc8 100644 --- a/x/subkeys/internal/ante/fee.go +++ b/x/subkeys/internal/ante/fee.go @@ -49,17 +49,13 @@ func (d DeductDelegatedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simu return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") } - // short-circuit on zero fee - fee := feeTx.GetFee() - if fee.IsZero() { - return next(ctx, tx, simulate) - } // sanity check from DeductFeeDecorator if addr := d.sk.GetModuleAddress(authTypes.FeeCollectorName); addr == nil { panic(fmt.Sprintf("%s module account has not been set", authTypes.FeeCollectorName)) } // see if there is a delegation + fee := feeTx.GetFee() var feePayer sdk.AccAddress if delTx, ok := tx.(DelegatedFeeTx); ok { feePayer = delTx.GetFeeAccount() @@ -82,6 +78,11 @@ func (d DeductDelegatedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simu if feePayerAcc == nil { return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "fee payer address: %s does not exist", feePayer) } + + // deduct fee if non-zero + if fee.IsZero() { + return next(ctx, tx, simulate) + } err = authAnte.DeductFees(d.sk, ctx, feePayerAcc, fee) if err != nil { return ctx, err diff --git a/x/subkeys/internal/ante/fee_test.go b/x/subkeys/internal/ante/fee_test.go index d96a418b6fa6..82c57cfffa39 100644 --- a/x/subkeys/internal/ante/fee_test.go +++ b/x/subkeys/internal/ante/fee_test.go @@ -114,9 +114,9 @@ func TestDeductFeesNoDelegation(t *testing.T) { signer: addr4, fee: 0, handler: ourAnteHandler, - valid: true, + valid: false, }, - "valid delegation (only ours)": { + "valid delegation without account (only ours)": { signerKey: priv3, signer: addr3, feeAccount: addr2, @@ -177,23 +177,21 @@ func TestDeductFeesNoDelegation(t *testing.T) { handler: anteHandlerStack, valid: true, }, - // TODO: cannot pay zero fees if account doesn't exist (is this good?) - // "no fee with no account (whole stack)": { - // signerKey: priv4, - // signer: addr4, - // fee: 0, - // handler: anteHandlerStack, - // valid: true, - // }, - // TODO: cannot delegate fees if account doesn't exist (this must change for delegation) - // "valid delegation (whole stack)": { - // signerKey: priv3, - // signer: addr3, - // feeAccount: addr2, - // fee: 50, - // handler: anteHandlerStack, - // valid: true, - // }, + "no fee with no account (whole stack)": { + signerKey: priv4, + signer: addr4, + fee: 0, + handler: anteHandlerStack, + valid: false, + }, + "valid delegation without account (whole stack)": { + signerKey: priv3, + signer: addr3, + feeAccount: addr2, + fee: 50, + handler: anteHandlerStack, + valid: true, + }, "no delegation (whole stack)": { signerKey: priv3, signer: addr3, From 529764f437e8725fc4e716b4429d0ed905d17211 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 18 Oct 2019 11:04:43 +0200 Subject: [PATCH 30/77] Allow paying delegated fees with no account --- x/subkeys/internal/ante/ante.go | 4 +++- x/subkeys/internal/ante/fee.go | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/x/subkeys/internal/ante/ante.go b/x/subkeys/internal/ante/ante.go index dcac1b0295f1..35560c713aee 100644 --- a/x/subkeys/internal/ante/ante.go +++ b/x/subkeys/internal/ante/ante.go @@ -17,9 +17,11 @@ func NewAnteHandler(ak authKeeper.AccountKeeper, supplyKeeper authTypes.SupplyKe authAnte.NewValidateBasicDecorator(), authAnte.NewValidateMemoDecorator(ak), authAnte.NewConsumeGasForTxSizeDecorator(ak), + // DeductDelegatedFeeDecorator will create an empty account if we sign with no tokens but valid validation + // This must be before SetPubKey, ValidateSigCount, SigVerification, which error if account doesn't exist yet + NewDeductDelegatedFeeDecorator(ak, supplyKeeper, dk), authAnte.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators authAnte.NewValidateSigCountDecorator(ak), - NewDeductDelegatedFeeDecorator(ak, supplyKeeper, dk), authAnte.NewSigGasConsumeDecorator(ak, sigGasConsumer), authAnte.NewSigVerificationDecorator(ak), authAnte.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator diff --git a/x/subkeys/internal/ante/fee.go b/x/subkeys/internal/ante/fee.go index be95b3c4fcc8..16f665eca0e3 100644 --- a/x/subkeys/internal/ante/fee.go +++ b/x/subkeys/internal/ante/fee.go @@ -79,6 +79,15 @@ func (d DeductDelegatedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simu return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "fee payer address: %s does not exist", feePayer) } + // if there was a valid delegation, ensure that the txSigner account exists (we create it if needed) + if !txSigner.Equals(feePayer) { + signerAcc := d.ak.GetAccount(ctx, txSigner) + if signerAcc == nil { + signerAcc = d.ak.NewAccountWithAddress(ctx, txSigner) + d.ak.SetAccount(ctx, signerAcc) + } + } + // deduct fee if non-zero if fee.IsZero() { return next(ctx, tx, simulate) From d293aae9161ca787a8fdb9079de4a1ee7bb664d4 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 18 Oct 2019 11:15:18 +0200 Subject: [PATCH 31/77] Pull mempool into delegated ante, for control on StdFee --- x/subkeys/internal/ante/ante.go | 2 +- x/subkeys/internal/ante/mempool.go | 55 +++++++++++++++++++++ x/subkeys/internal/ante/mempool_test.go | 64 +++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 x/subkeys/internal/ante/mempool.go create mode 100644 x/subkeys/internal/ante/mempool_test.go diff --git a/x/subkeys/internal/ante/ante.go b/x/subkeys/internal/ante/ante.go index 35560c713aee..2efe2365192e 100644 --- a/x/subkeys/internal/ante/ante.go +++ b/x/subkeys/internal/ante/ante.go @@ -13,7 +13,7 @@ import ( func NewAnteHandler(ak authKeeper.AccountKeeper, supplyKeeper authTypes.SupplyKeeper, dk keeper.Keeper, sigGasConsumer authAnte.SignatureVerificationGasConsumer) sdk.AnteHandler { return sdk.ChainAnteDecorators( authAnte.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first - authAnte.NewMempoolFeeDecorator(), + NewDelegatedMempoolFeeDecorator(), authAnte.NewValidateBasicDecorator(), authAnte.NewValidateMemoDecorator(ak), authAnte.NewConsumeGasForTxSizeDecorator(ak), diff --git a/x/subkeys/internal/ante/mempool.go b/x/subkeys/internal/ante/mempool.go new file mode 100644 index 000000000000..ccea3c47122f --- /dev/null +++ b/x/subkeys/internal/ante/mempool.go @@ -0,0 +1,55 @@ +package ante + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + // we depend on the auth module internals... maybe some more of this can be exported? + // but things like `x/auth/types/FeeCollectorName` are quite clearly tied to it + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// DelegatedMempoolFeeDecorator will check if the transaction's fee is at least as large +// as the local validator's minimum gasFee (defined in validator config). +// 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 DelegatedMempoolFeeDecorator +type DelegatedMempoolFeeDecorator struct{} + +func NewDelegatedMempoolFeeDecorator() DelegatedMempoolFeeDecorator { + return DelegatedMempoolFeeDecorator{} +} + +func (mfd DelegatedMempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + feeTx, ok := tx.(DelegatedFeeTx) + if !ok { + return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") + } + feeCoins := feeTx.GetFee() + gas := feeTx.GetGas() + + // Ensure that the provided fees meet a minimum threshold for the validator, + // if this is a CheckTx. This is only for local mempool purposes, and thus + // is only ran on check tx. + if ctx.IsCheckTx() && !simulate { + minGasPrices := ctx.MinGasPrices() + if !minGasPrices.IsZero() { + 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(int64(gas)) + for i, gp := range minGasPrices { + fee := gp.Amount.Mul(glDec) + requiredFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt()) + } + + if !feeCoins.IsAnyGTE(requiredFees) { + return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, requiredFees) + } + } + } + + return next(ctx, tx, simulate) +} diff --git a/x/subkeys/internal/ante/mempool_test.go b/x/subkeys/internal/ante/mempool_test.go new file mode 100644 index 000000000000..4b9c8b9608ec --- /dev/null +++ b/x/subkeys/internal/ante/mempool_test.go @@ -0,0 +1,64 @@ +package ante_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + // "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types" + "github.com/cosmos/cosmos-sdk/x/subkeys/internal/ante" +) + +func TestEnsureMempoolFees(t *testing.T) { + // setup + _, ctx := createTestApp(true) + + mfd := ante.NewDelegatedMempoolFeeDecorator() + antehandler := sdk.ChainAnteDecorators(mfd) + + // keys and addresses + priv1, _, addr1 := authtypes.KeyTestPubAddr() + + // msg and signatures + msg1 := authtypes.NewTestMsg(addr1) + fee := authtypes.NewStdFee(100000, sdk.NewCoins(sdk.NewInt64Coin("atom", 100))) + + msgs := []sdk.Msg{msg1} + + privs, accNums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} + // TODO + tx := authtypes.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) + + // Set high gas price so standard test fee fails + atomPrice := sdk.NewDecCoinFromDec("atom", sdk.NewDec(200).Quo(sdk.NewDec(100000))) + highGasPrice := []sdk.DecCoin{atomPrice} + ctx = ctx.WithMinGasPrices(highGasPrice) + + // Set IsCheckTx to true + ctx = ctx.WithIsCheckTx(true) + + // antehandler errors with insufficient fees + _, err := antehandler(ctx, tx, false) + require.NotNil(t, err, "Decorator should have errored on too low fee for local gasPrice") + + // Set IsCheckTx to false + ctx = ctx.WithIsCheckTx(false) + + // antehandler should not error since we do not check minGasPrice in DeliverTx + _, err = antehandler(ctx, tx, false) + require.Nil(t, err, "DelegatedMempoolFeeDecorator returned error in DeliverTx") + + // Set IsCheckTx back to true for testing sufficient mempool fee + ctx = ctx.WithIsCheckTx(true) + + atomPrice = sdk.NewDecCoinFromDec("atom", sdk.NewDec(0).Quo(sdk.NewDec(100000))) + lowGasPrice := []sdk.DecCoin{atomPrice} + ctx = ctx.WithMinGasPrices(lowGasPrice) + + _, err = antehandler(ctx, tx, false) + require.Nil(t, err, "Decorator should not have errored on fee higher than local gasPrice") +} From a8b3e3101d71845f7132afebd6d555020980b6aa Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 18 Oct 2019 11:39:17 +0200 Subject: [PATCH 32/77] Use custom DelegatedTx, DelegatedFee for subkeys --- x/subkeys/internal/ante/fee.go | 44 ++-- x/subkeys/internal/ante/fee_test.go | 7 +- x/subkeys/internal/ante/mempool.go | 2 +- x/subkeys/internal/ante/mempool_test.go | 7 +- x/subkeys/internal/types/tx/test_common.go | 26 +++ x/subkeys/internal/types/tx/tx.go | 241 +++++++++++++++++++++ 6 files changed, 295 insertions(+), 32 deletions(-) create mode 100644 x/subkeys/internal/types/tx/test_common.go create mode 100644 x/subkeys/internal/types/tx/tx.go diff --git a/x/subkeys/internal/ante/fee.go b/x/subkeys/internal/ante/fee.go index 16f665eca0e3..af502c7f24df 100644 --- a/x/subkeys/internal/ante/fee.go +++ b/x/subkeys/internal/ante/fee.go @@ -11,18 +11,22 @@ import ( authKeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authTypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/subkeys/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types/tx" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) var ( - _ DelegatedFeeTx = (*authTypes.StdTx)(nil) // assert StdTx implements DelegatedFeeTx + _ DelegatedFeeTx = (*tx.DelegatedTx)(nil) // assert StdTx implements DelegatedFeeTx ) // DelegatedFeeTx defines the interface to be implemented by Tx to use the DelegatedFeeDecorator type DelegatedFeeTx interface { - authAnte.FeeTx - GetFeeAccount() sdk.AccAddress + sdk.Tx + GetGas() uint64 + GetFee() sdk.Coins + FeePayer() sdk.AccAddress + MainSigner() sdk.AccAddress } // DeductDelegatedFeeDecorator deducts fees from the first signer of the tx @@ -44,9 +48,9 @@ func NewDeductDelegatedFeeDecorator(ak authKeeper.AccountKeeper, sk authTypes.Su } func (d DeductDelegatedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - feeTx, ok := tx.(authAnte.FeeTx) + feeTx, ok := tx.(DelegatedFeeTx) if !ok { - return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") + return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a DelegatedFeeTx") } // sanity check from DeductFeeDecorator @@ -54,23 +58,22 @@ func (d DeductDelegatedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simu panic(fmt.Sprintf("%s module account has not been set", authTypes.FeeCollectorName)) } - // see if there is a delegation fee := feeTx.GetFee() - var feePayer sdk.AccAddress - if delTx, ok := tx.(DelegatedFeeTx); ok { - feePayer = delTx.GetFeeAccount() - } + feePayer := feeTx.FeePayer() + txSigner := feeTx.MainSigner() - txSigner := feeTx.FeePayer() - if feePayer == nil { - // if this is not explicitly set, use the first signer as always - feePayer = txSigner - } else { - // ensure the delegation is allowed + // ensure the delegation is allowed, if we request a different fee payer + if !txSigner.Equals(feePayer) { allowed := d.dk.UseDelegatedFees(ctx, feePayer, txSigner, fee) if !allowed { return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s not allowed to pay fees from %s", txSigner, feePayer) } + // if there was a valid delegation, ensure that the txSigner account exists (we create it if needed) + signerAcc := d.ak.GetAccount(ctx, txSigner) + if signerAcc == nil { + signerAcc = d.ak.NewAccountWithAddress(ctx, txSigner) + d.ak.SetAccount(ctx, signerAcc) + } } // now, either way, we know that we are authorized to deduct the fees from the feePayer account @@ -79,15 +82,6 @@ func (d DeductDelegatedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simu return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "fee payer address: %s does not exist", feePayer) } - // if there was a valid delegation, ensure that the txSigner account exists (we create it if needed) - if !txSigner.Equals(feePayer) { - signerAcc := d.ak.GetAccount(ctx, txSigner) - if signerAcc == nil { - signerAcc = d.ak.NewAccountWithAddress(ctx, txSigner) - d.ak.SetAccount(ctx, signerAcc) - } - } - // deduct fee if non-zero if fee.IsZero() { return next(ctx, tx, simulate) diff --git a/x/subkeys/internal/ante/fee_test.go b/x/subkeys/internal/ante/fee_test.go index 82c57cfffa39..aa091760ad1b 100644 --- a/x/subkeys/internal/ante/fee_test.go +++ b/x/subkeys/internal/ante/fee_test.go @@ -13,7 +13,7 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/subkeys/internal/ante" "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types" - // delTypes "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types" + "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types/tx" ) func TestDeductFeesNoDelegation(t *testing.T) { @@ -221,10 +221,11 @@ func TestDeductFeesNoDelegation(t *testing.T) { for name, tc := range cases { t.Run(name, func(t *testing.T) { // msg and signatures - fee := authtypes.NewStdFee(100000, sdk.NewCoins(sdk.NewInt64Coin("atom", tc.fee))) + fee := tx.NewDelegatedFee(100000, sdk.NewCoins(sdk.NewInt64Coin("atom", tc.fee)), tc.feeAccount) msgs := []sdk.Msg{sdk.NewTestMsg(tc.signer)} privs, accNums, seqs := []crypto.PrivKey{tc.signerKey}, []uint64{0}, []uint64{0} - tx := authtypes.NewTestTxWithFeeAccount(ctx, msgs, privs, accNums, seqs, fee, tc.feeAccount) + + tx := tx.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) _, err := tc.handler(ctx, tx, false) if tc.valid { diff --git a/x/subkeys/internal/ante/mempool.go b/x/subkeys/internal/ante/mempool.go index ccea3c47122f..7beaa57542be 100644 --- a/x/subkeys/internal/ante/mempool.go +++ b/x/subkeys/internal/ante/mempool.go @@ -24,7 +24,7 @@ func NewDelegatedMempoolFeeDecorator() DelegatedMempoolFeeDecorator { func (mfd DelegatedMempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { feeTx, ok := tx.(DelegatedFeeTx) if !ok { - return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") + return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a DelegatedFeeTx") } feeCoins := feeTx.GetFee() gas := feeTx.GetGas() diff --git a/x/subkeys/internal/ante/mempool_test.go b/x/subkeys/internal/ante/mempool_test.go index 4b9c8b9608ec..347c00dd9215 100644 --- a/x/subkeys/internal/ante/mempool_test.go +++ b/x/subkeys/internal/ante/mempool_test.go @@ -9,8 +9,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - // "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types" "github.com/cosmos/cosmos-sdk/x/subkeys/internal/ante" + "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types/tx" ) func TestEnsureMempoolFees(t *testing.T) { @@ -25,13 +25,14 @@ func TestEnsureMempoolFees(t *testing.T) { // msg and signatures msg1 := authtypes.NewTestMsg(addr1) - fee := authtypes.NewStdFee(100000, sdk.NewCoins(sdk.NewInt64Coin("atom", 100))) + // TODO: try this with real delegation + fee := tx.NewDelegatedFee(100000, sdk.NewCoins(sdk.NewInt64Coin("atom", 100)), nil) msgs := []sdk.Msg{msg1} privs, accNums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} // TODO - tx := authtypes.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) + tx := tx.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) // Set high gas price so standard test fee fails atomPrice := sdk.NewDecCoinFromDec("atom", sdk.NewDec(200).Quo(sdk.NewDec(100000))) diff --git a/x/subkeys/internal/types/tx/test_common.go b/x/subkeys/internal/types/tx/test_common.go new file mode 100644 index 000000000000..cda6ae55e8ea --- /dev/null +++ b/x/subkeys/internal/types/tx/test_common.go @@ -0,0 +1,26 @@ +// noalias +package tx + +import ( + "github.com/tendermint/tendermint/crypto" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +func NewTestTx(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums []uint64, seqs []uint64, fee DelegatedFee) sdk.Tx { + sigs := make([]authtypes.StdSignature, len(privs)) + for i, priv := range privs { + signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, "") + + sig, err := priv.Sign(signBytes) + if err != nil { + panic(err) + } + + sigs[i] = authtypes.StdSignature{PubKey: priv.PubKey(), Signature: sig} + } + + tx := NewDelegatedTx(msgs, fee, sigs, "") + return tx +} diff --git a/x/subkeys/internal/types/tx/tx.go b/x/subkeys/internal/types/tx/tx.go new file mode 100644 index 000000000000..339983b7e08e --- /dev/null +++ b/x/subkeys/internal/types/tx/tx.go @@ -0,0 +1,241 @@ +package tx + +import ( + "encoding/json" + "fmt" + + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/multisig" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/exported" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +var ( + // _ sdk.Tx = (*DelegatedTx)(nil) + + maxGasWanted = uint64((1 << 63) - 1) +) + +// DelegatedTx is wraps a Msg with Fee and Signatures, +// adding the ability to delegate the fee payment +// NOTE: the first signature responsible for paying fees, either directly, +// or must be authorized to spend from the provided Fee.FeeAccount +type DelegatedTx struct { + Msgs []sdk.Msg `json:"msg" yaml:"msg"` + Fee DelegatedFee `json:"fee" yaml:"fee"` + Signatures []authtypes.StdSignature `json:"signatures" yaml:"signatures"` + Memo string `json:"memo" yaml:"memo"` + FeeAccount sdk.AccAddress `json:"fee_account" yaml:"fee_account"` +} + +func NewDelegatedTx(msgs []sdk.Msg, fee DelegatedFee, sigs []authtypes.StdSignature, memo string) DelegatedTx { + return DelegatedTx{ + Msgs: msgs, + Fee: fee, + Signatures: sigs, + Memo: memo, + } +} + +// GetMsgs returns the all the transaction's messages. +func (tx DelegatedTx) GetMsgs() []sdk.Msg { return tx.Msgs } + +// ValidateBasic does a simple and lightweight validation check that doesn't +// require access to any other information. +func (tx DelegatedTx) ValidateBasic() sdk.Error { + stdSigs := tx.GetSignatures() + + if tx.Fee.Gas > maxGasWanted { + return sdk.ErrGasOverflow(fmt.Sprintf("invalid gas supplied; %d > %d", tx.Fee.Gas, maxGasWanted)) + } + if tx.Fee.Amount.IsAnyNegative() { + return sdk.ErrInsufficientFee(fmt.Sprintf("invalid fee %s amount provided", tx.Fee.Amount)) + } + if len(stdSigs) == 0 { + return sdk.ErrNoSignatures("no signers") + } + if len(stdSigs) != len(tx.GetSigners()) { + return sdk.ErrUnauthorized("wrong number of signers") + } + + return nil +} + +// CountSubKeys counts the total number of keys for a multi-sig public key. +func CountSubKeys(pub crypto.PubKey) int { + v, ok := pub.(multisig.PubKeyMultisigThreshold) + if !ok { + return 1 + } + + numKeys := 0 + for _, subkey := range v.PubKeys { + numKeys += CountSubKeys(subkey) + } + + return numKeys +} + +// GetSigners returns the addresses that must sign the transaction. +// Addresses are returned in a deterministic order. +// They are accumulated from the GetSigners method for each Msg +// in the order they appear in tx.GetMsgs(). +// Duplicate addresses will be omitted. +func (tx DelegatedTx) GetSigners() []sdk.AccAddress { + seen := map[string]bool{} + var signers []sdk.AccAddress + for _, msg := range tx.GetMsgs() { + for _, addr := range msg.GetSigners() { + if !seen[addr.String()] { + signers = append(signers, addr) + seen[addr.String()] = true + } + } + } + return signers +} + +// GetMemo returns the memo +func (tx DelegatedTx) GetMemo() string { return tx.Memo } + +// GetSignatures returns the signature of signers who signed the Msg. +// CONTRACT: Length returned is same as length of +// pubkeys returned from MsgKeySigners, and the order +// matches. +// CONTRACT: If the signature is missing (ie the Msg is +// invalid), then the corresponding signature is +// .Empty(). +func (tx DelegatedTx) GetSignatures() [][]byte { + sigs := make([][]byte, len(tx.Signatures)) + for i, stdSig := range tx.Signatures { + sigs[i] = stdSig.Signature + } + return sigs +} + +// GetPubkeys returns the pubkeys of signers if the pubkey is included in the signature +// If pubkey is not included in the signature, then nil is in the slice instead +func (tx DelegatedTx) GetPubKeys() []crypto.PubKey { + pks := make([]crypto.PubKey, len(tx.Signatures)) + for i, stdSig := range tx.Signatures { + pks[i] = stdSig.PubKey + } + return pks +} + +// GetSignBytes returns the signBytes of the tx for a given signer +func (tx DelegatedTx) GetSignBytes(ctx sdk.Context, acc exported.Account) []byte { + genesis := ctx.BlockHeight() == 0 + chainID := ctx.ChainID() + var accNum uint64 + if !genesis { + accNum = acc.GetAccountNumber() + } + return StdSignBytes(chainID, accNum, acc.GetSequence(), tx.Fee, tx.Msgs, tx.Memo) +} + +// GetGas returns the Gas in DelegatedFee +func (tx DelegatedTx) GetGas() uint64 { return tx.Fee.Gas } + +// GetFee returns the FeeAmount in DelegatedFee +func (tx DelegatedTx) GetFee() sdk.Coins { return tx.Fee.Amount } + +// FeePayer returns the address that is responsible for paying fee +// This can be explicily set in DelegatedFee, or defaults to MainSigner +func (tx DelegatedTx) FeePayer() sdk.AccAddress { + if len(tx.Fee.FeeAccount) != 0 { + return tx.Fee.FeeAccount + } + return tx.MainSigner() +} + +// MainSigner returns the first signer of the tx, by default this +// account is responsible for fees, if not explicitly set. +func (tx DelegatedTx) MainSigner() sdk.AccAddress { + if len(tx.GetSigners()) != 0 { + return tx.GetSigners()[0] + } + return sdk.AccAddress{} +} + +// DelegatedFee includes the amount of coins paid in fees and the maximum +// gas to be used by the transaction. The ratio yields an effective "gasprice", +// which must be above some miminum to be accepted into the mempool. +type DelegatedFee struct { + Amount sdk.Coins `json:"amount" yaml:"amount"` + Gas uint64 `json:"gas" yaml:"gas"` + FeeAccount sdk.AccAddress `json:"fee_account,omitempty" yaml:"fee_account"` +} + +// NewDelegatedFee returns a new instance of DelegatedFee +func NewDelegatedFee(gas uint64, amount sdk.Coins, feeAccount sdk.AccAddress) DelegatedFee { + return DelegatedFee{ + Amount: amount, + Gas: gas, + FeeAccount: feeAccount, + } +} + +// Bytes for signing later +func (fee DelegatedFee) Bytes() []byte { + // normalize. XXX + // this is a sign of something ugly + // (in the lcd_test, client side its null, + // server side its []) + if len(fee.Amount) == 0 { + fee.Amount = sdk.NewCoins() + } + cdc := codec.New() + bz, err := cdc.MarshalJSON(fee) + if err != nil { + panic(err) + } + return bz +} + +// GasPrices returns the gas prices for a DelegatedFee. +// +// NOTE: The gas prices returned are not the true gas prices that were +// originally part of the submitted transaction because the fee is computed +// as fee = ceil(gasWanted * gasPrices). +func (fee DelegatedFee) GasPrices() sdk.DecCoins { + return sdk.NewDecCoins(fee.Amount).QuoDec(sdk.NewDec(int64(fee.Gas))) +} + +// DelegatedSignDoc is replay-prevention structure. +// It includes the result of msg.GetSignBytes(), +// as well as the ChainID (prevent cross chain replay) +// and the Sequence numbers for each signature (prevent +// inchain replay and enforce tx ordering per account). +type DelegatedSignDoc struct { + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + ChainID string `json:"chain_id" yaml:"chain_id"` + Fee json.RawMessage `json:"fee" yaml:"fee"` + Memo string `json:"memo" yaml:"memo"` + Msgs []json.RawMessage `json:"msgs" yaml:"msgs"` + Sequence uint64 `json:"sequence" yaml:"sequence"` +} + +// StdSignBytes returns the bytes to sign for a transaction. +func StdSignBytes(chainID string, accnum uint64, sequence uint64, fee DelegatedFee, msgs []sdk.Msg, memo string) []byte { + cdc := codec.New() + msgsBytes := make([]json.RawMessage, 0, len(msgs)) + for _, msg := range msgs { + msgsBytes = append(msgsBytes, json.RawMessage(msg.GetSignBytes())) + } + bz, err := cdc.MarshalJSON(DelegatedSignDoc{ + AccountNumber: accnum, + ChainID: chainID, + Fee: json.RawMessage(fee.Bytes()), + Memo: memo, + Msgs: msgsBytes, + Sequence: sequence, + }) + if err != nil { + panic(err) + } + return sdk.MustSortJSON(bz) +} From e939e989f8d4f39cb7b650ef145c7fb42652dea4 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 18 Oct 2019 11:42:22 +0200 Subject: [PATCH 33/77] Revert all changes to x/auth.StdTx --- simapp/test_helpers.go | 4 ++-- x/auth/ante/ante_test.go | 2 +- x/auth/client/cli/tx_multisign.go | 4 ++-- x/auth/client/cli/tx_sign.go | 2 +- x/auth/client/utils/rest.go | 4 ++-- x/auth/client/utils/tx.go | 2 +- x/auth/client/utils/tx_test.go | 8 ++------ x/auth/types/stdsignmsg.go | 15 +++++++-------- x/auth/types/stdtx.go | 18 +++++------------- x/auth/types/stdtx_test.go | 23 +++++++++++------------ x/auth/types/test_common.go | 27 +++++---------------------- x/auth/types/txbuilder.go | 21 ++++----------------- x/auth/types/txbuilder_test.go | 4 +--- x/distribution/client/cli/tx_test.go | 1 - x/genutil/types/genesis_state_test.go | 4 ++-- x/mock/app.go | 4 ++-- 16 files changed, 48 insertions(+), 95 deletions(-) diff --git a/simapp/test_helpers.go b/simapp/test_helpers.go index 9166d55e1b98..c108d9f9f5e1 100644 --- a/simapp/test_helpers.go +++ b/simapp/test_helpers.go @@ -120,7 +120,7 @@ func GenTx(msgs []sdk.Msg, accnums []uint64, seq []uint64, priv ...crypto.PrivKe for i, p := range priv { // use a empty chainID for ease of testing - sig, err := p.Sign(auth.StdSignBytes("", accnums[i], seq[i], fee, msgs, memo, nil)) + sig, err := p.Sign(auth.StdSignBytes("", accnums[i], seq[i], fee, msgs, memo)) if err != nil { panic(err) } @@ -131,7 +131,7 @@ func GenTx(msgs []sdk.Msg, accnums []uint64, seq []uint64, priv ...crypto.PrivKe } } - return auth.NewStdTx(msgs, fee, sigs, memo, nil) + return auth.NewStdTx(msgs, fee, sigs, memo) } // SignCheckDeliver checks a generated signed transaction and simulates a diff --git a/x/auth/ante/ante_test.go b/x/auth/ante/ante_test.go index 643feccf477b..1cbc7419c6c0 100644 --- a/x/auth/ante/ante_test.go +++ b/x/auth/ante/ante_test.go @@ -513,7 +513,7 @@ func TestAnteHandlerBadSignBytes(t *testing.T) { for _, cs := range cases { tx := types.NewTestTxWithSignBytes( msgs, privs, accnums, seqs, fee, - types.StdSignBytes(cs.chainID, cs.accnum, cs.seq, cs.fee, cs.msgs, "", nil), + types.StdSignBytes(cs.chainID, cs.accnum, cs.seq, cs.fee, cs.msgs, ""), "", ) checkInvalidTx(t, anteHandler, ctx, tx, false, cs.code) diff --git a/x/auth/client/cli/tx_multisign.go b/x/auth/client/cli/tx_multisign.go index ae020f7fcb54..e6d958a5d9cd 100644 --- a/x/auth/client/cli/tx_multisign.go +++ b/x/auth/client/cli/tx_multisign.go @@ -102,7 +102,7 @@ func makeMultiSignCmd(cdc *codec.Codec) func(cmd *cobra.Command, args []string) // Validate each signature sigBytes := types.StdSignBytes( txBldr.ChainID(), txBldr.AccountNumber(), txBldr.Sequence(), - stdTx.Fee, stdTx.GetMsgs(), stdTx.GetMemo(), stdTx.FeeAccount, + stdTx.Fee, stdTx.GetMsgs(), stdTx.GetMemo(), ) if ok := stdSig.PubKey.VerifyBytes(sigBytes, stdSig.Signature); !ok { return fmt.Errorf("couldn't verify signature") @@ -113,7 +113,7 @@ func makeMultiSignCmd(cdc *codec.Codec) func(cmd *cobra.Command, args []string) } newStdSig := types.StdSignature{Signature: cdc.MustMarshalBinaryBare(multisigSig), PubKey: multisigPub} - newTx := types.NewStdTx(stdTx.GetMsgs(), stdTx.Fee, []types.StdSignature{newStdSig}, stdTx.GetMemo(), stdTx.GetFeeAccount()) + newTx := types.NewStdTx(stdTx.GetMsgs(), stdTx.Fee, []types.StdSignature{newStdSig}, stdTx.GetMemo()) sigOnly := viper.GetBool(flagSigOnly) var json []byte diff --git a/x/auth/client/cli/tx_sign.go b/x/auth/client/cli/tx_sign.go index b5fb15031038..d4af880d3e91 100644 --- a/x/auth/client/cli/tx_sign.go +++ b/x/auth/client/cli/tx_sign.go @@ -230,7 +230,7 @@ func printAndValidateSigs( sigBytes := types.StdSignBytes( chainID, acc.GetAccountNumber(), acc.GetSequence(), - stdTx.Fee, stdTx.GetMsgs(), stdTx.GetMemo(), stdTx.GetFeeAccount(), + stdTx.Fee, stdTx.GetMsgs(), stdTx.GetMemo(), ) if ok := sig.VerifyBytes(sigBytes, sig.Signature); !ok { diff --git a/x/auth/client/utils/rest.go b/x/auth/client/utils/rest.go index dce53f67d8c3..9423fe629ee4 100644 --- a/x/auth/client/utils/rest.go +++ b/x/auth/client/utils/rest.go @@ -26,7 +26,7 @@ func WriteGenerateStdTxResponse(w http.ResponseWriter, cliCtx context.CLIContext txBldr := types.NewTxBuilder( GetTxEncoder(cliCtx.Codec), br.AccountNumber, br.Sequence, gas, gasAdj, - br.Simulate, br.ChainID, br.Memo, br.FeeAccount, br.Fees, br.GasPrices, + br.Simulate, br.ChainID, br.Memo, br.Fees, br.GasPrices, ) if br.Simulate || simAndExec { @@ -53,7 +53,7 @@ func WriteGenerateStdTxResponse(w http.ResponseWriter, cliCtx context.CLIContext return } - output, err := cliCtx.Codec.MarshalJSON(types.NewStdTx(stdMsg.Msgs, stdMsg.Fee, nil, stdMsg.Memo, stdMsg.FeeAccount)) + output, err := cliCtx.Codec.MarshalJSON(types.NewStdTx(stdMsg.Msgs, stdMsg.Fee, nil, stdMsg.Memo)) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return diff --git a/x/auth/client/utils/tx.go b/x/auth/client/utils/tx.go index a94b96717162..d2212a039ad4 100644 --- a/x/auth/client/utils/tx.go +++ b/x/auth/client/utils/tx.go @@ -349,7 +349,7 @@ func buildUnsignedStdTxOffline(txBldr authtypes.TxBuilder, cliCtx context.CLICon return stdTx, nil } - return authtypes.NewStdTx(stdSignMsg.Msgs, stdSignMsg.Fee, nil, stdSignMsg.Memo, stdSignMsg.FeeAccount), nil + return authtypes.NewStdTx(stdSignMsg.Msgs, stdSignMsg.Fee, nil, stdSignMsg.Memo), nil } func isTxSigner(user sdk.AccAddress, signers []sdk.AccAddress) bool { diff --git a/x/auth/client/utils/tx_test.go b/x/auth/client/utils/tx_test.go index e689d8690cca..cde10cc56029 100644 --- a/x/auth/client/utils/tx_test.go +++ b/x/auth/client/utils/tx_test.go @@ -99,9 +99,7 @@ func TestReadStdTxFromFile(t *testing.T) { // Build a test transaction fee := authtypes.NewStdFee(50000, sdk.Coins{sdk.NewInt64Coin("atom", 150)}) - feeAccount, err := sdk.AccAddressFromBech32("cosmos1zxcxurm8gwp43n4efqms6484gkdnnq763w03t6") - require.NoError(t, err) - stdTx := authtypes.NewStdTx([]sdk.Msg{}, fee, []authtypes.StdSignature{}, "foomemo", feeAccount) + stdTx := authtypes.NewStdTx([]sdk.Msg{}, fee, []authtypes.StdSignature{}, "foomemo") // Write it to the file encodedTx, _ := cdc.MarshalJSON(stdTx) @@ -112,13 +110,11 @@ func TestReadStdTxFromFile(t *testing.T) { decodedTx, err := ReadStdTxFromFile(cdc, jsonTxFile.Name()) require.NoError(t, err) require.Equal(t, decodedTx.Memo, "foomemo") - require.Equal(t, decodedTx.FeeAccount, feeAccount) - require.Equal(t, decodedTx.Fee, fee) } func compareEncoders(t *testing.T, expected sdk.TxEncoder, actual sdk.TxEncoder) { msgs := []sdk.Msg{sdk.NewTestMsg(addr)} - tx := authtypes.NewStdTx(msgs, authtypes.StdFee{}, []authtypes.StdSignature{}, "", nil) + tx := authtypes.NewStdTx(msgs, authtypes.StdFee{}, []authtypes.StdSignature{}, "") defaultEncoderBytes, err := expected(tx) require.NoError(t, err) diff --git a/x/auth/types/stdsignmsg.go b/x/auth/types/stdsignmsg.go index f27b00291fb8..e018dac40048 100644 --- a/x/auth/types/stdsignmsg.go +++ b/x/auth/types/stdsignmsg.go @@ -8,16 +8,15 @@ import ( // a Msg with the other requirements for a StdSignDoc before // it is signed. For use in the CLI. type StdSignMsg struct { - ChainID string `json:"chain_id" yaml:"chain_id"` - AccountNumber uint64 `json:"account_number" yaml:"account_number"` - Sequence uint64 `json:"sequence" yaml:"sequence"` - Fee StdFee `json:"fee" yaml:"fee"` - Msgs []sdk.Msg `json:"msgs" yaml:"msgs"` - Memo string `json:"memo" yaml:"memo"` - FeeAccount sdk.AccAddress `json:"fee_account" yaml:"fee_account"` + ChainID string `json:"chain_id" yaml:"chain_id"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` + Fee StdFee `json:"fee" yaml:"fee"` + Msgs []sdk.Msg `json:"msgs" yaml:"msgs"` + Memo string `json:"memo" yaml:"memo"` } // get message bytes func (msg StdSignMsg) Bytes() []byte { - return StdSignBytes(msg.ChainID, msg.AccountNumber, msg.Sequence, msg.Fee, msg.Msgs, msg.Memo, msg.FeeAccount) + return StdSignBytes(msg.ChainID, msg.AccountNumber, msg.Sequence, msg.Fee, msg.Msgs, msg.Memo) } diff --git a/x/auth/types/stdtx.go b/x/auth/types/stdtx.go index 713539d2ca2b..fa8b5d100d5f 100644 --- a/x/auth/types/stdtx.go +++ b/x/auth/types/stdtx.go @@ -26,16 +26,14 @@ type StdTx struct { Fee StdFee `json:"fee" yaml:"fee"` Signatures []StdSignature `json:"signatures" yaml:"signatures"` Memo string `json:"memo" yaml:"memo"` - FeeAccount sdk.AccAddress `json:"fee_account" yaml:"fee_account"` } -func NewStdTx(msgs []sdk.Msg, fee StdFee, sigs []StdSignature, memo string, feeAccount sdk.AccAddress) StdTx { +func NewStdTx(msgs []sdk.Msg, fee StdFee, sigs []StdSignature, memo string) StdTx { return StdTx{ Msgs: msgs, Fee: fee, Signatures: sigs, Memo: memo, - FeeAccount: feeAccount, } } @@ -100,9 +98,6 @@ func (tx StdTx) GetSigners() []sdk.AccAddress { // GetMemo returns the memo func (tx StdTx) GetMemo() string { return tx.Memo } -// GetFeeAccount returns the account paying the fees (often nil, default ot first signer) -func (tx StdTx) GetFeeAccount() sdk.AccAddress { return tx.FeeAccount } - // GetSignatures returns the signature of signers who signed the Msg. // CONTRACT: Length returned is same as length of // pubkeys returned from MsgKeySigners, and the order @@ -138,7 +133,7 @@ func (tx StdTx) GetSignBytes(ctx sdk.Context, acc exported.Account) []byte { } return StdSignBytes( - chainID, accNum, acc.GetSequence(), tx.Fee, tx.Msgs, tx.Memo, tx.FeeAccount, + chainID, accNum, acc.GetSequence(), tx.Fee, tx.Msgs, tx.Memo, ) } @@ -215,11 +210,10 @@ type StdSignDoc struct { Memo string `json:"memo" yaml:"memo"` Msgs []json.RawMessage `json:"msgs" yaml:"msgs"` Sequence uint64 `json:"sequence" yaml:"sequence"` - FeeAccount sdk.AccAddress `json:"fee_account" yaml:"fee_account"` } // StdSignBytes returns the bytes to sign for a transaction. -func StdSignBytes(chainID string, accnum uint64, sequence uint64, fee StdFee, msgs []sdk.Msg, memo string, feeAccount sdk.AccAddress) []byte { +func StdSignBytes(chainID string, accnum uint64, sequence uint64, fee StdFee, msgs []sdk.Msg, memo string) []byte { msgsBytes := make([]json.RawMessage, 0, len(msgs)) for _, msg := range msgs { msgsBytes = append(msgsBytes, json.RawMessage(msg.GetSignBytes())) @@ -231,7 +225,6 @@ func StdSignBytes(chainID string, accnum uint64, sequence uint64, fee StdFee, ms Memo: memo, Msgs: msgsBytes, Sequence: sequence, - FeeAccount: feeAccount, }) if err != nil { panic(err) @@ -241,9 +234,8 @@ func StdSignBytes(chainID string, accnum uint64, sequence uint64, fee StdFee, ms // StdSignature represents a sig type StdSignature struct { - // Pubkey is optional - crypto.PubKey `json:"pub_key" yaml:"pub_key"` - Signature []byte `json:"signature" yaml:"signature"` + crypto.PubKey `json:"pub_key" yaml:"pub_key"` // optional + Signature []byte `json:"signature" yaml:"signature"` } // DefaultTxDecoder logic for standard transaction decoding diff --git a/x/auth/types/stdtx_test.go b/x/auth/types/stdtx_test.go index c731b595cff2..375a15c49cc4 100644 --- a/x/auth/types/stdtx_test.go +++ b/x/auth/types/stdtx_test.go @@ -25,7 +25,7 @@ func TestStdTx(t *testing.T) { fee := NewTestStdFee() sigs := []StdSignature{} - tx := NewStdTx(msgs, fee, sigs, "", nil) + tx := NewStdTx(msgs, fee, sigs, "") require.Equal(t, msgs, tx.GetMsgs()) require.Equal(t, sigs, tx.Signatures) @@ -35,13 +35,12 @@ func TestStdTx(t *testing.T) { func TestStdSignBytes(t *testing.T) { type args struct { - chainID string - accnum uint64 - sequence uint64 - fee StdFee - msgs []sdk.Msg - memo string - feeAccount sdk.AccAddress + chainID string + accnum uint64 + sequence uint64 + fee StdFee + msgs []sdk.Msg + memo string } defaultFee := NewTestStdFee() tests := []struct { @@ -49,12 +48,12 @@ func TestStdSignBytes(t *testing.T) { want string }{ { - args{"1234", 3, 6, defaultFee, []sdk.Msg{sdk.NewTestMsg(addr)}, "memo", nil}, - fmt.Sprintf(`{"account_number":"3","chain_id":"1234","fee":{"amount":[{"amount":"150","denom":"atom"}],"gas":"100000"},"fee_account":"","memo":"memo","msgs":[["%s"]],"sequence":"6"}`, addr), + args{"1234", 3, 6, defaultFee, []sdk.Msg{sdk.NewTestMsg(addr)}, "memo"}, + fmt.Sprintf("{\"account_number\":\"3\",\"chain_id\":\"1234\",\"fee\":{\"amount\":[{\"amount\":\"150\",\"denom\":\"atom\"}],\"gas\":\"100000\"},\"memo\":\"memo\",\"msgs\":[[\"%s\"]],\"sequence\":\"6\"}", addr), }, } for i, tc := range tests { - got := string(StdSignBytes(tc.args.chainID, tc.args.accnum, tc.args.sequence, tc.args.fee, tc.args.msgs, tc.args.memo, tc.args.feeAccount)) + got := string(StdSignBytes(tc.args.chainID, tc.args.accnum, tc.args.sequence, tc.args.fee, tc.args.msgs, tc.args.memo)) require.Equal(t, tc.want, got, "Got unexpected result on test case i: %d", i) } } @@ -125,7 +124,7 @@ func TestDefaultTxEncoder(t *testing.T) { fee := NewTestStdFee() sigs := []StdSignature{} - tx := NewStdTx(msgs, fee, sigs, "", nil) + tx := NewStdTx(msgs, fee, sigs, "") cdcBytes, err := cdc.MarshalBinaryLengthPrefixed(tx) diff --git a/x/auth/types/test_common.go b/x/auth/types/test_common.go index 447ae77d1be3..9e2ba20e98d2 100644 --- a/x/auth/types/test_common.go +++ b/x/auth/types/test_common.go @@ -35,7 +35,7 @@ func KeyTestPubAddr() (crypto.PrivKey, crypto.PubKey, sdk.AccAddress) { func NewTestTx(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums []uint64, seqs []uint64, fee StdFee) sdk.Tx { sigs := make([]StdSignature, len(privs)) for i, priv := range privs { - signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, "", nil) + signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, "") sig, err := priv.Sign(signBytes) if err != nil { @@ -45,14 +45,14 @@ func NewTestTx(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: sig} } - tx := NewStdTx(msgs, fee, sigs, "", nil) + tx := NewStdTx(msgs, fee, sigs, "") return tx } func NewTestTxWithMemo(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums []uint64, seqs []uint64, fee StdFee, memo string) sdk.Tx { sigs := make([]StdSignature, len(privs)) for i, priv := range privs { - signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, memo, nil) + signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, memo) sig, err := priv.Sign(signBytes) if err != nil { @@ -62,24 +62,7 @@ func NewTestTxWithMemo(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: sig} } - tx := NewStdTx(msgs, fee, sigs, memo, nil) - return tx -} - -func NewTestTxWithFeeAccount(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums []uint64, seqs []uint64, fee StdFee, feeAccount sdk.AccAddress) sdk.Tx { - sigs := make([]StdSignature, len(privs)) - for i, priv := range privs { - signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, "", feeAccount) - - sig, err := priv.Sign(signBytes) - if err != nil { - panic(err) - } - - sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: sig} - } - - tx := NewStdTx(msgs, fee, sigs, "", feeAccount) + tx := NewStdTx(msgs, fee, sigs, memo) return tx } @@ -94,6 +77,6 @@ func NewTestTxWithSignBytes(msgs []sdk.Msg, privs []crypto.PrivKey, accNums []ui sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: sig} } - tx := NewStdTx(msgs, fee, sigs, memo, nil) + tx := NewStdTx(msgs, fee, sigs, memo) return tx } diff --git a/x/auth/types/txbuilder.go b/x/auth/types/txbuilder.go index d2852a98e564..7d60086a137d 100644 --- a/x/auth/types/txbuilder.go +++ b/x/auth/types/txbuilder.go @@ -24,7 +24,6 @@ type TxBuilder struct { simulateAndExecute bool chainID string memo string - feeAccount sdk.AccAddress fees sdk.Coins gasPrices sdk.DecCoins } @@ -32,7 +31,7 @@ type TxBuilder struct { // NewTxBuilder returns a new initialized TxBuilder. func NewTxBuilder( txEncoder sdk.TxEncoder, accNumber, seq, gas uint64, gasAdj float64, - simulateAndExecute bool, chainID, memo string, feeAccount sdk.AccAddress, fees sdk.Coins, gasPrices sdk.DecCoins, + simulateAndExecute bool, chainID, memo string, fees sdk.Coins, gasPrices sdk.DecCoins, ) TxBuilder { return TxBuilder{ @@ -45,7 +44,6 @@ func NewTxBuilder( simulateAndExecute: simulateAndExecute, chainID: chainID, memo: memo, - feeAccount: feeAccount, fees: fees, gasPrices: gasPrices, } @@ -103,9 +101,6 @@ func (bldr TxBuilder) ChainID() string { return bldr.chainID } // Memo returns the memo message func (bldr TxBuilder) Memo() string { return bldr.memo } -// FeeAccount returns an alternate account to pay fees -func (bldr *TxBuilder) FeeAccount() sdk.AccAddress { return bldr.feeAccount } - // Fees returns the fees for the transaction func (bldr TxBuilder) Fees() sdk.Coins { return bldr.fees } @@ -130,12 +125,6 @@ func (bldr TxBuilder) WithGas(gas uint64) TxBuilder { return bldr } -// WithFeeAccount returns a copy of the context with an updated FeeAccount. -func (bldr TxBuilder) WithFeeAccount(feeAccount sdk.AccAddress) TxBuilder { - bldr.feeAccount = feeAccount - return bldr -} - // WithFees returns a copy of the context with an updated fee. func (bldr TxBuilder) WithFees(fees string) TxBuilder { parsedFees, err := sdk.ParseCoins(fees) @@ -212,7 +201,6 @@ func (bldr TxBuilder) BuildSignMsg(msgs []sdk.Msg) (StdSignMsg, error) { AccountNumber: bldr.accountNumber, Sequence: bldr.sequence, Memo: bldr.memo, - FeeAccount: bldr.feeAccount, Msgs: msgs, Fee: NewStdFee(bldr.gas, fees), }, nil @@ -226,7 +214,7 @@ func (bldr TxBuilder) Sign(name, passphrase string, msg StdSignMsg) ([]byte, err return nil, err } - return bldr.txEncoder(NewStdTx(msg.Msgs, msg.Fee, []StdSignature{sig}, msg.Memo, msg.FeeAccount)) + return bldr.txEncoder(NewStdTx(msg.Msgs, msg.Fee, []StdSignature{sig}, msg.Memo)) } // BuildAndSign builds a single message to be signed, and signs a transaction @@ -250,7 +238,7 @@ func (bldr TxBuilder) BuildTxForSim(msgs []sdk.Msg) ([]byte, error) { // the ante handler will populate with a sentinel pubkey sigs := []StdSignature{{}} - return bldr.txEncoder(NewStdTx(signMsg.Msgs, signMsg.Fee, sigs, signMsg.Memo, signMsg.FeeAccount)) + return bldr.txEncoder(NewStdTx(signMsg.Msgs, signMsg.Fee, sigs, signMsg.Memo)) } // SignStdTx appends a signature to a StdTx and returns a copy of it. If append @@ -267,7 +255,6 @@ func (bldr TxBuilder) SignStdTx(name, passphrase string, stdTx StdTx, appendSig Fee: stdTx.Fee, Msgs: stdTx.GetMsgs(), Memo: stdTx.GetMemo(), - FeeAccount: stdTx.FeeAccount, }) if err != nil { return @@ -279,7 +266,7 @@ func (bldr TxBuilder) SignStdTx(name, passphrase string, stdTx StdTx, appendSig } else { sigs = append(sigs, stdSignature) } - signedStdTx = NewStdTx(stdTx.GetMsgs(), stdTx.Fee, sigs, stdTx.GetMemo(), bldr.feeAccount) + signedStdTx = NewStdTx(stdTx.GetMsgs(), stdTx.Fee, sigs, stdTx.GetMemo()) return } diff --git a/x/auth/types/txbuilder_test.go b/x/auth/types/txbuilder_test.go index 056b195201a4..c0a0e337a9ab 100644 --- a/x/auth/types/txbuilder_test.go +++ b/x/auth/types/txbuilder_test.go @@ -20,7 +20,6 @@ func TestTxBuilderBuild(t *testing.T) { SimulateGas bool ChainID string Memo string - FeeAccount sdk.AccAddress Fees sdk.Coins GasPrices sdk.DecCoins } @@ -137,8 +136,7 @@ func TestTxBuilderBuild(t *testing.T) { bldr := NewTxBuilder( tt.fields.TxEncoder, tt.fields.AccountNumber, tt.fields.Sequence, tt.fields.Gas, tt.fields.GasAdjustment, tt.fields.SimulateGas, - tt.fields.ChainID, tt.fields.Memo, tt.fields.FeeAccount, - tt.fields.Fees, tt.fields.GasPrices, + tt.fields.ChainID, tt.fields.Memo, tt.fields.Fees, tt.fields.GasPrices, ) got, err := bldr.BuildSignMsg(tt.msgs) require.Equal(t, tt.wantErr, (err != nil)) diff --git a/x/distribution/client/cli/tx_test.go b/x/distribution/client/cli/tx_test.go index 51ae03488d8b..af09d5445a89 100644 --- a/x/distribution/client/cli/tx_test.go +++ b/x/distribution/client/cli/tx_test.go @@ -24,7 +24,6 @@ func createFakeTxBuilder() auth.TxBuilder { false, "test_chain", "hello", - nil, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))), sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.NewDecWithPrec(10000, sdk.Precision))}, ) diff --git a/x/genutil/types/genesis_state_test.go b/x/genutil/types/genesis_state_test.go index 1f9ebf67a1e5..aed7cd7f311e 100644 --- a/x/genutil/types/genesis_state_test.go +++ b/x/genutil/types/genesis_state_test.go @@ -27,7 +27,7 @@ func TestValidateGenesisMultipleMessages(t *testing.T) { msg2 := stakingtypes.NewMsgCreateValidator(sdk.ValAddress(pk2.Address()), pk2, sdk.NewInt64Coin(sdk.DefaultBondDenom, 50), desc, comm, sdk.OneInt()) - genTxs := authtypes.NewStdTx([]sdk.Msg{msg1, msg2}, authtypes.StdFee{}, nil, "", nil) + genTxs := authtypes.NewStdTx([]sdk.Msg{msg1, msg2}, authtypes.StdFee{}, nil, "") genesisState := NewGenesisStateFromStdTx([]authtypes.StdTx{genTxs}) err := ValidateGenesis(genesisState) @@ -39,7 +39,7 @@ func TestValidateGenesisBadMessage(t *testing.T) { msg1 := stakingtypes.NewMsgEditValidator(sdk.ValAddress(pk1.Address()), desc, nil, nil) - genTxs := authtypes.NewStdTx([]sdk.Msg{msg1}, authtypes.StdFee{}, nil, "", nil) + genTxs := authtypes.NewStdTx([]sdk.Msg{msg1}, authtypes.StdFee{}, nil, "") genesisState := NewGenesisStateFromStdTx([]authtypes.StdTx{genTxs}) err := ValidateGenesis(genesisState) diff --git a/x/mock/app.go b/x/mock/app.go index 04e4230ee411..1e59e050507b 100644 --- a/x/mock/app.go +++ b/x/mock/app.go @@ -217,7 +217,7 @@ func GenTx(msgs []sdk.Msg, accnums []uint64, seq []uint64, priv ...crypto.PrivKe memo := "testmemotestmemo" for i, p := range priv { - sig, err := p.Sign(auth.StdSignBytes(chainID, accnums[i], seq[i], fee, msgs, memo, nil)) + sig, err := p.Sign(auth.StdSignBytes(chainID, accnums[i], seq[i], fee, msgs, memo)) if err != nil { panic(err) } @@ -228,7 +228,7 @@ func GenTx(msgs []sdk.Msg, accnums []uint64, seq []uint64, priv ...crypto.PrivKe } } - return auth.NewStdTx(msgs, fee, sigs, memo, nil) + return auth.NewStdTx(msgs, fee, sigs, memo) } // GeneratePrivKeys generates a total n secp256k1 private keys. From 6aef5c88b26b22eeac95486b8d5274a37d775138 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 18 Oct 2019 11:46:55 +0200 Subject: [PATCH 34/77] Appease scopelint --- simapp/app.go | 2 +- x/subkeys/internal/ante/fee_test.go | 3 ++- x/subkeys/internal/types/basic_fee_test.go | 3 ++- x/subkeys/internal/types/expiration_test.go | 9 ++++++--- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/simapp/app.go b/simapp/app.go index 118cda2d0195..db7f52fa715e 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -18,7 +18,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth/vesting" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/crisis" - "github.com/cosmos/cosmos-sdk/x/subkeys" distr "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/genutil" "github.com/cosmos/cosmos-sdk/x/gov" @@ -27,6 +26,7 @@ import ( paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/subkeys" "github.com/cosmos/cosmos-sdk/x/supply" ) diff --git a/x/subkeys/internal/ante/fee_test.go b/x/subkeys/internal/ante/fee_test.go index aa091760ad1b..de22d3aeee9a 100644 --- a/x/subkeys/internal/ante/fee_test.go +++ b/x/subkeys/internal/ante/fee_test.go @@ -218,7 +218,8 @@ func TestDeductFeesNoDelegation(t *testing.T) { }, } - for name, tc := range cases { + for name, stc := range cases { + tc := stc // to make scopelint happy t.Run(name, func(t *testing.T) { // msg and signatures fee := tx.NewDelegatedFee(100000, sdk.NewCoins(sdk.NewInt64Coin("atom", tc.fee)), tc.feeAccount) diff --git a/x/subkeys/internal/types/basic_fee_test.go b/x/subkeys/internal/types/basic_fee_test.go index 857153d05cc7..71e640166a90 100644 --- a/x/subkeys/internal/types/basic_fee_test.go +++ b/x/subkeys/internal/types/basic_fee_test.go @@ -82,7 +82,8 @@ func TestBasicFeeValidAllow(t *testing.T) { }, } - for name, tc := range cases { + for name, stc := range cases { + tc := stc // to make scopelint happy t.Run(name, func(t *testing.T) { err := tc.allow.ValidateBasic() if !tc.valid { diff --git a/x/subkeys/internal/types/expiration_test.go b/x/subkeys/internal/types/expiration_test.go index 5b032d204a08..0c154e2bc30f 100644 --- a/x/subkeys/internal/types/expiration_test.go +++ b/x/subkeys/internal/types/expiration_test.go @@ -48,7 +48,8 @@ func TestExpiresAt(t *testing.T) { }, } - for name, tc := range cases { + for name, stc := range cases { + tc := stc // to make scopelint happy t.Run(name, func(t *testing.T) { err := tc.example.ValidateBasic() assert.Equal(t, tc.zero, tc.example.IsZero()) @@ -107,7 +108,8 @@ func TestPeriodValid(t *testing.T) { }, } - for name, tc := range cases { + for name, stc := range cases { + tc := stc // to make scopelint happy t.Run(name, func(t *testing.T) { err := tc.period.ValidateBasic() if !tc.valid { @@ -150,7 +152,8 @@ func TestPeriodStep(t *testing.T) { }, } - for name, tc := range cases { + for name, stc := range cases { + tc := stc // to make scopelint happy t.Run(name, func(t *testing.T) { err := tc.period.ValidateBasic() require.NoError(t, err) From 121277295dca98d9881e8e482c5c13b5cc3e2d88 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 18 Oct 2019 11:56:02 +0200 Subject: [PATCH 35/77] Register DelegatedTx with codec --- x/subkeys/internal/types/tx/codec.go | 8 ++++++++ x/subkeys/internal/types/tx/tx.go | 2 +- x/subkeys/module.go | 6 +++--- 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 x/subkeys/internal/types/tx/codec.go diff --git a/x/subkeys/internal/types/tx/codec.go b/x/subkeys/internal/types/tx/codec.go new file mode 100644 index 000000000000..90baa93cf075 --- /dev/null +++ b/x/subkeys/internal/types/tx/codec.go @@ -0,0 +1,8 @@ +package tx + +import "github.com/cosmos/cosmos-sdk/codec" + +// RegisterCodec registers concrete types on the codec +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterConcrete(DelegatedTx{}, "cosmos-sdk/DelegatedTx", nil) +} diff --git a/x/subkeys/internal/types/tx/tx.go b/x/subkeys/internal/types/tx/tx.go index 339983b7e08e..a9a999b32f48 100644 --- a/x/subkeys/internal/types/tx/tx.go +++ b/x/subkeys/internal/types/tx/tx.go @@ -14,7 +14,7 @@ import ( ) var ( - // _ sdk.Tx = (*DelegatedTx)(nil) + _ sdk.Tx = DelegatedTx{} maxGasWanted = uint64((1 << 63) - 1) ) diff --git a/x/subkeys/module.go b/x/subkeys/module.go index b0b32332ea77..0364ee4a4549 100644 --- a/x/subkeys/module.go +++ b/x/subkeys/module.go @@ -12,13 +12,12 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + tx "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types/tx" ) // TODO: -// * cli -// * rest +// * docs // * periodic fee -// -> change StdFee instead of StdTx, etc? var ( _ module.AppModule = AppModule{} @@ -36,6 +35,7 @@ func (AppModuleBasic) Name() string { // RegisterCodec registers the delegation module's types for the given codec. func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { RegisterCodec(cdc) + tx.RegisterCodec(cdc) } // DefaultGenesis returns default genesis state as raw bytes for the delegation From 61df0c694337df04bed306ee8a687ff08488d662 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 21 Oct 2019 10:42:37 +0200 Subject: [PATCH 36/77] Address PR comments --- types/errors.go | 4 +- types/rest/rest.go | 21 +++---- x/subkeys/README.md | 121 ------------------------------------- x/subkeys/doc.go | 17 +++--- x/subkeys/exported/fees.go | 9 ++- x/subkeys/handler.go | 1 + 6 files changed, 28 insertions(+), 145 deletions(-) delete mode 100644 x/subkeys/README.md diff --git a/types/errors.go b/types/errors.go index d23d2e9bb322..bb8c98c54035 100644 --- a/types/errors.go +++ b/types/errors.go @@ -315,8 +315,8 @@ func ConvertError(err error) Error { if err == nil { return nil } - if sdk, ok := err.(Error); ok { - return sdk + if sdkError, ok := err.(Error); ok { + return sdkError } space, code, log := sdkerrors.ABCIInfo(err, false) return NewError(CodespaceType(space), CodeType(code), log) diff --git a/types/rest/rest.go b/types/rest/rest.go index 7a9048522177..89770c944650 100644 --- a/types/rest/rest.go +++ b/types/rest/rest.go @@ -47,17 +47,16 @@ type GasEstimateResponse struct { // BaseReq defines a structure that can be embedded in other request structures // that all share common "base" fields. type BaseReq struct { - From string `json:"from"` - Memo string `json:"memo"` - ChainID string `json:"chain_id"` - AccountNumber uint64 `json:"account_number"` - Sequence uint64 `json:"sequence"` - Fees sdk.Coins `json:"fees"` - FeeAccount sdk.AccAddress `json:"fee_account"` - GasPrices sdk.DecCoins `json:"gas_prices"` - Gas string `json:"gas"` - GasAdjustment string `json:"gas_adjustment"` - Simulate bool `json:"simulate"` + From string `json:"from"` + Memo string `json:"memo"` + ChainID string `json:"chain_id"` + AccountNumber uint64 `json:"account_number"` + Sequence uint64 `json:"sequence"` + Fees sdk.Coins `json:"fees"` + GasPrices sdk.DecCoins `json:"gas_prices"` + Gas string `json:"gas"` + GasAdjustment string `json:"gas_adjustment"` + Simulate bool `json:"simulate"` } // NewBaseReq creates a new basic request instance and sanitizes its values diff --git a/x/subkeys/README.md b/x/subkeys/README.md deleted file mode 100644 index 6a1cb65ca3e3..000000000000 --- a/x/subkeys/README.md +++ /dev/null @@ -1,121 +0,0 @@ -## Fee Delegation Work - -Much of this is ported from an older implementation of fee delegation from aaronc. - -To take a look at this (which had many changes to auth as well, which has been heavily refactored upstream): - -``` -git remote add keys https://github.com/cosmos-cg-key-management/cosmos-sdk.git -git fetch keys -git diff --compact-summary f1b08b85f 980d713f -``` - -This is based on https://gist.github.com/aaronc/b60628017352df5983791cad30babe56#fee-delegation - -In particular the following parts: - --------------------- - -The `delegation` module also allows for fee delegation via some -changes to the `AnteHandler` and `StdTx`. The behavior is similar -to that described above for `Msg` delegations except using -the interface `FeeAllowance` instead of `Capability`: - -```go -// FeeAllowance defines a permission for one account to use another account's balance -// to pay fees -type FeeAllowance interface { - // Accept checks whether this allowance allows the provided fees to be spent, - // and optionally updates the allowance or deletes it entirely - Accept(fee sdk.Coins, block abci.Header) (allow bool, updated FeeAllowance, delete bool) -} -``` - -An example `FeeAllowance` could be created that simply sets a `SpendLimit`: - -```go -type BasicFeeAllowance struct { - // SpendLimit specifies the maximum amount of tokens that can be spent - // by this capability and will be updated as tokens are spent. If it is - // empty, there is no spend limit and any amount of coins can be spent. - SpendLimit sdk.Coins -} - -func (cap BasicFeeAllowance) Accept(fee sdk.Coins, block abci.Header) (allow bool, updated FeeAllowance, delete bool) { - left, invalid := cap.SpendLimit.SafeSub(fee) - if invalid { - return false, nil, false - } - if left.IsZero() { - return true, nil, true - } - return true, BasicFeeAllowance{SpendLimit: left}, false -} - -``` - -Other `FeeAllowance` types could be created such as a daily spend limit. - -## `StdTx` and `AnteHandler` changes - -In order to support delegated fees `StdTx` and the `AnteHandler` needed to be changed. - -The field `FeeAccount` was added to `StdTx`. - -```go -type StdTx struct { - Msgs []sdk.Msg `json:"msg"` - Fee StdFee `json:"fee"` - Signatures []StdSignature `json:"signatures"` - Memo string `json:"memo"` - // FeeAccount is an optional account that fees can be spent from if such - // delegation is enabled - FeeAccount sdk.AccAddress `json:"fee_account"` -} -``` - -An interface `FeeDelegationHandler` (which is implemented by the `delegation` module) was created and a parameter for it was added to the default `AnteHandler`: - -```go -type FeeDelegationHandler interface { - // AllowDelegatedFees checks if the grantee can use the granter's account to spend the specified fees, updating - // any fee allowance in accordance with the provided fees - AllowDelegatedFees(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, fee sdk.Coins) bool -} - -// NewAnteHandler returns an AnteHandler that checks and increments sequence -// numbers, checks signatures & account numbers, and deducts fees from the first -// signer. -func NewAnteHandler(ak AccountKeeper, fck FeeCollectionKeeper, feeDelegationHandler FeeDelegationHandler, sigGasConsumer SignatureVerificationGasConsumer) sdk.AnteHandler { -``` - -Basically if someone sets `FeeAccount` on `StdTx`, the `AnteHandler` will call into the `delegation` module via its `FeeDelegationHandler` and check if the tx's fees have been delegated by that `FeeAccount` to the key actually signing the transaction. - -## Core `FeeAllowance` types - -```go -type BasicFeeAllowance struct { - // SpendLimit specifies the maximum amount of tokens that can be spent - // by this capability and will be updated as tokens are spent. If it is - // empty, there is no spend limit and any amount of coins can be spent. - SpendLimit sdk.Coins - // Expiration specifies an optional time when this allowance expires - Expiration time.Time -} - -type PeriodicFeeAllowance struct { - BasicFeeAllowance - // Period specifies the time duration in which PeriodSpendLimit coins can - // be spent before that allowance is reset - Period time.Duration - // PeriodSpendLimit specifies the maximum number of coins that can be spent - // in the Period - PeriodSpendLimit sdk.Coins - // PeriodCanSpend is the number of coins left to be spend before the PeriodReset time - PeriodCanSpend sdk.Coins - // PeriodReset is the time at which this period resets and a new one begins, - // it is calculated from the start time of the first transaction after the - // last period ended - PeriodReset time.Time -} -``` \ No newline at end of file diff --git a/x/subkeys/doc.go b/x/subkeys/doc.go index d21ec229452e..b644d60de59d 100644 --- a/x/subkeys/doc.go +++ b/x/subkeys/doc.go @@ -14,19 +14,20 @@ A user would delegate fees to a user using MsgDelegateFeeAllowance and revoke that delegation using MsgRevokeFeeAllowance. In both cases Granter is the one who is delegating fees and Grantee is the one who is receiving the delegation. So grantee would correspond to the one who is signing a transaction and the -granter would be the address they place in StdTx.FeeAccount. +granter would be the address they place in DelegatedFee.FeeAccount. The fee allowance that a grantee receives is specified by an implementation of the FeeAllowance interface. Two FeeAllowance implementations are provided in this package: BasicFeeAllowance and PeriodicFeeAllowance. -TODO: update if needed with new modular ante handler +In order to integrate this into an application, we must use the ante handles from +this package instead of the default auth implementations for DeductFee +(adding custom logic) and MempoolFee (updating it to be compatible with DelegatedTx). +An application can do this simply by using `x/delegate_fees/internal/ante.NewAnteHandler()` +when setting up the app instead of the version from auth. -In order to integrate this into an application, the "ante handler" which deducts -fees must call Keeper.AllowDelegatedFees to check if -the provided StdTx.Fee can be delegated from the Std.TxFeeAccount address -to the first signer of the transaction. An example usage would be: - -allow := feeDelegationKeeper.AllowDelegatedFees(ctx, signers[0], stdTx.FeeAccount, stdTx.Fee.Amount) +I did not pull this into the top level package as it pulls in dependencies on the internals +of `x/auth` and if I understand correctly, this is bad practice to depend on other modules +except for the external package. */ package subkeys diff --git a/x/subkeys/exported/fees.go b/x/subkeys/exported/fees.go index a9c57de6c823..9d87600c4b3d 100644 --- a/x/subkeys/exported/fees.go +++ b/x/subkeys/exported/fees.go @@ -7,15 +7,18 @@ import ( ) // FeeAllowance implementations are tied to a given delegator and delegatee, -// and are used to enforce limits on this payment. +// and are used to enforce fee delegation limits. type FeeAllowance interface { // Accept can use fee payment requested as well as timestamp/height of the current block - // to determine whether or not to process this. + // to determine whether or not to process this. This is checked in + // Keeper.UseDelegatedFees and the return values should match how it is handled there. + // // If it returns an error, the fee payment is rejected, otherwise it is accepted. // The FeeAllowance implementation is expected to update it's internal state // and will be saved again after an acceptance. + // // If remove is true (regardless of the error), the FeeAllowance will be deleted from storage - // (eg. when it expires) + // (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseDelegatedFees) Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (remove bool, err error) // ValidateBasic should evaluate this FeeAllowance for internal consistency. diff --git a/x/subkeys/handler.go b/x/subkeys/handler.go index a2438a542cf7..6093cc93286f 100644 --- a/x/subkeys/handler.go +++ b/x/subkeys/handler.go @@ -8,6 +8,7 @@ import ( func NewHandler(k Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + ctx = ctx.WithEventManager(sdk.NewEventManager()) switch msg := msg.(type) { case MsgDelegateFeeAllowance: grant := FeeAllowanceGrant(msg) From d684f92188689b6f590bf3c9a2432070adf2ab96 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 21 Oct 2019 10:46:42 +0200 Subject: [PATCH 37/77] Remove unnecessary DelegatedMempoolFeeDecorator --- x/subkeys/internal/ante/ante.go | 2 +- x/subkeys/internal/ante/mempool.go | 55 --------------------- x/subkeys/internal/ante/mempool_test.go | 65 ------------------------- 3 files changed, 1 insertion(+), 121 deletions(-) delete mode 100644 x/subkeys/internal/ante/mempool.go delete mode 100644 x/subkeys/internal/ante/mempool_test.go diff --git a/x/subkeys/internal/ante/ante.go b/x/subkeys/internal/ante/ante.go index 2efe2365192e..35560c713aee 100644 --- a/x/subkeys/internal/ante/ante.go +++ b/x/subkeys/internal/ante/ante.go @@ -13,7 +13,7 @@ import ( func NewAnteHandler(ak authKeeper.AccountKeeper, supplyKeeper authTypes.SupplyKeeper, dk keeper.Keeper, sigGasConsumer authAnte.SignatureVerificationGasConsumer) sdk.AnteHandler { return sdk.ChainAnteDecorators( authAnte.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first - NewDelegatedMempoolFeeDecorator(), + authAnte.NewMempoolFeeDecorator(), authAnte.NewValidateBasicDecorator(), authAnte.NewValidateMemoDecorator(ak), authAnte.NewConsumeGasForTxSizeDecorator(ak), diff --git a/x/subkeys/internal/ante/mempool.go b/x/subkeys/internal/ante/mempool.go deleted file mode 100644 index 7beaa57542be..000000000000 --- a/x/subkeys/internal/ante/mempool.go +++ /dev/null @@ -1,55 +0,0 @@ -package ante - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - - // we depend on the auth module internals... maybe some more of this can be exported? - // but things like `x/auth/types/FeeCollectorName` are quite clearly tied to it - - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -// DelegatedMempoolFeeDecorator will check if the transaction's fee is at least as large -// as the local validator's minimum gasFee (defined in validator config). -// 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 DelegatedMempoolFeeDecorator -type DelegatedMempoolFeeDecorator struct{} - -func NewDelegatedMempoolFeeDecorator() DelegatedMempoolFeeDecorator { - return DelegatedMempoolFeeDecorator{} -} - -func (mfd DelegatedMempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - feeTx, ok := tx.(DelegatedFeeTx) - if !ok { - return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a DelegatedFeeTx") - } - feeCoins := feeTx.GetFee() - gas := feeTx.GetGas() - - // Ensure that the provided fees meet a minimum threshold for the validator, - // if this is a CheckTx. This is only for local mempool purposes, and thus - // is only ran on check tx. - if ctx.IsCheckTx() && !simulate { - minGasPrices := ctx.MinGasPrices() - if !minGasPrices.IsZero() { - 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(int64(gas)) - for i, gp := range minGasPrices { - fee := gp.Amount.Mul(glDec) - requiredFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt()) - } - - if !feeCoins.IsAnyGTE(requiredFees) { - return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, requiredFees) - } - } - } - - return next(ctx, tx, simulate) -} diff --git a/x/subkeys/internal/ante/mempool_test.go b/x/subkeys/internal/ante/mempool_test.go deleted file mode 100644 index 347c00dd9215..000000000000 --- a/x/subkeys/internal/ante/mempool_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package ante_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" - - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - - "github.com/cosmos/cosmos-sdk/x/subkeys/internal/ante" - "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types/tx" -) - -func TestEnsureMempoolFees(t *testing.T) { - // setup - _, ctx := createTestApp(true) - - mfd := ante.NewDelegatedMempoolFeeDecorator() - antehandler := sdk.ChainAnteDecorators(mfd) - - // keys and addresses - priv1, _, addr1 := authtypes.KeyTestPubAddr() - - // msg and signatures - msg1 := authtypes.NewTestMsg(addr1) - // TODO: try this with real delegation - fee := tx.NewDelegatedFee(100000, sdk.NewCoins(sdk.NewInt64Coin("atom", 100)), nil) - - msgs := []sdk.Msg{msg1} - - privs, accNums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} - // TODO - tx := tx.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) - - // Set high gas price so standard test fee fails - atomPrice := sdk.NewDecCoinFromDec("atom", sdk.NewDec(200).Quo(sdk.NewDec(100000))) - highGasPrice := []sdk.DecCoin{atomPrice} - ctx = ctx.WithMinGasPrices(highGasPrice) - - // Set IsCheckTx to true - ctx = ctx.WithIsCheckTx(true) - - // antehandler errors with insufficient fees - _, err := antehandler(ctx, tx, false) - require.NotNil(t, err, "Decorator should have errored on too low fee for local gasPrice") - - // Set IsCheckTx to false - ctx = ctx.WithIsCheckTx(false) - - // antehandler should not error since we do not check minGasPrice in DeliverTx - _, err = antehandler(ctx, tx, false) - require.Nil(t, err, "DelegatedMempoolFeeDecorator returned error in DeliverTx") - - // Set IsCheckTx back to true for testing sufficient mempool fee - ctx = ctx.WithIsCheckTx(true) - - atomPrice = sdk.NewDecCoinFromDec("atom", sdk.NewDec(0).Quo(sdk.NewDec(100000))) - lowGasPrice := []sdk.DecCoin{atomPrice} - ctx = ctx.WithMinGasPrices(lowGasPrice) - - _, err = antehandler(ctx, tx, false) - require.Nil(t, err, "Decorator should not have errored on fee higher than local gasPrice") -} From aacf6fba1ac234f12982612290a12e0f0f7e29d7 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 21 Oct 2019 11:05:11 +0200 Subject: [PATCH 38/77] Cleaned up errors in querier --- x/subkeys/internal/keeper/keeper.go | 13 +++++-------- x/subkeys/internal/keeper/keeper_test.go | 2 +- x/subkeys/internal/keeper/querier.go | 18 ++++++++++++------ 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/x/subkeys/internal/keeper/keeper.go b/x/subkeys/internal/keeper/keeper.go index 272d88296fe4..dec90eba6448 100644 --- a/x/subkeys/internal/keeper/keeper.go +++ b/x/subkeys/internal/keeper/keeper.go @@ -31,7 +31,7 @@ func (k Keeper) DelegateFeeAllowance(ctx sdk.Context, grant types.FeeAllowanceGr } // RevokeFeeAllowance removes an existing grant -func (k Keeper) RevokeFeeAllowance(ctx sdk.Context, granter sdk.AccAddress, grantee sdk.AccAddress) { +func (k Keeper) RevokeFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) { store := ctx.KVStore(k.storeKey) key := types.FeeAllowanceKey(granter, grantee) store.Delete(key) @@ -40,7 +40,7 @@ func (k Keeper) RevokeFeeAllowance(ctx sdk.Context, granter sdk.AccAddress, gran // GetFeeAllowance returns the allowance between the granter and grantee. // If there is none, it returns nil, nil. // Returns an error on parsing issues -func (k Keeper) GetFeeAllowance(ctx sdk.Context, granter sdk.AccAddress, grantee sdk.AccAddress) (exported.FeeAllowance, error) { +func (k Keeper) GetFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) (exported.FeeAllowance, error) { grant, err := k.GetFeeGrant(ctx, granter, grantee) if grant == nil { return nil, err @@ -65,8 +65,8 @@ func (k Keeper) GetFeeGrant(ctx sdk.Context, granter sdk.AccAddress, grantee sdk return &grant, nil } -// GetAllMyFeeAllowances returns a list of all the grants from anyone to the given grantee. -func (k Keeper) GetAllMyFeeAllowances(ctx sdk.Context, grantee sdk.AccAddress) ([]types.FeeAllowanceGrant, error) { +// GetAllGranteeFeeAllowances returns a list of all the grants from anyone to the given grantee. +func (k Keeper) GetAllGranteeFeeAllowances(ctx sdk.Context, grantee sdk.AccAddress) ([]types.FeeAllowanceGrant, error) { store := ctx.KVStore(k.storeKey) var grants []types.FeeAllowanceGrant @@ -106,10 +106,7 @@ func (k Keeper) GetAllFeeAllowances(ctx sdk.Context) ([]types.FeeAllowanceGrant, } // UseDelegatedFees will try to pay the given fee from the granter's account as requested by the grantee -// (true, nil) will update the allowance, and assumes the AnteHandler deducts the given fees -// (false, nil) rejects payment on behalf of grantee -// (?, err) means there was a data parsing error (abort tx and log this info) -func (k Keeper) UseDelegatedFees(ctx sdk.Context, granter sdk.AccAddress, grantee sdk.AccAddress, fee sdk.Coins) bool { +func (k Keeper) UseDelegatedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins) bool { grant, err := k.GetFeeGrant(ctx, granter, grantee) if err != nil { // we should acknowledge a db issue somehow (better?) diff --git a/x/subkeys/internal/keeper/keeper_test.go b/x/subkeys/internal/keeper/keeper_test.go index 1f923c866a2c..159005bb3908 100644 --- a/x/subkeys/internal/keeper/keeper_test.go +++ b/x/subkeys/internal/keeper/keeper_test.go @@ -176,7 +176,7 @@ func TestKeeperCrud(t *testing.T) { for name, tc := range allCases { t.Run(name, func(t *testing.T) { - grants, err := k.GetAllMyFeeAllowances(ctx, tc.grantee) + grants, err := k.GetAllGranteeFeeAllowances(ctx, tc.grantee) require.NoError(t, err) assert.Equal(t, tc.grants, grants) }) diff --git a/x/subkeys/internal/keeper/querier.go b/x/subkeys/internal/keeper/querier.go index 4728c3e2bf2c..33c862ef48c2 100644 --- a/x/subkeys/internal/keeper/querier.go +++ b/x/subkeys/internal/keeper/querier.go @@ -2,6 +2,8 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types" abci "github.com/tendermint/tendermint/abci/types" ) @@ -12,23 +14,27 @@ const ( // NewQuerier creates a new querier func NewQuerier(keeper Keeper) sdk.Querier { return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, sdk.Error) { + var res []byte + var err error switch path[0] { case QueryGetFeeAllowances: - return queryGetFeeAllowances(ctx, path[1:], keeper) + res, err = queryGetFeeAllowances(ctx, path[1:], keeper) default: - return nil, sdk.ErrUnknownRequest("Unknown package subkeys query endpoint") + err = sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, + "Unknown package %s query endpoint", types.ModuleName) } + return res, sdk.ConvertError(err) } } -func queryGetFeeAllowances(ctx sdk.Context, args []string, keeper Keeper) ([]byte, sdk.Error) { +func queryGetFeeAllowances(ctx sdk.Context, args []string, keeper Keeper) ([]byte, error) { grantee := args[0] granteeAddr, err := sdk.AccAddressFromBech32(grantee) if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("invalid address", err.Error())) + return nil, sdkerrors.Wrapf(err, "invalid address") } - fees, err := keeper.GetAllMyFeeAllowances(ctx, granteeAddr) + fees, err := keeper.GetAllGranteeFeeAllowances(ctx, granteeAddr) if err != nil { return nil, sdk.ConvertError(err) } @@ -38,7 +44,7 @@ func queryGetFeeAllowances(ctx sdk.Context, args []string, keeper Keeper) ([]byt bz, err := keeper.cdc.MarshalJSON(fees) if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + return nil, sdkerrors.Wrapf(err, "could not marshal query result to JSON") } return bz, nil } From 92f64ccf87f06d7c9d79c91f78af30355ec19477 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 21 Oct 2019 11:12:48 +0200 Subject: [PATCH 39/77] Clean up message sign bytes --- x/subkeys/internal/types/msgs.go | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/x/subkeys/internal/types/msgs.go b/x/subkeys/internal/types/msgs.go index 7686f95a3a5a..9444c790a154 100644 --- a/x/subkeys/internal/types/msgs.go +++ b/x/subkeys/internal/types/msgs.go @@ -1,8 +1,6 @@ package types import ( - "encoding/json" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/subkeys/exported" ) @@ -39,11 +37,7 @@ func (msg MsgDelegateFeeAllowance) ValidateBasic() sdk.Error { } func (msg MsgDelegateFeeAllowance) GetSignBytes() []byte { - b, err := json.Marshal(msg) - if err != nil { - panic(err) - } - return sdk.MustSortJSON(b) + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) } func (msg MsgDelegateFeeAllowance) GetSigners() []sdk.AccAddress { @@ -79,11 +73,7 @@ func (msg MsgRevokeFeeAllowance) ValidateBasic() sdk.Error { } func (msg MsgRevokeFeeAllowance) GetSignBytes() []byte { - b, err := json.Marshal(msg) - if err != nil { - panic(err) - } - return sdk.MustSortJSON(b) + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) } func (msg MsgRevokeFeeAllowance) GetSigners() []sdk.AccAddress { From 4daf4af4d773171f1960cdf59ed9c3fea2e803e4 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 24 Oct 2019 18:06:36 +0200 Subject: [PATCH 40/77] Minor PR comments --- simapp/app.go | 1 - x/subkeys/handler.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/simapp/app.go b/simapp/app.go index db7f52fa715e..bc321bd8bcf2 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -223,7 +223,6 @@ func NewSimApp( distr.NewAppModule(app.DistrKeeper, app.SupplyKeeper), staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper), slashing.NewAppModule(app.SlashingKeeper, app.StakingKeeper), - // subkeys.NewAppModule(app.DelegationKeeper), ) app.sm.RegisterStoreDecoders() diff --git a/x/subkeys/handler.go b/x/subkeys/handler.go index 6093cc93286f..eb15b7daa79b 100644 --- a/x/subkeys/handler.go +++ b/x/subkeys/handler.go @@ -18,7 +18,7 @@ func NewHandler(k Keeper) sdk.Handler { k.RevokeFeeAllowance(ctx, msg.Granter, msg.Grantee) return sdk.Result{} default: - errMsg := fmt.Sprintf("Unrecognized data Msg type: %v", msg.Type()) + errMsg := fmt.Sprintf("Unrecognized data Msg type: %s", ModuleName) return sdk.ErrUnknownRequest(errMsg).Result() } } From a497575aab16811c0d60a7921f508bc06e6ff87d Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 24 Oct 2019 18:17:42 +0200 Subject: [PATCH 41/77] Replace GetAllFees... with Iterator variants --- x/subkeys/genesis.go | 9 +++++- x/subkeys/internal/keeper/keeper.go | 36 +++++++++++++----------- x/subkeys/internal/keeper/keeper_test.go | 6 +++- x/subkeys/internal/keeper/querier.go | 12 +++++--- 4 files changed, 40 insertions(+), 23 deletions(-) diff --git a/x/subkeys/genesis.go b/x/subkeys/genesis.go index 28b403921fbf..03c3b6169288 100644 --- a/x/subkeys/genesis.go +++ b/x/subkeys/genesis.go @@ -35,5 +35,12 @@ func ExportGenesis(ctx sdk.Context, k Keeper) (GenesisState, error) { // expiry of 1000. It would need a new method on the FeeAllowance interface. // // Currently, we handle expirations naively - return k.GetAllFeeAllowances(ctx) + + var grants []FeeAllowanceGrant + err := k.IterateAllFeeAllowances(ctx, func(grant FeeAllowanceGrant) bool { + // TODO: modify each one + grants = append(grants, grant) + return false + }) + return grants, err } diff --git a/x/subkeys/internal/keeper/keeper.go b/x/subkeys/internal/keeper/keeper.go index dec90eba6448..319e4bbd144e 100644 --- a/x/subkeys/internal/keeper/keeper.go +++ b/x/subkeys/internal/keeper/keeper.go @@ -65,44 +65,46 @@ func (k Keeper) GetFeeGrant(ctx sdk.Context, granter sdk.AccAddress, grantee sdk return &grant, nil } -// GetAllGranteeFeeAllowances returns a list of all the grants from anyone to the given grantee. -func (k Keeper) GetAllGranteeFeeAllowances(ctx sdk.Context, grantee sdk.AccAddress) ([]types.FeeAllowanceGrant, error) { +// IterateAllGranteeFeeAllowances iterates over all the grants from anyone to the given grantee. +// Callback to get all data, returns true to stop, false to keep reading +func (k Keeper) IterateAllGranteeFeeAllowances(ctx sdk.Context, grantee sdk.AccAddress, cb func(types.FeeAllowanceGrant) bool) error { store := ctx.KVStore(k.storeKey) - var grants []types.FeeAllowanceGrant - prefix := types.FeeAllowancePrefixByGrantee(grantee) iter := sdk.KVStorePrefixIterator(store, prefix) defer iter.Close() - for ; iter.Valid(); iter.Next() { + + stop := false + for ; iter.Valid() && !stop; iter.Next() { bz := iter.Value() var grant types.FeeAllowanceGrant err := k.cdc.UnmarshalBinaryBare(bz, &grant) if err != nil { - return nil, err + return err } - grants = append(grants, grant) + stop = cb(grant) } - return grants, nil + return nil } -// GetAllFeeAllowances returns a list of all the grants in the store. -// This is very expensive and only designed for export genesis -func (k Keeper) GetAllFeeAllowances(ctx sdk.Context) ([]types.FeeAllowanceGrant, error) { +// IterateAllFeeAllowances iterates over all the grants in the store. +// Callback to get all data, returns true to stop, false to keep reading +// Calling this without pagination is very expensive and only designed for export genesis +func (k Keeper) IterateAllFeeAllowances(ctx sdk.Context, cb func(types.FeeAllowanceGrant) bool) error { store := ctx.KVStore(k.storeKey) - var grants []types.FeeAllowanceGrant - iter := sdk.KVStorePrefixIterator(store, types.FeeAllowanceKeyPrefix) defer iter.Close() - for ; iter.Valid(); iter.Next() { + + stop := false + for ; iter.Valid() && !stop; iter.Next() { bz := iter.Value() var grant types.FeeAllowanceGrant err := k.cdc.UnmarshalBinaryBare(bz, &grant) if err != nil { - return nil, err + return err } - grants = append(grants, grant) + stop = cb(grant) } - return grants, nil + return nil } // UseDelegatedFees will try to pay the given fee from the granter's account as requested by the grantee diff --git a/x/subkeys/internal/keeper/keeper_test.go b/x/subkeys/internal/keeper/keeper_test.go index 159005bb3908..155b3efac93d 100644 --- a/x/subkeys/internal/keeper/keeper_test.go +++ b/x/subkeys/internal/keeper/keeper_test.go @@ -176,7 +176,11 @@ func TestKeeperCrud(t *testing.T) { for name, tc := range allCases { t.Run(name, func(t *testing.T) { - grants, err := k.GetAllGranteeFeeAllowances(ctx, tc.grantee) + var grants []types.FeeAllowanceGrant + err := k.IterateAllGranteeFeeAllowances(ctx, tc.grantee, func(grant types.FeeAllowanceGrant) bool { + grants = append(grants, grant) + return false + }) require.NoError(t, err) assert.Equal(t, tc.grants, grants) }) diff --git a/x/subkeys/internal/keeper/querier.go b/x/subkeys/internal/keeper/querier.go index 33c862ef48c2..03ce5ea9ffec 100644 --- a/x/subkeys/internal/keeper/querier.go +++ b/x/subkeys/internal/keeper/querier.go @@ -34,15 +34,19 @@ func queryGetFeeAllowances(ctx sdk.Context, args []string, keeper Keeper) ([]byt return nil, sdkerrors.Wrapf(err, "invalid address") } - fees, err := keeper.GetAllGranteeFeeAllowances(ctx, granteeAddr) + var grants []types.FeeAllowanceGrant + err = keeper.IterateAllGranteeFeeAllowances(ctx, granteeAddr, func(grant types.FeeAllowanceGrant) bool { + grants = append(grants, grant) + return false + }) if err != nil { - return nil, sdk.ConvertError(err) + return nil, err } - if fees == nil { + if grants == nil { return []byte("[]"), nil } - bz, err := keeper.cdc.MarshalJSON(fees) + bz, err := keeper.cdc.MarshalJSON(grants) if err != nil { return nil, sdkerrors.Wrapf(err, "could not marshal query result to JSON") } From 73eab922ee78773bcd5c15cc3c5ebd40865f0af5 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 24 Oct 2019 18:26:58 +0200 Subject: [PATCH 42/77] PrepareForExport adjusts grant expiration height --- x/subkeys/exported/fees.go | 5 +++++ x/subkeys/genesis.go | 15 +++++++-------- x/subkeys/internal/types/basic_fee.go | 7 +++++++ x/subkeys/internal/types/expiration.go | 9 +++++++++ x/subkeys/internal/types/grant.go | 9 +++++++++ 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/x/subkeys/exported/fees.go b/x/subkeys/exported/fees.go index 9d87600c4b3d..efe7eb321d7c 100644 --- a/x/subkeys/exported/fees.go +++ b/x/subkeys/exported/fees.go @@ -21,6 +21,11 @@ type FeeAllowance interface { // (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseDelegatedFees) Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (remove bool, err error) + // If we export fee allowances the timing info will be quite off (eg. go from height 100000 to 0) + // This callback allows the fee-allowance to change it's state and return a copy that is adjusted + // given the time and height of the actual dump (may safely return self if no changes needed) + PrepareForExport(dumpTime time.Time, dumpHeight int64) FeeAllowance + // ValidateBasic should evaluate this FeeAllowance for internal consistency. // Don't allow negative amounts, or negative periods for example. ValidateBasic() error diff --git a/x/subkeys/genesis.go b/x/subkeys/genesis.go index 03c3b6169288..2d4e1217d372 100644 --- a/x/subkeys/genesis.go +++ b/x/subkeys/genesis.go @@ -28,18 +28,17 @@ func InitGenesis(ctx sdk.Context, k Keeper, gen GenesisState) error { } // ExportGenesis will dump the contents of the keeper into a serializable GenesisState +// +// All expiration heights will be thrown off if we dump state and start at a new +// chain at height 0. Thus, we allow the Allowances to "prepare themselves" +// for export, like if they have exiry at 5000 and current is 4000, they export with +// expiry of 1000. It would need a new method on the FeeAllowance interface. func ExportGenesis(ctx sdk.Context, k Keeper) (GenesisState, error) { - // TODO: all expiration heights will be thrown off if we dump state and start at a new - // chain at height 0. Maybe we need to allow the Allowances to "prepare themselves" - // for export, like if they have exiry at 5000 and current is 4000, they export with - // expiry of 1000. It would need a new method on the FeeAllowance interface. - // - // Currently, we handle expirations naively - + time, height := ctx.BlockTime(), ctx.BlockHeight() var grants []FeeAllowanceGrant err := k.IterateAllFeeAllowances(ctx, func(grant FeeAllowanceGrant) bool { // TODO: modify each one - grants = append(grants, grant) + grants = append(grants, grant.PrepareForExport(time, height)) return false }) return grants, err diff --git a/x/subkeys/internal/types/basic_fee.go b/x/subkeys/internal/types/basic_fee.go index 9a110ff54f16..e1057d532807 100644 --- a/x/subkeys/internal/types/basic_fee.go +++ b/x/subkeys/internal/types/basic_fee.go @@ -35,6 +35,13 @@ func (a *BasicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockHeig return left.IsZero(), nil } +func (a *BasicFeeAllowance) PrepareForExport(dumpTime time.Time, dumpHeight int64) exported.FeeAllowance { + return &BasicFeeAllowance{ + SpendLimit: a.SpendLimit, + Expiration: a.Expiration.PrepareForExport(dumpTime, dumpHeight), + } +} + // ValidateBasic implements FeeAllowance and enforces basic sanity checks func (a BasicFeeAllowance) ValidateBasic() error { if !a.SpendLimit.IsValid() { diff --git a/x/subkeys/internal/types/expiration.go b/x/subkeys/internal/types/expiration.go index 09ae0ec449ad..91320b59c3e3 100644 --- a/x/subkeys/internal/types/expiration.go +++ b/x/subkeys/internal/types/expiration.go @@ -71,6 +71,15 @@ func (e *ExpiresAt) Step(p Period) error { return nil } +// PrepareForExport will deduct the dumpHeight from the expiration, so when this is +// reloaded after a hard fork, the actual number of allowed blocks is constant +func (e ExpiresAt) PrepareForExport(dumpTime time.Time, dumpHeight int64) ExpiresAt { + if e.Height != 0 { + e.Height -= dumpHeight + } + return e +} + // Period is a repeating unit of either clock time or number of blocks. // This is designed to be added to an ExpiresAt struct. type Period struct { diff --git a/x/subkeys/internal/types/grant.go b/x/subkeys/internal/types/grant.go index bb7006ac837f..76e57ec09c5d 100644 --- a/x/subkeys/internal/types/grant.go +++ b/x/subkeys/internal/types/grant.go @@ -1,6 +1,8 @@ package types import ( + "time" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/subkeys/exported" ) @@ -25,3 +27,10 @@ func (a FeeAllowanceGrant) ValidateBasic() error { } return a.Allowance.ValidateBasic() } + +// PrepareForExport will make all needed changes to the allowance to prepare to be +// re-imported at height 0, and return a copy of this grant. +func (a FeeAllowanceGrant) PrepareForExport(dumpTime time.Time, dumpHeight int64) FeeAllowanceGrant { + a.Allowance = a.Allowance.PrepareForExport(dumpTime, dumpHeight) + return a +} From 546fa8fe3447a95c25bae25529bbc40f7e998191 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 24 Oct 2019 18:32:03 +0200 Subject: [PATCH 43/77] Panic on de/serialization error in keeper --- x/subkeys/genesis.go | 8 ++----- x/subkeys/internal/ante/fee_test.go | 9 +++----- x/subkeys/internal/keeper/keeper.go | 19 ++++------------ x/subkeys/internal/keeper/keeper_test.go | 27 ++++++++--------------- x/subkeys/internal/keeper/querier_test.go | 6 ++--- x/subkeys/module.go | 5 +---- 6 files changed, 21 insertions(+), 53 deletions(-) diff --git a/x/subkeys/genesis.go b/x/subkeys/genesis.go index 2d4e1217d372..26287e1018ba 100644 --- a/x/subkeys/genesis.go +++ b/x/subkeys/genesis.go @@ -17,14 +17,10 @@ func (g GenesisState) ValidateBasic() error { } // InitGenesis will initialize the keeper from a *previously validated* GenesisState -func InitGenesis(ctx sdk.Context, k Keeper, gen GenesisState) error { +func InitGenesis(ctx sdk.Context, k Keeper, gen GenesisState) { for _, f := range gen { - err := k.DelegateFeeAllowance(ctx, f) - if err != nil { - return err - } + k.DelegateFeeAllowance(ctx, f) } - return nil } // ExportGenesis will dump the contents of the keeper into a serializable GenesisState diff --git a/x/subkeys/internal/ante/fee_test.go b/x/subkeys/internal/ante/fee_test.go index de22d3aeee9a..cc53e1ca578c 100644 --- a/x/subkeys/internal/ante/fee_test.go +++ b/x/subkeys/internal/ante/fee_test.go @@ -44,34 +44,31 @@ func TestDeductFeesNoDelegation(t *testing.T) { app.AccountKeeper.SetAccount(ctx, acc2) // Set delegation from addr2 to addr3 (plenty to pay) - err := app.DelegationKeeper.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + app.DelegationKeeper.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr2, Grantee: addr3, Allowance: &types.BasicFeeAllowance{ SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 500)), }, }) - require.NoError(t, err) // Set low delegation from addr2 to addr4 (delegation will reject) - err = app.DelegationKeeper.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + app.DelegationKeeper.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr2, Grantee: addr4, Allowance: &types.BasicFeeAllowance{ SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 20)), }, }) - require.NoError(t, err) // Set delegation from addr1 to addr4 (cannot cover this ) - err = app.DelegationKeeper.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + app.DelegationKeeper.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr2, Grantee: addr3, Allowance: &types.BasicFeeAllowance{ SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 500)), }, }) - require.NoError(t, err) cases := map[string]struct { signerKey crypto.PrivKey diff --git a/x/subkeys/internal/keeper/keeper.go b/x/subkeys/internal/keeper/keeper.go index 319e4bbd144e..d436cd0420d0 100644 --- a/x/subkeys/internal/keeper/keeper.go +++ b/x/subkeys/internal/keeper/keeper.go @@ -18,16 +18,11 @@ func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey) Keeper { } // DelegateFeeAllowance creates a new grant -func (k Keeper) DelegateFeeAllowance(ctx sdk.Context, grant types.FeeAllowanceGrant) error { +func (k Keeper) DelegateFeeAllowance(ctx sdk.Context, grant types.FeeAllowanceGrant) { store := ctx.KVStore(k.storeKey) key := types.FeeAllowanceKey(grant.Granter, grant.Grantee) - - bz, err := k.cdc.MarshalBinaryBare(grant) - if err != nil { - return err - } + bz := k.cdc.MustMarshalBinaryBare(grant) store.Set(key, bz) - return nil } // RevokeFeeAllowance removes an existing grant @@ -111,9 +106,7 @@ func (k Keeper) IterateAllFeeAllowances(ctx sdk.Context, cb func(types.FeeAllowa func (k Keeper) UseDelegatedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins) bool { grant, err := k.GetFeeGrant(ctx, granter, grantee) if err != nil { - // we should acknowledge a db issue somehow (better?) - ctx.Logger().Error(err.Error()) - return false + panic(err) } if grant == nil || grant.Allowance == nil { return false @@ -129,10 +122,6 @@ func (k Keeper) UseDelegatedFees(ctx sdk.Context, granter, grantee sdk.AccAddres } // if we accepted, store the updated state of the allowance - if err := k.DelegateFeeAllowance(ctx, *grant); err != nil { - // we should acknowledge a db issue somehow (better?) - ctx.Logger().Error(err.Error()) - return false - } + k.DelegateFeeAllowance(ctx, *grant) return true } diff --git a/x/subkeys/internal/keeper/keeper_test.go b/x/subkeys/internal/keeper/keeper_test.go index 155b3efac93d..a4589f3eed79 100644 --- a/x/subkeys/internal/keeper/keeper_test.go +++ b/x/subkeys/internal/keeper/keeper_test.go @@ -77,38 +77,31 @@ func TestKeeperCrud(t *testing.T) { } // let's set up some initial state here - err := k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr, Grantee: addr2, Allowance: &basic, }) - require.NoError(t, err) - err = k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr, Grantee: addr3, Allowance: &basic2, }) - require.NoError(t, err) - err = k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr2, Grantee: addr3, Allowance: &basic, }) - require.NoError(t, err) - err = k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr2, Grantee: addr4, Allowance: &basic, }) - require.NoError(t, err) - err = k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr4, Grantee: addr, Allowance: &basic2, }) - require.NoError(t, err) // remove some, overwrite other k.RevokeFeeAllowance(ctx, addr, addr2) k.RevokeFeeAllowance(ctx, addr, addr3) - err = k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr, Grantee: addr3, Allowance: &basic, }) - require.NoError(t, err) - err = k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr2, Grantee: addr3, Allowance: &basic2, }) - require.NoError(t, err) // end state: // addr -> addr3 (basic) @@ -255,14 +248,12 @@ func TestUseDelegatedFee(t *testing.T) { // let's set up some initial state here // addr -> addr2 (future) // addr -> addr3 (expired) - err := k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr, Grantee: addr2, Allowance: &future, }) - require.NoError(t, err) - err = k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr, Grantee: addr3, Allowance: &expired, }) - require.NoError(t, err) allowed := k.UseDelegatedFees(ctx, tc.granter, tc.grantee, tc.fee) require.Equal(t, tc.allowed, allowed) diff --git a/x/subkeys/internal/keeper/querier_test.go b/x/subkeys/internal/keeper/querier_test.go index e9bc45f68b26..53061eda4245 100644 --- a/x/subkeys/internal/keeper/querier_test.go +++ b/x/subkeys/internal/keeper/querier_test.go @@ -40,10 +40,8 @@ func TestQuery(t *testing.T) { } // let's set up some initial state here - err := k.DelegateFeeAllowance(ctx, grant1) - require.NoError(t, err) - err = k.DelegateFeeAllowance(ctx, grant2) - require.NoError(t, err) + k.DelegateFeeAllowance(ctx, grant1) + k.DelegateFeeAllowance(ctx, grant2) // now try some queries cases := map[string]struct { diff --git a/x/subkeys/module.go b/x/subkeys/module.go index 0364ee4a4549..0136e8d1240a 100644 --- a/x/subkeys/module.go +++ b/x/subkeys/module.go @@ -132,10 +132,7 @@ func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.Va if err != nil { panic(err) } - err = InitGenesis(ctx, am.keeper, genesisState) - if err != nil { - panic(err) - } + InitGenesis(ctx, am.keeper, genesisState) return []abci.ValidatorUpdate{} } From 5f9e391de8f785f9ceebc53682bc9261473987db Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 24 Oct 2019 18:39:58 +0200 Subject: [PATCH 44/77] Move custom ante handler chain to tests, update docs --- x/subkeys/doc.go | 10 +++++----- x/subkeys/internal/ante/ante.go | 29 ----------------------------- x/subkeys/internal/ante/fee_test.go | 27 ++++++++++++++++++++++++++- 3 files changed, 31 insertions(+), 35 deletions(-) delete mode 100644 x/subkeys/internal/ante/ante.go diff --git a/x/subkeys/doc.go b/x/subkeys/doc.go index b644d60de59d..b2d3afaa0952 100644 --- a/x/subkeys/doc.go +++ b/x/subkeys/doc.go @@ -20,11 +20,11 @@ The fee allowance that a grantee receives is specified by an implementation of the FeeAllowance interface. Two FeeAllowance implementations are provided in this package: BasicFeeAllowance and PeriodicFeeAllowance. -In order to integrate this into an application, we must use the ante handles from -this package instead of the default auth implementations for DeductFee -(adding custom logic) and MempoolFee (updating it to be compatible with DelegatedTx). -An application can do this simply by using `x/delegate_fees/internal/ante.NewAnteHandler()` -when setting up the app instead of the version from auth. +In order to integrate this into an application, we must use the DeductDelegatedFeeDecorator +ante handler from this package instead of the default DeductFeeDecorator from auth. +To allow handling txs from empty accounts (with fees paid from an existing account), +we have to re-order the decorators as well. You can see an example in +`x/delegate_fees/internal/ante/fee_test.go:newAnteHandler()` I did not pull this into the top level package as it pulls in dependencies on the internals of `x/auth` and if I understand correctly, this is bad practice to depend on other modules diff --git a/x/subkeys/internal/ante/ante.go b/x/subkeys/internal/ante/ante.go deleted file mode 100644 index 35560c713aee..000000000000 --- a/x/subkeys/internal/ante/ante.go +++ /dev/null @@ -1,29 +0,0 @@ -package ante - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - authAnte "github.com/cosmos/cosmos-sdk/x/auth/ante" - authKeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - authTypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/subkeys/internal/keeper" -) - -// NewAnteHandler is just like auth.NewAnteHandler, except we use the DeductDelegatedFeeDecorator -// in order to allow payment of fees via a delegation. -func NewAnteHandler(ak authKeeper.AccountKeeper, supplyKeeper authTypes.SupplyKeeper, dk keeper.Keeper, sigGasConsumer authAnte.SignatureVerificationGasConsumer) sdk.AnteHandler { - return sdk.ChainAnteDecorators( - authAnte.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first - authAnte.NewMempoolFeeDecorator(), - authAnte.NewValidateBasicDecorator(), - authAnte.NewValidateMemoDecorator(ak), - authAnte.NewConsumeGasForTxSizeDecorator(ak), - // DeductDelegatedFeeDecorator will create an empty account if we sign with no tokens but valid validation - // This must be before SetPubKey, ValidateSigCount, SigVerification, which error if account doesn't exist yet - NewDeductDelegatedFeeDecorator(ak, supplyKeeper, dk), - authAnte.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators - authAnte.NewValidateSigCountDecorator(ak), - authAnte.NewSigGasConsumeDecorator(ak, sigGasConsumer), - authAnte.NewSigVerificationDecorator(ak), - authAnte.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator - ) -} diff --git a/x/subkeys/internal/ante/fee_test.go b/x/subkeys/internal/ante/fee_test.go index cc53e1ca578c..01c6e429dfdb 100644 --- a/x/subkeys/internal/ante/fee_test.go +++ b/x/subkeys/internal/ante/fee_test.go @@ -10,12 +10,37 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" + authante "github.com/cosmos/cosmos-sdk/x/auth/ante" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/subkeys/internal/ante" + "github.com/cosmos/cosmos-sdk/x/subkeys/internal/keeper" "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types" "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types/tx" ) +// newAnteHandler is just like auth.NewAnteHandler, except we use the DeductDelegatedFeeDecorator +// in order to allow payment of fees via a delegation. +// +// This is used for our full-stack tests +func newAnteHandler(ak authkeeper.AccountKeeper, supplyKeeper authtypes.SupplyKeeper, dk keeper.Keeper, sigGasConsumer authante.SignatureVerificationGasConsumer) sdk.AnteHandler { + return sdk.ChainAnteDecorators( + authante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first + authante.NewMempoolFeeDecorator(), + authante.NewValidateBasicDecorator(), + authante.NewValidateMemoDecorator(ak), + authante.NewConsumeGasForTxSizeDecorator(ak), + // DeductDelegatedFeeDecorator will create an empty account if we sign with no tokens but valid validation + // This must be before SetPubKey, ValidateSigCount, SigVerification, which error if account doesn't exist yet + ante.NewDeductDelegatedFeeDecorator(ak, supplyKeeper, dk), + authante.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators + authante.NewValidateSigCountDecorator(ak), + authante.NewSigGasConsumeDecorator(ak, sigGasConsumer), + authante.NewSigVerificationDecorator(ak), + authante.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator + ) +} + func TestDeductFeesNoDelegation(t *testing.T) { // setup app, ctx := createTestApp(true) @@ -25,7 +50,7 @@ func TestDeductFeesNoDelegation(t *testing.T) { ourAnteHandler := sdk.ChainAnteDecorators(dfd) // this tests the whole stack - anteHandlerStack := ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, app.DelegationKeeper, SigGasNoConsumer) + anteHandlerStack := newAnteHandler(app.AccountKeeper, app.SupplyKeeper, app.DelegationKeeper, SigGasNoConsumer) // keys and addresses priv1, _, addr1 := authtypes.KeyTestPubAddr() From c45dc71d354d78f0f2ee268dcdfff61e94650f07 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 24 Oct 2019 18:53:36 +0200 Subject: [PATCH 45/77] More cleanup --- x/subkeys/doc.go | 4 ---- x/subkeys/exported/fees.go | 2 +- x/subkeys/internal/keeper/keeper.go | 16 ++++++++-------- x/subkeys/internal/types/msgs.go | 2 +- x/subkeys/internal/types/tx/tx.go | 2 +- x/subkeys/module.go | 9 +-------- 6 files changed, 12 insertions(+), 23 deletions(-) diff --git a/x/subkeys/doc.go b/x/subkeys/doc.go index b2d3afaa0952..e8aad2a46a99 100644 --- a/x/subkeys/doc.go +++ b/x/subkeys/doc.go @@ -25,9 +25,5 @@ ante handler from this package instead of the default DeductFeeDecorator from au To allow handling txs from empty accounts (with fees paid from an existing account), we have to re-order the decorators as well. You can see an example in `x/delegate_fees/internal/ante/fee_test.go:newAnteHandler()` - -I did not pull this into the top level package as it pulls in dependencies on the internals -of `x/auth` and if I understand correctly, this is bad practice to depend on other modules -except for the external package. */ package subkeys diff --git a/x/subkeys/exported/fees.go b/x/subkeys/exported/fees.go index efe7eb321d7c..1ad02e65182e 100644 --- a/x/subkeys/exported/fees.go +++ b/x/subkeys/exported/fees.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// FeeAllowance implementations are tied to a given delegator and delegatee, +// FeeAllowance implementations are tied to a given fee delegator and delegatee, // and are used to enforce fee delegation limits. type FeeAllowance interface { // Accept can use fee payment requested as well as timestamp/height of the current block diff --git a/x/subkeys/internal/keeper/keeper.go b/x/subkeys/internal/keeper/keeper.go index d436cd0420d0..dd76eb956e51 100644 --- a/x/subkeys/internal/keeper/keeper.go +++ b/x/subkeys/internal/keeper/keeper.go @@ -37,27 +37,27 @@ func (k Keeper) RevokeFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddr // Returns an error on parsing issues func (k Keeper) GetFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) (exported.FeeAllowance, error) { grant, err := k.GetFeeGrant(ctx, granter, grantee) - if grant == nil { + if err != nil { return nil, err } - return grant.Allowance, err + return grant.Allowance, nil } // GetFeeGrant returns entire grant between both accounts -func (k Keeper) GetFeeGrant(ctx sdk.Context, granter sdk.AccAddress, grantee sdk.AccAddress) (*types.FeeAllowanceGrant, error) { +func (k Keeper) GetFeeGrant(ctx sdk.Context, granter sdk.AccAddress, grantee sdk.AccAddress) (types.FeeAllowanceGrant, error) { store := ctx.KVStore(k.storeKey) key := types.FeeAllowanceKey(granter, grantee) bz := store.Get(key) if len(bz) == 0 { - return nil, nil + return types.FeeAllowanceGrant{}, nil } var grant types.FeeAllowanceGrant err := k.cdc.UnmarshalBinaryBare(bz, &grant) if err != nil { - return nil, err + return types.FeeAllowanceGrant{}, err } - return &grant, nil + return grant, nil } // IterateAllGranteeFeeAllowances iterates over all the grants from anyone to the given grantee. @@ -108,7 +108,7 @@ func (k Keeper) UseDelegatedFees(ctx sdk.Context, granter, grantee sdk.AccAddres if err != nil { panic(err) } - if grant == nil || grant.Allowance == nil { + if grant.Allowance == nil { return false } @@ -122,6 +122,6 @@ func (k Keeper) UseDelegatedFees(ctx sdk.Context, granter, grantee sdk.AccAddres } // if we accepted, store the updated state of the allowance - k.DelegateFeeAllowance(ctx, *grant) + k.DelegateFeeAllowance(ctx, grant) return true } diff --git a/x/subkeys/internal/types/msgs.go b/x/subkeys/internal/types/msgs.go index 9444c790a154..d8b8add8fbc6 100644 --- a/x/subkeys/internal/types/msgs.go +++ b/x/subkeys/internal/types/msgs.go @@ -19,7 +19,7 @@ func NewMsgDelegateFeeAllowance(granter sdk.AccAddress, grantee sdk.AccAddress, } func (msg MsgDelegateFeeAllowance) Route() string { - return "delegation" + return RouterKey } func (msg MsgDelegateFeeAllowance) Type() string { diff --git a/x/subkeys/internal/types/tx/tx.go b/x/subkeys/internal/types/tx/tx.go index a9a999b32f48..448151b948f1 100644 --- a/x/subkeys/internal/types/tx/tx.go +++ b/x/subkeys/internal/types/tx/tx.go @@ -19,7 +19,7 @@ var ( maxGasWanted = uint64((1 << 63) - 1) ) -// DelegatedTx is wraps a Msg with Fee and Signatures, +// DelegatedTx wraps a Msg with Fee and Signatures, // adding the ability to delegate the fee payment // NOTE: the first signature responsible for paying fees, either directly, // or must be authorized to spend from the provided Fee.FeeAccount diff --git a/x/subkeys/module.go b/x/subkeys/module.go index 0136e8d1240a..e84faa4ec26d 100644 --- a/x/subkeys/module.go +++ b/x/subkeys/module.go @@ -15,10 +15,6 @@ import ( tx "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types/tx" ) -// TODO: -// * docs -// * periodic fee - var ( _ module.AppModule = AppModule{} _ module.AppModuleBasic = AppModuleBasic{} @@ -51,11 +47,8 @@ func (a AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { } func (a AppModuleBasic) getValidatedGenesis(bz json.RawMessage) (GenesisState, error) { - cdc := codec.New() - a.RegisterCodec(cdc) - var data GenesisState - err := cdc.UnmarshalJSON(bz, &data) + err := ModuleCdc.UnmarshalJSON(bz, &data) if err != nil { return nil, err } From 2be48ef718a1dc43ea43ffa7fecc5e1e5e743dd7 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 24 Oct 2019 18:56:45 +0200 Subject: [PATCH 46/77] More doc cleanup --- x/subkeys/genesis.go | 2 +- x/subkeys/internal/keeper/querier.go | 2 +- x/subkeys/internal/types/msgs.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x/subkeys/genesis.go b/x/subkeys/genesis.go index 26287e1018ba..3274ec9c863a 100644 --- a/x/subkeys/genesis.go +++ b/x/subkeys/genesis.go @@ -27,7 +27,7 @@ func InitGenesis(ctx sdk.Context, k Keeper, gen GenesisState) { // // All expiration heights will be thrown off if we dump state and start at a new // chain at height 0. Thus, we allow the Allowances to "prepare themselves" -// for export, like if they have exiry at 5000 and current is 4000, they export with +// for export, like if they have expiry at 5000 and current is 4000, they export with // expiry of 1000. It would need a new method on the FeeAllowance interface. func ExportGenesis(ctx sdk.Context, k Keeper) (GenesisState, error) { time, height := ctx.BlockTime(), ctx.BlockHeight() diff --git a/x/subkeys/internal/keeper/querier.go b/x/subkeys/internal/keeper/querier.go index 03ce5ea9ffec..32a5ccfd6338 100644 --- a/x/subkeys/internal/keeper/querier.go +++ b/x/subkeys/internal/keeper/querier.go @@ -21,7 +21,7 @@ func NewQuerier(keeper Keeper) sdk.Querier { res, err = queryGetFeeAllowances(ctx, path[1:], keeper) default: err = sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, - "Unknown package %s query endpoint", types.ModuleName) + "unknown package %s query endpoint", types.ModuleName) } return res, sdk.ConvertError(err) } diff --git a/x/subkeys/internal/types/msgs.go b/x/subkeys/internal/types/msgs.go index d8b8add8fbc6..e53a3a76f601 100644 --- a/x/subkeys/internal/types/msgs.go +++ b/x/subkeys/internal/types/msgs.go @@ -55,7 +55,7 @@ func NewMsgRevokeFeeAllowance(granter sdk.AccAddress, grantee sdk.AccAddress) Ms } func (msg MsgRevokeFeeAllowance) Route() string { - return "delegation" + return RouterKey } func (msg MsgRevokeFeeAllowance) Type() string { From e8a624fe3de365444c30eec158cb46630a4fdeb8 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 24 Oct 2019 19:01:47 +0200 Subject: [PATCH 47/77] Renamed subkeys module to fee_grant --- simapp/app.go | 14 +++++++------- x/{subkeys => fee_grant}/alias.go | 10 +++++----- x/{subkeys => fee_grant}/doc.go | 4 ++-- x/{subkeys => fee_grant}/exported/fees.go | 0 x/{subkeys => fee_grant}/genesis.go | 2 +- x/{subkeys => fee_grant}/handler.go | 2 +- x/{subkeys => fee_grant}/internal/ante/fee.go | 4 ++-- x/{subkeys => fee_grant}/internal/ante/fee_test.go | 8 ++++---- x/{subkeys => fee_grant}/internal/keeper/keeper.go | 4 ++-- .../internal/keeper/keeper_test.go | 6 +++--- .../internal/keeper/querier.go | 2 +- .../internal/keeper/querier_test.go | 4 ++-- .../internal/types/basic_fee.go | 2 +- .../internal/types/basic_fee_test.go | 0 x/{subkeys => fee_grant}/internal/types/codec.go | 2 +- x/{subkeys => fee_grant}/internal/types/errors.go | 0 .../internal/types/expiration.go | 0 .../internal/types/expiration_test.go | 0 x/{subkeys => fee_grant}/internal/types/grant.go | 2 +- .../internal/types/grant_test.go | 0 x/{subkeys => fee_grant}/internal/types/key.go | 0 x/{subkeys => fee_grant}/internal/types/msgs.go | 2 +- .../internal/types/tx/codec.go | 0 .../internal/types/tx/test_common.go | 0 x/{subkeys => fee_grant}/internal/types/tx/tx.go | 0 x/{subkeys => fee_grant}/module.go | 4 ++-- 26 files changed, 36 insertions(+), 36 deletions(-) rename x/{subkeys => fee_grant}/alias.go (86%) rename x/{subkeys => fee_grant}/doc.go (94%) rename x/{subkeys => fee_grant}/exported/fees.go (100%) rename x/{subkeys => fee_grant}/genesis.go (98%) rename x/{subkeys => fee_grant}/handler.go (97%) rename x/{subkeys => fee_grant}/internal/ante/fee.go (96%) rename x/{subkeys => fee_grant}/internal/ante/fee_test.go (97%) rename x/{subkeys => fee_grant}/internal/keeper/keeper.go (97%) rename x/{subkeys => fee_grant}/internal/keeper/keeper_test.go (97%) rename x/{subkeys => fee_grant}/internal/keeper/querier.go (95%) rename x/{subkeys => fee_grant}/internal/keeper/querier_test.go (94%) rename x/{subkeys => fee_grant}/internal/types/basic_fee.go (96%) rename x/{subkeys => fee_grant}/internal/types/basic_fee_test.go (100%) rename x/{subkeys => fee_grant}/internal/types/codec.go (90%) rename x/{subkeys => fee_grant}/internal/types/errors.go (100%) rename x/{subkeys => fee_grant}/internal/types/expiration.go (100%) rename x/{subkeys => fee_grant}/internal/types/expiration_test.go (100%) rename x/{subkeys => fee_grant}/internal/types/grant.go (95%) rename x/{subkeys => fee_grant}/internal/types/grant_test.go (100%) rename x/{subkeys => fee_grant}/internal/types/key.go (100%) rename x/{subkeys => fee_grant}/internal/types/msgs.go (97%) rename x/{subkeys => fee_grant}/internal/types/tx/codec.go (100%) rename x/{subkeys => fee_grant}/internal/types/tx/test_common.go (100%) rename x/{subkeys => fee_grant}/internal/types/tx/tx.go (100%) rename x/{subkeys => fee_grant}/module.go (98%) diff --git a/simapp/app.go b/simapp/app.go index bc321bd8bcf2..6a3b9e9d1bf7 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -26,7 +26,7 @@ import ( paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/subkeys" + "github.com/cosmos/cosmos-sdk/x/fee_grant" "github.com/cosmos/cosmos-sdk/x/supply" ) @@ -54,7 +54,7 @@ var ( params.AppModuleBasic{}, crisis.AppModuleBasic{}, slashing.AppModuleBasic{}, - subkeys.AppModuleBasic{}, + fee_grant.AppModuleBasic{}, ) // module account permissions @@ -101,7 +101,7 @@ type SimApp struct { DistrKeeper distr.Keeper GovKeeper gov.Keeper CrisisKeeper crisis.Keeper - DelegationKeeper subkeys.Keeper + DelegationKeeper fee_grant.Keeper ParamsKeeper params.Keeper // the module manager @@ -125,7 +125,7 @@ func NewSimApp( keys := sdk.NewKVStoreKeys(bam.MainStoreKey, auth.StoreKey, staking.StoreKey, supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey, - gov.StoreKey, params.StoreKey, subkeys.StoreKey) + gov.StoreKey, params.StoreKey, fee_grant.StoreKey) tkeys := sdk.NewTransientStoreKeys(params.TStoreKey) app := &SimApp{ @@ -159,7 +159,7 @@ func NewSimApp( app.SlashingKeeper = slashing.NewKeeper(app.cdc, keys[slashing.StoreKey], &stakingKeeper, slashingSubspace, slashing.DefaultCodespace) app.CrisisKeeper = crisis.NewKeeper(crisisSubspace, invCheckPeriod, app.SupplyKeeper, auth.FeeCollectorName) - app.DelegationKeeper = subkeys.NewKeeper(app.cdc, keys[subkeys.StoreKey]) + app.DelegationKeeper = fee_grant.NewKeeper(app.cdc, keys[fee_grant.StoreKey]) // register the proposal types govRouter := gov.NewRouter() @@ -188,7 +188,7 @@ func NewSimApp( distr.NewAppModule(app.DistrKeeper, app.SupplyKeeper), slashing.NewAppModule(app.SlashingKeeper, app.StakingKeeper), staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper), - subkeys.NewAppModule(app.DelegationKeeper), + fee_grant.NewAppModule(app.DelegationKeeper), ) // During begin block slashing happens after distr.BeginBlocker so that @@ -204,7 +204,7 @@ func NewSimApp( auth.ModuleName, distr.ModuleName, staking.ModuleName, bank.ModuleName, slashing.ModuleName, gov.ModuleName, mint.ModuleName, supply.ModuleName, crisis.ModuleName, - genutil.ModuleName, subkeys.ModuleName, + genutil.ModuleName, fee_grant.ModuleName, ) app.mm.RegisterInvariants(&app.CrisisKeeper) diff --git a/x/subkeys/alias.go b/x/fee_grant/alias.go similarity index 86% rename from x/subkeys/alias.go rename to x/fee_grant/alias.go index 9bdce90331f7..6f4011bb6b6b 100644 --- a/x/subkeys/alias.go +++ b/x/fee_grant/alias.go @@ -1,13 +1,13 @@ // nolint // autogenerated code using github.com/rigelrozanski/multitool // aliases generated for the following subdirectories: -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/subkeys/internal/types -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/subkeys/internal/keeper -package subkeys +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/fee_grant/internal/keeper +package fee_grant import ( - "github.com/cosmos/cosmos-sdk/x/subkeys/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types" + "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types" ) const ( diff --git a/x/subkeys/doc.go b/x/fee_grant/doc.go similarity index 94% rename from x/subkeys/doc.go rename to x/fee_grant/doc.go index e8aad2a46a99..87e9503fcec0 100644 --- a/x/subkeys/doc.go +++ b/x/fee_grant/doc.go @@ -1,5 +1,5 @@ /* -Package subkeys provides functionality for delegating sub-permissions +Package fee_grant provides functionality for delegating sub-permissions from one account (key) to another account (key). The first implementation allows the delegation for the payment of transaction fees. @@ -26,4 +26,4 @@ To allow handling txs from empty accounts (with fees paid from an existing accou we have to re-order the decorators as well. You can see an example in `x/delegate_fees/internal/ante/fee_test.go:newAnteHandler()` */ -package subkeys +package fee_grant diff --git a/x/subkeys/exported/fees.go b/x/fee_grant/exported/fees.go similarity index 100% rename from x/subkeys/exported/fees.go rename to x/fee_grant/exported/fees.go diff --git a/x/subkeys/genesis.go b/x/fee_grant/genesis.go similarity index 98% rename from x/subkeys/genesis.go rename to x/fee_grant/genesis.go index 3274ec9c863a..60259d682f9b 100644 --- a/x/subkeys/genesis.go +++ b/x/fee_grant/genesis.go @@ -1,4 +1,4 @@ -package subkeys +package fee_grant import sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/x/subkeys/handler.go b/x/fee_grant/handler.go similarity index 97% rename from x/subkeys/handler.go rename to x/fee_grant/handler.go index eb15b7daa79b..7fac0ae6297d 100644 --- a/x/subkeys/handler.go +++ b/x/fee_grant/handler.go @@ -1,4 +1,4 @@ -package subkeys +package fee_grant import ( "fmt" diff --git a/x/subkeys/internal/ante/fee.go b/x/fee_grant/internal/ante/fee.go similarity index 96% rename from x/subkeys/internal/ante/fee.go rename to x/fee_grant/internal/ante/fee.go index af502c7f24df..287ea2a1f279 100644 --- a/x/subkeys/internal/ante/fee.go +++ b/x/fee_grant/internal/ante/fee.go @@ -10,8 +10,8 @@ import ( authAnte "github.com/cosmos/cosmos-sdk/x/auth/ante" authKeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authTypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/subkeys/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types/tx" + "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types/tx" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) diff --git a/x/subkeys/internal/ante/fee_test.go b/x/fee_grant/internal/ante/fee_test.go similarity index 97% rename from x/subkeys/internal/ante/fee_test.go rename to x/fee_grant/internal/ante/fee_test.go index 01c6e429dfdb..f922c27d4c40 100644 --- a/x/subkeys/internal/ante/fee_test.go +++ b/x/fee_grant/internal/ante/fee_test.go @@ -13,10 +13,10 @@ import ( authante "github.com/cosmos/cosmos-sdk/x/auth/ante" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/subkeys/internal/ante" - "github.com/cosmos/cosmos-sdk/x/subkeys/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types" - "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types/tx" + "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/ante" + "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types" + "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types/tx" ) // newAnteHandler is just like auth.NewAnteHandler, except we use the DeductDelegatedFeeDecorator diff --git a/x/subkeys/internal/keeper/keeper.go b/x/fee_grant/internal/keeper/keeper.go similarity index 97% rename from x/subkeys/internal/keeper/keeper.go rename to x/fee_grant/internal/keeper/keeper.go index dd76eb956e51..72b7ea976339 100644 --- a/x/subkeys/internal/keeper/keeper.go +++ b/x/fee_grant/internal/keeper/keeper.go @@ -3,8 +3,8 @@ package keeper import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/subkeys/exported" - "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types" + "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" + "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types" ) type Keeper struct { diff --git a/x/subkeys/internal/keeper/keeper_test.go b/x/fee_grant/internal/keeper/keeper_test.go similarity index 97% rename from x/subkeys/internal/keeper/keeper_test.go rename to x/fee_grant/internal/keeper/keeper_test.go index a4589f3eed79..727032405e5b 100644 --- a/x/subkeys/internal/keeper/keeper_test.go +++ b/x/fee_grant/internal/keeper/keeper_test.go @@ -13,9 +13,9 @@ import ( codec "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/subkeys/exported" - "github.com/cosmos/cosmos-sdk/x/subkeys/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types" + "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" + "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types" ) type testInput struct { diff --git a/x/subkeys/internal/keeper/querier.go b/x/fee_grant/internal/keeper/querier.go similarity index 95% rename from x/subkeys/internal/keeper/querier.go rename to x/fee_grant/internal/keeper/querier.go index 32a5ccfd6338..4fc64535aee0 100644 --- a/x/subkeys/internal/keeper/querier.go +++ b/x/fee_grant/internal/keeper/querier.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types" + "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types" abci "github.com/tendermint/tendermint/abci/types" ) diff --git a/x/subkeys/internal/keeper/querier_test.go b/x/fee_grant/internal/keeper/querier_test.go similarity index 94% rename from x/subkeys/internal/keeper/querier_test.go rename to x/fee_grant/internal/keeper/querier_test.go index 53061eda4245..aa2618dbeaad 100644 --- a/x/subkeys/internal/keeper/querier_test.go +++ b/x/fee_grant/internal/keeper/querier_test.go @@ -9,8 +9,8 @@ import ( codec "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/subkeys/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types" + "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types" ) func TestQuery(t *testing.T) { diff --git a/x/subkeys/internal/types/basic_fee.go b/x/fee_grant/internal/types/basic_fee.go similarity index 96% rename from x/subkeys/internal/types/basic_fee.go rename to x/fee_grant/internal/types/basic_fee.go index e1057d532807..3d31a04e8378 100644 --- a/x/subkeys/internal/types/basic_fee.go +++ b/x/fee_grant/internal/types/basic_fee.go @@ -4,7 +4,7 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/subkeys/exported" + "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" ) // BasicFeeAllowance implements FeeAllowance with a one-time grant of tokens diff --git a/x/subkeys/internal/types/basic_fee_test.go b/x/fee_grant/internal/types/basic_fee_test.go similarity index 100% rename from x/subkeys/internal/types/basic_fee_test.go rename to x/fee_grant/internal/types/basic_fee_test.go diff --git a/x/subkeys/internal/types/codec.go b/x/fee_grant/internal/types/codec.go similarity index 90% rename from x/subkeys/internal/types/codec.go rename to x/fee_grant/internal/types/codec.go index cbc66ed8612f..f3cebd6d371f 100644 --- a/x/subkeys/internal/types/codec.go +++ b/x/fee_grant/internal/types/codec.go @@ -2,7 +2,7 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/subkeys/exported" + "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" ) // RegisterCodec registers the account types and interface diff --git a/x/subkeys/internal/types/errors.go b/x/fee_grant/internal/types/errors.go similarity index 100% rename from x/subkeys/internal/types/errors.go rename to x/fee_grant/internal/types/errors.go diff --git a/x/subkeys/internal/types/expiration.go b/x/fee_grant/internal/types/expiration.go similarity index 100% rename from x/subkeys/internal/types/expiration.go rename to x/fee_grant/internal/types/expiration.go diff --git a/x/subkeys/internal/types/expiration_test.go b/x/fee_grant/internal/types/expiration_test.go similarity index 100% rename from x/subkeys/internal/types/expiration_test.go rename to x/fee_grant/internal/types/expiration_test.go diff --git a/x/subkeys/internal/types/grant.go b/x/fee_grant/internal/types/grant.go similarity index 95% rename from x/subkeys/internal/types/grant.go rename to x/fee_grant/internal/types/grant.go index 76e57ec09c5d..962e0f17ac0b 100644 --- a/x/subkeys/internal/types/grant.go +++ b/x/fee_grant/internal/types/grant.go @@ -4,7 +4,7 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/subkeys/exported" + "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" ) // FeeAllowanceGrant is stored in the KVStore to record a grant with full context diff --git a/x/subkeys/internal/types/grant_test.go b/x/fee_grant/internal/types/grant_test.go similarity index 100% rename from x/subkeys/internal/types/grant_test.go rename to x/fee_grant/internal/types/grant_test.go diff --git a/x/subkeys/internal/types/key.go b/x/fee_grant/internal/types/key.go similarity index 100% rename from x/subkeys/internal/types/key.go rename to x/fee_grant/internal/types/key.go diff --git a/x/subkeys/internal/types/msgs.go b/x/fee_grant/internal/types/msgs.go similarity index 97% rename from x/subkeys/internal/types/msgs.go rename to x/fee_grant/internal/types/msgs.go index e53a3a76f601..a19eb291bd2f 100644 --- a/x/subkeys/internal/types/msgs.go +++ b/x/fee_grant/internal/types/msgs.go @@ -2,7 +2,7 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/subkeys/exported" + "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" ) // MsgDelegateFeeAllowance adds permission for Grantee to spend up to Allowance diff --git a/x/subkeys/internal/types/tx/codec.go b/x/fee_grant/internal/types/tx/codec.go similarity index 100% rename from x/subkeys/internal/types/tx/codec.go rename to x/fee_grant/internal/types/tx/codec.go diff --git a/x/subkeys/internal/types/tx/test_common.go b/x/fee_grant/internal/types/tx/test_common.go similarity index 100% rename from x/subkeys/internal/types/tx/test_common.go rename to x/fee_grant/internal/types/tx/test_common.go diff --git a/x/subkeys/internal/types/tx/tx.go b/x/fee_grant/internal/types/tx/tx.go similarity index 100% rename from x/subkeys/internal/types/tx/tx.go rename to x/fee_grant/internal/types/tx/tx.go diff --git a/x/subkeys/module.go b/x/fee_grant/module.go similarity index 98% rename from x/subkeys/module.go rename to x/fee_grant/module.go index e84faa4ec26d..de061775dc40 100644 --- a/x/subkeys/module.go +++ b/x/fee_grant/module.go @@ -1,4 +1,4 @@ -package subkeys +package fee_grant import ( "encoding/json" @@ -12,7 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - tx "github.com/cosmos/cosmos-sdk/x/subkeys/internal/types/tx" + tx "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types/tx" ) var ( From 67560991f2b21e0df6020cf78c5ed1c7e782d13e Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 24 Oct 2019 19:06:22 +0200 Subject: [PATCH 48/77] Rename subkeys/delegation to fee grant in all strings --- simapp/app.go | 28 ++++++++++---------- x/fee_grant/exported/fees.go | 2 +- x/fee_grant/internal/ante/fee.go | 4 +-- x/fee_grant/internal/ante/fee_test.go | 30 ++++++++++----------- x/fee_grant/internal/types/codec.go | 2 +- x/fee_grant/internal/types/key.go | 2 +- x/fee_grant/internal/types/msgs.go | 2 +- x/fee_grant/module.go | 38 +++++++++++++-------------- 8 files changed, 54 insertions(+), 54 deletions(-) diff --git a/simapp/app.go b/simapp/app.go index 6a3b9e9d1bf7..942c56888ecb 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -19,6 +19,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/crisis" distr "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/fee_grant" "github.com/cosmos/cosmos-sdk/x/genutil" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/mint" @@ -26,7 +27,6 @@ import ( paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/fee_grant" "github.com/cosmos/cosmos-sdk/x/supply" ) @@ -92,17 +92,17 @@ type SimApp struct { tkeys map[string]*sdk.TransientStoreKey // keepers - AccountKeeper auth.AccountKeeper - BankKeeper bank.Keeper - SupplyKeeper supply.Keeper - StakingKeeper staking.Keeper - SlashingKeeper slashing.Keeper - MintKeeper mint.Keeper - DistrKeeper distr.Keeper - GovKeeper gov.Keeper - CrisisKeeper crisis.Keeper - DelegationKeeper fee_grant.Keeper - ParamsKeeper params.Keeper + AccountKeeper auth.AccountKeeper + BankKeeper bank.Keeper + SupplyKeeper supply.Keeper + StakingKeeper staking.Keeper + SlashingKeeper slashing.Keeper + MintKeeper mint.Keeper + DistrKeeper distr.Keeper + GovKeeper gov.Keeper + CrisisKeeper crisis.Keeper + FeeGrantKeeper fee_grant.Keeper + ParamsKeeper params.Keeper // the module manager mm *module.Manager @@ -159,7 +159,7 @@ func NewSimApp( app.SlashingKeeper = slashing.NewKeeper(app.cdc, keys[slashing.StoreKey], &stakingKeeper, slashingSubspace, slashing.DefaultCodespace) app.CrisisKeeper = crisis.NewKeeper(crisisSubspace, invCheckPeriod, app.SupplyKeeper, auth.FeeCollectorName) - app.DelegationKeeper = fee_grant.NewKeeper(app.cdc, keys[fee_grant.StoreKey]) + app.FeeGrantKeeper = fee_grant.NewKeeper(app.cdc, keys[fee_grant.StoreKey]) // register the proposal types govRouter := gov.NewRouter() @@ -188,7 +188,7 @@ func NewSimApp( distr.NewAppModule(app.DistrKeeper, app.SupplyKeeper), slashing.NewAppModule(app.SlashingKeeper, app.StakingKeeper), staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper), - fee_grant.NewAppModule(app.DelegationKeeper), + fee_grant.NewAppModule(app.FeeGrantKeeper), ) // During begin block slashing happens after distr.BeginBlocker so that diff --git a/x/fee_grant/exported/fees.go b/x/fee_grant/exported/fees.go index 1ad02e65182e..4c217bff6d6e 100644 --- a/x/fee_grant/exported/fees.go +++ b/x/fee_grant/exported/fees.go @@ -7,7 +7,7 @@ import ( ) // FeeAllowance implementations are tied to a given fee delegator and delegatee, -// and are used to enforce fee delegation limits. +// and are used to enforce fee grant limits. type FeeAllowance interface { // Accept can use fee payment requested as well as timestamp/height of the current block // to determine whether or not to process this. This is checked in diff --git a/x/fee_grant/internal/ante/fee.go b/x/fee_grant/internal/ante/fee.go index 287ea2a1f279..75fd2d136d3d 100644 --- a/x/fee_grant/internal/ante/fee.go +++ b/x/fee_grant/internal/ante/fee.go @@ -62,13 +62,13 @@ func (d DeductDelegatedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simu feePayer := feeTx.FeePayer() txSigner := feeTx.MainSigner() - // ensure the delegation is allowed, if we request a different fee payer + // ensure the grant is allowed, if we request a different fee payer if !txSigner.Equals(feePayer) { allowed := d.dk.UseDelegatedFees(ctx, feePayer, txSigner, fee) if !allowed { return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s not allowed to pay fees from %s", txSigner, feePayer) } - // if there was a valid delegation, ensure that the txSigner account exists (we create it if needed) + // if there was a valid grant, ensure that the txSigner account exists (we create it if needed) signerAcc := d.ak.GetAccount(ctx, txSigner) if signerAcc == nil { signerAcc = d.ak.NewAccountWithAddress(ctx, txSigner) diff --git a/x/fee_grant/internal/ante/fee_test.go b/x/fee_grant/internal/ante/fee_test.go index f922c27d4c40..2e6b798e37fc 100644 --- a/x/fee_grant/internal/ante/fee_test.go +++ b/x/fee_grant/internal/ante/fee_test.go @@ -20,7 +20,7 @@ import ( ) // newAnteHandler is just like auth.NewAnteHandler, except we use the DeductDelegatedFeeDecorator -// in order to allow payment of fees via a delegation. +// in order to allow payment of fees via a grant. // // This is used for our full-stack tests func newAnteHandler(ak authkeeper.AccountKeeper, supplyKeeper authtypes.SupplyKeeper, dk keeper.Keeper, sigGasConsumer authante.SignatureVerificationGasConsumer) sdk.AnteHandler { @@ -46,11 +46,11 @@ func TestDeductFeesNoDelegation(t *testing.T) { app, ctx := createTestApp(true) // this just tests our handler - dfd := ante.NewDeductDelegatedFeeDecorator(app.AccountKeeper, app.SupplyKeeper, app.DelegationKeeper) + dfd := ante.NewDeductDelegatedFeeDecorator(app.AccountKeeper, app.SupplyKeeper, app.FeeGrantKeeper) ourAnteHandler := sdk.ChainAnteDecorators(dfd) // this tests the whole stack - anteHandlerStack := newAnteHandler(app.AccountKeeper, app.SupplyKeeper, app.DelegationKeeper, SigGasNoConsumer) + anteHandlerStack := newAnteHandler(app.AccountKeeper, app.SupplyKeeper, app.FeeGrantKeeper, SigGasNoConsumer) // keys and addresses priv1, _, addr1 := authtypes.KeyTestPubAddr() @@ -68,8 +68,8 @@ func TestDeductFeesNoDelegation(t *testing.T) { acc2.SetCoins([]sdk.Coin{sdk.NewCoin("atom", sdk.NewInt(99999))}) app.AccountKeeper.SetAccount(ctx, acc2) - // Set delegation from addr2 to addr3 (plenty to pay) - app.DelegationKeeper.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + // Set grant from addr2 to addr3 (plenty to pay) + app.FeeGrantKeeper.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr2, Grantee: addr3, Allowance: &types.BasicFeeAllowance{ @@ -77,8 +77,8 @@ func TestDeductFeesNoDelegation(t *testing.T) { }, }) - // Set low delegation from addr2 to addr4 (delegation will reject) - app.DelegationKeeper.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + // Set low grant from addr2 to addr4 (keeper will reject) + app.FeeGrantKeeper.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr2, Grantee: addr4, Allowance: &types.BasicFeeAllowance{ @@ -86,8 +86,8 @@ func TestDeductFeesNoDelegation(t *testing.T) { }, }) - // Set delegation from addr1 to addr4 (cannot cover this ) - app.DelegationKeeper.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + // Set grant from addr1 to addr4 (cannot cover this ) + app.FeeGrantKeeper.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr2, Grantee: addr3, Allowance: &types.BasicFeeAllowance{ @@ -138,7 +138,7 @@ func TestDeductFeesNoDelegation(t *testing.T) { handler: ourAnteHandler, valid: false, }, - "valid delegation without account (only ours)": { + "valid fee grant without account (only ours)": { signerKey: priv3, signer: addr3, feeAccount: addr2, @@ -146,7 +146,7 @@ func TestDeductFeesNoDelegation(t *testing.T) { handler: ourAnteHandler, valid: true, }, - "no delegation (only ours)": { + "no fee grant (only ours)": { signerKey: priv3, signer: addr3, feeAccount: addr1, @@ -162,7 +162,7 @@ func TestDeductFeesNoDelegation(t *testing.T) { handler: ourAnteHandler, valid: false, }, - "granter cannot cover allowed delegation (only ours)": { + "granter cannot cover allowed fee grant (only ours)": { signerKey: priv4, signer: addr4, feeAccount: addr1, @@ -206,7 +206,7 @@ func TestDeductFeesNoDelegation(t *testing.T) { handler: anteHandlerStack, valid: false, }, - "valid delegation without account (whole stack)": { + "valid fee grant without account (whole stack)": { signerKey: priv3, signer: addr3, feeAccount: addr2, @@ -214,7 +214,7 @@ func TestDeductFeesNoDelegation(t *testing.T) { handler: anteHandlerStack, valid: true, }, - "no delegation (whole stack)": { + "no fee grant (whole stack)": { signerKey: priv3, signer: addr3, feeAccount: addr1, @@ -230,7 +230,7 @@ func TestDeductFeesNoDelegation(t *testing.T) { handler: anteHandlerStack, valid: false, }, - "granter cannot cover allowed delegation (whole stack)": { + "granter cannot cover allowed fee grant (whole stack)": { signerKey: priv4, signer: addr4, feeAccount: addr1, diff --git a/x/fee_grant/internal/types/codec.go b/x/fee_grant/internal/types/codec.go index f3cebd6d371f..44c266be45b1 100644 --- a/x/fee_grant/internal/types/codec.go +++ b/x/fee_grant/internal/types/codec.go @@ -8,7 +8,7 @@ import ( // RegisterCodec registers the account types and interface func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*exported.FeeAllowance)(nil), nil) - cdc.RegisterConcrete(&BasicFeeAllowance{}, "delegation/BasicFeeAllowance", nil) + cdc.RegisterConcrete(&BasicFeeAllowance{}, "feegrant/BasicFeeAllowance", nil) } // ModuleCdc generic sealed codec to be used throughout module diff --git a/x/fee_grant/internal/types/key.go b/x/fee_grant/internal/types/key.go index 88b2901326c0..675cb41097eb 100644 --- a/x/fee_grant/internal/types/key.go +++ b/x/fee_grant/internal/types/key.go @@ -8,7 +8,7 @@ import ( const ( // ModuleName is the module name constant used in many places - ModuleName = "delegation" + ModuleName = "feegrant" // StoreKey is the store key string for supply StoreKey = ModuleName diff --git a/x/fee_grant/internal/types/msgs.go b/x/fee_grant/internal/types/msgs.go index a19eb291bd2f..f4dea99143af 100644 --- a/x/fee_grant/internal/types/msgs.go +++ b/x/fee_grant/internal/types/msgs.go @@ -7,7 +7,7 @@ import ( // MsgDelegateFeeAllowance adds permission for Grantee to spend up to Allowance // of fees from the account of Granter. -// If there was already an existing delegation, this overwrites it. +// If there was already an existing grant, this overwrites it. type MsgDelegateFeeAllowance struct { Granter sdk.AccAddress `json:"granter" yaml:"granter"` Grantee sdk.AccAddress `json:"grantee" yaml:"grantee"` diff --git a/x/fee_grant/module.go b/x/fee_grant/module.go index de061775dc40..525c918c76fe 100644 --- a/x/fee_grant/module.go +++ b/x/fee_grant/module.go @@ -20,27 +20,27 @@ var ( _ module.AppModuleBasic = AppModuleBasic{} ) -// AppModuleBasic defines the basic application module used by the delegation module. +// AppModuleBasic defines the basic application module used by the fee_grant module. type AppModuleBasic struct{} -// Name returns the delegation module's name. +// Name returns the fee_grant module's name. func (AppModuleBasic) Name() string { return ModuleName } -// RegisterCodec registers the delegation module's types for the given codec. +// RegisterCodec registers the fee_grant module's types for the given codec. func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { RegisterCodec(cdc) tx.RegisterCodec(cdc) } -// DefaultGenesis returns default genesis state as raw bytes for the delegation +// DefaultGenesis returns default genesis state as raw bytes for the fee_grant // module. func (AppModuleBasic) DefaultGenesis() json.RawMessage { return []byte("[]") } -// ValidateGenesis performs genesis state validation for the delegation module. +// ValidateGenesis performs genesis state validation for the fee_grant module. func (a AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { _, err := a.getValidatedGenesis(bz) return err @@ -55,19 +55,19 @@ func (a AppModuleBasic) getValidatedGenesis(bz json.RawMessage) (GenesisState, e return data, data.ValidateBasic() } -// RegisterRESTRoutes registers the REST routes for the delegation module. +// RegisterRESTRoutes registers the REST routes for the fee_grant module. func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { // TODO // rest.RegisterRoutes(ctx, rtr) } -// GetTxCmd returns the root tx command for the delegation module. +// GetTxCmd returns the root tx command for the fee_grant module. func (AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { // TODO return nil } -// GetQueryCmd returns no root query command for the delegation module. +// GetQueryCmd returns no root query command for the fee_grant module. func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { // TODO return nil @@ -76,7 +76,7 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { //____________________________________________________________________________ -// AppModule implements an application module for the delegation module. +// AppModule implements an application module for the fee_grant module. type AppModule struct { AppModuleBasic keeper Keeper @@ -90,35 +90,35 @@ func NewAppModule(keeper Keeper) AppModule { } } -// Name returns the delegation module's name. +// Name returns the fee_grant module's name. func (AppModule) Name() string { return ModuleName } -// RegisterInvariants registers the delegation module invariants. +// RegisterInvariants registers the fee_grant module invariants. func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} -// Route returns the message routing key for the delegation module. +// Route returns the message routing key for the fee_grant module. func (AppModule) Route() string { return RouterKey } -// NewHandler returns an sdk.Handler for the delegation module. +// NewHandler returns an sdk.Handler for the fee_grant module. func (am AppModule) NewHandler() sdk.Handler { return NewHandler(am.keeper) } -// QuerierRoute returns the delegation module's querier route name. +// QuerierRoute returns the fee_grant module's querier route name. func (AppModule) QuerierRoute() string { return QuerierRoute } -// NewQuerierHandler returns the delegation module sdk.Querier. +// NewQuerierHandler returns the fee_grant module sdk.Querier. func (am AppModule) NewQuerierHandler() sdk.Querier { return NewQuerier(am.keeper) } -// InitGenesis performs genesis initialization for the delegation module. It returns +// InitGenesis performs genesis initialization for the fee_grant module. It returns // no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { genesisState, err := am.getValidatedGenesis(data) @@ -129,7 +129,7 @@ func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.Va return []abci.ValidatorUpdate{} } -// ExportGenesis returns the exported genesis state as raw bytes for the delegation +// ExportGenesis returns the exported genesis state as raw bytes for the fee_grant // module. func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { gs, err := ExportGenesis(ctx, am.keeper) @@ -139,10 +139,10 @@ func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { return ModuleCdc.MustMarshalJSON(gs) } -// BeginBlock returns the begin blocker for the delegation module. +// BeginBlock returns the begin blocker for the fee_grant module. func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} -// EndBlock returns the end blocker for the delegation module. It returns no validator +// EndBlock returns the end blocker for the fee_grant module. It returns no validator // updates. func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} From 320bad481365f79a699c96ba461b54afe534ef3a Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 24 Oct 2019 19:08:34 +0200 Subject: [PATCH 49/77] Modify Msg and Keeper methods to use Grant not Delegate --- x/fee_grant/alias.go | 16 +++--- x/fee_grant/exported/fees.go | 4 +- x/fee_grant/genesis.go | 2 +- x/fee_grant/handler.go | 4 +- x/fee_grant/internal/ante/fee.go | 24 ++++---- x/fee_grant/internal/ante/fee_test.go | 16 +++--- x/fee_grant/internal/keeper/keeper.go | 10 ++-- x/fee_grant/internal/keeper/keeper_test.go | 22 ++++---- x/fee_grant/internal/keeper/querier_test.go | 4 +- x/fee_grant/internal/types/msgs.go | 18 +++--- x/fee_grant/internal/types/tx/codec.go | 2 +- x/fee_grant/internal/types/tx/test_common.go | 4 +- x/fee_grant/internal/types/tx/tx.go | 58 ++++++++++---------- 13 files changed, 92 insertions(+), 92 deletions(-) diff --git a/x/fee_grant/alias.go b/x/fee_grant/alias.go index 6f4011bb6b6b..4012d4533f32 100644 --- a/x/fee_grant/alias.go +++ b/x/fee_grant/alias.go @@ -34,7 +34,7 @@ var ( BlockPeriod = types.BlockPeriod FeeAllowanceKey = types.FeeAllowanceKey FeeAllowancePrefixByGrantee = types.FeeAllowancePrefixByGrantee - NewMsgDelegateFeeAllowance = types.NewMsgDelegateFeeAllowance + NewMsgGrantFeeAllowance = types.NewMsgGrantFeeAllowance NewMsgRevokeFeeAllowance = types.NewMsgRevokeFeeAllowance NewKeeper = keeper.NewKeeper NewQuerier = keeper.NewQuerier @@ -45,11 +45,11 @@ var ( ) type ( - BasicFeeAllowance = types.BasicFeeAllowance - ExpiresAt = types.ExpiresAt - Period = types.Period - FeeAllowanceGrant = types.FeeAllowanceGrant - MsgDelegateFeeAllowance = types.MsgDelegateFeeAllowance - MsgRevokeFeeAllowance = types.MsgRevokeFeeAllowance - Keeper = keeper.Keeper + BasicFeeAllowance = types.BasicFeeAllowance + ExpiresAt = types.ExpiresAt + Period = types.Period + FeeAllowanceGrant = types.FeeAllowanceGrant + MsgGrantFeeAllowance = types.MsgGrantFeeAllowance + MsgRevokeFeeAllowance = types.MsgRevokeFeeAllowance + Keeper = keeper.Keeper ) diff --git a/x/fee_grant/exported/fees.go b/x/fee_grant/exported/fees.go index 4c217bff6d6e..7f8a55ab8acf 100644 --- a/x/fee_grant/exported/fees.go +++ b/x/fee_grant/exported/fees.go @@ -11,14 +11,14 @@ import ( type FeeAllowance interface { // Accept can use fee payment requested as well as timestamp/height of the current block // to determine whether or not to process this. This is checked in - // Keeper.UseDelegatedFees and the return values should match how it is handled there. + // Keeper.UseGrantedFees and the return values should match how it is handled there. // // If it returns an error, the fee payment is rejected, otherwise it is accepted. // The FeeAllowance implementation is expected to update it's internal state // and will be saved again after an acceptance. // // If remove is true (regardless of the error), the FeeAllowance will be deleted from storage - // (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseDelegatedFees) + // (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseGrantedFees) Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (remove bool, err error) // If we export fee allowances the timing info will be quite off (eg. go from height 100000 to 0) diff --git a/x/fee_grant/genesis.go b/x/fee_grant/genesis.go index 60259d682f9b..20aebe8636f3 100644 --- a/x/fee_grant/genesis.go +++ b/x/fee_grant/genesis.go @@ -19,7 +19,7 @@ func (g GenesisState) ValidateBasic() error { // InitGenesis will initialize the keeper from a *previously validated* GenesisState func InitGenesis(ctx sdk.Context, k Keeper, gen GenesisState) { for _, f := range gen { - k.DelegateFeeAllowance(ctx, f) + k.GrantFeeAllowance(ctx, f) } } diff --git a/x/fee_grant/handler.go b/x/fee_grant/handler.go index 7fac0ae6297d..3e5773069482 100644 --- a/x/fee_grant/handler.go +++ b/x/fee_grant/handler.go @@ -10,9 +10,9 @@ func NewHandler(k Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { ctx = ctx.WithEventManager(sdk.NewEventManager()) switch msg := msg.(type) { - case MsgDelegateFeeAllowance: + case MsgGrantFeeAllowance: grant := FeeAllowanceGrant(msg) - k.DelegateFeeAllowance(ctx, grant) + k.GrantFeeAllowance(ctx, grant) return sdk.Result{} case MsgRevokeFeeAllowance: k.RevokeFeeAllowance(ctx, msg.Granter, msg.Grantee) diff --git a/x/fee_grant/internal/ante/fee.go b/x/fee_grant/internal/ante/fee.go index 75fd2d136d3d..4ae70f6e0315 100644 --- a/x/fee_grant/internal/ante/fee.go +++ b/x/fee_grant/internal/ante/fee.go @@ -17,11 +17,11 @@ import ( ) var ( - _ DelegatedFeeTx = (*tx.DelegatedTx)(nil) // assert StdTx implements DelegatedFeeTx + _ GrantedFeeTx = (*tx.FeeGrantTx)(nil) // assert StdTx implements GrantedFeeTx ) -// DelegatedFeeTx defines the interface to be implemented by Tx to use the DelegatedFeeDecorator -type DelegatedFeeTx interface { +// GrantedFeeTx defines the interface to be implemented by Tx to use the GrantedFeeDecorator +type GrantedFeeTx interface { sdk.Tx GetGas() uint64 GetFee() sdk.Coins @@ -29,28 +29,28 @@ type DelegatedFeeTx interface { MainSigner() sdk.AccAddress } -// DeductDelegatedFeeDecorator deducts fees from the first signer of the tx +// DeductGrantedFeeDecorator deducts fees from the first signer of the tx // If the first signer does not have the funds to pay for the fees, return with InsufficientFunds error // Call next AnteHandler if fees successfully deducted -// CONTRACT: Tx must implement DelegatedFeeTx interface to use DeductDelegatedFeeDecorator -type DeductDelegatedFeeDecorator struct { +// CONTRACT: Tx must implement GrantedFeeTx interface to use DeductGrantedFeeDecorator +type DeductGrantedFeeDecorator struct { ak authKeeper.AccountKeeper dk keeper.Keeper sk authTypes.SupplyKeeper } -func NewDeductDelegatedFeeDecorator(ak authKeeper.AccountKeeper, sk authTypes.SupplyKeeper, dk keeper.Keeper) DeductDelegatedFeeDecorator { - return DeductDelegatedFeeDecorator{ +func NewDeductGrantedFeeDecorator(ak authKeeper.AccountKeeper, sk authTypes.SupplyKeeper, dk keeper.Keeper) DeductGrantedFeeDecorator { + return DeductGrantedFeeDecorator{ ak: ak, dk: dk, sk: sk, } } -func (d DeductDelegatedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - feeTx, ok := tx.(DelegatedFeeTx) +func (d DeductGrantedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + feeTx, ok := tx.(GrantedFeeTx) if !ok { - return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a DelegatedFeeTx") + return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a GrantedFeeTx") } // sanity check from DeductFeeDecorator @@ -64,7 +64,7 @@ func (d DeductDelegatedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simu // ensure the grant is allowed, if we request a different fee payer if !txSigner.Equals(feePayer) { - allowed := d.dk.UseDelegatedFees(ctx, feePayer, txSigner, fee) + allowed := d.dk.UseGrantedFees(ctx, feePayer, txSigner, fee) if !allowed { return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s not allowed to pay fees from %s", txSigner, feePayer) } diff --git a/x/fee_grant/internal/ante/fee_test.go b/x/fee_grant/internal/ante/fee_test.go index 2e6b798e37fc..f97350647cee 100644 --- a/x/fee_grant/internal/ante/fee_test.go +++ b/x/fee_grant/internal/ante/fee_test.go @@ -19,7 +19,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types/tx" ) -// newAnteHandler is just like auth.NewAnteHandler, except we use the DeductDelegatedFeeDecorator +// newAnteHandler is just like auth.NewAnteHandler, except we use the DeductGrantedFeeDecorator // in order to allow payment of fees via a grant. // // This is used for our full-stack tests @@ -30,9 +30,9 @@ func newAnteHandler(ak authkeeper.AccountKeeper, supplyKeeper authtypes.SupplyKe authante.NewValidateBasicDecorator(), authante.NewValidateMemoDecorator(ak), authante.NewConsumeGasForTxSizeDecorator(ak), - // DeductDelegatedFeeDecorator will create an empty account if we sign with no tokens but valid validation + // DeductGrantedFeeDecorator will create an empty account if we sign with no tokens but valid validation // This must be before SetPubKey, ValidateSigCount, SigVerification, which error if account doesn't exist yet - ante.NewDeductDelegatedFeeDecorator(ak, supplyKeeper, dk), + ante.NewDeductGrantedFeeDecorator(ak, supplyKeeper, dk), authante.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators authante.NewValidateSigCountDecorator(ak), authante.NewSigGasConsumeDecorator(ak, sigGasConsumer), @@ -46,7 +46,7 @@ func TestDeductFeesNoDelegation(t *testing.T) { app, ctx := createTestApp(true) // this just tests our handler - dfd := ante.NewDeductDelegatedFeeDecorator(app.AccountKeeper, app.SupplyKeeper, app.FeeGrantKeeper) + dfd := ante.NewDeductGrantedFeeDecorator(app.AccountKeeper, app.SupplyKeeper, app.FeeGrantKeeper) ourAnteHandler := sdk.ChainAnteDecorators(dfd) // this tests the whole stack @@ -69,7 +69,7 @@ func TestDeductFeesNoDelegation(t *testing.T) { app.AccountKeeper.SetAccount(ctx, acc2) // Set grant from addr2 to addr3 (plenty to pay) - app.FeeGrantKeeper.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + app.FeeGrantKeeper.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr2, Grantee: addr3, Allowance: &types.BasicFeeAllowance{ @@ -78,7 +78,7 @@ func TestDeductFeesNoDelegation(t *testing.T) { }) // Set low grant from addr2 to addr4 (keeper will reject) - app.FeeGrantKeeper.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + app.FeeGrantKeeper.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr2, Grantee: addr4, Allowance: &types.BasicFeeAllowance{ @@ -87,7 +87,7 @@ func TestDeductFeesNoDelegation(t *testing.T) { }) // Set grant from addr1 to addr4 (cannot cover this ) - app.FeeGrantKeeper.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + app.FeeGrantKeeper.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr2, Grantee: addr3, Allowance: &types.BasicFeeAllowance{ @@ -244,7 +244,7 @@ func TestDeductFeesNoDelegation(t *testing.T) { tc := stc // to make scopelint happy t.Run(name, func(t *testing.T) { // msg and signatures - fee := tx.NewDelegatedFee(100000, sdk.NewCoins(sdk.NewInt64Coin("atom", tc.fee)), tc.feeAccount) + fee := tx.NewGrantedFee(100000, sdk.NewCoins(sdk.NewInt64Coin("atom", tc.fee)), tc.feeAccount) msgs := []sdk.Msg{sdk.NewTestMsg(tc.signer)} privs, accNums, seqs := []crypto.PrivKey{tc.signerKey}, []uint64{0}, []uint64{0} diff --git a/x/fee_grant/internal/keeper/keeper.go b/x/fee_grant/internal/keeper/keeper.go index 72b7ea976339..09d5d7e33867 100644 --- a/x/fee_grant/internal/keeper/keeper.go +++ b/x/fee_grant/internal/keeper/keeper.go @@ -17,8 +17,8 @@ func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey) Keeper { return Keeper{cdc: cdc, storeKey: storeKey} } -// DelegateFeeAllowance creates a new grant -func (k Keeper) DelegateFeeAllowance(ctx sdk.Context, grant types.FeeAllowanceGrant) { +// GrantFeeAllowance creates a new grant +func (k Keeper) GrantFeeAllowance(ctx sdk.Context, grant types.FeeAllowanceGrant) { store := ctx.KVStore(k.storeKey) key := types.FeeAllowanceKey(grant.Granter, grant.Grantee) bz := k.cdc.MustMarshalBinaryBare(grant) @@ -102,8 +102,8 @@ func (k Keeper) IterateAllFeeAllowances(ctx sdk.Context, cb func(types.FeeAllowa return nil } -// UseDelegatedFees will try to pay the given fee from the granter's account as requested by the grantee -func (k Keeper) UseDelegatedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins) bool { +// UseGrantedFees will try to pay the given fee from the granter's account as requested by the grantee +func (k Keeper) UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins) bool { grant, err := k.GetFeeGrant(ctx, granter, grantee) if err != nil { panic(err) @@ -122,6 +122,6 @@ func (k Keeper) UseDelegatedFees(ctx sdk.Context, granter, grantee sdk.AccAddres } // if we accepted, store the updated state of the allowance - k.DelegateFeeAllowance(ctx, grant) + k.GrantFeeAllowance(ctx, grant) return true } diff --git a/x/fee_grant/internal/keeper/keeper_test.go b/x/fee_grant/internal/keeper/keeper_test.go index 727032405e5b..8919eb2653ea 100644 --- a/x/fee_grant/internal/keeper/keeper_test.go +++ b/x/fee_grant/internal/keeper/keeper_test.go @@ -77,29 +77,29 @@ func TestKeeperCrud(t *testing.T) { } // let's set up some initial state here - k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr, Grantee: addr2, Allowance: &basic, }) - k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr, Grantee: addr3, Allowance: &basic2, }) - k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr2, Grantee: addr3, Allowance: &basic, }) - k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr2, Grantee: addr4, Allowance: &basic, }) - k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr4, Grantee: addr, Allowance: &basic2, }) // remove some, overwrite other k.RevokeFeeAllowance(ctx, addr, addr2) k.RevokeFeeAllowance(ctx, addr, addr3) - k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr, Grantee: addr3, Allowance: &basic, }) - k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr2, Grantee: addr3, Allowance: &basic2, }) @@ -180,7 +180,7 @@ func TestKeeperCrud(t *testing.T) { } } -func TestUseDelegatedFee(t *testing.T) { +func TestUseGrantedFee(t *testing.T) { input := setupTestInput() ctx := input.ctx k := input.dk @@ -248,14 +248,14 @@ func TestUseDelegatedFee(t *testing.T) { // let's set up some initial state here // addr -> addr2 (future) // addr -> addr3 (expired) - k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr, Grantee: addr2, Allowance: &future, }) - k.DelegateFeeAllowance(ctx, types.FeeAllowanceGrant{ + k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr, Grantee: addr3, Allowance: &expired, }) - allowed := k.UseDelegatedFees(ctx, tc.granter, tc.grantee, tc.fee) + allowed := k.UseGrantedFees(ctx, tc.granter, tc.grantee, tc.fee) require.Equal(t, tc.allowed, allowed) loaded, err := k.GetFeeAllowance(ctx, tc.granter, tc.grantee) diff --git a/x/fee_grant/internal/keeper/querier_test.go b/x/fee_grant/internal/keeper/querier_test.go index aa2618dbeaad..c7c9ad676a1f 100644 --- a/x/fee_grant/internal/keeper/querier_test.go +++ b/x/fee_grant/internal/keeper/querier_test.go @@ -40,8 +40,8 @@ func TestQuery(t *testing.T) { } // let's set up some initial state here - k.DelegateFeeAllowance(ctx, grant1) - k.DelegateFeeAllowance(ctx, grant2) + k.GrantFeeAllowance(ctx, grant1) + k.GrantFeeAllowance(ctx, grant2) // now try some queries cases := map[string]struct { diff --git a/x/fee_grant/internal/types/msgs.go b/x/fee_grant/internal/types/msgs.go index f4dea99143af..9a9b370b4a5c 100644 --- a/x/fee_grant/internal/types/msgs.go +++ b/x/fee_grant/internal/types/msgs.go @@ -5,28 +5,28 @@ import ( "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" ) -// MsgDelegateFeeAllowance adds permission for Grantee to spend up to Allowance +// MsgGrantFeeAllowance adds permission for Grantee to spend up to Allowance // of fees from the account of Granter. // If there was already an existing grant, this overwrites it. -type MsgDelegateFeeAllowance struct { +type MsgGrantFeeAllowance struct { Granter sdk.AccAddress `json:"granter" yaml:"granter"` Grantee sdk.AccAddress `json:"grantee" yaml:"grantee"` Allowance exported.FeeAllowance `json:"allowance" yaml:"allowance"` } -func NewMsgDelegateFeeAllowance(granter sdk.AccAddress, grantee sdk.AccAddress, allowance exported.FeeAllowance) MsgDelegateFeeAllowance { - return MsgDelegateFeeAllowance{Granter: granter, Grantee: grantee, Allowance: allowance} +func NewMsgGrantFeeAllowance(granter sdk.AccAddress, grantee sdk.AccAddress, allowance exported.FeeAllowance) MsgGrantFeeAllowance { + return MsgGrantFeeAllowance{Granter: granter, Grantee: grantee, Allowance: allowance} } -func (msg MsgDelegateFeeAllowance) Route() string { +func (msg MsgGrantFeeAllowance) Route() string { return RouterKey } -func (msg MsgDelegateFeeAllowance) Type() string { +func (msg MsgGrantFeeAllowance) Type() string { return "delegate-fee-allowance" } -func (msg MsgDelegateFeeAllowance) ValidateBasic() sdk.Error { +func (msg MsgGrantFeeAllowance) ValidateBasic() sdk.Error { if msg.Granter.Empty() { return sdk.ErrInvalidAddress("missing granter address") } @@ -36,11 +36,11 @@ func (msg MsgDelegateFeeAllowance) ValidateBasic() sdk.Error { return sdk.ConvertError(msg.Allowance.ValidateBasic()) } -func (msg MsgDelegateFeeAllowance) GetSignBytes() []byte { +func (msg MsgGrantFeeAllowance) GetSignBytes() []byte { return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) } -func (msg MsgDelegateFeeAllowance) GetSigners() []sdk.AccAddress { +func (msg MsgGrantFeeAllowance) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Granter} } diff --git a/x/fee_grant/internal/types/tx/codec.go b/x/fee_grant/internal/types/tx/codec.go index 90baa93cf075..50e482b48bd3 100644 --- a/x/fee_grant/internal/types/tx/codec.go +++ b/x/fee_grant/internal/types/tx/codec.go @@ -4,5 +4,5 @@ import "github.com/cosmos/cosmos-sdk/codec" // RegisterCodec registers concrete types on the codec func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(DelegatedTx{}, "cosmos-sdk/DelegatedTx", nil) + cdc.RegisterConcrete(FeeGrantTx{}, "cosmos-sdk/FeeGrantTx", nil) } diff --git a/x/fee_grant/internal/types/tx/test_common.go b/x/fee_grant/internal/types/tx/test_common.go index cda6ae55e8ea..f33e1c222879 100644 --- a/x/fee_grant/internal/types/tx/test_common.go +++ b/x/fee_grant/internal/types/tx/test_common.go @@ -8,7 +8,7 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) -func NewTestTx(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums []uint64, seqs []uint64, fee DelegatedFee) sdk.Tx { +func NewTestTx(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums []uint64, seqs []uint64, fee GrantedFee) sdk.Tx { sigs := make([]authtypes.StdSignature, len(privs)) for i, priv := range privs { signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, "") @@ -21,6 +21,6 @@ func NewTestTx(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums sigs[i] = authtypes.StdSignature{PubKey: priv.PubKey(), Signature: sig} } - tx := NewDelegatedTx(msgs, fee, sigs, "") + tx := NewFeeGrantTx(msgs, fee, sigs, "") return tx } diff --git a/x/fee_grant/internal/types/tx/tx.go b/x/fee_grant/internal/types/tx/tx.go index 448151b948f1..a02eb73ab95d 100644 --- a/x/fee_grant/internal/types/tx/tx.go +++ b/x/fee_grant/internal/types/tx/tx.go @@ -14,25 +14,25 @@ import ( ) var ( - _ sdk.Tx = DelegatedTx{} + _ sdk.Tx = FeeGrantTx{} maxGasWanted = uint64((1 << 63) - 1) ) -// DelegatedTx wraps a Msg with Fee and Signatures, +// FeeGrantTx wraps a Msg with Fee and Signatures, // adding the ability to delegate the fee payment // NOTE: the first signature responsible for paying fees, either directly, // or must be authorized to spend from the provided Fee.FeeAccount -type DelegatedTx struct { +type FeeGrantTx struct { Msgs []sdk.Msg `json:"msg" yaml:"msg"` - Fee DelegatedFee `json:"fee" yaml:"fee"` + Fee GrantedFee `json:"fee" yaml:"fee"` Signatures []authtypes.StdSignature `json:"signatures" yaml:"signatures"` Memo string `json:"memo" yaml:"memo"` FeeAccount sdk.AccAddress `json:"fee_account" yaml:"fee_account"` } -func NewDelegatedTx(msgs []sdk.Msg, fee DelegatedFee, sigs []authtypes.StdSignature, memo string) DelegatedTx { - return DelegatedTx{ +func NewFeeGrantTx(msgs []sdk.Msg, fee GrantedFee, sigs []authtypes.StdSignature, memo string) FeeGrantTx { + return FeeGrantTx{ Msgs: msgs, Fee: fee, Signatures: sigs, @@ -41,11 +41,11 @@ func NewDelegatedTx(msgs []sdk.Msg, fee DelegatedFee, sigs []authtypes.StdSignat } // GetMsgs returns the all the transaction's messages. -func (tx DelegatedTx) GetMsgs() []sdk.Msg { return tx.Msgs } +func (tx FeeGrantTx) GetMsgs() []sdk.Msg { return tx.Msgs } // ValidateBasic does a simple and lightweight validation check that doesn't // require access to any other information. -func (tx DelegatedTx) ValidateBasic() sdk.Error { +func (tx FeeGrantTx) ValidateBasic() sdk.Error { stdSigs := tx.GetSignatures() if tx.Fee.Gas > maxGasWanted { @@ -84,7 +84,7 @@ func CountSubKeys(pub crypto.PubKey) int { // They are accumulated from the GetSigners method for each Msg // in the order they appear in tx.GetMsgs(). // Duplicate addresses will be omitted. -func (tx DelegatedTx) GetSigners() []sdk.AccAddress { +func (tx FeeGrantTx) GetSigners() []sdk.AccAddress { seen := map[string]bool{} var signers []sdk.AccAddress for _, msg := range tx.GetMsgs() { @@ -99,7 +99,7 @@ func (tx DelegatedTx) GetSigners() []sdk.AccAddress { } // GetMemo returns the memo -func (tx DelegatedTx) GetMemo() string { return tx.Memo } +func (tx FeeGrantTx) GetMemo() string { return tx.Memo } // GetSignatures returns the signature of signers who signed the Msg. // CONTRACT: Length returned is same as length of @@ -108,7 +108,7 @@ func (tx DelegatedTx) GetMemo() string { return tx.Memo } // CONTRACT: If the signature is missing (ie the Msg is // invalid), then the corresponding signature is // .Empty(). -func (tx DelegatedTx) GetSignatures() [][]byte { +func (tx FeeGrantTx) GetSignatures() [][]byte { sigs := make([][]byte, len(tx.Signatures)) for i, stdSig := range tx.Signatures { sigs[i] = stdSig.Signature @@ -118,7 +118,7 @@ func (tx DelegatedTx) GetSignatures() [][]byte { // GetPubkeys returns the pubkeys of signers if the pubkey is included in the signature // If pubkey is not included in the signature, then nil is in the slice instead -func (tx DelegatedTx) GetPubKeys() []crypto.PubKey { +func (tx FeeGrantTx) GetPubKeys() []crypto.PubKey { pks := make([]crypto.PubKey, len(tx.Signatures)) for i, stdSig := range tx.Signatures { pks[i] = stdSig.PubKey @@ -127,7 +127,7 @@ func (tx DelegatedTx) GetPubKeys() []crypto.PubKey { } // GetSignBytes returns the signBytes of the tx for a given signer -func (tx DelegatedTx) GetSignBytes(ctx sdk.Context, acc exported.Account) []byte { +func (tx FeeGrantTx) GetSignBytes(ctx sdk.Context, acc exported.Account) []byte { genesis := ctx.BlockHeight() == 0 chainID := ctx.ChainID() var accNum uint64 @@ -137,15 +137,15 @@ func (tx DelegatedTx) GetSignBytes(ctx sdk.Context, acc exported.Account) []byte return StdSignBytes(chainID, accNum, acc.GetSequence(), tx.Fee, tx.Msgs, tx.Memo) } -// GetGas returns the Gas in DelegatedFee -func (tx DelegatedTx) GetGas() uint64 { return tx.Fee.Gas } +// GetGas returns the Gas in GrantedFee +func (tx FeeGrantTx) GetGas() uint64 { return tx.Fee.Gas } -// GetFee returns the FeeAmount in DelegatedFee -func (tx DelegatedTx) GetFee() sdk.Coins { return tx.Fee.Amount } +// GetFee returns the FeeAmount in GrantedFee +func (tx FeeGrantTx) GetFee() sdk.Coins { return tx.Fee.Amount } // FeePayer returns the address that is responsible for paying fee -// This can be explicily set in DelegatedFee, or defaults to MainSigner -func (tx DelegatedTx) FeePayer() sdk.AccAddress { +// This can be explicily set in GrantedFee, or defaults to MainSigner +func (tx FeeGrantTx) FeePayer() sdk.AccAddress { if len(tx.Fee.FeeAccount) != 0 { return tx.Fee.FeeAccount } @@ -154,25 +154,25 @@ func (tx DelegatedTx) FeePayer() sdk.AccAddress { // MainSigner returns the first signer of the tx, by default this // account is responsible for fees, if not explicitly set. -func (tx DelegatedTx) MainSigner() sdk.AccAddress { +func (tx FeeGrantTx) MainSigner() sdk.AccAddress { if len(tx.GetSigners()) != 0 { return tx.GetSigners()[0] } return sdk.AccAddress{} } -// DelegatedFee includes the amount of coins paid in fees and the maximum +// GrantedFee includes the amount of coins paid in fees and the maximum // gas to be used by the transaction. The ratio yields an effective "gasprice", // which must be above some miminum to be accepted into the mempool. -type DelegatedFee struct { +type GrantedFee struct { Amount sdk.Coins `json:"amount" yaml:"amount"` Gas uint64 `json:"gas" yaml:"gas"` FeeAccount sdk.AccAddress `json:"fee_account,omitempty" yaml:"fee_account"` } -// NewDelegatedFee returns a new instance of DelegatedFee -func NewDelegatedFee(gas uint64, amount sdk.Coins, feeAccount sdk.AccAddress) DelegatedFee { - return DelegatedFee{ +// NewGrantedFee returns a new instance of GrantedFee +func NewGrantedFee(gas uint64, amount sdk.Coins, feeAccount sdk.AccAddress) GrantedFee { + return GrantedFee{ Amount: amount, Gas: gas, FeeAccount: feeAccount, @@ -180,7 +180,7 @@ func NewDelegatedFee(gas uint64, amount sdk.Coins, feeAccount sdk.AccAddress) De } // Bytes for signing later -func (fee DelegatedFee) Bytes() []byte { +func (fee GrantedFee) Bytes() []byte { // normalize. XXX // this is a sign of something ugly // (in the lcd_test, client side its null, @@ -196,12 +196,12 @@ func (fee DelegatedFee) Bytes() []byte { return bz } -// GasPrices returns the gas prices for a DelegatedFee. +// GasPrices returns the gas prices for a GrantedFee. // // NOTE: The gas prices returned are not the true gas prices that were // originally part of the submitted transaction because the fee is computed // as fee = ceil(gasWanted * gasPrices). -func (fee DelegatedFee) GasPrices() sdk.DecCoins { +func (fee GrantedFee) GasPrices() sdk.DecCoins { return sdk.NewDecCoins(fee.Amount).QuoDec(sdk.NewDec(int64(fee.Gas))) } @@ -220,7 +220,7 @@ type DelegatedSignDoc struct { } // StdSignBytes returns the bytes to sign for a transaction. -func StdSignBytes(chainID string, accnum uint64, sequence uint64, fee DelegatedFee, msgs []sdk.Msg, memo string) []byte { +func StdSignBytes(chainID string, accnum uint64, sequence uint64, fee GrantedFee, msgs []sdk.Msg, memo string) []byte { cdc := codec.New() msgsBytes := make([]json.RawMessage, 0, len(msgs)) for _, msg := range msgs { From 4b4391bcac9500bfc2c5bf45bbdde84cc7a32200 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 29 Oct 2019 14:22:30 +0100 Subject: [PATCH 50/77] Add PeriodicFeeAllowance --- x/fee_grant/internal/types/errors.go | 8 +- x/fee_grant/internal/types/expiration.go | 60 ++++++---- x/fee_grant/internal/types/expiration_test.go | 30 ++--- x/fee_grant/internal/types/periodic_fee.go | 113 ++++++++++++++++++ 4 files changed, 171 insertions(+), 40 deletions(-) create mode 100644 x/fee_grant/internal/types/periodic_fee.go diff --git a/x/fee_grant/internal/types/errors.go b/x/fee_grant/internal/types/errors.go index 1d5c57115030..d07db5c31557 100644 --- a/x/fee_grant/internal/types/errors.go +++ b/x/fee_grant/internal/types/errors.go @@ -10,7 +10,7 @@ const ( CodeFeeLimitExceeded sdk.CodeType = 1 CodeFeeLimitExpired sdk.CodeType = 2 - CodeInvalidPeriod sdk.CodeType = 3 + CodeInvalidDuration sdk.CodeType = 3 ) // ErrFeeLimitExceeded error if there are not enough allowance to cover the fees @@ -23,7 +23,7 @@ func ErrFeeLimitExpired() sdk.Error { return sdk.NewError(DefaultCodespace, CodeFeeLimitExpired, "fee limit expired") } -// ErrInvalidPeriod error if the period is invalid or doesn't match the expiration -func ErrInvalidPeriod(reason string) sdk.Error { - return sdk.NewError(DefaultCodespace, CodeInvalidPeriod, reason) +// ErrInvalidDuration error if the Duration is invalid or doesn't match the expiration +func ErrInvalidDuration(reason string) sdk.Error { + return sdk.NewError(DefaultCodespace, CodeInvalidDuration, reason) } diff --git a/x/fee_grant/internal/types/expiration.go b/x/fee_grant/internal/types/expiration.go index 91320b59c3e3..20be853eeb65 100644 --- a/x/fee_grant/internal/types/expiration.go +++ b/x/fee_grant/internal/types/expiration.go @@ -23,10 +23,10 @@ func ExpiresAtHeight(h int64) ExpiresAt { // Note that empty expiration is allowed func (e ExpiresAt) ValidateBasic() error { if !e.Time.IsZero() && e.Height != 0 { - return ErrInvalidPeriod("both time and height are set") + return ErrInvalidDuration("both time and height are set") } if e.Height < 0 { - return ErrInvalidPeriod("negative height") + return ErrInvalidDuration("negative height") } return nil } @@ -36,6 +36,15 @@ func (e ExpiresAt) IsZero() bool { return e.Time.IsZero() && e.Height == 0 } +// FastForward produces a new Expiration with the time or height set to the +// new value, depending on what was set on the original expiration +func (e ExpiresAt) FastForward(t time.Time, h int64) ExpiresAt { + if !e.Time.IsZero() { + return ExpiresAtTime(t) + } + return ExpiresAtHeight(h) +} + // IsExpired returns if the time or height is *equal to* or greater // than the defined expiration point. Note that it is expired upon // an exact match. @@ -50,25 +59,34 @@ func (e ExpiresAt) IsExpired(t time.Time, h int64) bool { // IsCompatible returns true iff the two use the same units. // If false, they cannot be added. -func (e ExpiresAt) IsCompatible(p Period) bool { +func (e ExpiresAt) IsCompatible(p Duration) bool { if !e.Time.IsZero() { return p.Clock > 0 } return p.Block > 0 } -// Step will increase the expiration point by one period -// It returns an error if the period is incompatible -func (e *ExpiresAt) Step(p Period) error { +// Step will increase the expiration point by one Duration +// It returns an error if the Duration is incompatible +func (e ExpiresAt) Step(p Duration) (ExpiresAt, error) { if !e.IsCompatible(p) { - return ErrInvalidPeriod("expires_at and period have different units") + return ExpiresAt{}, ErrInvalidDuration("expires_at and Duration have different units") } if !e.Time.IsZero() { e.Time = e.Time.Add(p.Clock) } else { e.Height += p.Block } - return nil + return e, nil +} + +// MustStep is like Step, but panics on error +func (e ExpiresAt) MustStep(p Duration) ExpiresAt { + res, err := e.Step(p) + if err != nil { + panic(err) + } + return res } // PrepareForExport will deduct the dumpHeight from the expiration, so when this is @@ -80,37 +98,37 @@ func (e ExpiresAt) PrepareForExport(dumpTime time.Time, dumpHeight int64) Expire return e } -// Period is a repeating unit of either clock time or number of blocks. +// Duration is a repeating unit of either clock time or number of blocks. // This is designed to be added to an ExpiresAt struct. -type Period struct { +type Duration struct { Clock time.Duration `json:"clock" yaml:"clock"` Block int64 `json:"block" yaml:"block"` } -// ClockPeriod creates an period by clock time -func ClockPeriod(d time.Duration) Period { - return Period{Clock: d} +// ClockDuration creates an Duration by clock time +func ClockDuration(d time.Duration) Duration { + return Duration{Clock: d} } -// BlockPeriod creates an period by block height -func BlockPeriod(h int64) Period { - return Period{Block: h} +// BlockDuration creates an Duration by block height +func BlockDuration(h int64) Duration { + return Duration{Block: h} } // ValidateBasic performs basic sanity checks // Note that exactly one must be set and it must be positive -func (p Period) ValidateBasic() error { +func (p Duration) ValidateBasic() error { if p.Block == 0 && p.Clock == 0 { - return ErrInvalidPeriod("neither time and height are set") + return ErrInvalidDuration("neither time and height are set") } if p.Block != 0 && p.Clock != 0 { - return ErrInvalidPeriod("both time and height are set") + return ErrInvalidDuration("both time and height are set") } if p.Block < 0 { - return ErrInvalidPeriod("negative block step") + return ErrInvalidDuration("negative block step") } if p.Clock < 0 { - return ErrInvalidPeriod("negative clock step") + return ErrInvalidDuration("negative clock step") } return nil } diff --git a/x/fee_grant/internal/types/expiration_test.go b/x/fee_grant/internal/types/expiration_test.go index 0c154e2bc30f..ab475fdf6312 100644 --- a/x/fee_grant/internal/types/expiration_test.go +++ b/x/fee_grant/internal/types/expiration_test.go @@ -69,41 +69,41 @@ func TestExpiresAt(t *testing.T) { } } -func TestPeriodValid(t *testing.T) { +func TestDurationValid(t *testing.T) { now := time.Now() cases := map[string]struct { - period Period + period Duration valid bool compatible ExpiresAt incompatible ExpiresAt }{ "basic height": { - period: BlockPeriod(100), + period: BlockDuration(100), valid: true, compatible: ExpiresAtHeight(50), incompatible: ExpiresAtTime(now), }, "basic time": { - period: ClockPeriod(time.Hour), + period: ClockDuration(time.Hour), valid: true, compatible: ExpiresAtTime(now), incompatible: ExpiresAtHeight(50), }, "zero": { - period: Period{}, + period: Duration{}, valid: false, }, "double": { - period: Period{Block: 100, Clock: time.Hour}, + period: Duration{Block: 100, Clock: time.Hour}, valid: false, }, "negative clock": { - period: ClockPeriod(-1 * time.Hour), + period: ClockDuration(-1 * time.Hour), valid: false, }, "negative block": { - period: BlockPeriod(-5), + period: BlockDuration(-5), valid: false, }, } @@ -124,30 +124,30 @@ func TestPeriodValid(t *testing.T) { } } -func TestPeriodStep(t *testing.T) { +func TestDurationStep(t *testing.T) { now := time.Now() cases := map[string]struct { expires ExpiresAt - period Period + period Duration valid bool result ExpiresAt }{ "add height": { expires: ExpiresAtHeight(789), - period: BlockPeriod(100), + period: BlockDuration(100), valid: true, result: ExpiresAtHeight(889), }, "add time": { expires: ExpiresAtTime(now), - period: ClockPeriod(time.Hour), + period: ClockDuration(time.Hour), valid: true, result: ExpiresAtTime(now.Add(time.Hour)), }, "mismatch": { expires: ExpiresAtHeight(789), - period: ClockPeriod(time.Hour), + period: ClockDuration(time.Hour), valid: false, }, } @@ -160,13 +160,13 @@ func TestPeriodStep(t *testing.T) { err = tc.expires.ValidateBasic() require.NoError(t, err) - err = tc.expires.Step(tc.period) + next, err := tc.expires.Step(tc.period) if !tc.valid { require.Error(t, err) return } require.NoError(t, err) - require.Equal(t, tc.result, tc.expires) + require.Equal(t, tc.result, next) }) } } diff --git a/x/fee_grant/internal/types/periodic_fee.go b/x/fee_grant/internal/types/periodic_fee.go new file mode 100644 index 000000000000..453fa8e85d15 --- /dev/null +++ b/x/fee_grant/internal/types/periodic_fee.go @@ -0,0 +1,113 @@ +package types + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" +) + +// PeriodicFeeAllowance extends FeeAllowance to allow for both a maximum cap, +// as well as a limit per time period. +type PeriodicFeeAllowance struct { + Basic BasicFeeAllowance + + // Period is the duration of one period + Period Duration + // PeriodSpendLimit is the maximum amount of tokens to be spent in this period + PeriodSpendLimit sdk.Coins + + // PeriodCanSpend is how much is available until PeriodReset + PeriodCanSpend sdk.Coins + + // PeriodRest is when the PeriodCanSpend is updated + PeriodReset ExpiresAt +} + +var _ exported.FeeAllowance = (*PeriodicFeeAllowance)(nil) + +// Accept implements FeeAllowance and deducts the fees from the SpendLimit if possible +func (a *PeriodicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (remove bool, err error) { + if a.Basic.Expiration.IsExpired(blockTime, blockHeight) { + return true, ErrFeeLimitExpired() + } + + a.TryResetPeriod(blockTime, blockHeight) + + // deduct from both the current period and the max amount + var isNeg bool + a.PeriodSpendLimit, isNeg = a.PeriodSpendLimit.SafeSub(fee) + if isNeg { + return false, ErrFeeLimitExceeded() + } + a.Basic.SpendLimit, isNeg = a.Basic.SpendLimit.SafeSub(fee) + if isNeg { + return false, ErrFeeLimitExceeded() + } + + return a.Basic.SpendLimit.IsZero(), nil +} + +// TryResetPeriod will reset the period if we hit the conditions +func (a *PeriodicFeeAllowance) TryResetPeriod(blockTime time.Time, blockHeight int64) { + if !a.PeriodReset.IsExpired(blockTime, blockHeight) { + return + } + // set CanSpend to the lesser of PeriodSpendLimit and the TotalLimit + if _, isNeg := a.Basic.SpendLimit.SafeSub(a.PeriodSpendLimit); isNeg { + a.PeriodCanSpend = a.Basic.SpendLimit + } else { + a.PeriodCanSpend = a.PeriodSpendLimit + } + + // If we are within the period, step from expiration (eg. if you always do one tx per day, it will always reset the same time) + // If we are more then one period out (eg. no activity in a week), reset is one period from this time + a.PeriodReset = a.PeriodReset.MustStep(a.Period) + if a.PeriodReset.IsExpired(blockTime, blockHeight) { + a.PeriodReset = a.PeriodReset.FastForward(blockTime, blockHeight).MustStep(a.Period) + } +} + +// PrepareForExport adjusts all absolute block height (period reset, basic.expiration) +// with the dump height so they make sense after dump +func (a *PeriodicFeeAllowance) PrepareForExport(dumpTime time.Time, dumpHeight int64) exported.FeeAllowance { + return &PeriodicFeeAllowance{ + Basic: BasicFeeAllowance{ + SpendLimit: a.Basic.SpendLimit, + Expiration: a.Basic.Expiration.PrepareForExport(dumpTime, dumpHeight), + }, + PeriodSpendLimit: a.PeriodSpendLimit, + PeriodCanSpend: a.PeriodCanSpend, + Period: a.Period, + PeriodReset: a.PeriodReset.PrepareForExport(dumpTime, dumpHeight), + } +} + +// ValidateBasic implements FeeAllowance and enforces basic sanity checks +func (a PeriodicFeeAllowance) ValidateBasic() error { + if err := a.Basic.ValidateBasic(); err != nil { + return err + } + + if !a.PeriodSpendLimit.IsValid() { + return sdk.ErrInvalidCoins("spend amount is invalid: " + a.PeriodSpendLimit.String()) + } + if !a.PeriodSpendLimit.IsAllPositive() { + return sdk.ErrInvalidCoins("spend limit must be positive") + } + if !a.PeriodCanSpend.IsValid() { + return sdk.ErrInvalidCoins("can spend amount is invalid: " + a.PeriodCanSpend.String()) + } + // We allow 0 for CanSpend + if a.PeriodCanSpend.IsAnyNegative() { + return sdk.ErrInvalidCoins("can spend must not be negative") + } + + // TODO: ensure PeriodSpendLimit can be subtracted from total (same coin types) + + // check times + if err := a.Period.ValidateBasic(); err != nil { + return err + } + return a.PeriodReset.ValidateBasic() +} From 9ec284b10ce6c92c96b71085ff0e074fbd3398e7 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 29 Oct 2019 14:23:40 +0100 Subject: [PATCH 51/77] Update aliases --- x/fee_grant/alias.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/x/fee_grant/alias.go b/x/fee_grant/alias.go index 4012d4533f32..d1134fa1f056 100644 --- a/x/fee_grant/alias.go +++ b/x/fee_grant/alias.go @@ -14,7 +14,7 @@ const ( DefaultCodespace = types.DefaultCodespace CodeFeeLimitExceeded = types.CodeFeeLimitExceeded CodeFeeLimitExpired = types.CodeFeeLimitExpired - CodeInvalidPeriod = types.CodeInvalidPeriod + CodeInvalidDuration = types.CodeInvalidDuration ModuleName = types.ModuleName StoreKey = types.StoreKey RouterKey = types.RouterKey @@ -27,11 +27,11 @@ var ( RegisterCodec = types.RegisterCodec ErrFeeLimitExceeded = types.ErrFeeLimitExceeded ErrFeeLimitExpired = types.ErrFeeLimitExpired - ErrInvalidPeriod = types.ErrInvalidPeriod + ErrInvalidDuration = types.ErrInvalidDuration ExpiresAtTime = types.ExpiresAtTime ExpiresAtHeight = types.ExpiresAtHeight - ClockPeriod = types.ClockPeriod - BlockPeriod = types.BlockPeriod + ClockDuration = types.ClockDuration + BlockDuration = types.BlockDuration FeeAllowanceKey = types.FeeAllowanceKey FeeAllowancePrefixByGrantee = types.FeeAllowancePrefixByGrantee NewMsgGrantFeeAllowance = types.NewMsgGrantFeeAllowance @@ -47,9 +47,10 @@ var ( type ( BasicFeeAllowance = types.BasicFeeAllowance ExpiresAt = types.ExpiresAt - Period = types.Period + Duration = types.Duration FeeAllowanceGrant = types.FeeAllowanceGrant MsgGrantFeeAllowance = types.MsgGrantFeeAllowance MsgRevokeFeeAllowance = types.MsgRevokeFeeAllowance + PeriodicFeeAllowance = types.PeriodicFeeAllowance Keeper = keeper.Keeper ) From 4b34cf633881468c15d8790c801f987241e125b5 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 29 Oct 2019 14:55:22 +0100 Subject: [PATCH 52/77] Cover all accept cases for PeriodicFeeAllowance --- x/fee_grant/internal/types/periodic_fee.go | 4 +- .../internal/types/periodic_fee_test.go | 187 ++++++++++++++++++ 2 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 x/fee_grant/internal/types/periodic_fee_test.go diff --git a/x/fee_grant/internal/types/periodic_fee.go b/x/fee_grant/internal/types/periodic_fee.go index 453fa8e85d15..cfa13966f2b5 100644 --- a/x/fee_grant/internal/types/periodic_fee.go +++ b/x/fee_grant/internal/types/periodic_fee.go @@ -36,7 +36,7 @@ func (a *PeriodicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockH // deduct from both the current period and the max amount var isNeg bool - a.PeriodSpendLimit, isNeg = a.PeriodSpendLimit.SafeSub(fee) + a.PeriodCanSpend, isNeg = a.PeriodCanSpend.SafeSub(fee) if isNeg { return false, ErrFeeLimitExceeded() } @@ -50,7 +50,7 @@ func (a *PeriodicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockH // TryResetPeriod will reset the period if we hit the conditions func (a *PeriodicFeeAllowance) TryResetPeriod(blockTime time.Time, blockHeight int64) { - if !a.PeriodReset.IsExpired(blockTime, blockHeight) { + if !a.PeriodReset.IsZero() && !a.PeriodReset.IsExpired(blockTime, blockHeight) { return } // set CanSpend to the lesser of PeriodSpendLimit and the TotalLimit diff --git a/x/fee_grant/internal/types/periodic_fee_test.go b/x/fee_grant/internal/types/periodic_fee_test.go new file mode 100644 index 000000000000..940983d3037b --- /dev/null +++ b/x/fee_grant/internal/types/periodic_fee_test.go @@ -0,0 +1,187 @@ +package types + +import ( + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPeriodicFeeValidAllow(t *testing.T) { + atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) + smallAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 43)) + leftAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 512)) + oneAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 1)) + + cases := map[string]struct { + allow PeriodicFeeAllowance + // all other checks are ignored if valid=false + fee sdk.Coins + blockTime time.Time + blockHeight int64 + valid bool + accept bool + remove bool + remains sdk.Coins + remainsPeriod sdk.Coins + periodReset ExpiresAt + }{ + "empty": { + allow: PeriodicFeeAllowance{}, + valid: false, + }, + "only basic": { + allow: PeriodicFeeAllowance{ + Basic: BasicFeeAllowance{ + SpendLimit: atom, + Expiration: ExpiresAtHeight(100), + }, + }, + valid: false, + }, + "empty basic": { + allow: PeriodicFeeAllowance{ + Period: BlockDuration(50), + PeriodSpendLimit: smallAtom, + }, + valid: false, + }, + "first time": { + allow: PeriodicFeeAllowance{ + Basic: BasicFeeAllowance{ + SpendLimit: atom, + Expiration: ExpiresAtHeight(100), + }, + Period: BlockDuration(10), + PeriodSpendLimit: smallAtom, + }, + valid: true, + fee: smallAtom, + blockHeight: 75, + accept: true, + remove: false, + remainsPeriod: nil, + remains: leftAtom, + periodReset: ExpiresAtHeight(85), + }, + "same period": { + allow: PeriodicFeeAllowance{ + Basic: BasicFeeAllowance{ + SpendLimit: atom, + Expiration: ExpiresAtHeight(100), + }, + Period: BlockDuration(10), + PeriodReset: ExpiresAtHeight(80), + PeriodSpendLimit: leftAtom, + PeriodCanSpend: smallAtom, + }, + valid: true, + fee: smallAtom, + blockHeight: 75, + accept: true, + remove: false, + remainsPeriod: nil, + remains: leftAtom, + periodReset: ExpiresAtHeight(80), + }, + "step one period": { + allow: PeriodicFeeAllowance{ + Basic: BasicFeeAllowance{ + SpendLimit: atom, + Expiration: ExpiresAtHeight(100), + }, + Period: BlockDuration(10), + PeriodReset: ExpiresAtHeight(70), + PeriodSpendLimit: leftAtom, + }, + valid: true, + fee: leftAtom, + blockHeight: 75, + accept: true, + remove: false, + remainsPeriod: nil, + remains: smallAtom, + periodReset: ExpiresAtHeight(80), // one step from last reset, not now + }, + "step limited by global allowance": { + allow: PeriodicFeeAllowance{ + Basic: BasicFeeAllowance{ + SpendLimit: smallAtom, + Expiration: ExpiresAtHeight(100), + }, + Period: BlockDuration(10), + PeriodReset: ExpiresAtHeight(70), + PeriodSpendLimit: atom, + }, + valid: true, + fee: oneAtom, + blockHeight: 75, + accept: true, + remove: false, + remainsPeriod: smallAtom.Sub(oneAtom), + remains: smallAtom.Sub(oneAtom), + periodReset: ExpiresAtHeight(80), // one step from last reset, not now + }, + "expired": { + allow: PeriodicFeeAllowance{ + Basic: BasicFeeAllowance{ + SpendLimit: atom, + Expiration: ExpiresAtHeight(100), + }, + Period: BlockDuration(10), + PeriodSpendLimit: smallAtom, + }, + valid: true, + fee: smallAtom, + blockHeight: 101, + accept: false, + remove: true, + }, + "over period limit": { + allow: PeriodicFeeAllowance{ + Basic: BasicFeeAllowance{ + SpendLimit: atom, + Expiration: ExpiresAtHeight(100), + }, + Period: BlockDuration(10), + PeriodReset: ExpiresAtHeight(80), + PeriodSpendLimit: leftAtom, + PeriodCanSpend: smallAtom, + }, + valid: true, + fee: leftAtom, + blockHeight: 70, + accept: false, + remove: true, + }, + } + + for name, stc := range cases { + tc := stc // to make scopelint happy + t.Run(name, func(t *testing.T) { + err := tc.allow.ValidateBasic() + if !tc.valid { + require.Error(t, err) + return + } + require.NoError(t, err) + + // now try to deduct + remove, err := tc.allow.Accept(tc.fee, tc.blockTime, tc.blockHeight) + if !tc.accept { + require.Error(t, err) + return + } + require.NoError(t, err) + + require.Equal(t, tc.remove, remove) + if !remove { + assert.Equal(t, tc.remains, tc.allow.Basic.SpendLimit) + assert.Equal(t, tc.remainsPeriod, tc.allow.PeriodCanSpend) + assert.Equal(t, tc.periodReset, tc.allow.PeriodReset) + } + }) + } +} From 78f1f7ad1a1c3fee59d7be84082b17bf95ced0d8 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 29 Oct 2019 14:56:53 +0100 Subject: [PATCH 53/77] Et tu scopelint? --- x/fee_grant/internal/types/grant_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/x/fee_grant/internal/types/grant_test.go b/x/fee_grant/internal/types/grant_test.go index 527806f1bccd..3a07559e27e1 100644 --- a/x/fee_grant/internal/types/grant_test.go +++ b/x/fee_grant/internal/types/grant_test.go @@ -74,6 +74,7 @@ func TestGrant(t *testing.T) { } for name, tc := range cases { + tc := tc t.Run(name, func(t *testing.T) { err := tc.grant.ValidateBasic() if !tc.valid { From a1951b444d48a80ae9ed4c97c2367ced858f87e0 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 10:55:44 +0100 Subject: [PATCH 54/77] Update docs as requested --- x/fee_grant/doc.go | 3 +-- x/fee_grant/genesis.go | 4 ++-- x/fee_grant/handler.go | 3 +++ x/fee_grant/internal/ante/fee.go | 20 ++++++++++---------- x/fee_grant/internal/types/basic_fee.go | 3 +++ x/fee_grant/internal/types/periodic_fee.go | 13 ++++++++++--- 6 files changed, 29 insertions(+), 17 deletions(-) diff --git a/x/fee_grant/doc.go b/x/fee_grant/doc.go index 87e9503fcec0..0400ec9226c1 100644 --- a/x/fee_grant/doc.go +++ b/x/fee_grant/doc.go @@ -1,8 +1,7 @@ /* -Package fee_grant provides functionality for delegating sub-permissions +Package fee_grant provides functionality for delegating the payment of transaction fees from one account (key) to another account (key). -The first implementation allows the delegation for the payment of transaction fees. Effectively, this allows for a user to pay fees using the balance of an account different from their own. Example use cases would be allowing a key on a device to pay for fees using a master wallet, or a third party service allowing users to diff --git a/x/fee_grant/genesis.go b/x/fee_grant/genesis.go index 20aebe8636f3..79e581c96403 100644 --- a/x/fee_grant/genesis.go +++ b/x/fee_grant/genesis.go @@ -28,12 +28,12 @@ func InitGenesis(ctx sdk.Context, k Keeper, gen GenesisState) { // All expiration heights will be thrown off if we dump state and start at a new // chain at height 0. Thus, we allow the Allowances to "prepare themselves" // for export, like if they have expiry at 5000 and current is 4000, they export with -// expiry of 1000. It would need a new method on the FeeAllowance interface. +// expiry of 1000. Every FeeAllowance has a method `PrepareForExport` that allows +// them to perform any changes needed prior to export. func ExportGenesis(ctx sdk.Context, k Keeper) (GenesisState, error) { time, height := ctx.BlockTime(), ctx.BlockHeight() var grants []FeeAllowanceGrant err := k.IterateAllFeeAllowances(ctx, func(grant FeeAllowanceGrant) bool { - // TODO: modify each one grants = append(grants, grant.PrepareForExport(time, height)) return false }) diff --git a/x/fee_grant/handler.go b/x/fee_grant/handler.go index 3e5773069482..5dd49ca0239a 100644 --- a/x/fee_grant/handler.go +++ b/x/fee_grant/handler.go @@ -9,14 +9,17 @@ import ( func NewHandler(k Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { ctx = ctx.WithEventManager(sdk.NewEventManager()) + switch msg := msg.(type) { case MsgGrantFeeAllowance: grant := FeeAllowanceGrant(msg) k.GrantFeeAllowance(ctx, grant) return sdk.Result{} + case MsgRevokeFeeAllowance: k.RevokeFeeAllowance(ctx, msg.Granter, msg.Grantee) return sdk.Result{} + default: errMsg := fmt.Sprintf("Unrecognized data Msg type: %s", ModuleName) return sdk.ErrUnknownRequest(errMsg).Result() diff --git a/x/fee_grant/internal/ante/fee.go b/x/fee_grant/internal/ante/fee.go index 4ae70f6e0315..6f20d6a1336f 100644 --- a/x/fee_grant/internal/ante/fee.go +++ b/x/fee_grant/internal/ante/fee.go @@ -7,9 +7,9 @@ import ( // we depend on the auth module internals... maybe some more of this can be exported? // but things like `x/auth/types/FeeCollectorName` are quite clearly tied to it - authAnte "github.com/cosmos/cosmos-sdk/x/auth/ante" - authKeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - authTypes "github.com/cosmos/cosmos-sdk/x/auth/types" + authante "github.com/cosmos/cosmos-sdk/x/auth/ante" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/keeper" "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types/tx" @@ -17,7 +17,7 @@ import ( ) var ( - _ GrantedFeeTx = (*tx.FeeGrantTx)(nil) // assert StdTx implements GrantedFeeTx + _ GrantedFeeTx = (*tx.FeeGrantTx)(nil) // assert FeeGrantTx implements GrantedFeeTx ) // GrantedFeeTx defines the interface to be implemented by Tx to use the GrantedFeeDecorator @@ -34,12 +34,12 @@ type GrantedFeeTx interface { // Call next AnteHandler if fees successfully deducted // CONTRACT: Tx must implement GrantedFeeTx interface to use DeductGrantedFeeDecorator type DeductGrantedFeeDecorator struct { - ak authKeeper.AccountKeeper + ak authkeeper.AccountKeeper dk keeper.Keeper - sk authTypes.SupplyKeeper + sk authtypes.SupplyKeeper } -func NewDeductGrantedFeeDecorator(ak authKeeper.AccountKeeper, sk authTypes.SupplyKeeper, dk keeper.Keeper) DeductGrantedFeeDecorator { +func NewDeductGrantedFeeDecorator(ak authkeeper.AccountKeeper, sk authtypes.SupplyKeeper, dk keeper.Keeper) DeductGrantedFeeDecorator { return DeductGrantedFeeDecorator{ ak: ak, dk: dk, @@ -54,8 +54,8 @@ func (d DeductGrantedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula } // sanity check from DeductFeeDecorator - if addr := d.sk.GetModuleAddress(authTypes.FeeCollectorName); addr == nil { - panic(fmt.Sprintf("%s module account has not been set", authTypes.FeeCollectorName)) + if addr := d.sk.GetModuleAddress(authtypes.FeeCollectorName); addr == nil { + panic(fmt.Sprintf("%s module account has not been set", authtypes.FeeCollectorName)) } fee := feeTx.GetFee() @@ -86,7 +86,7 @@ func (d DeductGrantedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula if fee.IsZero() { return next(ctx, tx, simulate) } - err = authAnte.DeductFees(d.sk, ctx, feePayerAcc, fee) + err = authante.DeductFees(d.sk, ctx, feePayerAcc, fee) if err != nil { return ctx, err } diff --git a/x/fee_grant/internal/types/basic_fee.go b/x/fee_grant/internal/types/basic_fee.go index 3d31a04e8378..fc5a9397ff38 100644 --- a/x/fee_grant/internal/types/basic_fee.go +++ b/x/fee_grant/internal/types/basic_fee.go @@ -35,6 +35,9 @@ func (a *BasicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockHeig return left.IsZero(), nil } +// PrepareForExport will adjust the expiration based on export time. In particular, +// it will subtract the dumpHeight from any height-based expiration to ensure that +// the elapsed number of blocks this allowance is valid for is fixed. func (a *BasicFeeAllowance) PrepareForExport(dumpTime time.Time, dumpHeight int64) exported.FeeAllowance { return &BasicFeeAllowance{ SpendLimit: a.SpendLimit, diff --git a/x/fee_grant/internal/types/periodic_fee.go b/x/fee_grant/internal/types/periodic_fee.go index cfa13966f2b5..b5243a1a2b88 100644 --- a/x/fee_grant/internal/types/periodic_fee.go +++ b/x/fee_grant/internal/types/periodic_fee.go @@ -48,7 +48,12 @@ func (a *PeriodicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockH return a.Basic.SpendLimit.IsZero(), nil } -// TryResetPeriod will reset the period if we hit the conditions +// TryResetPeriod will check if the PeriodReset has been hit. If not, it is a no-op. +// If we hit the reset period, it will top up the PeriodCanSpend amount to +// min(PeriodicSpendLimit, a.Basic.SpendLimit) so it is never more than the maximum allowed. +// It will also update the PeriodReset. If we are within one Period, it will update from the +// last PeriodReset (eg. if you always do one tx per day, it will always reset the same time) +// If we are more then one period out (eg. no activity in a week), reset is one Period from the execution of this method func (a *PeriodicFeeAllowance) TryResetPeriod(blockTime time.Time, blockHeight int64) { if !a.PeriodReset.IsZero() && !a.PeriodReset.IsExpired(blockTime, blockHeight) { return @@ -68,8 +73,10 @@ func (a *PeriodicFeeAllowance) TryResetPeriod(blockTime time.Time, blockHeight i } } -// PrepareForExport adjusts all absolute block height (period reset, basic.expiration) -// with the dump height so they make sense after dump +// PrepareForExport will adjust the expiration based on export time. In particular, +// it will subtract the dumpHeight from any height-based expiration to ensure that +// the elapsed number of blocks this allowance is valid for is fixed. +// (For PeriodReset and Basic.Expiration) func (a *PeriodicFeeAllowance) PrepareForExport(dumpTime time.Time, dumpHeight int64) exported.FeeAllowance { return &PeriodicFeeAllowance{ Basic: BasicFeeAllowance{ From 17c39ca7b1f8717743534efb474248cc3d037997 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 10:59:35 +0100 Subject: [PATCH 55/77] Remove error return from GetFeeGrant --- x/fee_grant/internal/keeper/keeper.go | 28 +++++++++------------- x/fee_grant/internal/keeper/keeper_test.go | 6 ++--- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/x/fee_grant/internal/keeper/keeper.go b/x/fee_grant/internal/keeper/keeper.go index 09d5d7e33867..d99786022408 100644 --- a/x/fee_grant/internal/keeper/keeper.go +++ b/x/fee_grant/internal/keeper/keeper.go @@ -35,29 +35,26 @@ func (k Keeper) RevokeFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddr // GetFeeAllowance returns the allowance between the granter and grantee. // If there is none, it returns nil, nil. // Returns an error on parsing issues -func (k Keeper) GetFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) (exported.FeeAllowance, error) { - grant, err := k.GetFeeGrant(ctx, granter, grantee) - if err != nil { - return nil, err +func (k Keeper) GetFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) exported.FeeAllowance { + grant, found := k.GetFeeGrant(ctx, granter, grantee) + if !found { + return nil } - return grant.Allowance, nil + return grant.Allowance } // GetFeeGrant returns entire grant between both accounts -func (k Keeper) GetFeeGrant(ctx sdk.Context, granter sdk.AccAddress, grantee sdk.AccAddress) (types.FeeAllowanceGrant, error) { +func (k Keeper) GetFeeGrant(ctx sdk.Context, granter sdk.AccAddress, grantee sdk.AccAddress) (types.FeeAllowanceGrant, bool) { store := ctx.KVStore(k.storeKey) key := types.FeeAllowanceKey(granter, grantee) bz := store.Get(key) if len(bz) == 0 { - return types.FeeAllowanceGrant{}, nil + return types.FeeAllowanceGrant{}, false } var grant types.FeeAllowanceGrant - err := k.cdc.UnmarshalBinaryBare(bz, &grant) - if err != nil { - return types.FeeAllowanceGrant{}, err - } - return grant, nil + k.cdc.MustUnmarshalBinaryBare(bz, &grant) + return grant, true } // IterateAllGranteeFeeAllowances iterates over all the grants from anyone to the given grantee. @@ -104,11 +101,8 @@ func (k Keeper) IterateAllFeeAllowances(ctx sdk.Context, cb func(types.FeeAllowa // UseGrantedFees will try to pay the given fee from the granter's account as requested by the grantee func (k Keeper) UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins) bool { - grant, err := k.GetFeeGrant(ctx, granter, grantee) - if err != nil { - panic(err) - } - if grant.Allowance == nil { + grant, found := k.GetFeeGrant(ctx, granter, grantee) + if !found || grant.Allowance == nil { return false } diff --git a/x/fee_grant/internal/keeper/keeper_test.go b/x/fee_grant/internal/keeper/keeper_test.go index 8919eb2653ea..af2e685104ed 100644 --- a/x/fee_grant/internal/keeper/keeper_test.go +++ b/x/fee_grant/internal/keeper/keeper_test.go @@ -136,8 +136,7 @@ func TestKeeperCrud(t *testing.T) { for name, tc := range cases { t.Run(name, func(t *testing.T) { - allow, err := k.GetFeeAllowance(ctx, tc.granter, tc.grantee) - require.NoError(t, err) + allow := k.GetFeeAllowance(ctx, tc.granter, tc.grantee) if tc.allowance == nil { require.Nil(t, allow) return @@ -258,8 +257,7 @@ func TestUseGrantedFee(t *testing.T) { allowed := k.UseGrantedFees(ctx, tc.granter, tc.grantee, tc.fee) require.Equal(t, tc.allowed, allowed) - loaded, err := k.GetFeeAllowance(ctx, tc.granter, tc.grantee) - require.NoError(t, err) + loaded := k.GetFeeAllowance(ctx, tc.granter, tc.grantee) require.Equal(t, tc.final, loaded) }) } From 9c399bbab7137db897e7569f6566bfb591b9d725 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 11:05:33 +0100 Subject: [PATCH 56/77] Code cleanup as requested by PR --- x/fee_grant/internal/keeper/querier.go | 6 ++++-- x/fee_grant/internal/types/basic_fee.go | 13 +++++++++++-- x/fee_grant/internal/types/periodic_fee.go | 13 +++++++++++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/x/fee_grant/internal/keeper/querier.go b/x/fee_grant/internal/keeper/querier.go index 4fc64535aee0..ba5303f56f02 100644 --- a/x/fee_grant/internal/keeper/querier.go +++ b/x/fee_grant/internal/keeper/querier.go @@ -14,8 +14,10 @@ const ( // NewQuerier creates a new querier func NewQuerier(keeper Keeper) sdk.Querier { return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, sdk.Error) { - var res []byte - var err error + var ( + res []byte + err error + ) switch path[0] { case QueryGetFeeAllowances: res, err = queryGetFeeAllowances(ctx, path[1:], keeper) diff --git a/x/fee_grant/internal/types/basic_fee.go b/x/fee_grant/internal/types/basic_fee.go index fc5a9397ff38..94f83960c79a 100644 --- a/x/fee_grant/internal/types/basic_fee.go +++ b/x/fee_grant/internal/types/basic_fee.go @@ -20,8 +20,17 @@ type BasicFeeAllowance struct { var _ exported.FeeAllowance = (*BasicFeeAllowance)(nil) -// Accept implements FeeAllowance and deducts the fees from the SpendLimit if possible -func (a *BasicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (remove bool, err error) { +// Accept can use fee payment requested as well as timestamp/height of the current block +// to determine whether or not to process this. This is checked in +// Keeper.UseGrantedFees and the return values should match how it is handled there. +// +// If it returns an error, the fee payment is rejected, otherwise it is accepted. +// The FeeAllowance implementation is expected to update it's internal state +// and will be saved again after an acceptance. +// +// If remove is true (regardless of the error), the FeeAllowance will be deleted from storage +// (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseGrantedFees) +func (a *BasicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (bool, error) { if a.Expiration.IsExpired(blockTime, blockHeight) { return true, ErrFeeLimitExpired() } diff --git a/x/fee_grant/internal/types/periodic_fee.go b/x/fee_grant/internal/types/periodic_fee.go index b5243a1a2b88..96ae81554e67 100644 --- a/x/fee_grant/internal/types/periodic_fee.go +++ b/x/fee_grant/internal/types/periodic_fee.go @@ -26,8 +26,17 @@ type PeriodicFeeAllowance struct { var _ exported.FeeAllowance = (*PeriodicFeeAllowance)(nil) -// Accept implements FeeAllowance and deducts the fees from the SpendLimit if possible -func (a *PeriodicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (remove bool, err error) { +// Accept can use fee payment requested as well as timestamp/height of the current block +// to determine whether or not to process this. This is checked in +// Keeper.UseGrantedFees and the return values should match how it is handled there. +// +// If it returns an error, the fee payment is rejected, otherwise it is accepted. +// The FeeAllowance implementation is expected to update it's internal state +// and will be saved again after an acceptance. +// +// If remove is true (regardless of the error), the FeeAllowance will be deleted from storage +// (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseGrantedFees) +func (a *PeriodicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (bool, error) { if a.Basic.Expiration.IsExpired(blockTime, blockHeight) { return true, ErrFeeLimitExpired() } From 21b0ac574f7d9e938a79d49a8699edadf8243e62 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 11:24:38 +0100 Subject: [PATCH 57/77] Updated all errors to use new sdk/errors package --- x/fee_grant/alias.go | 19 +++++++-------- x/fee_grant/internal/types/basic_fee.go | 9 +++---- x/fee_grant/internal/types/errors.go | 28 ++++++++-------------- x/fee_grant/internal/types/expiration.go | 20 +++++++++------- x/fee_grant/internal/types/periodic_fee.go | 7 +++--- 5 files changed, 39 insertions(+), 44 deletions(-) diff --git a/x/fee_grant/alias.go b/x/fee_grant/alias.go index d1134fa1f056..8736c2f7e6cf 100644 --- a/x/fee_grant/alias.go +++ b/x/fee_grant/alias.go @@ -1,8 +1,8 @@ // nolint // autogenerated code using github.com/rigelrozanski/multitool // aliases generated for the following subdirectories: -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types // ALIASGEN: github.com/cosmos/cosmos-sdk/x/fee_grant/internal/keeper +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types package fee_grant import ( @@ -11,23 +11,19 @@ import ( ) const ( + QueryGetFeeAllowances = keeper.QueryGetFeeAllowances DefaultCodespace = types.DefaultCodespace - CodeFeeLimitExceeded = types.CodeFeeLimitExceeded - CodeFeeLimitExpired = types.CodeFeeLimitExpired - CodeInvalidDuration = types.CodeInvalidDuration ModuleName = types.ModuleName StoreKey = types.StoreKey RouterKey = types.RouterKey QuerierRoute = types.QuerierRoute - QueryGetFeeAllowances = keeper.QueryGetFeeAllowances ) var ( // functions aliases + NewKeeper = keeper.NewKeeper + NewQuerier = keeper.NewQuerier RegisterCodec = types.RegisterCodec - ErrFeeLimitExceeded = types.ErrFeeLimitExceeded - ErrFeeLimitExpired = types.ErrFeeLimitExpired - ErrInvalidDuration = types.ErrInvalidDuration ExpiresAtTime = types.ExpiresAtTime ExpiresAtHeight = types.ExpiresAtHeight ClockDuration = types.ClockDuration @@ -36,15 +32,17 @@ var ( FeeAllowancePrefixByGrantee = types.FeeAllowancePrefixByGrantee NewMsgGrantFeeAllowance = types.NewMsgGrantFeeAllowance NewMsgRevokeFeeAllowance = types.NewMsgRevokeFeeAllowance - NewKeeper = keeper.NewKeeper - NewQuerier = keeper.NewQuerier // variable aliases ModuleCdc = types.ModuleCdc + ErrFeeLimitExceeded = types.ErrFeeLimitExceeded + ErrFeeLimitExpired = types.ErrFeeLimitExpired + ErrInvalidDuration = types.ErrInvalidDuration FeeAllowanceKeyPrefix = types.FeeAllowanceKeyPrefix ) type ( + Keeper = keeper.Keeper BasicFeeAllowance = types.BasicFeeAllowance ExpiresAt = types.ExpiresAt Duration = types.Duration @@ -52,5 +50,4 @@ type ( MsgGrantFeeAllowance = types.MsgGrantFeeAllowance MsgRevokeFeeAllowance = types.MsgRevokeFeeAllowance PeriodicFeeAllowance = types.PeriodicFeeAllowance - Keeper = keeper.Keeper ) diff --git a/x/fee_grant/internal/types/basic_fee.go b/x/fee_grant/internal/types/basic_fee.go index 94f83960c79a..4ce41be4a65b 100644 --- a/x/fee_grant/internal/types/basic_fee.go +++ b/x/fee_grant/internal/types/basic_fee.go @@ -4,6 +4,7 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" ) @@ -32,12 +33,12 @@ var _ exported.FeeAllowance = (*BasicFeeAllowance)(nil) // (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseGrantedFees) func (a *BasicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (bool, error) { if a.Expiration.IsExpired(blockTime, blockHeight) { - return true, ErrFeeLimitExpired() + return true, sdkerrors.Wrap(ErrFeeLimitExpired, "basic allowance") } left, invalid := a.SpendLimit.SafeSub(fee) if invalid { - return false, ErrFeeLimitExceeded() + return false, sdkerrors.Wrap(ErrFeeLimitExceeded, "basic allowance") } a.SpendLimit = left @@ -57,10 +58,10 @@ func (a *BasicFeeAllowance) PrepareForExport(dumpTime time.Time, dumpHeight int6 // ValidateBasic implements FeeAllowance and enforces basic sanity checks func (a BasicFeeAllowance) ValidateBasic() error { if !a.SpendLimit.IsValid() { - return sdk.ErrInvalidCoins("send amount is invalid: " + a.SpendLimit.String()) + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "send amount is invalid: %s", a.SpendLimit) } if !a.SpendLimit.IsAllPositive() { - return sdk.ErrInvalidCoins("spend limit must be positive") + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "spend limit must be positive") } return a.Expiration.ValidateBasic() } diff --git a/x/fee_grant/internal/types/errors.go b/x/fee_grant/internal/types/errors.go index d07db5c31557..01dbe87a727d 100644 --- a/x/fee_grant/internal/types/errors.go +++ b/x/fee_grant/internal/types/errors.go @@ -1,29 +1,21 @@ package types import ( - sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) // Codes for governance errors const ( - DefaultCodespace sdk.CodespaceType = ModuleName - - CodeFeeLimitExceeded sdk.CodeType = 1 - CodeFeeLimitExpired sdk.CodeType = 2 - CodeInvalidDuration sdk.CodeType = 3 + DefaultCodespace = ModuleName ) -// ErrFeeLimitExceeded error if there are not enough allowance to cover the fees -func ErrFeeLimitExceeded() sdk.Error { - return sdk.NewError(DefaultCodespace, CodeFeeLimitExceeded, "fee limit exceeded") -} +var ( + // ErrFeeLimitExceeded error if there are not enough allowance to cover the fees + ErrFeeLimitExceeded = sdkerrors.Register(DefaultCodespace, 1, "fee limit exceeded") -// ErrFeeLimitExpired error if the allowance has expired -func ErrFeeLimitExpired() sdk.Error { - return sdk.NewError(DefaultCodespace, CodeFeeLimitExpired, "fee limit expired") -} + // ErrFeeLimitExpired error if the allowance has expired + ErrFeeLimitExpired = sdkerrors.Register(DefaultCodespace, 2, "fee limit expired") -// ErrInvalidDuration error if the Duration is invalid or doesn't match the expiration -func ErrInvalidDuration(reason string) sdk.Error { - return sdk.NewError(DefaultCodespace, CodeInvalidDuration, reason) -} + // ErrInvalidDuration error if the Duration is invalid or doesn't match the expiration + ErrInvalidDuration = sdkerrors.Register(DefaultCodespace, 3, "invalid duration") +) diff --git a/x/fee_grant/internal/types/expiration.go b/x/fee_grant/internal/types/expiration.go index 20be853eeb65..5a048eee2d0c 100644 --- a/x/fee_grant/internal/types/expiration.go +++ b/x/fee_grant/internal/types/expiration.go @@ -1,6 +1,10 @@ package types -import "time" +import ( + "time" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) // ExpiresAt is a point in time where something expires. // It may be *either* block time or block height @@ -23,10 +27,10 @@ func ExpiresAtHeight(h int64) ExpiresAt { // Note that empty expiration is allowed func (e ExpiresAt) ValidateBasic() error { if !e.Time.IsZero() && e.Height != 0 { - return ErrInvalidDuration("both time and height are set") + return sdkerrors.Wrap(ErrInvalidDuration, "both time and height are set") } if e.Height < 0 { - return ErrInvalidDuration("negative height") + return sdkerrors.Wrap(ErrInvalidDuration, "negative height") } return nil } @@ -70,7 +74,7 @@ func (e ExpiresAt) IsCompatible(p Duration) bool { // It returns an error if the Duration is incompatible func (e ExpiresAt) Step(p Duration) (ExpiresAt, error) { if !e.IsCompatible(p) { - return ExpiresAt{}, ErrInvalidDuration("expires_at and Duration have different units") + return ExpiresAt{}, sdkerrors.Wrap(ErrInvalidDuration, "expires_at and Duration have different units") } if !e.Time.IsZero() { e.Time = e.Time.Add(p.Clock) @@ -119,16 +123,16 @@ func BlockDuration(h int64) Duration { // Note that exactly one must be set and it must be positive func (p Duration) ValidateBasic() error { if p.Block == 0 && p.Clock == 0 { - return ErrInvalidDuration("neither time and height are set") + return sdkerrors.Wrap(ErrInvalidDuration, "neither time and height are set") } if p.Block != 0 && p.Clock != 0 { - return ErrInvalidDuration("both time and height are set") + return sdkerrors.Wrap(ErrInvalidDuration, "both time and height are set") } if p.Block < 0 { - return ErrInvalidDuration("negative block step") + return sdkerrors.Wrap(ErrInvalidDuration, "negative block step") } if p.Clock < 0 { - return ErrInvalidDuration("negative clock step") + return sdkerrors.Wrap(ErrInvalidDuration, "negative clock step") } return nil } diff --git a/x/fee_grant/internal/types/periodic_fee.go b/x/fee_grant/internal/types/periodic_fee.go index 96ae81554e67..dfc8b93fadd6 100644 --- a/x/fee_grant/internal/types/periodic_fee.go +++ b/x/fee_grant/internal/types/periodic_fee.go @@ -4,6 +4,7 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" ) @@ -38,7 +39,7 @@ var _ exported.FeeAllowance = (*PeriodicFeeAllowance)(nil) // (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseGrantedFees) func (a *PeriodicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (bool, error) { if a.Basic.Expiration.IsExpired(blockTime, blockHeight) { - return true, ErrFeeLimitExpired() + return true, sdkerrors.Wrap(ErrFeeLimitExpired, "absolute limit") } a.TryResetPeriod(blockTime, blockHeight) @@ -47,11 +48,11 @@ func (a *PeriodicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockH var isNeg bool a.PeriodCanSpend, isNeg = a.PeriodCanSpend.SafeSub(fee) if isNeg { - return false, ErrFeeLimitExceeded() + return false, sdkerrors.Wrap(ErrFeeLimitExceeded, "period limit") } a.Basic.SpendLimit, isNeg = a.Basic.SpendLimit.SafeSub(fee) if isNeg { - return false, ErrFeeLimitExceeded() + return false, sdkerrors.Wrap(ErrFeeLimitExceeded, "absolute limit") } return a.Basic.SpendLimit.IsZero(), nil From 2222d0eb811066494da0758e416d200c9583639a Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 11:38:54 +0100 Subject: [PATCH 58/77] Use test suite for keeper tests --- x/fee_grant/internal/keeper/keeper_test.go | 111 ++++++++++---------- x/fee_grant/internal/keeper/querier_test.go | 15 ++- 2 files changed, 64 insertions(+), 62 deletions(-) diff --git a/x/fee_grant/internal/keeper/keeper_test.go b/x/fee_grant/internal/keeper/keeper_test.go index af2e685104ed..dbff579c80b9 100644 --- a/x/fee_grant/internal/keeper/keeper_test.go +++ b/x/fee_grant/internal/keeper/keeper_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" @@ -18,18 +19,26 @@ import ( "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types" ) -type testInput struct { +type KeeperTestSuite struct { + suite.Suite + cdc *codec.Codec ctx sdk.Context dk keeper.Keeper + + addr sdk.AccAddress + addr2 sdk.AccAddress + addr3 sdk.AccAddress + addr4 sdk.AccAddress } -func setupTestInput() testInput { +func (suite *KeeperTestSuite) SetupTest() { db := dbm.NewMemDB() cdc := codec.New() types.RegisterCodec(cdc) sdk.RegisterCodec(cdc) + suite.cdc = cdc delCapKey := sdk.NewKVStoreKey("delKey") @@ -37,19 +46,15 @@ func setupTestInput() testInput { ms.MountStoreWithDB(delCapKey, sdk.StoreTypeIAVL, db) ms.LoadLatestVersion() - dk := keeper.NewKeeper(cdc, delCapKey) + suite.dk = keeper.NewKeeper(cdc, delCapKey) + suite.ctx = sdk.NewContext(ms, abci.Header{ChainID: "test-chain-id", Time: time.Now().UTC(), Height: 1234}, false, log.NewNopLogger()) - ctx := sdk.NewContext(ms, abci.Header{ChainID: "test-chain-id", Time: time.Now().UTC(), Height: 1234}, false, log.NewNopLogger()) - return testInput{cdc: cdc, ctx: ctx, dk: dk} -} + suite.addr = mustAddr("cosmos157ez5zlaq0scm9aycwphhqhmg3kws4qusmekll") + suite.addr2 = mustAddr("cosmos1rjxwm0rwyuldsg00qf5lt26wxzzppjzxs2efdw") + suite.addr3 = mustAddr("cosmos1qk93t4j0yyzgqgt6k5qf8deh8fq6smpn3ntu3x") + suite.addr4 = mustAddr("cosmos1p9qh4ldfd6n0qehujsal4k7g0e37kel90rc4ts") -var ( - // some valid cosmos keys.... - addr = mustAddr("cosmos157ez5zlaq0scm9aycwphhqhmg3kws4qusmekll") - addr2 = mustAddr("cosmos1rjxwm0rwyuldsg00qf5lt26wxzzppjzxs2efdw") - addr3 = mustAddr("cosmos1qk93t4j0yyzgqgt6k5qf8deh8fq6smpn3ntu3x") - addr4 = mustAddr("cosmos1p9qh4ldfd6n0qehujsal4k7g0e37kel90rc4ts") -) +} func mustAddr(acc string) sdk.AccAddress { addr, err := sdk.AccAddressFromBech32(acc) @@ -59,10 +64,9 @@ func mustAddr(acc string) sdk.AccAddress { return addr } -func TestKeeperCrud(t *testing.T) { - input := setupTestInput() - ctx := input.ctx - k := input.dk +func (suite *KeeperTestSuite) TestKeeperCrud(t *testing.T) { + ctx := suite.ctx + k := suite.dk // some helpers atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) @@ -78,29 +82,29 @@ func TestKeeperCrud(t *testing.T) { // let's set up some initial state here k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ - Granter: addr, Grantee: addr2, Allowance: &basic, + Granter: suite.addr, Grantee: suite.addr2, Allowance: &basic, }) k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ - Granter: addr, Grantee: addr3, Allowance: &basic2, + Granter: suite.addr, Grantee: suite.addr3, Allowance: &basic2, }) k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ - Granter: addr2, Grantee: addr3, Allowance: &basic, + Granter: suite.addr2, Grantee: suite.addr3, Allowance: &basic, }) k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ - Granter: addr2, Grantee: addr4, Allowance: &basic, + Granter: suite.addr2, Grantee: suite.addr4, Allowance: &basic, }) k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ - Granter: addr4, Grantee: addr, Allowance: &basic2, + Granter: suite.addr4, Grantee: suite.addr, Allowance: &basic2, }) // remove some, overwrite other - k.RevokeFeeAllowance(ctx, addr, addr2) - k.RevokeFeeAllowance(ctx, addr, addr3) + k.RevokeFeeAllowance(ctx, suite.addr, suite.addr2) + k.RevokeFeeAllowance(ctx, suite.addr, suite.addr3) k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ - Granter: addr, Grantee: addr3, Allowance: &basic, + Granter: suite.addr, Grantee: suite.addr3, Allowance: &basic, }) k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ - Granter: addr2, Grantee: addr3, Allowance: &basic2, + Granter: suite.addr2, Grantee: suite.addr3, Allowance: &basic2, }) // end state: @@ -115,21 +119,21 @@ func TestKeeperCrud(t *testing.T) { allowance exported.FeeAllowance }{ "addr revoked": { - granter: addr, - grantee: addr2, + granter: suite.addr, + grantee: suite.addr2, }, "addr revoked and added": { - granter: addr, - grantee: addr3, + granter: suite.addr, + grantee: suite.addr3, allowance: &basic, }, "addr never there": { - granter: addr, - grantee: addr4, + granter: suite.addr, + grantee: suite.addr4, }, "addr modified": { - granter: addr2, - grantee: addr3, + granter: suite.addr2, + grantee: suite.addr3, allowance: &basic2, }, } @@ -151,17 +155,17 @@ func TestKeeperCrud(t *testing.T) { grants []types.FeeAllowanceGrant }{ "addr2 has none": { - grantee: addr2, + grantee: suite.addr2, }, "addr has one": { - grantee: addr, - grants: []types.FeeAllowanceGrant{{Granter: addr4, Grantee: addr, Allowance: &basic2}}, + grantee: suite.addr, + grants: []types.FeeAllowanceGrant{{Granter: suite.addr4, Grantee: suite.addr, Allowance: &basic2}}, }, "addr3 has two": { - grantee: addr3, + grantee: suite.addr3, grants: []types.FeeAllowanceGrant{ - {Granter: addr, Grantee: addr3, Allowance: &basic}, - {Granter: addr2, Grantee: addr3, Allowance: &basic2}, + {Granter: suite.addr, Grantee: suite.addr3, Allowance: &basic}, + {Granter: suite.addr2, Grantee: suite.addr3, Allowance: &basic2}, }, }, } @@ -179,10 +183,9 @@ func TestKeeperCrud(t *testing.T) { } } -func TestUseGrantedFee(t *testing.T) { - input := setupTestInput() - ctx := input.ctx - k := input.dk +func (suite *KeeperTestSuite) TestUseGrantedFee(t *testing.T) { + ctx := suite.ctx + k := suite.dk // some helpers atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) @@ -213,29 +216,29 @@ func TestUseGrantedFee(t *testing.T) { final exported.FeeAllowance }{ "use entire pot": { - granter: addr, - grantee: addr2, + granter: suite.addr, + grantee: suite.addr2, fee: atom, allowed: true, final: nil, }, "expired and removed": { - granter: addr, - grantee: addr3, + granter: suite.addr, + grantee: suite.addr3, fee: eth, allowed: false, final: nil, }, "too high": { - granter: addr, - grantee: addr2, + granter: suite.addr, + grantee: suite.addr2, fee: hugeAtom, allowed: false, final: &future, }, "use a little": { - granter: addr, - grantee: addr2, + granter: suite.addr, + grantee: suite.addr2, fee: smallAtom, allowed: true, final: &futureAfterSmall, @@ -248,10 +251,10 @@ func TestUseGrantedFee(t *testing.T) { // addr -> addr2 (future) // addr -> addr3 (expired) k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ - Granter: addr, Grantee: addr2, Allowance: &future, + Granter: suite.addr, Grantee: suite.addr2, Allowance: &future, }) k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ - Granter: addr, Grantee: addr3, Allowance: &expired, + Granter: suite.addr, Grantee: suite.addr3, Allowance: &expired, }) allowed := k.UseGrantedFees(ctx, tc.granter, tc.grantee, tc.fee) diff --git a/x/fee_grant/internal/keeper/querier_test.go b/x/fee_grant/internal/keeper/querier_test.go index c7c9ad676a1f..ea6d76c4441c 100644 --- a/x/fee_grant/internal/keeper/querier_test.go +++ b/x/fee_grant/internal/keeper/querier_test.go @@ -13,26 +13,25 @@ import ( "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types" ) -func TestQuery(t *testing.T) { - input := setupTestInput() - ctx := input.ctx - k := input.dk +func (suite *KeeperTestSuite) TestQuery(t *testing.T) { + ctx := suite.ctx + k := suite.dk cdc := codec.New() types.RegisterCodec(cdc) // some helpers grant1 := types.FeeAllowanceGrant{ - Granter: addr, - Grantee: addr3, + Granter: suite.addr, + Grantee: suite.addr3, Allowance: &types.BasicFeeAllowance{ SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 555)), Expiration: types.ExpiresAtHeight(334455), }, } grant2 := types.FeeAllowanceGrant{ - Granter: addr2, - Grantee: addr3, + Granter: suite.addr2, + Grantee: suite.addr3, Allowance: &types.BasicFeeAllowance{ SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("eth", 123)), Expiration: types.ExpiresAtHeight(334455), From eda42d4fe08d1dfda9c42a86b0aa1ace1a363f25 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 12:12:33 +0100 Subject: [PATCH 59/77] Clean up alias.go file --- x/fee_grant/alias.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x/fee_grant/alias.go b/x/fee_grant/alias.go index 8736c2f7e6cf..c91f79291ab1 100644 --- a/x/fee_grant/alias.go +++ b/x/fee_grant/alias.go @@ -1,9 +1,10 @@ +package fee_grant + // nolint // autogenerated code using github.com/rigelrozanski/multitool // aliases generated for the following subdirectories: // ALIASGEN: github.com/cosmos/cosmos-sdk/x/fee_grant/internal/keeper // ALIASGEN: github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types -package fee_grant import ( "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/keeper" From a56b472b1f5a07e185c85aaf4ca741411b4c74ab Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 12:19:54 +0100 Subject: [PATCH 60/77] Define expected interfaces in exported, rather than importing from account --- x/fee_grant/exported/external.go | 21 +++++++++++++++++++++ x/fee_grant/internal/ante/fee.go | 9 +++++---- 2 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 x/fee_grant/exported/external.go diff --git a/x/fee_grant/exported/external.go b/x/fee_grant/exported/external.go new file mode 100644 index 000000000000..df0e9625ae24 --- /dev/null +++ b/x/fee_grant/exported/external.go @@ -0,0 +1,21 @@ +package exported + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + auth "github.com/cosmos/cosmos-sdk/x/auth/exported" + supply "github.com/cosmos/cosmos-sdk/x/supply/exported" +) + +// SupplyKeeper defines the expected supply Keeper (noalias) +type SupplyKeeper interface { + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) sdk.Error + GetModuleAccount(ctx sdk.Context, moduleName string) supply.ModuleAccountI + GetModuleAddress(moduleName string) sdk.AccAddress +} + +// SupplyKeeper defines the expected auth Account Keeper (noalias) +type AccountKeeper interface { + NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) auth.Account + GetAccount(ctx sdk.Context, addr sdk.AccAddress) auth.Account + SetAccount(ctx sdk.Context, acc auth.Account) +} diff --git a/x/fee_grant/internal/ante/fee.go b/x/fee_grant/internal/ante/fee.go index 6f20d6a1336f..c4abba0fb961 100644 --- a/x/fee_grant/internal/ante/fee.go +++ b/x/fee_grant/internal/ante/fee.go @@ -8,8 +8,9 @@ import ( // we depend on the auth module internals... maybe some more of this can be exported? // but things like `x/auth/types/FeeCollectorName` are quite clearly tied to it authante "github.com/cosmos/cosmos-sdk/x/auth/ante" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/keeper" "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types/tx" @@ -34,12 +35,12 @@ type GrantedFeeTx interface { // Call next AnteHandler if fees successfully deducted // CONTRACT: Tx must implement GrantedFeeTx interface to use DeductGrantedFeeDecorator type DeductGrantedFeeDecorator struct { - ak authkeeper.AccountKeeper + ak exported.AccountKeeper dk keeper.Keeper - sk authtypes.SupplyKeeper + sk exported.SupplyKeeper } -func NewDeductGrantedFeeDecorator(ak authkeeper.AccountKeeper, sk authtypes.SupplyKeeper, dk keeper.Keeper) DeductGrantedFeeDecorator { +func NewDeductGrantedFeeDecorator(ak exported.AccountKeeper, sk exported.SupplyKeeper, dk keeper.Keeper) DeductGrantedFeeDecorator { return DeductGrantedFeeDecorator{ ak: ak, dk: dk, From be29328098e3f1c0b2071e4ebbaddef1954554a4 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 12:21:54 +0100 Subject: [PATCH 61/77] Remove dependency on auth/ante --- x/fee_grant/internal/ante/fee.go | 38 ++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/x/fee_grant/internal/ante/fee.go b/x/fee_grant/internal/ante/fee.go index c4abba0fb961..42af0f78f35a 100644 --- a/x/fee_grant/internal/ante/fee.go +++ b/x/fee_grant/internal/ante/fee.go @@ -7,7 +7,7 @@ import ( // we depend on the auth module internals... maybe some more of this can be exported? // but things like `x/auth/types/FeeCollectorName` are quite clearly tied to it - authante "github.com/cosmos/cosmos-sdk/x/auth/ante" + auth "github.com/cosmos/cosmos-sdk/x/auth/exported" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" @@ -87,9 +87,43 @@ func (d DeductGrantedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula if fee.IsZero() { return next(ctx, tx, simulate) } - err = authante.DeductFees(d.sk, ctx, feePayerAcc, fee) + err = DeductFees(d.sk, ctx, feePayerAcc, fee) if err != nil { return ctx, err } return next(ctx, tx, simulate) } + +// DeductFees deducts fees from the given account. +// +// Copied from auth/ante to avoid the import +func DeductFees(supplyKeeper exported.SupplyKeeper, ctx sdk.Context, acc auth.Account, fees sdk.Coins) error { + blockTime := ctx.BlockHeader().Time + coins := acc.GetCoins() + + if !fees.IsValid() { + return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees) + } + + // verify the account has enough funds to pay for fees + _, hasNeg := coins.SafeSub(fees) + if hasNeg { + return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, + "insufficient funds to pay for fees; %s < %s", coins, fees) + } + + // Validate the account has enough "spendable" coins as this will cover cases + // such as vesting accounts. + spendableCoins := acc.SpendableCoins(blockTime) + if _, hasNeg := spendableCoins.SafeSub(fees); hasNeg { + return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, + "insufficient funds to pay for fees; %s < %s", spendableCoins, fees) + } + + err := supplyKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), authtypes.FeeCollectorName, fees) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error()) + } + + return nil +} From 786bf0199d7288df641ff895590b1e9a22825c97 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 12:29:43 +0100 Subject: [PATCH 62/77] Improve godoc, Logger --- x/fee_grant/internal/keeper/keeper.go | 13 ++++++++++++- x/fee_grant/internal/types/codec.go | 1 + x/fee_grant/internal/types/msgs.go | 2 +- x/fee_grant/internal/types/periodic_fee.go | 3 +++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/x/fee_grant/internal/keeper/keeper.go b/x/fee_grant/internal/keeper/keeper.go index d99786022408..e1b55fb541bd 100644 --- a/x/fee_grant/internal/keeper/keeper.go +++ b/x/fee_grant/internal/keeper/keeper.go @@ -1,22 +1,33 @@ package keeper import ( + "fmt" + + "github.com/tendermint/tendermint/libs/log" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types" ) +// Keeper manages state of all fee grants, as well as calculating approval. +// It must have a codec with all available allowances registered. type Keeper struct { cdc *codec.Codec storeKey sdk.StoreKey } -// NewKeeper creates a DelegationKeeper +// NewKeeper creates a fee grant Keeper func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey) Keeper { return Keeper{cdc: cdc, storeKey: storeKey} } +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} + // GrantFeeAllowance creates a new grant func (k Keeper) GrantFeeAllowance(ctx sdk.Context, grant types.FeeAllowanceGrant) { store := ctx.KVStore(k.storeKey) diff --git a/x/fee_grant/internal/types/codec.go b/x/fee_grant/internal/types/codec.go index 44c266be45b1..91457c313327 100644 --- a/x/fee_grant/internal/types/codec.go +++ b/x/fee_grant/internal/types/codec.go @@ -9,6 +9,7 @@ import ( func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*exported.FeeAllowance)(nil), nil) cdc.RegisterConcrete(&BasicFeeAllowance{}, "feegrant/BasicFeeAllowance", nil) + cdc.RegisterConcrete(&PeriodicFeeAllowance{}, "feegrant/PeriodicFeeAllowance", nil) } // ModuleCdc generic sealed codec to be used throughout module diff --git a/x/fee_grant/internal/types/msgs.go b/x/fee_grant/internal/types/msgs.go index 9a9b370b4a5c..885914d4626b 100644 --- a/x/fee_grant/internal/types/msgs.go +++ b/x/fee_grant/internal/types/msgs.go @@ -23,7 +23,7 @@ func (msg MsgGrantFeeAllowance) Route() string { } func (msg MsgGrantFeeAllowance) Type() string { - return "delegate-fee-allowance" + return "grant-fee-allowance" } func (msg MsgGrantFeeAllowance) ValidateBasic() sdk.Error { diff --git a/x/fee_grant/internal/types/periodic_fee.go b/x/fee_grant/internal/types/periodic_fee.go index dfc8b93fadd6..9f4211c039fd 100644 --- a/x/fee_grant/internal/types/periodic_fee.go +++ b/x/fee_grant/internal/types/periodic_fee.go @@ -11,6 +11,9 @@ import ( // PeriodicFeeAllowance extends FeeAllowance to allow for both a maximum cap, // as well as a limit per time period. type PeriodicFeeAllowance struct { + // Basic contains the absolute limits over all time. + // These limit (total and expiration) are enforced in addition to the + // periodic limits defined below (which renew every period) Basic BasicFeeAllowance // Period is the duration of one period From 5f2bcb5b5d9f7adf44a1af0ae3ac6d8e2a334499 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 12:34:19 +0100 Subject: [PATCH 63/77] Cleaned up ExpiresAt --- x/fee_grant/internal/types/expiration.go | 30 ++++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/x/fee_grant/internal/types/expiration.go b/x/fee_grant/internal/types/expiration.go index 5a048eee2d0c..7c552f02e8d9 100644 --- a/x/fee_grant/internal/types/expiration.go +++ b/x/fee_grant/internal/types/expiration.go @@ -63,30 +63,30 @@ func (e ExpiresAt) IsExpired(t time.Time, h int64) bool { // IsCompatible returns true iff the two use the same units. // If false, they cannot be added. -func (e ExpiresAt) IsCompatible(p Duration) bool { +func (e ExpiresAt) IsCompatible(d Duration) bool { if !e.Time.IsZero() { - return p.Clock > 0 + return d.Clock > 0 } - return p.Block > 0 + return d.Block > 0 } // Step will increase the expiration point by one Duration // It returns an error if the Duration is incompatible -func (e ExpiresAt) Step(p Duration) (ExpiresAt, error) { - if !e.IsCompatible(p) { - return ExpiresAt{}, sdkerrors.Wrap(ErrInvalidDuration, "expires_at and Duration have different units") +func (e ExpiresAt) Step(d Duration) (ExpiresAt, error) { + if !e.IsCompatible(d) { + return ExpiresAt{}, sdkerrors.Wrap(ErrInvalidDuration, "expiration time and provided duration have different units") } if !e.Time.IsZero() { - e.Time = e.Time.Add(p.Clock) + e.Time = e.Time.Add(d.Clock) } else { - e.Height += p.Block + e.Height += d.Block } return e, nil } // MustStep is like Step, but panics on error -func (e ExpiresAt) MustStep(p Duration) ExpiresAt { - res, err := e.Step(p) +func (e ExpiresAt) MustStep(d Duration) ExpiresAt { + res, err := e.Step(d) if err != nil { panic(err) } @@ -121,17 +121,17 @@ func BlockDuration(h int64) Duration { // ValidateBasic performs basic sanity checks // Note that exactly one must be set and it must be positive -func (p Duration) ValidateBasic() error { - if p.Block == 0 && p.Clock == 0 { +func (d Duration) ValidateBasic() error { + if d.Block == 0 && d.Clock == 0 { return sdkerrors.Wrap(ErrInvalidDuration, "neither time and height are set") } - if p.Block != 0 && p.Clock != 0 { + if d.Block != 0 && d.Clock != 0 { return sdkerrors.Wrap(ErrInvalidDuration, "both time and height are set") } - if p.Block < 0 { + if d.Block < 0 { return sdkerrors.Wrap(ErrInvalidDuration, "negative block step") } - if p.Clock < 0 { + if d.Clock < 0 { return sdkerrors.Wrap(ErrInvalidDuration, "negative clock step") } return nil From 314b099b44222e71c2a084f0999db346216f8269 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 12:42:40 +0100 Subject: [PATCH 64/77] Improve error reporting with UseGrantedFee --- x/fee_grant/internal/ante/fee.go | 6 +++--- x/fee_grant/internal/keeper/keeper.go | 12 +++++++----- x/fee_grant/internal/types/errors.go | 3 +++ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/x/fee_grant/internal/ante/fee.go b/x/fee_grant/internal/ante/fee.go index 42af0f78f35a..365b904d81e3 100644 --- a/x/fee_grant/internal/ante/fee.go +++ b/x/fee_grant/internal/ante/fee.go @@ -65,9 +65,9 @@ func (d DeductGrantedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula // ensure the grant is allowed, if we request a different fee payer if !txSigner.Equals(feePayer) { - allowed := d.dk.UseGrantedFees(ctx, feePayer, txSigner, fee) - if !allowed { - return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s not allowed to pay fees from %s", txSigner, feePayer) + err := d.dk.UseGrantedFees(ctx, feePayer, txSigner, fee) + if err != nil { + return ctx, sdkerrors.Wrapf(err, "%s not allowed to pay fees from %s", txSigner, feePayer) } // if there was a valid grant, ensure that the txSigner account exists (we create it if needed) signerAcc := d.ak.GetAccount(ctx, txSigner) diff --git a/x/fee_grant/internal/keeper/keeper.go b/x/fee_grant/internal/keeper/keeper.go index e1b55fb541bd..6ebb4c9d8c32 100644 --- a/x/fee_grant/internal/keeper/keeper.go +++ b/x/fee_grant/internal/keeper/keeper.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types" ) @@ -111,22 +112,23 @@ func (k Keeper) IterateAllFeeAllowances(ctx sdk.Context, cb func(types.FeeAllowa } // UseGrantedFees will try to pay the given fee from the granter's account as requested by the grantee -func (k Keeper) UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins) bool { +func (k Keeper) UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins) error { grant, found := k.GetFeeGrant(ctx, granter, grantee) if !found || grant.Allowance == nil { - return false + return sdkerrors.Wrapf(types.ErrNoAllowance, "grant missing") } remove, err := grant.Allowance.Accept(fee, ctx.BlockTime(), ctx.BlockHeight()) if remove { k.RevokeFeeAllowance(ctx, granter, grantee) - return err == nil + // note this returns nil if err == nil + return sdkerrors.Wrap(err, "removed grant") } if err != nil { - return false + return sdkerrors.Wrap(err, "invalid grant") } // if we accepted, store the updated state of the allowance k.GrantFeeAllowance(ctx, grant) - return true + return nil } diff --git a/x/fee_grant/internal/types/errors.go b/x/fee_grant/internal/types/errors.go index 01dbe87a727d..cea99327f440 100644 --- a/x/fee_grant/internal/types/errors.go +++ b/x/fee_grant/internal/types/errors.go @@ -18,4 +18,7 @@ var ( // ErrInvalidDuration error if the Duration is invalid or doesn't match the expiration ErrInvalidDuration = sdkerrors.Register(DefaultCodespace, 3, "invalid duration") + + // ErrNoAllowance error if there is no allowance for that pair + ErrNoAllowance = sdkerrors.Register(DefaultCodespace, 4, "no allowance") ) From 29958e0d9ba8df1afd684c608a72ef4d236185cd Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 12:48:23 +0100 Subject: [PATCH 65/77] Enforce period limit subset of basic limit --- x/fee_grant/internal/types/periodic_fee.go | 13 ++++++++----- x/fee_grant/internal/types/periodic_fee_test.go | 12 ++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/x/fee_grant/internal/types/periodic_fee.go b/x/fee_grant/internal/types/periodic_fee.go index 9f4211c039fd..459deb41c776 100644 --- a/x/fee_grant/internal/types/periodic_fee.go +++ b/x/fee_grant/internal/types/periodic_fee.go @@ -110,20 +110,23 @@ func (a PeriodicFeeAllowance) ValidateBasic() error { } if !a.PeriodSpendLimit.IsValid() { - return sdk.ErrInvalidCoins("spend amount is invalid: " + a.PeriodSpendLimit.String()) + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "spend amount is invalid: %s", a.PeriodSpendLimit) } if !a.PeriodSpendLimit.IsAllPositive() { - return sdk.ErrInvalidCoins("spend limit must be positive") + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "spend limit must be positive") } if !a.PeriodCanSpend.IsValid() { - return sdk.ErrInvalidCoins("can spend amount is invalid: " + a.PeriodCanSpend.String()) + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "can spend amount is invalid: %s", a.PeriodCanSpend) } // We allow 0 for CanSpend if a.PeriodCanSpend.IsAnyNegative() { - return sdk.ErrInvalidCoins("can spend must not be negative") + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "can spend must not be negative") } - // TODO: ensure PeriodSpendLimit can be subtracted from total (same coin types) + // ensure PeriodSpendLimit can be subtracted from total (same coin types) + if !a.PeriodSpendLimit.DenomsSubsetOf(a.Basic.SpendLimit) { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "period spend limit has different currency than basic spend limit") + } // check times if err := a.Period.ValidateBasic(); err != nil { diff --git a/x/fee_grant/internal/types/periodic_fee_test.go b/x/fee_grant/internal/types/periodic_fee_test.go index 940983d3037b..40a8ca864184 100644 --- a/x/fee_grant/internal/types/periodic_fee_test.go +++ b/x/fee_grant/internal/types/periodic_fee_test.go @@ -14,6 +14,7 @@ func TestPeriodicFeeValidAllow(t *testing.T) { smallAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 43)) leftAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 512)) oneAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 1)) + eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 1)) cases := map[string]struct { allow PeriodicFeeAllowance @@ -48,6 +49,17 @@ func TestPeriodicFeeValidAllow(t *testing.T) { }, valid: false, }, + "mismatched currencies": { + allow: PeriodicFeeAllowance{ + Basic: BasicFeeAllowance{ + SpendLimit: atom, + Expiration: ExpiresAtHeight(100), + }, + Period: BlockDuration(10), + PeriodSpendLimit: eth, + }, + valid: false, + }, "first time": { allow: PeriodicFeeAllowance{ Basic: BasicFeeAllowance{ From fb5b97ab78ac1a14dc76c19c2f84706b2afd0aac Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 13:00:59 +0100 Subject: [PATCH 66/77] Add events --- x/fee_grant/handler.go | 18 +++++++++++++----- x/fee_grant/internal/keeper/keeper.go | 23 +++++++++++++++++++++++ x/fee_grant/internal/types/events.go | 11 +++++++++++ 3 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 x/fee_grant/internal/types/events.go diff --git a/x/fee_grant/handler.go b/x/fee_grant/handler.go index 5dd49ca0239a..7382b5d04cad 100644 --- a/x/fee_grant/handler.go +++ b/x/fee_grant/handler.go @@ -12,13 +12,10 @@ func NewHandler(k Keeper) sdk.Handler { switch msg := msg.(type) { case MsgGrantFeeAllowance: - grant := FeeAllowanceGrant(msg) - k.GrantFeeAllowance(ctx, grant) - return sdk.Result{} + return handleGrantFee(ctx, k, msg) case MsgRevokeFeeAllowance: - k.RevokeFeeAllowance(ctx, msg.Granter, msg.Grantee) - return sdk.Result{} + return handleRevokeFee(ctx, k, msg) default: errMsg := fmt.Sprintf("Unrecognized data Msg type: %s", ModuleName) @@ -26,3 +23,14 @@ func NewHandler(k Keeper) sdk.Handler { } } } + +func handleGrantFee(ctx sdk.Context, k Keeper, msg MsgGrantFeeAllowance) sdk.Result { + grant := FeeAllowanceGrant(msg) + k.GrantFeeAllowance(ctx, grant) + return sdk.Result{} +} + +func handleRevokeFee(ctx sdk.Context, k Keeper, msg MsgRevokeFeeAllowance) sdk.Result { + k.RevokeFeeAllowance(ctx, msg.Granter, msg.Grantee) + return sdk.Result{} +} diff --git a/x/fee_grant/internal/keeper/keeper.go b/x/fee_grant/internal/keeper/keeper.go index 6ebb4c9d8c32..d5bc7c63f3ba 100644 --- a/x/fee_grant/internal/keeper/keeper.go +++ b/x/fee_grant/internal/keeper/keeper.go @@ -35,6 +35,13 @@ func (k Keeper) GrantFeeAllowance(ctx sdk.Context, grant types.FeeAllowanceGrant key := types.FeeAllowanceKey(grant.Granter, grant.Grantee) bz := k.cdc.MustMarshalBinaryBare(grant) store.Set(key, bz) + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeSetFeeGrant, + sdk.NewAttribute(types.AttributeKeyGranter, grant.Granter.String()), + sdk.NewAttribute(types.AttributeKeyGrantee, grant.Grantee.String()), + ), + ) } // RevokeFeeAllowance removes an existing grant @@ -42,6 +49,13 @@ func (k Keeper) RevokeFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddr store := ctx.KVStore(k.storeKey) key := types.FeeAllowanceKey(granter, grantee) store.Delete(key) + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeRevokeFeeGrant, + sdk.NewAttribute(types.AttributeKeyGranter, granter.String()), + sdk.NewAttribute(types.AttributeKeyGrantee, grantee.String()), + ), + ) } // GetFeeAllowance returns the allowance between the granter and grantee. @@ -119,6 +133,15 @@ func (k Keeper) UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, } remove, err := grant.Allowance.Accept(fee, ctx.BlockTime(), ctx.BlockHeight()) + if err == nil { + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeUseFeeGrant, + sdk.NewAttribute(types.AttributeKeyGranter, granter.String()), + sdk.NewAttribute(types.AttributeKeyGrantee, grantee.String()), + ), + ) + } if remove { k.RevokeFeeAllowance(ctx, granter, grantee) // note this returns nil if err == nil diff --git a/x/fee_grant/internal/types/events.go b/x/fee_grant/internal/types/events.go new file mode 100644 index 000000000000..34e5ad9882ee --- /dev/null +++ b/x/fee_grant/internal/types/events.go @@ -0,0 +1,11 @@ +package types + +// evidence module events +const ( + EventTypeUseFeeGrant = "use_fee_grant" + EventTypeRevokeFeeGrant = "revoke_fee_grant" + EventTypeSetFeeGrant = "set_fee_grant" + + AttributeKeyGranter = "granter" + AttributeKeyGrantee = "grantee" +) From eedb8d8047e4e13d54686fad28ccfa4d6bfecbb0 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 13:03:46 +0100 Subject: [PATCH 67/77] Rename fee_grant to feegrant --- simapp/app.go | 16 +++---- x/fee_grant/internal/types/events.go | 11 ----- x/{fee_grant => feegrant}/alias.go | 10 ++--- x/{fee_grant => feegrant}/doc.go | 4 +- .../exported/external.go | 0 x/{fee_grant => feegrant}/exported/fees.go | 0 x/{fee_grant => feegrant}/genesis.go | 2 +- x/{fee_grant => feegrant}/handler.go | 2 +- .../internal/ante/fee.go | 6 +-- .../internal/ante/fee_test.go | 8 ++-- .../internal/keeper/keeper.go | 4 +- .../internal/keeper/keeper_test.go | 6 +-- .../internal/keeper/querier.go | 2 +- .../internal/keeper/querier_test.go | 4 +- .../internal/types/basic_fee.go | 2 +- .../internal/types/basic_fee_test.go | 0 .../internal/types/codec.go | 2 +- .../internal/types/errors.go | 0 x/feegrant/internal/types/events.go | 11 +++++ .../internal/types/expiration.go | 0 .../internal/types/expiration_test.go | 0 .../internal/types/grant.go | 2 +- .../internal/types/grant_test.go | 0 .../internal/types/key.go | 0 .../internal/types/msgs.go | 2 +- .../internal/types/periodic_fee.go | 2 +- .../internal/types/periodic_fee_test.go | 0 .../internal/types/tx/codec.go | 0 .../internal/types/tx/test_common.go | 0 .../internal/types/tx/tx.go | 0 x/{fee_grant => feegrant}/module.go | 42 +++++++++---------- 31 files changed, 69 insertions(+), 69 deletions(-) delete mode 100644 x/fee_grant/internal/types/events.go rename x/{fee_grant => feegrant}/alias.go (86%) rename x/{fee_grant => feegrant}/doc.go (93%) rename x/{fee_grant => feegrant}/exported/external.go (100%) rename x/{fee_grant => feegrant}/exported/fees.go (100%) rename x/{fee_grant => feegrant}/genesis.go (98%) rename x/{fee_grant => feegrant}/handler.go (97%) rename x/{fee_grant => feegrant}/internal/ante/fee.go (95%) rename x/{fee_grant => feegrant}/internal/ante/fee_test.go (97%) rename x/{fee_grant => feegrant}/internal/keeper/keeper.go (97%) rename x/{fee_grant => feegrant}/internal/keeper/keeper_test.go (97%) rename x/{fee_grant => feegrant}/internal/keeper/querier.go (95%) rename x/{fee_grant => feegrant}/internal/keeper/querier_test.go (94%) rename x/{fee_grant => feegrant}/internal/types/basic_fee.go (97%) rename x/{fee_grant => feegrant}/internal/types/basic_fee_test.go (100%) rename x/{fee_grant => feegrant}/internal/types/codec.go (91%) rename x/{fee_grant => feegrant}/internal/types/errors.go (100%) create mode 100644 x/feegrant/internal/types/events.go rename x/{fee_grant => feegrant}/internal/types/expiration.go (100%) rename x/{fee_grant => feegrant}/internal/types/expiration_test.go (100%) rename x/{fee_grant => feegrant}/internal/types/grant.go (95%) rename x/{fee_grant => feegrant}/internal/types/grant_test.go (100%) rename x/{fee_grant => feegrant}/internal/types/key.go (100%) rename x/{fee_grant => feegrant}/internal/types/msgs.go (97%) rename x/{fee_grant => feegrant}/internal/types/periodic_fee.go (99%) rename x/{fee_grant => feegrant}/internal/types/periodic_fee_test.go (100%) rename x/{fee_grant => feegrant}/internal/types/tx/codec.go (100%) rename x/{fee_grant => feegrant}/internal/types/tx/test_common.go (100%) rename x/{fee_grant => feegrant}/internal/types/tx/tx.go (100%) rename x/{fee_grant => feegrant}/module.go (72%) diff --git a/simapp/app.go b/simapp/app.go index 98ff544c1ba5..818fb3029bd8 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -21,7 +21,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/crisis" distr "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/evidence" - "github.com/cosmos/cosmos-sdk/x/fee_grant" + "github.com/cosmos/cosmos-sdk/x/feegrant" "github.com/cosmos/cosmos-sdk/x/genutil" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/mint" @@ -56,7 +56,7 @@ var ( params.AppModuleBasic{}, crisis.AppModuleBasic{}, slashing.AppModuleBasic{}, - fee_grant.AppModuleBasic{}, + feegrant.AppModuleBasic{}, evidence.AppModuleBasic{}, ) @@ -107,7 +107,7 @@ type SimApp struct { DistrKeeper distr.Keeper GovKeeper gov.Keeper CrisisKeeper crisis.Keeper - FeeGrantKeeper fee_grant.Keeper + FeeGrantKeeper feegrant.Keeper ParamsKeeper params.Keeper EvidenceKeeper evidence.Keeper @@ -133,7 +133,7 @@ func NewSimApp( keys := sdk.NewKVStoreKeys( bam.MainStoreKey, auth.StoreKey, staking.StoreKey, supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey, gov.StoreKey, params.StoreKey, evidence.StoreKey, - fee_grant.StoreKey, + feegrant.StoreKey, ) tkeys := sdk.NewTransientStoreKeys(params.TStoreKey) @@ -186,8 +186,8 @@ func NewSimApp( app.CrisisKeeper = crisis.NewKeeper( app.subspaces[crisis.ModuleName], invCheckPeriod, app.SupplyKeeper, auth.FeeCollectorName, ) - app.FeeGrantKeeper = fee_grant.NewKeeper( - app.cdc, keys[fee_grant.StoreKey], + app.FeeGrantKeeper = feegrant.NewKeeper( + app.cdc, keys[feegrant.StoreKey], ) // create evidence keeper with router @@ -228,7 +228,7 @@ func NewSimApp( distr.NewAppModule(app.DistrKeeper, app.SupplyKeeper), slashing.NewAppModule(app.SlashingKeeper, app.StakingKeeper), staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper), - fee_grant.NewAppModule(app.FeeGrantKeeper), + feegrant.NewAppModule(app.FeeGrantKeeper), evidence.NewAppModule(app.EvidenceKeeper), ) @@ -243,7 +243,7 @@ func NewSimApp( app.mm.SetOrderInitGenesis( auth.ModuleName, distr.ModuleName, staking.ModuleName, bank.ModuleName, slashing.ModuleName, gov.ModuleName, mint.ModuleName, supply.ModuleName, - crisis.ModuleName, genutil.ModuleName, evidence.ModuleName, fee_grant.ModuleName, + crisis.ModuleName, genutil.ModuleName, evidence.ModuleName, feegrant.ModuleName, ) app.mm.RegisterInvariants(&app.CrisisKeeper) diff --git a/x/fee_grant/internal/types/events.go b/x/fee_grant/internal/types/events.go deleted file mode 100644 index 34e5ad9882ee..000000000000 --- a/x/fee_grant/internal/types/events.go +++ /dev/null @@ -1,11 +0,0 @@ -package types - -// evidence module events -const ( - EventTypeUseFeeGrant = "use_fee_grant" - EventTypeRevokeFeeGrant = "revoke_fee_grant" - EventTypeSetFeeGrant = "set_fee_grant" - - AttributeKeyGranter = "granter" - AttributeKeyGrantee = "grantee" -) diff --git a/x/fee_grant/alias.go b/x/feegrant/alias.go similarity index 86% rename from x/fee_grant/alias.go rename to x/feegrant/alias.go index c91f79291ab1..98122746f185 100644 --- a/x/fee_grant/alias.go +++ b/x/feegrant/alias.go @@ -1,14 +1,14 @@ -package fee_grant +package feegrant // nolint // autogenerated code using github.com/rigelrozanski/multitool // aliases generated for the following subdirectories: -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/fee_grant/internal/keeper -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/feegrant/internal/keeper +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/feegrant/internal/types import ( - "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types" + "github.com/cosmos/cosmos-sdk/x/feegrant/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/feegrant/internal/types" ) const ( diff --git a/x/fee_grant/doc.go b/x/feegrant/doc.go similarity index 93% rename from x/fee_grant/doc.go rename to x/feegrant/doc.go index 0400ec9226c1..2c08f07ec3a4 100644 --- a/x/fee_grant/doc.go +++ b/x/feegrant/doc.go @@ -1,5 +1,5 @@ /* -Package fee_grant provides functionality for delegating the payment of transaction fees +Package feegrant provides functionality for delegating the payment of transaction fees from one account (key) to another account (key). Effectively, this allows for a user to pay fees using the balance of an account @@ -25,4 +25,4 @@ To allow handling txs from empty accounts (with fees paid from an existing accou we have to re-order the decorators as well. You can see an example in `x/delegate_fees/internal/ante/fee_test.go:newAnteHandler()` */ -package fee_grant +package feegrant diff --git a/x/fee_grant/exported/external.go b/x/feegrant/exported/external.go similarity index 100% rename from x/fee_grant/exported/external.go rename to x/feegrant/exported/external.go diff --git a/x/fee_grant/exported/fees.go b/x/feegrant/exported/fees.go similarity index 100% rename from x/fee_grant/exported/fees.go rename to x/feegrant/exported/fees.go diff --git a/x/fee_grant/genesis.go b/x/feegrant/genesis.go similarity index 98% rename from x/fee_grant/genesis.go rename to x/feegrant/genesis.go index 79e581c96403..1c888689f107 100644 --- a/x/fee_grant/genesis.go +++ b/x/feegrant/genesis.go @@ -1,4 +1,4 @@ -package fee_grant +package feegrant import sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/x/fee_grant/handler.go b/x/feegrant/handler.go similarity index 97% rename from x/fee_grant/handler.go rename to x/feegrant/handler.go index 7382b5d04cad..1d790e681bd3 100644 --- a/x/fee_grant/handler.go +++ b/x/feegrant/handler.go @@ -1,4 +1,4 @@ -package fee_grant +package feegrant import ( "fmt" diff --git a/x/fee_grant/internal/ante/fee.go b/x/feegrant/internal/ante/fee.go similarity index 95% rename from x/fee_grant/internal/ante/fee.go rename to x/feegrant/internal/ante/fee.go index 365b904d81e3..a91c3dfd6bd8 100644 --- a/x/fee_grant/internal/ante/fee.go +++ b/x/feegrant/internal/ante/fee.go @@ -10,9 +10,9 @@ import ( auth "github.com/cosmos/cosmos-sdk/x/auth/exported" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" - "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types/tx" + "github.com/cosmos/cosmos-sdk/x/feegrant/exported" + "github.com/cosmos/cosmos-sdk/x/feegrant/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/feegrant/internal/types/tx" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) diff --git a/x/fee_grant/internal/ante/fee_test.go b/x/feegrant/internal/ante/fee_test.go similarity index 97% rename from x/fee_grant/internal/ante/fee_test.go rename to x/feegrant/internal/ante/fee_test.go index f97350647cee..1db9491d005d 100644 --- a/x/fee_grant/internal/ante/fee_test.go +++ b/x/feegrant/internal/ante/fee_test.go @@ -13,10 +13,10 @@ import ( authante "github.com/cosmos/cosmos-sdk/x/auth/ante" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/ante" - "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types" - "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types/tx" + "github.com/cosmos/cosmos-sdk/x/feegrant/internal/ante" + "github.com/cosmos/cosmos-sdk/x/feegrant/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/feegrant/internal/types" + "github.com/cosmos/cosmos-sdk/x/feegrant/internal/types/tx" ) // newAnteHandler is just like auth.NewAnteHandler, except we use the DeductGrantedFeeDecorator diff --git a/x/fee_grant/internal/keeper/keeper.go b/x/feegrant/internal/keeper/keeper.go similarity index 97% rename from x/fee_grant/internal/keeper/keeper.go rename to x/feegrant/internal/keeper/keeper.go index d5bc7c63f3ba..66b1b32b6eb7 100644 --- a/x/fee_grant/internal/keeper/keeper.go +++ b/x/feegrant/internal/keeper/keeper.go @@ -8,8 +8,8 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" - "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types" + "github.com/cosmos/cosmos-sdk/x/feegrant/exported" + "github.com/cosmos/cosmos-sdk/x/feegrant/internal/types" ) // Keeper manages state of all fee grants, as well as calculating approval. diff --git a/x/fee_grant/internal/keeper/keeper_test.go b/x/feegrant/internal/keeper/keeper_test.go similarity index 97% rename from x/fee_grant/internal/keeper/keeper_test.go rename to x/feegrant/internal/keeper/keeper_test.go index dbff579c80b9..980594b7db9d 100644 --- a/x/fee_grant/internal/keeper/keeper_test.go +++ b/x/feegrant/internal/keeper/keeper_test.go @@ -14,9 +14,9 @@ import ( codec "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" - "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types" + "github.com/cosmos/cosmos-sdk/x/feegrant/exported" + "github.com/cosmos/cosmos-sdk/x/feegrant/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/feegrant/internal/types" ) type KeeperTestSuite struct { diff --git a/x/fee_grant/internal/keeper/querier.go b/x/feegrant/internal/keeper/querier.go similarity index 95% rename from x/fee_grant/internal/keeper/querier.go rename to x/feegrant/internal/keeper/querier.go index ba5303f56f02..9310b04091ec 100644 --- a/x/fee_grant/internal/keeper/querier.go +++ b/x/feegrant/internal/keeper/querier.go @@ -3,7 +3,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types" + "github.com/cosmos/cosmos-sdk/x/feegrant/internal/types" abci "github.com/tendermint/tendermint/abci/types" ) diff --git a/x/fee_grant/internal/keeper/querier_test.go b/x/feegrant/internal/keeper/querier_test.go similarity index 94% rename from x/fee_grant/internal/keeper/querier_test.go rename to x/feegrant/internal/keeper/querier_test.go index ea6d76c4441c..a136ca2a1f98 100644 --- a/x/fee_grant/internal/keeper/querier_test.go +++ b/x/feegrant/internal/keeper/querier_test.go @@ -9,8 +9,8 @@ import ( codec "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types" + "github.com/cosmos/cosmos-sdk/x/feegrant/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/feegrant/internal/types" ) func (suite *KeeperTestSuite) TestQuery(t *testing.T) { diff --git a/x/fee_grant/internal/types/basic_fee.go b/x/feegrant/internal/types/basic_fee.go similarity index 97% rename from x/fee_grant/internal/types/basic_fee.go rename to x/feegrant/internal/types/basic_fee.go index 4ce41be4a65b..bc39194421f1 100644 --- a/x/fee_grant/internal/types/basic_fee.go +++ b/x/feegrant/internal/types/basic_fee.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" + "github.com/cosmos/cosmos-sdk/x/feegrant/exported" ) // BasicFeeAllowance implements FeeAllowance with a one-time grant of tokens diff --git a/x/fee_grant/internal/types/basic_fee_test.go b/x/feegrant/internal/types/basic_fee_test.go similarity index 100% rename from x/fee_grant/internal/types/basic_fee_test.go rename to x/feegrant/internal/types/basic_fee_test.go diff --git a/x/fee_grant/internal/types/codec.go b/x/feegrant/internal/types/codec.go similarity index 91% rename from x/fee_grant/internal/types/codec.go rename to x/feegrant/internal/types/codec.go index 91457c313327..c9de87a827b2 100644 --- a/x/fee_grant/internal/types/codec.go +++ b/x/feegrant/internal/types/codec.go @@ -2,7 +2,7 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" + "github.com/cosmos/cosmos-sdk/x/feegrant/exported" ) // RegisterCodec registers the account types and interface diff --git a/x/fee_grant/internal/types/errors.go b/x/feegrant/internal/types/errors.go similarity index 100% rename from x/fee_grant/internal/types/errors.go rename to x/feegrant/internal/types/errors.go diff --git a/x/feegrant/internal/types/events.go b/x/feegrant/internal/types/events.go new file mode 100644 index 000000000000..28caa2e6c9f8 --- /dev/null +++ b/x/feegrant/internal/types/events.go @@ -0,0 +1,11 @@ +package types + +// evidence module events +const ( + EventTypeUseFeeGrant = "use_feegrant" + EventTypeRevokeFeeGrant = "revoke_feegrant" + EventTypeSetFeeGrant = "set_feegrant" + + AttributeKeyGranter = "granter" + AttributeKeyGrantee = "grantee" +) diff --git a/x/fee_grant/internal/types/expiration.go b/x/feegrant/internal/types/expiration.go similarity index 100% rename from x/fee_grant/internal/types/expiration.go rename to x/feegrant/internal/types/expiration.go diff --git a/x/fee_grant/internal/types/expiration_test.go b/x/feegrant/internal/types/expiration_test.go similarity index 100% rename from x/fee_grant/internal/types/expiration_test.go rename to x/feegrant/internal/types/expiration_test.go diff --git a/x/fee_grant/internal/types/grant.go b/x/feegrant/internal/types/grant.go similarity index 95% rename from x/fee_grant/internal/types/grant.go rename to x/feegrant/internal/types/grant.go index 962e0f17ac0b..8c04aab7505b 100644 --- a/x/fee_grant/internal/types/grant.go +++ b/x/feegrant/internal/types/grant.go @@ -4,7 +4,7 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" + "github.com/cosmos/cosmos-sdk/x/feegrant/exported" ) // FeeAllowanceGrant is stored in the KVStore to record a grant with full context diff --git a/x/fee_grant/internal/types/grant_test.go b/x/feegrant/internal/types/grant_test.go similarity index 100% rename from x/fee_grant/internal/types/grant_test.go rename to x/feegrant/internal/types/grant_test.go diff --git a/x/fee_grant/internal/types/key.go b/x/feegrant/internal/types/key.go similarity index 100% rename from x/fee_grant/internal/types/key.go rename to x/feegrant/internal/types/key.go diff --git a/x/fee_grant/internal/types/msgs.go b/x/feegrant/internal/types/msgs.go similarity index 97% rename from x/fee_grant/internal/types/msgs.go rename to x/feegrant/internal/types/msgs.go index 885914d4626b..98e2d3e3c5dc 100644 --- a/x/fee_grant/internal/types/msgs.go +++ b/x/feegrant/internal/types/msgs.go @@ -2,7 +2,7 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" + "github.com/cosmos/cosmos-sdk/x/feegrant/exported" ) // MsgGrantFeeAllowance adds permission for Grantee to spend up to Allowance diff --git a/x/fee_grant/internal/types/periodic_fee.go b/x/feegrant/internal/types/periodic_fee.go similarity index 99% rename from x/fee_grant/internal/types/periodic_fee.go rename to x/feegrant/internal/types/periodic_fee.go index 459deb41c776..9716dbf3a7cc 100644 --- a/x/fee_grant/internal/types/periodic_fee.go +++ b/x/feegrant/internal/types/periodic_fee.go @@ -5,7 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/fee_grant/exported" + "github.com/cosmos/cosmos-sdk/x/feegrant/exported" ) // PeriodicFeeAllowance extends FeeAllowance to allow for both a maximum cap, diff --git a/x/fee_grant/internal/types/periodic_fee_test.go b/x/feegrant/internal/types/periodic_fee_test.go similarity index 100% rename from x/fee_grant/internal/types/periodic_fee_test.go rename to x/feegrant/internal/types/periodic_fee_test.go diff --git a/x/fee_grant/internal/types/tx/codec.go b/x/feegrant/internal/types/tx/codec.go similarity index 100% rename from x/fee_grant/internal/types/tx/codec.go rename to x/feegrant/internal/types/tx/codec.go diff --git a/x/fee_grant/internal/types/tx/test_common.go b/x/feegrant/internal/types/tx/test_common.go similarity index 100% rename from x/fee_grant/internal/types/tx/test_common.go rename to x/feegrant/internal/types/tx/test_common.go diff --git a/x/fee_grant/internal/types/tx/tx.go b/x/feegrant/internal/types/tx/tx.go similarity index 100% rename from x/fee_grant/internal/types/tx/tx.go rename to x/feegrant/internal/types/tx/tx.go diff --git a/x/fee_grant/module.go b/x/feegrant/module.go similarity index 72% rename from x/fee_grant/module.go rename to x/feegrant/module.go index 525c918c76fe..ebb217878976 100644 --- a/x/fee_grant/module.go +++ b/x/feegrant/module.go @@ -1,4 +1,4 @@ -package fee_grant +package feegrant import ( "encoding/json" @@ -12,7 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - tx "github.com/cosmos/cosmos-sdk/x/fee_grant/internal/types/tx" + tx "github.com/cosmos/cosmos-sdk/x/feegrant/internal/types/tx" ) var ( @@ -20,27 +20,27 @@ var ( _ module.AppModuleBasic = AppModuleBasic{} ) -// AppModuleBasic defines the basic application module used by the fee_grant module. +// AppModuleBasic defines the basic application module used by the feegrant module. type AppModuleBasic struct{} -// Name returns the fee_grant module's name. +// Name returns the feegrant module's name. func (AppModuleBasic) Name() string { return ModuleName } -// RegisterCodec registers the fee_grant module's types for the given codec. +// RegisterCodec registers the feegrant module's types for the given codec. func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { RegisterCodec(cdc) tx.RegisterCodec(cdc) } -// DefaultGenesis returns default genesis state as raw bytes for the fee_grant +// DefaultGenesis returns default genesis state as raw bytes for the feegrant // module. func (AppModuleBasic) DefaultGenesis() json.RawMessage { return []byte("[]") } -// ValidateGenesis performs genesis state validation for the fee_grant module. +// ValidateGenesis performs genesis state validation for the feegrant module. func (a AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { _, err := a.getValidatedGenesis(bz) return err @@ -55,19 +55,19 @@ func (a AppModuleBasic) getValidatedGenesis(bz json.RawMessage) (GenesisState, e return data, data.ValidateBasic() } -// RegisterRESTRoutes registers the REST routes for the fee_grant module. +// RegisterRESTRoutes registers the REST routes for the feegrant module. func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { // TODO // rest.RegisterRoutes(ctx, rtr) } -// GetTxCmd returns the root tx command for the fee_grant module. +// GetTxCmd returns the root tx command for the feegrant module. func (AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { // TODO return nil } -// GetQueryCmd returns no root query command for the fee_grant module. +// GetQueryCmd returns no root query command for the feegrant module. func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { // TODO return nil @@ -76,7 +76,7 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { //____________________________________________________________________________ -// AppModule implements an application module for the fee_grant module. +// AppModule implements an application module for the feegrant module. type AppModule struct { AppModuleBasic keeper Keeper @@ -90,35 +90,35 @@ func NewAppModule(keeper Keeper) AppModule { } } -// Name returns the fee_grant module's name. +// Name returns the feegrant module's name. func (AppModule) Name() string { return ModuleName } -// RegisterInvariants registers the fee_grant module invariants. +// RegisterInvariants registers the feegrant module invariants. func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} -// Route returns the message routing key for the fee_grant module. +// Route returns the message routing key for the feegrant module. func (AppModule) Route() string { return RouterKey } -// NewHandler returns an sdk.Handler for the fee_grant module. +// NewHandler returns an sdk.Handler for the feegrant module. func (am AppModule) NewHandler() sdk.Handler { return NewHandler(am.keeper) } -// QuerierRoute returns the fee_grant module's querier route name. +// QuerierRoute returns the feegrant module's querier route name. func (AppModule) QuerierRoute() string { return QuerierRoute } -// NewQuerierHandler returns the fee_grant module sdk.Querier. +// NewQuerierHandler returns the feegrant module sdk.Querier. func (am AppModule) NewQuerierHandler() sdk.Querier { return NewQuerier(am.keeper) } -// InitGenesis performs genesis initialization for the fee_grant module. It returns +// InitGenesis performs genesis initialization for the feegrant module. It returns // no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { genesisState, err := am.getValidatedGenesis(data) @@ -129,7 +129,7 @@ func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.Va return []abci.ValidatorUpdate{} } -// ExportGenesis returns the exported genesis state as raw bytes for the fee_grant +// ExportGenesis returns the exported genesis state as raw bytes for the feegrant // module. func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { gs, err := ExportGenesis(ctx, am.keeper) @@ -139,10 +139,10 @@ func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { return ModuleCdc.MustMarshalJSON(gs) } -// BeginBlock returns the begin blocker for the fee_grant module. +// BeginBlock returns the begin blocker for the feegrant module. func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} -// EndBlock returns the end blocker for the fee_grant module. It returns no validator +// EndBlock returns the end blocker for the feegrant module. It returns no validator // updates. func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} From 88df2559e6d2e96c4a0ad5aef357a1be579db2fd Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 13:12:51 +0100 Subject: [PATCH 68/77] Ensure KeeperTestSuite actually runs --- x/feegrant/internal/keeper/keeper_test.go | 39 +++++++++++++--------- x/feegrant/internal/keeper/querier_test.go | 17 ++++------ 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/x/feegrant/internal/keeper/keeper_test.go b/x/feegrant/internal/keeper/keeper_test.go index 980594b7db9d..890d0bab71cc 100644 --- a/x/feegrant/internal/keeper/keeper_test.go +++ b/x/feegrant/internal/keeper/keeper_test.go @@ -4,8 +4,6 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" @@ -32,6 +30,10 @@ type KeeperTestSuite struct { addr4 sdk.AccAddress } +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} + func (suite *KeeperTestSuite) SetupTest() { db := dbm.NewMemDB() @@ -64,7 +66,7 @@ func mustAddr(acc string) sdk.AccAddress { return addr } -func (suite *KeeperTestSuite) TestKeeperCrud(t *testing.T) { +func (suite *KeeperTestSuite) TestKeeperCrud() { ctx := suite.ctx k := suite.dk @@ -139,14 +141,15 @@ func (suite *KeeperTestSuite) TestKeeperCrud(t *testing.T) { } for name, tc := range cases { - t.Run(name, func(t *testing.T) { + tc := tc + suite.Run(name, func() { allow := k.GetFeeAllowance(ctx, tc.granter, tc.grantee) if tc.allowance == nil { - require.Nil(t, allow) + suite.Nil(allow) return } - require.NotNil(t, allow) - require.Equal(t, tc.allowance, allow) + suite.NotNil(allow) + suite.Equal(tc.allowance, allow) }) } @@ -171,19 +174,20 @@ func (suite *KeeperTestSuite) TestKeeperCrud(t *testing.T) { } for name, tc := range allCases { - t.Run(name, func(t *testing.T) { + tc := tc + suite.Run(name, func() { var grants []types.FeeAllowanceGrant err := k.IterateAllGranteeFeeAllowances(ctx, tc.grantee, func(grant types.FeeAllowanceGrant) bool { grants = append(grants, grant) return false }) - require.NoError(t, err) - assert.Equal(t, tc.grants, grants) + suite.NoError(err) + suite.Equal(tc.grants, grants) }) } } -func (suite *KeeperTestSuite) TestUseGrantedFee(t *testing.T) { +func (suite *KeeperTestSuite) TestUseGrantedFee() { ctx := suite.ctx k := suite.dk @@ -246,7 +250,8 @@ func (suite *KeeperTestSuite) TestUseGrantedFee(t *testing.T) { } for name, tc := range cases { - t.Run(name, func(t *testing.T) { + tc := tc + suite.Run(name, func() { // let's set up some initial state here // addr -> addr2 (future) // addr -> addr3 (expired) @@ -257,11 +262,15 @@ func (suite *KeeperTestSuite) TestUseGrantedFee(t *testing.T) { Granter: suite.addr, Grantee: suite.addr3, Allowance: &expired, }) - allowed := k.UseGrantedFees(ctx, tc.granter, tc.grantee, tc.fee) - require.Equal(t, tc.allowed, allowed) + err := k.UseGrantedFees(ctx, tc.granter, tc.grantee, tc.fee) + if tc.allowed { + suite.NoError(err) + } else { + suite.Error(err) + } loaded := k.GetFeeAllowance(ctx, tc.granter, tc.grantee) - require.Equal(t, tc.final, loaded) + suite.Equal(tc.final, loaded) }) } } diff --git a/x/feegrant/internal/keeper/querier_test.go b/x/feegrant/internal/keeper/querier_test.go index a136ca2a1f98..0df0044a70f7 100644 --- a/x/feegrant/internal/keeper/querier_test.go +++ b/x/feegrant/internal/keeper/querier_test.go @@ -1,10 +1,6 @@ package keeper_test import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" codec "github.com/cosmos/cosmos-sdk/codec" @@ -13,7 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/feegrant/internal/types" ) -func (suite *KeeperTestSuite) TestQuery(t *testing.T) { +func (suite *KeeperTestSuite) TestQuery() { ctx := suite.ctx k := suite.dk @@ -66,19 +62,20 @@ func (suite *KeeperTestSuite) TestQuery(t *testing.T) { querier := keeper.NewQuerier(k) for name, tc := range cases { - t.Run(name, func(t *testing.T) { + tc := tc + suite.Run(name, func() { bz, err := querier(ctx, tc.path, abci.RequestQuery{}) if !tc.valid { - require.Error(t, err) + suite.Error(err) return } - require.NoError(t, err) + suite.NoError(err) var grants []types.FeeAllowanceGrant serr := cdc.UnmarshalJSON(bz, &grants) - require.NoError(t, serr) + suite.NoError(serr) - assert.Equal(t, tc.res, grants) + suite.Equal(tc.res, grants) }) } From dbf357832551f77dbfd3696d353ba9498a8b8c4e Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 14:04:17 +0100 Subject: [PATCH 69/77] Move types/tx to types --- x/feegrant/internal/ante/fee.go | 4 ++-- x/feegrant/internal/ante/fee_test.go | 5 ++--- x/feegrant/internal/types/codec.go | 2 ++ x/feegrant/internal/types/{tx => }/test_common.go | 3 ++- x/feegrant/internal/types/{tx => }/tx.go | 2 +- x/feegrant/internal/types/tx/codec.go | 8 -------- x/feegrant/module.go | 2 -- 7 files changed, 9 insertions(+), 17 deletions(-) rename x/feegrant/internal/types/{tx => }/test_common.go (97%) rename x/feegrant/internal/types/{tx => }/tx.go (99%) delete mode 100644 x/feegrant/internal/types/tx/codec.go diff --git a/x/feegrant/internal/ante/fee.go b/x/feegrant/internal/ante/fee.go index a91c3dfd6bd8..296e152122a4 100644 --- a/x/feegrant/internal/ante/fee.go +++ b/x/feegrant/internal/ante/fee.go @@ -12,13 +12,13 @@ import ( "github.com/cosmos/cosmos-sdk/x/feegrant/exported" "github.com/cosmos/cosmos-sdk/x/feegrant/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/feegrant/internal/types/tx" + "github.com/cosmos/cosmos-sdk/x/feegrant/internal/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) var ( - _ GrantedFeeTx = (*tx.FeeGrantTx)(nil) // assert FeeGrantTx implements GrantedFeeTx + _ GrantedFeeTx = (*types.FeeGrantTx)(nil) // assert FeeGrantTx implements GrantedFeeTx ) // GrantedFeeTx defines the interface to be implemented by Tx to use the GrantedFeeDecorator diff --git a/x/feegrant/internal/ante/fee_test.go b/x/feegrant/internal/ante/fee_test.go index 1db9491d005d..bcc6d1859b9a 100644 --- a/x/feegrant/internal/ante/fee_test.go +++ b/x/feegrant/internal/ante/fee_test.go @@ -16,7 +16,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/feegrant/internal/ante" "github.com/cosmos/cosmos-sdk/x/feegrant/internal/keeper" "github.com/cosmos/cosmos-sdk/x/feegrant/internal/types" - "github.com/cosmos/cosmos-sdk/x/feegrant/internal/types/tx" ) // newAnteHandler is just like auth.NewAnteHandler, except we use the DeductGrantedFeeDecorator @@ -244,11 +243,11 @@ func TestDeductFeesNoDelegation(t *testing.T) { tc := stc // to make scopelint happy t.Run(name, func(t *testing.T) { // msg and signatures - fee := tx.NewGrantedFee(100000, sdk.NewCoins(sdk.NewInt64Coin("atom", tc.fee)), tc.feeAccount) + fee := types.NewGrantedFee(100000, sdk.NewCoins(sdk.NewInt64Coin("atom", tc.fee)), tc.feeAccount) msgs := []sdk.Msg{sdk.NewTestMsg(tc.signer)} privs, accNums, seqs := []crypto.PrivKey{tc.signerKey}, []uint64{0}, []uint64{0} - tx := tx.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) + tx := types.NewTestTx(ctx, msgs, privs, accNums, seqs, fee) _, err := tc.handler(ctx, tx, false) if tc.valid { diff --git a/x/feegrant/internal/types/codec.go b/x/feegrant/internal/types/codec.go index c9de87a827b2..68460bde3537 100644 --- a/x/feegrant/internal/types/codec.go +++ b/x/feegrant/internal/types/codec.go @@ -10,6 +10,8 @@ func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*exported.FeeAllowance)(nil), nil) cdc.RegisterConcrete(&BasicFeeAllowance{}, "feegrant/BasicFeeAllowance", nil) cdc.RegisterConcrete(&PeriodicFeeAllowance{}, "feegrant/PeriodicFeeAllowance", nil) + + cdc.RegisterConcrete(FeeGrantTx{}, "cosmos-sdk/FeeGrantTx", nil) } // ModuleCdc generic sealed codec to be used throughout module diff --git a/x/feegrant/internal/types/tx/test_common.go b/x/feegrant/internal/types/test_common.go similarity index 97% rename from x/feegrant/internal/types/tx/test_common.go rename to x/feegrant/internal/types/test_common.go index f33e1c222879..bb3686650c48 100644 --- a/x/feegrant/internal/types/tx/test_common.go +++ b/x/feegrant/internal/types/test_common.go @@ -1,5 +1,6 @@ // noalias -package tx + +package types import ( "github.com/tendermint/tendermint/crypto" diff --git a/x/feegrant/internal/types/tx/tx.go b/x/feegrant/internal/types/tx.go similarity index 99% rename from x/feegrant/internal/types/tx/tx.go rename to x/feegrant/internal/types/tx.go index a02eb73ab95d..72a6b6d8ab6e 100644 --- a/x/feegrant/internal/types/tx/tx.go +++ b/x/feegrant/internal/types/tx.go @@ -1,4 +1,4 @@ -package tx +package types import ( "encoding/json" diff --git a/x/feegrant/internal/types/tx/codec.go b/x/feegrant/internal/types/tx/codec.go deleted file mode 100644 index 50e482b48bd3..000000000000 --- a/x/feegrant/internal/types/tx/codec.go +++ /dev/null @@ -1,8 +0,0 @@ -package tx - -import "github.com/cosmos/cosmos-sdk/codec" - -// RegisterCodec registers concrete types on the codec -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(FeeGrantTx{}, "cosmos-sdk/FeeGrantTx", nil) -} diff --git a/x/feegrant/module.go b/x/feegrant/module.go index ebb217878976..6406b74961f7 100644 --- a/x/feegrant/module.go +++ b/x/feegrant/module.go @@ -12,7 +12,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - tx "github.com/cosmos/cosmos-sdk/x/feegrant/internal/types/tx" ) var ( @@ -31,7 +30,6 @@ func (AppModuleBasic) Name() string { // RegisterCodec registers the feegrant module's types for the given codec. func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { RegisterCodec(cdc) - tx.RegisterCodec(cdc) } // DefaultGenesis returns default genesis state as raw bytes for the feegrant From 375f114b1c9c6906747015e92c1e793ced1e3b9d Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 14:05:20 +0100 Subject: [PATCH 70/77] Update alias file, include ante --- x/feegrant/alias.go | 81 ++++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/x/feegrant/alias.go b/x/feegrant/alias.go index 98122746f185..66f6023a8fd4 100644 --- a/x/feegrant/alias.go +++ b/x/feegrant/alias.go @@ -1,54 +1,73 @@ package feegrant -// nolint -// autogenerated code using github.com/rigelrozanski/multitool -// aliases generated for the following subdirectories: -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/feegrant/internal/keeper -// ALIASGEN: github.com/cosmos/cosmos-sdk/x/feegrant/internal/types - import ( + "github.com/cosmos/cosmos-sdk/x/feegrant/internal/ante" "github.com/cosmos/cosmos-sdk/x/feegrant/internal/keeper" "github.com/cosmos/cosmos-sdk/x/feegrant/internal/types" ) +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/feegrant/internal/ante +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/feegrant/internal/types +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/feegrant/internal/keeper + const ( - QueryGetFeeAllowances = keeper.QueryGetFeeAllowances - DefaultCodespace = types.DefaultCodespace - ModuleName = types.ModuleName - StoreKey = types.StoreKey - RouterKey = types.RouterKey - QuerierRoute = types.QuerierRoute + DefaultCodespace = types.DefaultCodespace + EventTypeUseFeeGrant = types.EventTypeUseFeeGrant + EventTypeRevokeFeeGrant = types.EventTypeRevokeFeeGrant + EventTypeSetFeeGrant = types.EventTypeSetFeeGrant + AttributeKeyGranter = types.AttributeKeyGranter + AttributeKeyGrantee = types.AttributeKeyGrantee + ModuleName = types.ModuleName + StoreKey = types.StoreKey + RouterKey = types.RouterKey + QuerierRoute = types.QuerierRoute + QueryGetFeeAllowances = keeper.QueryGetFeeAllowances ) var ( // functions aliases - NewKeeper = keeper.NewKeeper - NewQuerier = keeper.NewQuerier - RegisterCodec = types.RegisterCodec - ExpiresAtTime = types.ExpiresAtTime - ExpiresAtHeight = types.ExpiresAtHeight - ClockDuration = types.ClockDuration - BlockDuration = types.BlockDuration - FeeAllowanceKey = types.FeeAllowanceKey - FeeAllowancePrefixByGrantee = types.FeeAllowancePrefixByGrantee - NewMsgGrantFeeAllowance = types.NewMsgGrantFeeAllowance - NewMsgRevokeFeeAllowance = types.NewMsgRevokeFeeAllowance + NewDeductGrantedFeeDecorator = ante.NewDeductGrantedFeeDecorator + DeductFees = ante.DeductFees + RegisterCodec = types.RegisterCodec + ExpiresAtTime = types.ExpiresAtTime + ExpiresAtHeight = types.ExpiresAtHeight + ClockDuration = types.ClockDuration + BlockDuration = types.BlockDuration + FeeAllowanceKey = types.FeeAllowanceKey + FeeAllowancePrefixByGrantee = types.FeeAllowancePrefixByGrantee + NewMsgGrantFeeAllowance = types.NewMsgGrantFeeAllowance + NewMsgRevokeFeeAllowance = types.NewMsgRevokeFeeAllowance + NewFeeGrantTx = types.NewFeeGrantTx + CountSubKeys = types.CountSubKeys + NewGrantedFee = types.NewGrantedFee + StdSignBytes = types.StdSignBytes + NewKeeper = keeper.NewKeeper + NewQuerier = keeper.NewQuerier // variable aliases ModuleCdc = types.ModuleCdc ErrFeeLimitExceeded = types.ErrFeeLimitExceeded ErrFeeLimitExpired = types.ErrFeeLimitExpired ErrInvalidDuration = types.ErrInvalidDuration + ErrNoAllowance = types.ErrNoAllowance FeeAllowanceKeyPrefix = types.FeeAllowanceKeyPrefix ) type ( - Keeper = keeper.Keeper - BasicFeeAllowance = types.BasicFeeAllowance - ExpiresAt = types.ExpiresAt - Duration = types.Duration - FeeAllowanceGrant = types.FeeAllowanceGrant - MsgGrantFeeAllowance = types.MsgGrantFeeAllowance - MsgRevokeFeeAllowance = types.MsgRevokeFeeAllowance - PeriodicFeeAllowance = types.PeriodicFeeAllowance + GrantedFeeTx = ante.GrantedFeeTx + DeductGrantedFeeDecorator = ante.DeductGrantedFeeDecorator + BasicFeeAllowance = types.BasicFeeAllowance + ExpiresAt = types.ExpiresAt + Duration = types.Duration + FeeAllowanceGrant = types.FeeAllowanceGrant + MsgGrantFeeAllowance = types.MsgGrantFeeAllowance + MsgRevokeFeeAllowance = types.MsgRevokeFeeAllowance + PeriodicFeeAllowance = types.PeriodicFeeAllowance + FeeGrantTx = types.FeeGrantTx + GrantedFee = types.GrantedFee + DelegatedSignDoc = types.DelegatedSignDoc + Keeper = keeper.Keeper ) From 9af9f43261a07ff5648d3434d4c2a494e9ac5538 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 14:12:19 +0100 Subject: [PATCH 71/77] I do need nolint in alias.go --- x/feegrant/alias.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/feegrant/alias.go b/x/feegrant/alias.go index 66f6023a8fd4..4abfe46fe13a 100644 --- a/x/feegrant/alias.go +++ b/x/feegrant/alias.go @@ -1,3 +1,4 @@ +// nolint package feegrant import ( @@ -6,7 +7,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/feegrant/internal/types" ) -// nolint // autogenerated code using github.com/rigelrozanski/multitool // aliases generated for the following subdirectories: // ALIASGEN: github.com/cosmos/cosmos-sdk/x/feegrant/internal/ante From 3c390e5000d8d92b1ceaf3dcbe18ffd0103ade9d Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 7 Nov 2019 18:21:06 +0100 Subject: [PATCH 72/77] Properly emit events in the handler. Use cosmos-sdk in amino types --- x/feegrant/handler.go | 4 ++-- x/feegrant/internal/types/codec.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x/feegrant/handler.go b/x/feegrant/handler.go index 1d790e681bd3..b383c14b0f66 100644 --- a/x/feegrant/handler.go +++ b/x/feegrant/handler.go @@ -27,10 +27,10 @@ func NewHandler(k Keeper) sdk.Handler { func handleGrantFee(ctx sdk.Context, k Keeper, msg MsgGrantFeeAllowance) sdk.Result { grant := FeeAllowanceGrant(msg) k.GrantFeeAllowance(ctx, grant) - return sdk.Result{} + return sdk.Result{Events: ctx.EventManager().Events()} } func handleRevokeFee(ctx sdk.Context, k Keeper, msg MsgRevokeFeeAllowance) sdk.Result { k.RevokeFeeAllowance(ctx, msg.Granter, msg.Grantee) - return sdk.Result{} + return sdk.Result{Events: ctx.EventManager().Events()} } diff --git a/x/feegrant/internal/types/codec.go b/x/feegrant/internal/types/codec.go index 68460bde3537..541bf6d325a6 100644 --- a/x/feegrant/internal/types/codec.go +++ b/x/feegrant/internal/types/codec.go @@ -8,8 +8,8 @@ import ( // RegisterCodec registers the account types and interface func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*exported.FeeAllowance)(nil), nil) - cdc.RegisterConcrete(&BasicFeeAllowance{}, "feegrant/BasicFeeAllowance", nil) - cdc.RegisterConcrete(&PeriodicFeeAllowance{}, "feegrant/PeriodicFeeAllowance", nil) + cdc.RegisterConcrete(&BasicFeeAllowance{}, "cosmos-sdk/BasicFeeAllowance", nil) + cdc.RegisterConcrete(&PeriodicFeeAllowance{}, "cosmos-sdk/PeriodicFeeAllowance", nil) cdc.RegisterConcrete(FeeGrantTx{}, "cosmos-sdk/FeeGrantTx", nil) } From 720e9f4b241ab6830a5061b57b5ea46c83891ecf Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 9 Mar 2020 15:13:56 -0400 Subject: [PATCH 73/77] Update godoc --- x/feegrant/ante/fee.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/x/feegrant/ante/fee.go b/x/feegrant/ante/fee.go index 158a4dfb0445..454a3f406fa2 100644 --- a/x/feegrant/ante/fee.go +++ b/x/feegrant/ante/fee.go @@ -35,7 +35,7 @@ type DeductGrantedFeeDecorator struct { sk exported.SupplyKeeper } -func NewDeductGrantedFeeDecorator(ak exported.AccountKeeper, sk exported.SupplyKeeper, K keeper.Keeper) DeductGrantedFeeDecorator { +func NewDeductGrantedFeeDecorator(ak exported.AccountKeeper, sk exported.SupplyKeeper, k keeper.Keeper) DeductGrantedFeeDecorator { return DeductGrantedFeeDecorator{ ak: ak, k: k, @@ -43,6 +43,11 @@ func NewDeductGrantedFeeDecorator(ak exported.AccountKeeper, sk exported.SupplyK } } +// AnteHandle performs a decorated ante-handler responsible for deducting transaction +// fees. Fees will be deducted from the account designated by the FeePayer on a +// transaction by default. However, if the fee payer differs from the transaction +// signer, the handler will check if a fee grant has been authorized. If the +// transaction's signer does not exist, it will be created. func (d DeductGrantedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { feeTx, ok := tx.(GrantedFeeTx) if !ok { @@ -79,11 +84,12 @@ func (d DeductGrantedFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "fee payer address: %s does not exist", feePayer) } - // deduct fee if non-zero + // move on if there is no fee to deduct if fee.IsZero() { return next(ctx, tx, simulate) } + // deduct fee if non-zero err = auth.DeductFees(d.sk, ctx, feePayerAcc, fee) if err != nil { return ctx, err From eb46dd00f50f908c963934c515040d7681063718 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 9 Mar 2020 15:18:23 -0400 Subject: [PATCH 74/77] Linting... --- x/feegrant/keeper/keeper.go | 11 +++++++++++ x/feegrant/module.go | 8 +++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/x/feegrant/keeper/keeper.go b/x/feegrant/keeper/keeper.go index 142b9549b18d..263b5faabadb 100644 --- a/x/feegrant/keeper/keeper.go +++ b/x/feegrant/keeper/keeper.go @@ -34,7 +34,9 @@ func (k Keeper) GrantFeeAllowance(ctx sdk.Context, grant types.FeeAllowanceGrant store := ctx.KVStore(k.storeKey) key := types.FeeAllowanceKey(grant.Granter, grant.Grantee) bz := k.cdc.MustMarshalBinaryBare(grant) + store.Set(key, bz) + ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeSetFeeGrant, @@ -48,7 +50,9 @@ func (k Keeper) GrantFeeAllowance(ctx sdk.Context, grant types.FeeAllowanceGrant func (k Keeper) RevokeFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) { store := ctx.KVStore(k.storeKey) key := types.FeeAllowanceKey(granter, grantee) + store.Delete(key) + ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeRevokeFeeGrant, @@ -66,6 +70,7 @@ func (k Keeper) GetFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress if !found { return nil } + return grant.Allowance } @@ -80,6 +85,7 @@ func (k Keeper) GetFeeGrant(ctx sdk.Context, granter sdk.AccAddress, grantee sdk var grant types.FeeAllowanceGrant k.cdc.MustUnmarshalBinaryBare(bz, &grant) + return grant, true } @@ -95,12 +101,15 @@ func (k Keeper) IterateAllGranteeFeeAllowances(ctx sdk.Context, grantee sdk.AccA for ; iter.Valid() && !stop; iter.Next() { bz := iter.Value() var grant types.FeeAllowanceGrant + err := k.cdc.UnmarshalBinaryBare(bz, &grant) if err != nil { return err } + stop = cb(grant) } + return nil } @@ -145,11 +154,13 @@ func (k Keeper) UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, ), ) } + if remove { k.RevokeFeeAllowance(ctx, granter, grantee) // note this returns nil if err == nil return sdkerrors.Wrap(err, "removed grant") } + if err != nil { return sdkerrors.Wrap(err, "invalid grant") } diff --git a/x/feegrant/module.go b/x/feegrant/module.go index ff6ceae44e18..72699c3c5043 100644 --- a/x/feegrant/module.go +++ b/x/feegrant/module.go @@ -19,6 +19,10 @@ var ( _ module.AppModuleBasic = AppModuleBasic{} ) +// ---------------------------------------------------------------------------- +// AppModuleBasic +// ---------------------------------------------------------------------------- + // AppModuleBasic defines the basic application module used by the feegrant module. type AppModuleBasic struct{} @@ -73,7 +77,9 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { return nil } -//____________________________________________________________________________ +// ---------------------------------------------------------------------------- +// AppModule +// ---------------------------------------------------------------------------- // AppModule implements an application module for the feegrant module. type AppModule struct { From 63cca04cb95c4e68e6de3ccf68944805192b5943 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 9 Mar 2020 15:19:07 -0400 Subject: [PATCH 75/77] Update errors --- x/feegrant/types/errors.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/x/feegrant/types/errors.go b/x/feegrant/types/errors.go index cea99327f440..9b824253ae9d 100644 --- a/x/feegrant/types/errors.go +++ b/x/feegrant/types/errors.go @@ -11,14 +11,11 @@ const ( var ( // ErrFeeLimitExceeded error if there are not enough allowance to cover the fees - ErrFeeLimitExceeded = sdkerrors.Register(DefaultCodespace, 1, "fee limit exceeded") - + ErrFeeLimitExceeded = sdkerrors.Register(DefaultCodespace, 2, "fee limit exceeded") // ErrFeeLimitExpired error if the allowance has expired - ErrFeeLimitExpired = sdkerrors.Register(DefaultCodespace, 2, "fee limit expired") - + ErrFeeLimitExpired = sdkerrors.Register(DefaultCodespace, 3, "fee limit expired") // ErrInvalidDuration error if the Duration is invalid or doesn't match the expiration - ErrInvalidDuration = sdkerrors.Register(DefaultCodespace, 3, "invalid duration") - + ErrInvalidDuration = sdkerrors.Register(DefaultCodespace, 4, "invalid duration") // ErrNoAllowance error if there is no allowance for that pair - ErrNoAllowance = sdkerrors.Register(DefaultCodespace, 4, "no allowance") + ErrNoAllowance = sdkerrors.Register(DefaultCodespace, 5, "no allowance") ) From 8eddbb7636021631402226b961b0b7e82a876814 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 9 Mar 2020 15:40:38 -0400 Subject: [PATCH 76/77] Update pkg doc and fix ante-handler order --- simapp/ante.go | 5 ++++- x/feegrant/doc.go | 27 ++++++++++++++------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/simapp/ante.go b/simapp/ante.go index f3f01803529f..f7db7f20f811 100644 --- a/simapp/ante.go +++ b/simapp/ante.go @@ -23,9 +23,12 @@ func NewAnteHandler( authante.NewValidateBasicDecorator(), authante.NewValidateMemoDecorator(ak), authante.NewConsumeGasForTxSizeDecorator(ak), + // DeductGrantedFeeDecorator will create an empty account if we sign with no + // tokens but valid validation. This must be before SetPubKey, ValidateSigCount, + // SigVerification, which error if account doesn't exist yet. + feegrantante.NewDeductGrantedFeeDecorator(ak, supplyKeeper, feeGrantKeeper), authante.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators authante.NewValidateSigCountDecorator(ak), - feegrantante.NewDeductGrantedFeeDecorator(ak, supplyKeeper, feeGrantKeeper), authante.NewSigGasConsumeDecorator(ak, sigGasConsumer), authante.NewSigVerificationDecorator(ak), authante.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator diff --git a/x/feegrant/doc.go b/x/feegrant/doc.go index 2c08f07ec3a4..4f190e3140c7 100644 --- a/x/feegrant/doc.go +++ b/x/feegrant/doc.go @@ -1,28 +1,29 @@ /* -Package feegrant provides functionality for delegating the payment of transaction fees -from one account (key) to another account (key). +Package feegrant provides functionality for authorizing the payment of transaction +fees from one account (key) to another account (key). Effectively, this allows for a user to pay fees using the balance of an account different from their own. Example use cases would be allowing a key on a device to pay for fees using a master wallet, or a third party service allowing users to pay for transactions without ever really holding their own tokens. This package -provides ways for specifying fee allowances such that delegating fees -to another account can be done with clear and safe restrictions. +provides ways for specifying fee allowances such that authorizing fee payment to +another account can be done with clear and safe restrictions. -A user would delegate fees to a user using MsgDelegateFeeAllowance and revoke -that delegation using MsgRevokeFeeAllowance. In both cases Granter is the one -who is delegating fees and Grantee is the one who is receiving the delegation. -So grantee would correspond to the one who is signing a transaction and the -granter would be the address they place in DelegatedFee.FeeAccount. +A user would authorize granting fee payment to another user using +MsgDelegateFeeAllowance and revoke that delegation using MsgRevokeFeeAllowance. +In both cases, Granter is the one who is authorizing fee payment and Grantee is +the one who is receiving the fee payment authorization. So grantee would correspond +to the one who is signing a transaction and the granter would be the address that +pays the fees. The fee allowance that a grantee receives is specified by an implementation of the FeeAllowance interface. Two FeeAllowance implementations are provided in this package: BasicFeeAllowance and PeriodicFeeAllowance. -In order to integrate this into an application, we must use the DeductDelegatedFeeDecorator -ante handler from this package instead of the default DeductFeeDecorator from auth. +In order to integrate this into an application, we must use the DeductGrantedFeeDecorator +ante handler from this package instead of the default DeductFeeDecorator from x/auth. + To allow handling txs from empty accounts (with fees paid from an existing account), -we have to re-order the decorators as well. You can see an example in -`x/delegate_fees/internal/ante/fee_test.go:newAnteHandler()` +we have to re-order the decorators as well. */ package feegrant From f32db1d2a7d5094fb32a12604e5bfa25f5006cf8 Mon Sep 17 00:00:00 2001 From: SaReN Date: Tue, 7 Apr 2020 19:10:00 +0530 Subject: [PATCH 77/77] Merge PR #5782: Migrate x/feegrant to proto --- simapp/ante.go | 3 +- simapp/app.go | 2 +- x/feegrant/alias.go | 3 +- x/feegrant/ante/fee.go | 7 +- x/feegrant/ante/fee_test.go | 12 +- x/feegrant/genesis.go | 6 +- x/feegrant/handler.go | 5 +- x/feegrant/keeper/keeper.go | 39 +- x/feegrant/keeper/keeper_test.go | 83 +- x/feegrant/keeper/querier.go | 3 +- x/feegrant/keeper/querier_test.go | 10 +- x/feegrant/types/basic_fee.go | 16 +- x/feegrant/types/basic_fee_test.go | 14 +- x/feegrant/types/codec.go | 20 +- .../external.go => types/expected_keepers.go} | 2 +- x/feegrant/types/expiration.go | 14 - x/feegrant/types/expiration_test.go | 22 +- x/feegrant/{exported => types}/fees.go | 6 +- x/feegrant/types/grant.go | 25 +- x/feegrant/types/grant_test.go | 22 +- x/feegrant/types/msgs.go | 26 +- x/feegrant/types/periodic_fee.go | 25 +- x/feegrant/types/types.pb.go | 2531 +++++++++++++++++ x/feegrant/types/types.proto | 105 + 24 files changed, 2812 insertions(+), 189 deletions(-) rename x/feegrant/{exported/external.go => types/expected_keepers.go} (97%) rename x/feegrant/{exported => types}/fees.go (96%) create mode 100644 x/feegrant/types/types.pb.go create mode 100644 x/feegrant/types/types.proto diff --git a/simapp/ante.go b/simapp/ante.go index f7db7f20f811..e53f8638d577 100644 --- a/simapp/ante.go +++ b/simapp/ante.go @@ -6,14 +6,13 @@ import ( authante "github.com/cosmos/cosmos-sdk/x/auth/ante" "github.com/cosmos/cosmos-sdk/x/feegrant" feegrantante "github.com/cosmos/cosmos-sdk/x/feegrant/ante" - feegrantexported "github.com/cosmos/cosmos-sdk/x/feegrant/exported" ) // NewAnteHandler returns an AnteHandler that checks and increments sequence // numbers, checks signatures & account numbers, and deducts fees from the first // signer. func NewAnteHandler( - ak auth.AccountKeeper, supplyKeeper feegrantexported.SupplyKeeper, feeGrantKeeper feegrant.Keeper, + ak auth.AccountKeeper, supplyKeeper feegrant.SupplyKeeper, feeGrantKeeper feegrant.Keeper, sigGasConsumer auth.SignatureVerificationGasConsumer, ) sdk.AnteHandler { diff --git a/simapp/app.go b/simapp/app.go index c50ee95dba5b..28e5603290b2 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -190,7 +190,7 @@ func NewSimApp( app.CrisisKeeper = crisis.NewKeeper( app.subspaces[crisis.ModuleName], invCheckPeriod, app.SupplyKeeper, auth.FeeCollectorName, ) - app.FeeGrantKeeper = feegrant.NewKeeper(app.cdc, keys[feegrant.StoreKey]) + app.FeeGrantKeeper = feegrant.NewKeeper(appCodec, keys[feegrant.StoreKey]) app.UpgradeKeeper = upgrade.NewKeeper(skipUpgradeHeights, keys[upgrade.StoreKey], appCodec, homePath) // create evidence keeper with router diff --git a/x/feegrant/alias.go b/x/feegrant/alias.go index cb0ce18183db..74ea930daf17 100644 --- a/x/feegrant/alias.go +++ b/x/feegrant/alias.go @@ -31,7 +31,6 @@ var ( BlockDuration = types.BlockDuration FeeAllowanceKey = types.FeeAllowanceKey FeeAllowancePrefixByGrantee = types.FeeAllowancePrefixByGrantee - NewMsgGrantFeeAllowance = types.NewMsgGrantFeeAllowance NewMsgRevokeFeeAllowance = types.NewMsgRevokeFeeAllowance NewFeeGrantTx = types.NewFeeGrantTx CountSubKeys = types.CountSubKeys @@ -54,6 +53,7 @@ type ( BasicFeeAllowance = types.BasicFeeAllowance ExpiresAt = types.ExpiresAt Duration = types.Duration + FeeAllowance = types.FeeAllowance FeeAllowanceGrant = types.FeeAllowanceGrant MsgGrantFeeAllowance = types.MsgGrantFeeAllowance MsgRevokeFeeAllowance = types.MsgRevokeFeeAllowance @@ -61,5 +61,6 @@ type ( FeeGrantTx = types.FeeGrantTx GrantedFee = types.GrantedFee DelegatedSignDoc = types.DelegatedSignDoc + SupplyKeeper = types.SupplyKeeper Keeper = keeper.Keeper ) diff --git a/x/feegrant/ante/fee.go b/x/feegrant/ante/fee.go index 454a3f406fa2..af3cf51852a8 100644 --- a/x/feegrant/ante/fee.go +++ b/x/feegrant/ante/fee.go @@ -6,7 +6,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/feegrant/exported" "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" "github.com/cosmos/cosmos-sdk/x/feegrant/types" ) @@ -30,12 +29,12 @@ type GrantedFeeTx interface { // Call next AnteHandler if fees successfully deducted // CONTRACT: Tx must implement GrantedFeeTx interface to use DeductGrantedFeeDecorator type DeductGrantedFeeDecorator struct { - ak exported.AccountKeeper + ak types.AccountKeeper k keeper.Keeper - sk exported.SupplyKeeper + sk types.SupplyKeeper } -func NewDeductGrantedFeeDecorator(ak exported.AccountKeeper, sk exported.SupplyKeeper, k keeper.Keeper) DeductGrantedFeeDecorator { +func NewDeductGrantedFeeDecorator(ak types.AccountKeeper, sk types.SupplyKeeper, k keeper.Keeper) DeductGrantedFeeDecorator { return DeductGrantedFeeDecorator{ ak: ak, k: k, diff --git a/x/feegrant/ante/fee_test.go b/x/feegrant/ante/fee_test.go index de682db87816..de1773f0c4c3 100644 --- a/x/feegrant/ante/fee_test.go +++ b/x/feegrant/ante/fee_test.go @@ -70,27 +70,33 @@ func TestDeductFeesNoDelegation(t *testing.T) { app.FeeGrantKeeper.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr2, Grantee: addr3, - Allowance: &types.BasicFeeAllowance{ + Allowance: &types.FeeAllowance{Sum: &types.FeeAllowance_BasicFeeAllowance{BasicFeeAllowance: &types.BasicFeeAllowance{ SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 500)), }, + }, + }, }) // Set low grant from addr2 to addr4 (keeper will reject) app.FeeGrantKeeper.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr2, Grantee: addr4, - Allowance: &types.BasicFeeAllowance{ + Allowance: &types.FeeAllowance{Sum: &types.FeeAllowance_BasicFeeAllowance{BasicFeeAllowance: &types.BasicFeeAllowance{ SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 20)), }, + }, + }, }) // Set grant from addr1 to addr4 (cannot cover this ) app.FeeGrantKeeper.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ Granter: addr2, Grantee: addr3, - Allowance: &types.BasicFeeAllowance{ + Allowance: &types.FeeAllowance{Sum: &types.FeeAllowance_BasicFeeAllowance{BasicFeeAllowance: &types.BasicFeeAllowance{ SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 500)), }, + }, + }, }) cases := map[string]struct { diff --git a/x/feegrant/genesis.go b/x/feegrant/genesis.go index 8d5b53e4bbb4..d32598a6e167 100644 --- a/x/feegrant/genesis.go +++ b/x/feegrant/genesis.go @@ -1,6 +1,8 @@ package feegrant -import sdk "github.com/cosmos/cosmos-sdk/types" +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) // GenesisState contains a set of fee allowances, persisted from the store type GenesisState []FeeAllowanceGrant @@ -8,7 +10,7 @@ type GenesisState []FeeAllowanceGrant // ValidateBasic ensures all grants in the genesis state are valid func (g GenesisState) ValidateBasic() error { for _, f := range g { - err := f.ValidateBasic() + err := f.GetFeeGrant().ValidateBasic() if err != nil { return err } diff --git a/x/feegrant/handler.go b/x/feegrant/handler.go index dd513898594c..40d483a41790 100644 --- a/x/feegrant/handler.go +++ b/x/feegrant/handler.go @@ -23,8 +23,9 @@ func NewHandler(k Keeper) sdk.Handler { } func handleGrantFee(ctx sdk.Context, k Keeper, msg MsgGrantFeeAllowance) (*sdk.Result, error) { - grant := FeeAllowanceGrant(msg) - k.GrantFeeAllowance(ctx, grant) + feegrant := FeeAllowanceGrant(msg) + + k.GrantFeeAllowance(ctx, feegrant) return &sdk.Result{Events: ctx.EventManager().Events()}, nil } diff --git a/x/feegrant/keeper/keeper.go b/x/feegrant/keeper/keeper.go index 263b5faabadb..d3fb815f06a5 100644 --- a/x/feegrant/keeper/keeper.go +++ b/x/feegrant/keeper/keeper.go @@ -8,19 +8,18 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/feegrant/exported" "github.com/cosmos/cosmos-sdk/x/feegrant/types" ) // Keeper manages state of all fee grants, as well as calculating approval. // It must have a codec with all available allowances registered. type Keeper struct { - cdc *codec.Codec + cdc codec.Marshaler storeKey sdk.StoreKey } // NewKeeper creates a fee grant Keeper -func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey) Keeper { +func NewKeeper(cdc codec.Marshaler, storeKey sdk.StoreKey) Keeper { return Keeper{cdc: cdc, storeKey: storeKey} } @@ -33,8 +32,7 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { func (k Keeper) GrantFeeAllowance(ctx sdk.Context, grant types.FeeAllowanceGrant) { store := ctx.KVStore(k.storeKey) key := types.FeeAllowanceKey(grant.Granter, grant.Grantee) - bz := k.cdc.MustMarshalBinaryBare(grant) - + bz := k.cdc.MustMarshalBinaryBare(&grant) store.Set(key, bz) ctx.EventManager().EmitEvent( @@ -65,7 +63,7 @@ func (k Keeper) RevokeFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddr // GetFeeAllowance returns the allowance between the granter and grantee. // If there is none, it returns nil, nil. // Returns an error on parsing issues -func (k Keeper) GetFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) exported.FeeAllowance { +func (k Keeper) GetFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) *types.FeeAllowance { grant, found := k.GetFeeGrant(ctx, granter, grantee) if !found { return nil @@ -83,10 +81,10 @@ func (k Keeper) GetFeeGrant(ctx sdk.Context, granter sdk.AccAddress, grantee sdk return types.FeeAllowanceGrant{}, false } - var grant types.FeeAllowanceGrant - k.cdc.MustUnmarshalBinaryBare(bz, &grant) + var feegrant types.FeeAllowanceGrant + k.cdc.MustUnmarshalBinaryBare(bz, &feegrant) - return grant, true + return feegrant, true } // IterateAllGranteeFeeAllowances iterates over all the grants from anyone to the given grantee. @@ -100,14 +98,11 @@ func (k Keeper) IterateAllGranteeFeeAllowances(ctx sdk.Context, grantee sdk.AccA stop := false for ; iter.Valid() && !stop; iter.Next() { bz := iter.Value() - var grant types.FeeAllowanceGrant - err := k.cdc.UnmarshalBinaryBare(bz, &grant) - if err != nil { - return err - } + var feeGrant types.FeeAllowanceGrant + k.cdc.MustUnmarshalBinaryBare(bz, &feeGrant) - stop = cb(grant) + stop = cb(feeGrant) } return nil @@ -124,14 +119,10 @@ func (k Keeper) IterateAllFeeAllowances(ctx sdk.Context, cb func(types.FeeAllowa stop := false for ; iter.Valid() && !stop; iter.Next() { bz := iter.Value() - var grant types.FeeAllowanceGrant - - err := k.cdc.UnmarshalBinaryBare(bz, &grant) - if err != nil { - return err - } + var feeGrant types.FeeAllowanceGrant + k.cdc.MustUnmarshalBinaryBare(bz, &feeGrant) - stop = cb(grant) + stop = cb(feeGrant) } return nil @@ -140,11 +131,11 @@ func (k Keeper) IterateAllFeeAllowances(ctx sdk.Context, cb func(types.FeeAllowa // UseGrantedFees will try to pay the given fee from the granter's account as requested by the grantee func (k Keeper) UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins) error { grant, found := k.GetFeeGrant(ctx, granter, grantee) - if !found || grant.Allowance == nil { + if !found || grant.GetFeeGrant() == nil { return sdkerrors.Wrapf(types.ErrNoAllowance, "grant missing") } - remove, err := grant.Allowance.Accept(fee, ctx.BlockTime(), ctx.BlockHeight()) + remove, err := grant.GetFeeGrant().Accept(fee, ctx.BlockTime(), ctx.BlockHeight()) if err == nil { ctx.EventManager().EmitEvent( sdk.NewEvent( diff --git a/x/feegrant/keeper/keeper_test.go b/x/feegrant/keeper/keeper_test.go index 754266ba9768..e579922999b6 100644 --- a/x/feegrant/keeper/keeper_test.go +++ b/x/feegrant/keeper/keeper_test.go @@ -10,9 +10,9 @@ import ( dbm "github.com/tendermint/tm-db" codec "github.com/cosmos/cosmos-sdk/codec" + codecstd "github.com/cosmos/cosmos-sdk/codec/std" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/feegrant/exported" "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" "github.com/cosmos/cosmos-sdk/x/feegrant/types" ) @@ -41,14 +41,14 @@ func (suite *KeeperTestSuite) SetupTest() { types.RegisterCodec(cdc) sdk.RegisterCodec(cdc) suite.cdc = cdc - + appCodec := codecstd.NewAppCodec(cdc) delCapKey := sdk.NewKVStoreKey("delKey") ms := store.NewCommitMultiStore(db) ms.MountStoreWithDB(delCapKey, sdk.StoreTypeIAVL, db) ms.LoadLatestVersion() - suite.dk = keeper.NewKeeper(cdc, delCapKey) + suite.dk = keeper.NewKeeper(appCodec, delCapKey) suite.ctx = sdk.NewContext(ms, abci.Header{ChainID: "test-chain-id", Time: time.Now().UTC(), Height: 1234}, false, log.NewNopLogger()) suite.addr = mustAddr("cosmos157ez5zlaq0scm9aycwphhqhmg3kws4qusmekll") @@ -73,40 +73,58 @@ func (suite *KeeperTestSuite) TestKeeperCrud() { // some helpers atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123)) - basic := types.BasicFeeAllowance{ + basic := &types.FeeAllowance{Sum: &types.FeeAllowance_BasicFeeAllowance{BasicFeeAllowance: &types.BasicFeeAllowance{ SpendLimit: atom, Expiration: types.ExpiresAtHeight(334455), + }, + }, } - basic2 := types.BasicFeeAllowance{ + basic2 := &types.FeeAllowance{Sum: &types.FeeAllowance_BasicFeeAllowance{BasicFeeAllowance: &types.BasicFeeAllowance{ SpendLimit: eth, Expiration: types.ExpiresAtHeight(172436), + }, + }, } // let's set up some initial state here k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ - Granter: suite.addr, Grantee: suite.addr2, Allowance: &basic, + Granter: suite.addr, + Grantee: suite.addr2, + Allowance: basic, }) k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ - Granter: suite.addr, Grantee: suite.addr3, Allowance: &basic2, + Granter: suite.addr, + Grantee: suite.addr3, + Allowance: basic2, }) k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ - Granter: suite.addr2, Grantee: suite.addr3, Allowance: &basic, + Granter: suite.addr2, + Grantee: suite.addr3, + Allowance: basic, }) k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ - Granter: suite.addr2, Grantee: suite.addr4, Allowance: &basic, + Granter: suite.addr2, + Grantee: suite.addr4, + Allowance: basic, }) k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ - Granter: suite.addr4, Grantee: suite.addr, Allowance: &basic2, + Granter: suite.addr4, + Grantee: suite.addr, + Allowance: basic2, }) // remove some, overwrite other k.RevokeFeeAllowance(ctx, suite.addr, suite.addr2) k.RevokeFeeAllowance(ctx, suite.addr, suite.addr3) k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ - Granter: suite.addr, Grantee: suite.addr3, Allowance: &basic, + Granter: suite.addr, + Grantee: suite.addr3, + Allowance: basic, }) k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ - Granter: suite.addr2, Grantee: suite.addr3, Allowance: &basic2, + Granter: suite.addr2, + Grantee: suite.addr3, + Allowance: basic2, }) // end state: @@ -118,7 +136,7 @@ func (suite *KeeperTestSuite) TestKeeperCrud() { cases := map[string]struct { grantee sdk.AccAddress granter sdk.AccAddress - allowance exported.FeeAllowance + allowance *types.FeeAllowance }{ "addr revoked": { granter: suite.addr, @@ -127,7 +145,7 @@ func (suite *KeeperTestSuite) TestKeeperCrud() { "addr revoked and added": { granter: suite.addr, grantee: suite.addr3, - allowance: &basic, + allowance: basic, }, "addr never there": { granter: suite.addr, @@ -136,7 +154,7 @@ func (suite *KeeperTestSuite) TestKeeperCrud() { "addr modified": { granter: suite.addr2, grantee: suite.addr3, - allowance: &basic2, + allowance: basic2, }, } @@ -162,13 +180,13 @@ func (suite *KeeperTestSuite) TestKeeperCrud() { }, "addr has one": { grantee: suite.addr, - grants: []types.FeeAllowanceGrant{{Granter: suite.addr4, Grantee: suite.addr, Allowance: &basic2}}, + grants: []types.FeeAllowanceGrant{{Granter: suite.addr4, Grantee: suite.addr, Allowance: basic2}}, }, "addr3 has two": { grantee: suite.addr3, grants: []types.FeeAllowanceGrant{ - {Granter: suite.addr, Grantee: suite.addr3, Allowance: &basic}, - {Granter: suite.addr2, Grantee: suite.addr3, Allowance: &basic2}, + {Granter: suite.addr, Grantee: suite.addr3, Allowance: basic}, + {Granter: suite.addr2, Grantee: suite.addr3, Allowance: basic2}, }, }, } @@ -194,21 +212,28 @@ func (suite *KeeperTestSuite) TestUseGrantedFee() { // some helpers atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123)) - future := types.BasicFeeAllowance{ + future := &types.FeeAllowance{Sum: &types.FeeAllowance_BasicFeeAllowance{BasicFeeAllowance: &types.BasicFeeAllowance{ SpendLimit: atom, Expiration: types.ExpiresAtHeight(5678), + }, + }, } - expired := types.BasicFeeAllowance{ + + expired := &types.FeeAllowance{Sum: &types.FeeAllowance_BasicFeeAllowance{BasicFeeAllowance: &types.BasicFeeAllowance{ SpendLimit: eth, Expiration: types.ExpiresAtHeight(55), + }, + }, } // for testing limits of the contract hugeAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 9999)) smallAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 1)) - futureAfterSmall := types.BasicFeeAllowance{ + futureAfterSmall := &types.FeeAllowance{Sum: &types.FeeAllowance_BasicFeeAllowance{BasicFeeAllowance: &types.BasicFeeAllowance{ SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 554)), Expiration: types.ExpiresAtHeight(5678), + }, + }, } // then lots of queries @@ -217,7 +242,7 @@ func (suite *KeeperTestSuite) TestUseGrantedFee() { granter sdk.AccAddress fee sdk.Coins allowed bool - final exported.FeeAllowance + final *types.FeeAllowance }{ "use entire pot": { granter: suite.addr, @@ -238,14 +263,14 @@ func (suite *KeeperTestSuite) TestUseGrantedFee() { grantee: suite.addr2, fee: hugeAtom, allowed: false, - final: &future, + final: future, }, "use a little": { granter: suite.addr, grantee: suite.addr2, fee: smallAtom, allowed: true, - final: &futureAfterSmall, + final: futureAfterSmall, }, } @@ -255,11 +280,16 @@ func (suite *KeeperTestSuite) TestUseGrantedFee() { // let's set up some initial state here // addr -> addr2 (future) // addr -> addr3 (expired) + k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ - Granter: suite.addr, Grantee: suite.addr2, Allowance: &future, + Granter: suite.addr, + Grantee: suite.addr2, + Allowance: future, }) k.GrantFeeAllowance(ctx, types.FeeAllowanceGrant{ - Granter: suite.addr, Grantee: suite.addr3, Allowance: &expired, + Granter: suite.addr, + Grantee: suite.addr3, + Allowance: expired, }) err := k.UseGrantedFees(ctx, tc.granter, tc.grantee, tc.fee) @@ -270,6 +300,7 @@ func (suite *KeeperTestSuite) TestUseGrantedFee() { } loaded := k.GetFeeAllowance(ctx, tc.granter, tc.grantee) + suite.Equal(tc.final, loaded) }) } diff --git a/x/feegrant/keeper/querier.go b/x/feegrant/keeper/querier.go index 1ebe5dfdfe6b..41b0ab7970c4 100644 --- a/x/feegrant/keeper/querier.go +++ b/x/feegrant/keeper/querier.go @@ -3,6 +3,7 @@ package keeper import ( abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/feegrant/types" @@ -52,7 +53,7 @@ func queryGetFeeAllowances(ctx sdk.Context, args []string, keeper Keeper) ([]byt return []byte("[]"), nil } - bz, err := keeper.cdc.MarshalJSON(grants) + bz, err := codec.MarshalJSONIndent(keeper.cdc, grants) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } diff --git a/x/feegrant/keeper/querier_test.go b/x/feegrant/keeper/querier_test.go index 8a3e21ad423d..3efd772b33f7 100644 --- a/x/feegrant/keeper/querier_test.go +++ b/x/feegrant/keeper/querier_test.go @@ -20,20 +20,20 @@ func (suite *KeeperTestSuite) TestQuery() { grant1 := types.FeeAllowanceGrant{ Granter: suite.addr, Grantee: suite.addr3, - Allowance: &types.BasicFeeAllowance{ + Allowance: &types.FeeAllowance{Sum: &types.FeeAllowance_BasicFeeAllowance{BasicFeeAllowance: &types.BasicFeeAllowance{ SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 555)), Expiration: types.ExpiresAtHeight(334455), }, + }, + }, } grant2 := types.FeeAllowanceGrant{ Granter: suite.addr2, Grantee: suite.addr3, - Allowance: &types.BasicFeeAllowance{ + Allowance: &types.FeeAllowance{Sum: &types.FeeAllowance_BasicFeeAllowance{BasicFeeAllowance: &types.BasicFeeAllowance{ SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("eth", 123)), - Expiration: types.ExpiresAtHeight(334455), - }, + Expiration: types.ExpiresAtHeight(334455)}}}, } - // let's set up some initial state here k.GrantFeeAllowance(ctx, grant1) k.GrantFeeAllowance(ctx, grant2) diff --git a/x/feegrant/types/basic_fee.go b/x/feegrant/types/basic_fee.go index bc39194421f1..52c079ca64ee 100644 --- a/x/feegrant/types/basic_fee.go +++ b/x/feegrant/types/basic_fee.go @@ -5,21 +5,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/feegrant/exported" ) -// BasicFeeAllowance implements FeeAllowance with a one-time grant of tokens -// that optionally expires. The delegatee can use up to SpendLimit to cover fees. -type BasicFeeAllowance struct { - // SpendLimit is the maximum amount of tokens to be spent - SpendLimit sdk.Coins - - // Expiration specifies an optional time or height when this allowance expires. - // If Expiration.IsZero() then it never expires - Expiration ExpiresAt -} - -var _ exported.FeeAllowance = (*BasicFeeAllowance)(nil) +var _ FeeAllowanceI = (*BasicFeeAllowance)(nil) // Accept can use fee payment requested as well as timestamp/height of the current block // to determine whether or not to process this. This is checked in @@ -48,7 +36,7 @@ func (a *BasicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockHeig // PrepareForExport will adjust the expiration based on export time. In particular, // it will subtract the dumpHeight from any height-based expiration to ensure that // the elapsed number of blocks this allowance is valid for is fixed. -func (a *BasicFeeAllowance) PrepareForExport(dumpTime time.Time, dumpHeight int64) exported.FeeAllowance { +func (a *BasicFeeAllowance) PrepareForExport(dumpTime time.Time, dumpHeight int64) FeeAllowanceI { return &BasicFeeAllowance{ SpendLimit: a.SpendLimit, Expiration: a.Expiration.PrepareForExport(dumpTime, dumpHeight), diff --git a/x/feegrant/types/basic_fee_test.go b/x/feegrant/types/basic_fee_test.go index 71e640166a90..887fa5fbf3c5 100644 --- a/x/feegrant/types/basic_fee_test.go +++ b/x/feegrant/types/basic_fee_test.go @@ -16,7 +16,7 @@ func TestBasicFeeValidAllow(t *testing.T) { leftAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 512)) cases := map[string]struct { - allow BasicFeeAllowance + allow *BasicFeeAllowance // all other checks are ignored if valid=false fee sdk.Coins blockTime time.Time @@ -27,11 +27,11 @@ func TestBasicFeeValidAllow(t *testing.T) { remains sdk.Coins }{ "empty": { - allow: BasicFeeAllowance{}, + allow: &BasicFeeAllowance{}, valid: false, }, "small fee": { - allow: BasicFeeAllowance{ + allow: &BasicFeeAllowance{ SpendLimit: atom, }, valid: true, @@ -41,7 +41,7 @@ func TestBasicFeeValidAllow(t *testing.T) { remains: leftAtom, }, "all fee": { - allow: BasicFeeAllowance{ + allow: &BasicFeeAllowance{ SpendLimit: smallAtom, }, valid: true, @@ -50,7 +50,7 @@ func TestBasicFeeValidAllow(t *testing.T) { remove: true, }, "wrong fee": { - allow: BasicFeeAllowance{ + allow: &BasicFeeAllowance{ SpendLimit: smallAtom, }, valid: true, @@ -58,7 +58,7 @@ func TestBasicFeeValidAllow(t *testing.T) { accept: false, }, "non-expired": { - allow: BasicFeeAllowance{ + allow: &BasicFeeAllowance{ SpendLimit: atom, Expiration: ExpiresAtHeight(100), }, @@ -70,7 +70,7 @@ func TestBasicFeeValidAllow(t *testing.T) { remains: leftAtom, }, "expired": { - allow: BasicFeeAllowance{ + allow: &BasicFeeAllowance{ SpendLimit: atom, Expiration: ExpiresAtHeight(100), }, diff --git a/x/feegrant/types/codec.go b/x/feegrant/types/codec.go index 541bf6d325a6..a051889d6b2d 100644 --- a/x/feegrant/types/codec.go +++ b/x/feegrant/types/codec.go @@ -2,24 +2,26 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/feegrant/exported" ) // RegisterCodec registers the account types and interface func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterInterface((*exported.FeeAllowance)(nil), nil) + cdc.RegisterInterface((*isFeeAllowance_Sum)(nil), nil) + cdc.RegisterInterface((*FeeAllowanceI)(nil), nil) + cdc.RegisterConcrete(&FeeAllowance_BasicFeeAllowance{}, "cosmos-sdk/ProtoBasicFeeAllowance", nil) cdc.RegisterConcrete(&BasicFeeAllowance{}, "cosmos-sdk/BasicFeeAllowance", nil) cdc.RegisterConcrete(&PeriodicFeeAllowance{}, "cosmos-sdk/PeriodicFeeAllowance", nil) - cdc.RegisterConcrete(FeeGrantTx{}, "cosmos-sdk/FeeGrantTx", nil) } -// ModuleCdc generic sealed codec to be used throughout module -var ModuleCdc *codec.Codec +var ( + amino = codec.New() + + ModuleCdc = codec.NewHybridCodec(amino) +) func init() { - cdc := codec.New() - RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - ModuleCdc = cdc.Seal() + RegisterCodec(amino) + codec.RegisterCrypto(amino) + amino.Seal() } diff --git a/x/feegrant/exported/external.go b/x/feegrant/types/expected_keepers.go similarity index 97% rename from x/feegrant/exported/external.go rename to x/feegrant/types/expected_keepers.go index 1713ecb500f3..e60e5ad08171 100644 --- a/x/feegrant/exported/external.go +++ b/x/feegrant/types/expected_keepers.go @@ -1,4 +1,4 @@ -package exported +package types import ( sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/x/feegrant/types/expiration.go b/x/feegrant/types/expiration.go index 7c552f02e8d9..2bfdc3e412d0 100644 --- a/x/feegrant/types/expiration.go +++ b/x/feegrant/types/expiration.go @@ -6,13 +6,6 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) -// ExpiresAt is a point in time where something expires. -// It may be *either* block time or block height -type ExpiresAt struct { - Time time.Time `json:"time" yaml:"time"` - Height int64 `json:"height" yaml:"height"` -} - // ExpiresAtTime creates an expiration at the given time func ExpiresAtTime(t time.Time) ExpiresAt { return ExpiresAt{Time: t} @@ -102,13 +95,6 @@ func (e ExpiresAt) PrepareForExport(dumpTime time.Time, dumpHeight int64) Expire return e } -// Duration is a repeating unit of either clock time or number of blocks. -// This is designed to be added to an ExpiresAt struct. -type Duration struct { - Clock time.Duration `json:"clock" yaml:"clock"` - Block int64 `json:"block" yaml:"block"` -} - // ClockDuration creates an Duration by clock time func ClockDuration(d time.Duration) Duration { return Duration{Clock: d} diff --git a/x/feegrant/types/expiration_test.go b/x/feegrant/types/expiration_test.go index ab475fdf6312..6cab366eabdc 100644 --- a/x/feegrant/types/expiration_test.go +++ b/x/feegrant/types/expiration_test.go @@ -15,20 +15,20 @@ func TestExpiresAt(t *testing.T) { example ExpiresAt valid bool zero bool - before *ExpiresAt - after *ExpiresAt + before ExpiresAt + after ExpiresAt }{ "basic": { example: ExpiresAtHeight(100), valid: true, - before: &ExpiresAt{Height: 50, Time: now}, - after: &ExpiresAt{Height: 122, Time: now}, + before: ExpiresAt{Height: 50, Time: now}, + after: ExpiresAt{Height: 122, Time: now}, }, "zero": { example: ExpiresAt{}, zero: true, valid: true, - before: &ExpiresAt{Height: 1}, + before: ExpiresAt{Height: 1}, }, "double": { example: ExpiresAt{Height: 100, Time: now}, @@ -37,14 +37,14 @@ func TestExpiresAt(t *testing.T) { "match height": { example: ExpiresAtHeight(1000), valid: true, - before: &ExpiresAt{Height: 999, Time: now}, - after: &ExpiresAt{Height: 1000, Time: now}, + before: ExpiresAt{Height: 999, Time: now}, + after: ExpiresAt{Height: 1000, Time: now}, }, "match time": { example: ExpiresAtTime(now), valid: true, - before: &ExpiresAt{Height: 43, Time: now.Add(-1 * time.Second)}, - after: &ExpiresAt{Height: 76, Time: now}, + before: ExpiresAt{Height: 43, Time: now.Add(-1 * time.Second)}, + after: ExpiresAt{Height: 76, Time: now}, }, } @@ -59,10 +59,10 @@ func TestExpiresAt(t *testing.T) { } require.NoError(t, err) - if tc.before != nil { + if !tc.before.IsZero() { assert.Equal(t, false, tc.example.IsExpired(tc.before.Time, tc.before.Height)) } - if tc.after != nil { + if !tc.after.IsZero() { assert.Equal(t, true, tc.example.IsExpired(tc.after.Time, tc.after.Height)) } }) diff --git a/x/feegrant/exported/fees.go b/x/feegrant/types/fees.go similarity index 96% rename from x/feegrant/exported/fees.go rename to x/feegrant/types/fees.go index 7f8a55ab8acf..cc526628f5f9 100644 --- a/x/feegrant/exported/fees.go +++ b/x/feegrant/types/fees.go @@ -1,4 +1,4 @@ -package exported +package types import ( "time" @@ -8,7 +8,7 @@ import ( // FeeAllowance implementations are tied to a given fee delegator and delegatee, // and are used to enforce fee grant limits. -type FeeAllowance interface { +type FeeAllowanceI interface { // Accept can use fee payment requested as well as timestamp/height of the current block // to determine whether or not to process this. This is checked in // Keeper.UseGrantedFees and the return values should match how it is handled there. @@ -24,7 +24,7 @@ type FeeAllowance interface { // If we export fee allowances the timing info will be quite off (eg. go from height 100000 to 0) // This callback allows the fee-allowance to change it's state and return a copy that is adjusted // given the time and height of the actual dump (may safely return self if no changes needed) - PrepareForExport(dumpTime time.Time, dumpHeight int64) FeeAllowance + PrepareForExport(dumpTime time.Time, dumpHeight int64) FeeAllowanceI // ValidateBasic should evaluate this FeeAllowance for internal consistency. // Don't allow negative amounts, or negative periods for example. diff --git a/x/feegrant/types/grant.go b/x/feegrant/types/grant.go index 08c872b24ca7..a3c6b142ef49 100644 --- a/x/feegrant/types/grant.go +++ b/x/feegrant/types/grant.go @@ -3,19 +3,11 @@ package types import ( "time" - sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/feegrant/exported" ) -// FeeAllowanceGrant is stored in the KVStore to record a grant with full context -type FeeAllowanceGrant struct { - Granter sdk.AccAddress `json:"granter" yaml:"granter"` - Grantee sdk.AccAddress `json:"grantee" yaml:"grantee"` - Allowance exported.FeeAllowance `json:"allowance" yaml:"allowance"` -} - -// ValidateBasic ensures that +// ValidateBasic performs basic validation on +// FeeAllowanceGrant func (a FeeAllowanceGrant) ValidateBasic() error { if a.Granter.Empty() { return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "missing granter address") @@ -27,12 +19,19 @@ func (a FeeAllowanceGrant) ValidateBasic() error { return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "cannot self-grant fee authorization") } - return a.Allowance.ValidateBasic() + return a.GetFeeGrant().ValidateBasic() } -// PrepareForExport will make all needed changes to the allowance to prepare to be +func (a FeeAllowanceGrant) GetFeeGrant() FeeAllowanceI { + return a.Allowance.GetFeeAllowanceI() +} + +// PrepareForExport will m ake all needed changes to the allowance to prepare to be // re-imported at height 0, and return a copy of this grant. func (a FeeAllowanceGrant) PrepareForExport(dumpTime time.Time, dumpHeight int64) FeeAllowanceGrant { - a.Allowance = a.Allowance.PrepareForExport(dumpTime, dumpHeight) + err := a.GetFeeGrant().PrepareForExport(dumpTime, dumpHeight) + if err != nil { + return FeeAllowanceGrant{} + } return a } diff --git a/x/feegrant/types/grant_test.go b/x/feegrant/types/grant_test.go index 3a07559e27e1..a82b15ecd9f1 100644 --- a/x/feegrant/types/grant_test.go +++ b/x/feegrant/types/grant_test.go @@ -27,47 +27,57 @@ func TestGrant(t *testing.T) { grant: FeeAllowanceGrant{ Grantee: addr, Granter: addr2, - Allowance: &BasicFeeAllowance{ + Allowance: &FeeAllowance{Sum: &FeeAllowance_BasicFeeAllowance{BasicFeeAllowance: &BasicFeeAllowance{ SpendLimit: atom, Expiration: ExpiresAtHeight(100), }, + }, + }, }, valid: true, }, "no grantee": { grant: FeeAllowanceGrant{ Granter: addr2, - Allowance: &BasicFeeAllowance{ + Allowance: &FeeAllowance{Sum: &FeeAllowance_BasicFeeAllowance{BasicFeeAllowance: &BasicFeeAllowance{ SpendLimit: atom, Expiration: ExpiresAtHeight(100), }, + }, + }, }, }, "no granter": { grant: FeeAllowanceGrant{ Grantee: addr2, - Allowance: &BasicFeeAllowance{ + Allowance: &FeeAllowance{Sum: &FeeAllowance_BasicFeeAllowance{BasicFeeAllowance: &BasicFeeAllowance{ SpendLimit: atom, Expiration: ExpiresAtHeight(100), }, + }, + }, }, }, "self-grant": { grant: FeeAllowanceGrant{ Grantee: addr2, Granter: addr2, - Allowance: &BasicFeeAllowance{ + Allowance: &FeeAllowance{Sum: &FeeAllowance_BasicFeeAllowance{BasicFeeAllowance: &BasicFeeAllowance{ SpendLimit: atom, Expiration: ExpiresAtHeight(100), }, + }, + }, }, }, "bad allowance": { grant: FeeAllowanceGrant{ Grantee: addr, Granter: addr2, - Allowance: &BasicFeeAllowance{ - Expiration: ExpiresAtHeight(100), + Allowance: &FeeAllowance{Sum: &FeeAllowance_BasicFeeAllowance{BasicFeeAllowance: &BasicFeeAllowance{ + Expiration: ExpiresAtHeight(0), + }, + }, }, }, }, diff --git a/x/feegrant/types/msgs.go b/x/feegrant/types/msgs.go index 54e4c374efff..3f7f1690cd36 100644 --- a/x/feegrant/types/msgs.go +++ b/x/feegrant/types/msgs.go @@ -3,20 +3,18 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/feegrant/exported" ) -// MsgGrantFeeAllowance adds permission for Grantee to spend up to Allowance -// of fees from the account of Granter. -// If there was already an existing grant, this overwrites it. -type MsgGrantFeeAllowance struct { - Granter sdk.AccAddress `json:"granter" yaml:"granter"` - Grantee sdk.AccAddress `json:"grantee" yaml:"grantee"` - Allowance exported.FeeAllowance `json:"allowance" yaml:"allowance"` +func (msg MsgGrantFeeAllowance) NewMsgGrantFeeAllowance(feeAllowance *FeeAllowance, granter, grantee sdk.AccAddress) (MsgGrantFeeAllowance, error) { + return MsgGrantFeeAllowance{ + Granter: granter, + Grantee: grantee, + Allowance: feeAllowance, + }, nil } -func NewMsgGrantFeeAllowance(granter sdk.AccAddress, grantee sdk.AccAddress, allowance exported.FeeAllowance) MsgGrantFeeAllowance { - return MsgGrantFeeAllowance{Granter: granter, Grantee: grantee, Allowance: allowance} +func (msg MsgGrantFeeAllowance) GetFeeGrant() FeeAllowanceI { + return msg.Allowance.GetFeeAllowanceI() } func (msg MsgGrantFeeAllowance) Route() string { @@ -35,7 +33,7 @@ func (msg MsgGrantFeeAllowance) ValidateBasic() error { return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "missing grantee address") } - return msg.Allowance.ValidateBasic() + return nil } func (msg MsgGrantFeeAllowance) GetSignBytes() []byte { @@ -46,12 +44,6 @@ func (msg MsgGrantFeeAllowance) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Granter} } -// MsgRevokeFeeAllowance removes any existing FeeAllowance from Granter to Grantee. -type MsgRevokeFeeAllowance struct { - Granter sdk.AccAddress `json:"granter" yaml:"granter"` - Grantee sdk.AccAddress `json:"grantee" yaml:"granter"` -} - func NewMsgRevokeFeeAllowance(granter sdk.AccAddress, grantee sdk.AccAddress) MsgRevokeFeeAllowance { return MsgRevokeFeeAllowance{Granter: granter, Grantee: grantee} } diff --git a/x/feegrant/types/periodic_fee.go b/x/feegrant/types/periodic_fee.go index 9716dbf3a7cc..8108a70f4402 100644 --- a/x/feegrant/types/periodic_fee.go +++ b/x/feegrant/types/periodic_fee.go @@ -5,30 +5,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/feegrant/exported" ) -// PeriodicFeeAllowance extends FeeAllowance to allow for both a maximum cap, -// as well as a limit per time period. -type PeriodicFeeAllowance struct { - // Basic contains the absolute limits over all time. - // These limit (total and expiration) are enforced in addition to the - // periodic limits defined below (which renew every period) - Basic BasicFeeAllowance - - // Period is the duration of one period - Period Duration - // PeriodSpendLimit is the maximum amount of tokens to be spent in this period - PeriodSpendLimit sdk.Coins - - // PeriodCanSpend is how much is available until PeriodReset - PeriodCanSpend sdk.Coins - - // PeriodRest is when the PeriodCanSpend is updated - PeriodReset ExpiresAt -} - -var _ exported.FeeAllowance = (*PeriodicFeeAllowance)(nil) +var _ FeeAllowanceI = (*PeriodicFeeAllowance)(nil) // Accept can use fee payment requested as well as timestamp/height of the current block // to determine whether or not to process this. This is checked in @@ -90,7 +69,7 @@ func (a *PeriodicFeeAllowance) TryResetPeriod(blockTime time.Time, blockHeight i // it will subtract the dumpHeight from any height-based expiration to ensure that // the elapsed number of blocks this allowance is valid for is fixed. // (For PeriodReset and Basic.Expiration) -func (a *PeriodicFeeAllowance) PrepareForExport(dumpTime time.Time, dumpHeight int64) exported.FeeAllowance { +func (a *PeriodicFeeAllowance) PrepareForExport(dumpTime time.Time, dumpHeight int64) FeeAllowanceI { return &PeriodicFeeAllowance{ Basic: BasicFeeAllowance{ SpendLimit: a.Basic.SpendLimit, diff --git a/x/feegrant/types/types.pb.go b/x/feegrant/types/types.pb.go new file mode 100644 index 000000000000..79c6e9672521 --- /dev/null +++ b/x/feegrant/types/types.pb.go @@ -0,0 +1,2531 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: x/feegrant/types/types.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/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + _ "github.com/golang/protobuf/ptypes/timestamp" + _ "github.com/regen-network/cosmos-proto" + io "io" + math "math" + math_bits "math/bits" + time "time" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +// 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 + +// FeeAllowance defines the application-level fee allowance to be used in +// feegrant module +type FeeAllowance struct { + // sum defines a set of all acceptable concrete feeallowance implementations. + // + // Types that are valid to be assigned to Sum: + // *FeeAllowance_BasicFeeAllowance + // *FeeAllowance_PeriodicFeeAllowance + Sum isFeeAllowance_Sum `protobuf_oneof:"sum"` +} + +func (m *FeeAllowance) Reset() { *m = FeeAllowance{} } +func (m *FeeAllowance) String() string { return proto.CompactTextString(m) } +func (*FeeAllowance) ProtoMessage() {} +func (*FeeAllowance) Descriptor() ([]byte, []int) { + return fileDescriptor_86c534389d2c5768, []int{0} +} +func (m *FeeAllowance) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FeeAllowance) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FeeAllowance.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 *FeeAllowance) XXX_Merge(src proto.Message) { + xxx_messageInfo_FeeAllowance.Merge(m, src) +} +func (m *FeeAllowance) XXX_Size() int { + return m.Size() +} +func (m *FeeAllowance) XXX_DiscardUnknown() { + xxx_messageInfo_FeeAllowance.DiscardUnknown(m) +} + +var xxx_messageInfo_FeeAllowance proto.InternalMessageInfo + +type isFeeAllowance_Sum interface { + isFeeAllowance_Sum() + Equal(interface{}) bool + MarshalTo([]byte) (int, error) + Size() int +} + +type FeeAllowance_BasicFeeAllowance struct { + BasicFeeAllowance *BasicFeeAllowance `protobuf:"bytes,1,opt,name=basic_fee_allowance,json=basicFeeAllowance,proto3,oneof" json:"basic_fee_allowance,omitempty"` +} +type FeeAllowance_PeriodicFeeAllowance struct { + PeriodicFeeAllowance *PeriodicFeeAllowance `protobuf:"bytes,2,opt,name=periodic_fee_allowance,json=periodicFeeAllowance,proto3,oneof" json:"periodic_fee_allowance,omitempty"` +} + +func (*FeeAllowance_BasicFeeAllowance) isFeeAllowance_Sum() {} +func (*FeeAllowance_PeriodicFeeAllowance) isFeeAllowance_Sum() {} + +func (m *FeeAllowance) GetSum() isFeeAllowance_Sum { + if m != nil { + return m.Sum + } + return nil +} + +func (m *FeeAllowance) GetBasicFeeAllowance() *BasicFeeAllowance { + if x, ok := m.GetSum().(*FeeAllowance_BasicFeeAllowance); ok { + return x.BasicFeeAllowance + } + return nil +} + +func (m *FeeAllowance) GetPeriodicFeeAllowance() *PeriodicFeeAllowance { + if x, ok := m.GetSum().(*FeeAllowance_PeriodicFeeAllowance); ok { + return x.PeriodicFeeAllowance + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*FeeAllowance) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*FeeAllowance_BasicFeeAllowance)(nil), + (*FeeAllowance_PeriodicFeeAllowance)(nil), + } +} + +// MsgGrantFeeAllowance adds permission for Grantee to spend up to Allowance +// of fees from the account of Granter. +type MsgGrantFeeAllowance struct { + Granter github_com_cosmos_cosmos_sdk_types.AccAddress `protobuf:"bytes,1,opt,name=granter,proto3,casttype=github.com/cosmos/cosmos-sdk/types.AccAddress" json:"granter,omitempty"` + Grantee github_com_cosmos_cosmos_sdk_types.AccAddress `protobuf:"bytes,2,opt,name=grantee,proto3,casttype=github.com/cosmos/cosmos-sdk/types.AccAddress" json:"grantee,omitempty"` + Allowance *FeeAllowance `protobuf:"bytes,3,opt,name=allowance,proto3" json:"allowance,omitempty"` +} + +func (m *MsgGrantFeeAllowance) Reset() { *m = MsgGrantFeeAllowance{} } +func (m *MsgGrantFeeAllowance) String() string { return proto.CompactTextString(m) } +func (*MsgGrantFeeAllowance) ProtoMessage() {} +func (*MsgGrantFeeAllowance) Descriptor() ([]byte, []int) { + return fileDescriptor_86c534389d2c5768, []int{1} +} +func (m *MsgGrantFeeAllowance) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgGrantFeeAllowance) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgGrantFeeAllowance.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 *MsgGrantFeeAllowance) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgGrantFeeAllowance.Merge(m, src) +} +func (m *MsgGrantFeeAllowance) XXX_Size() int { + return m.Size() +} +func (m *MsgGrantFeeAllowance) XXX_DiscardUnknown() { + xxx_messageInfo_MsgGrantFeeAllowance.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgGrantFeeAllowance proto.InternalMessageInfo + +// MsgRevokeFeeAllowance removes any existing FeeAllowance from Granter to Grantee. +type MsgRevokeFeeAllowance struct { + Granter github_com_cosmos_cosmos_sdk_types.AccAddress `protobuf:"bytes,1,opt,name=granter,proto3,casttype=github.com/cosmos/cosmos-sdk/types.AccAddress" json:"granter,omitempty"` + Grantee github_com_cosmos_cosmos_sdk_types.AccAddress `protobuf:"bytes,2,opt,name=grantee,proto3,casttype=github.com/cosmos/cosmos-sdk/types.AccAddress" json:"grantee,omitempty"` +} + +func (m *MsgRevokeFeeAllowance) Reset() { *m = MsgRevokeFeeAllowance{} } +func (m *MsgRevokeFeeAllowance) String() string { return proto.CompactTextString(m) } +func (*MsgRevokeFeeAllowance) ProtoMessage() {} +func (*MsgRevokeFeeAllowance) Descriptor() ([]byte, []int) { + return fileDescriptor_86c534389d2c5768, []int{2} +} +func (m *MsgRevokeFeeAllowance) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRevokeFeeAllowance) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRevokeFeeAllowance.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 *MsgRevokeFeeAllowance) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRevokeFeeAllowance.Merge(m, src) +} +func (m *MsgRevokeFeeAllowance) XXX_Size() int { + return m.Size() +} +func (m *MsgRevokeFeeAllowance) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRevokeFeeAllowance.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRevokeFeeAllowance proto.InternalMessageInfo + +func (m *MsgRevokeFeeAllowance) GetGranter() github_com_cosmos_cosmos_sdk_types.AccAddress { + if m != nil { + return m.Granter + } + return nil +} + +func (m *MsgRevokeFeeAllowance) GetGrantee() github_com_cosmos_cosmos_sdk_types.AccAddress { + if m != nil { + return m.Grantee + } + return nil +} + +// BasicFeeAllowance implements FeeAllowance with a one-time grant of tokens +// that optionally expires. The delegatee can use up to SpendLimit to cover fees. +type BasicFeeAllowance struct { + SpendLimit github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=spend_limit,json=spendLimit,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"spend_limit" yaml:"spend_limit"` + Expiration ExpiresAt `protobuf:"bytes,2,opt,name=expiration,proto3" json:"expiration"` +} + +func (m *BasicFeeAllowance) Reset() { *m = BasicFeeAllowance{} } +func (m *BasicFeeAllowance) String() string { return proto.CompactTextString(m) } +func (*BasicFeeAllowance) ProtoMessage() {} +func (*BasicFeeAllowance) Descriptor() ([]byte, []int) { + return fileDescriptor_86c534389d2c5768, []int{3} +} +func (m *BasicFeeAllowance) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BasicFeeAllowance) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BasicFeeAllowance.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 *BasicFeeAllowance) XXX_Merge(src proto.Message) { + xxx_messageInfo_BasicFeeAllowance.Merge(m, src) +} +func (m *BasicFeeAllowance) XXX_Size() int { + return m.Size() +} +func (m *BasicFeeAllowance) XXX_DiscardUnknown() { + xxx_messageInfo_BasicFeeAllowance.DiscardUnknown(m) +} + +var xxx_messageInfo_BasicFeeAllowance proto.InternalMessageInfo + +// PeriodicFeeAllowance extends FeeAllowance to allow for both a maximum cap, +// as well as a limit per time period. +type PeriodicFeeAllowance struct { + Basic BasicFeeAllowance `protobuf:"bytes,1,opt,name=basic,proto3" json:"basic"` + Period Duration `protobuf:"bytes,2,opt,name=period,proto3" json:"period"` + PeriodSpendLimit github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=period_spend_limit,json=periodSpendLimit,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"period_spend_limit" yaml:"period_spend_limit"` + PeriodCanSpend github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,4,rep,name=period_can_spend,json=periodCanSpend,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"period_can_spend" yaml:"period_can_spend"` + PeriodReset ExpiresAt `protobuf:"bytes,5,opt,name=period_reset,json=periodReset,proto3" json:"period_reset" yaml:"period_reset"` +} + +func (m *PeriodicFeeAllowance) Reset() { *m = PeriodicFeeAllowance{} } +func (m *PeriodicFeeAllowance) String() string { return proto.CompactTextString(m) } +func (*PeriodicFeeAllowance) ProtoMessage() {} +func (*PeriodicFeeAllowance) Descriptor() ([]byte, []int) { + return fileDescriptor_86c534389d2c5768, []int{4} +} +func (m *PeriodicFeeAllowance) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PeriodicFeeAllowance) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PeriodicFeeAllowance.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 *PeriodicFeeAllowance) XXX_Merge(src proto.Message) { + xxx_messageInfo_PeriodicFeeAllowance.Merge(m, src) +} +func (m *PeriodicFeeAllowance) XXX_Size() int { + return m.Size() +} +func (m *PeriodicFeeAllowance) XXX_DiscardUnknown() { + xxx_messageInfo_PeriodicFeeAllowance.DiscardUnknown(m) +} + +var xxx_messageInfo_PeriodicFeeAllowance proto.InternalMessageInfo + +// Duration is a repeating unit of either clock time or number of blocks. +// This is designed to be added to an ExpiresAt struct. +type Duration struct { + Clock time.Duration `protobuf:"bytes,1,opt,name=clock,proto3,stdduration" json:"clock"` + Block int64 `protobuf:"varint,2,opt,name=block,proto3" json:"block,omitempty"` +} + +func (m *Duration) Reset() { *m = Duration{} } +func (m *Duration) String() string { return proto.CompactTextString(m) } +func (*Duration) ProtoMessage() {} +func (*Duration) Descriptor() ([]byte, []int) { + return fileDescriptor_86c534389d2c5768, []int{5} +} +func (m *Duration) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Duration) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Duration.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 *Duration) XXX_Merge(src proto.Message) { + xxx_messageInfo_Duration.Merge(m, src) +} +func (m *Duration) XXX_Size() int { + return m.Size() +} +func (m *Duration) XXX_DiscardUnknown() { + xxx_messageInfo_Duration.DiscardUnknown(m) +} + +var xxx_messageInfo_Duration proto.InternalMessageInfo + +func (m *Duration) GetClock() time.Duration { + if m != nil { + return m.Clock + } + return 0 +} + +func (m *Duration) GetBlock() int64 { + if m != nil { + return m.Block + } + return 0 +} + +// ExpiresAt is a point in time where something expires. +// It may be *either* block time or block height +type ExpiresAt struct { + Time time.Time `protobuf:"bytes,1,opt,name=time,proto3,stdtime" json:"time"` + Height int64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` +} + +func (m *ExpiresAt) Reset() { *m = ExpiresAt{} } +func (m *ExpiresAt) String() string { return proto.CompactTextString(m) } +func (*ExpiresAt) ProtoMessage() {} +func (*ExpiresAt) Descriptor() ([]byte, []int) { + return fileDescriptor_86c534389d2c5768, []int{6} +} +func (m *ExpiresAt) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ExpiresAt) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ExpiresAt.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 *ExpiresAt) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExpiresAt.Merge(m, src) +} +func (m *ExpiresAt) XXX_Size() int { + return m.Size() +} +func (m *ExpiresAt) XXX_DiscardUnknown() { + xxx_messageInfo_ExpiresAt.DiscardUnknown(m) +} + +var xxx_messageInfo_ExpiresAt proto.InternalMessageInfo + +func (m *ExpiresAt) GetTime() time.Time { + if m != nil { + return m.Time + } + return time.Time{} +} + +func (m *ExpiresAt) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +// FeeAllowanceGrant is stored in the KVStore to record a grant with full context +type FeeAllowanceGrant struct { + Granter github_com_cosmos_cosmos_sdk_types.AccAddress `protobuf:"bytes,1,opt,name=granter,proto3,casttype=github.com/cosmos/cosmos-sdk/types.AccAddress" json:"granter,omitempty"` + Grantee github_com_cosmos_cosmos_sdk_types.AccAddress `protobuf:"bytes,2,opt,name=grantee,proto3,casttype=github.com/cosmos/cosmos-sdk/types.AccAddress" json:"grantee,omitempty"` + Allowance *FeeAllowance `protobuf:"bytes,3,opt,name=allowance,proto3" json:"allowance,omitempty"` +} + +func (m *FeeAllowanceGrant) Reset() { *m = FeeAllowanceGrant{} } +func (m *FeeAllowanceGrant) String() string { return proto.CompactTextString(m) } +func (*FeeAllowanceGrant) ProtoMessage() {} +func (*FeeAllowanceGrant) Descriptor() ([]byte, []int) { + return fileDescriptor_86c534389d2c5768, []int{7} +} +func (m *FeeAllowanceGrant) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FeeAllowanceGrant) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FeeAllowanceGrant.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 *FeeAllowanceGrant) XXX_Merge(src proto.Message) { + xxx_messageInfo_FeeAllowanceGrant.Merge(m, src) +} +func (m *FeeAllowanceGrant) XXX_Size() int { + return m.Size() +} +func (m *FeeAllowanceGrant) XXX_DiscardUnknown() { + xxx_messageInfo_FeeAllowanceGrant.DiscardUnknown(m) +} + +var xxx_messageInfo_FeeAllowanceGrant proto.InternalMessageInfo + +func init() { + proto.RegisterType((*FeeAllowance)(nil), "cosmos_sdk.x.feegrant.v1.FeeAllowance") + proto.RegisterType((*MsgGrantFeeAllowance)(nil), "cosmos_sdk.x.feegrant.v1.MsgGrantFeeAllowance") + proto.RegisterType((*MsgRevokeFeeAllowance)(nil), "cosmos_sdk.x.feegrant.v1.MsgRevokeFeeAllowance") + proto.RegisterType((*BasicFeeAllowance)(nil), "cosmos_sdk.x.feegrant.v1.BasicFeeAllowance") + proto.RegisterType((*PeriodicFeeAllowance)(nil), "cosmos_sdk.x.feegrant.v1.PeriodicFeeAllowance") + proto.RegisterType((*Duration)(nil), "cosmos_sdk.x.feegrant.v1.Duration") + proto.RegisterType((*ExpiresAt)(nil), "cosmos_sdk.x.feegrant.v1.ExpiresAt") + proto.RegisterType((*FeeAllowanceGrant)(nil), "cosmos_sdk.x.feegrant.v1.FeeAllowanceGrant") +} + +func init() { proto.RegisterFile("x/feegrant/types/types.proto", fileDescriptor_86c534389d2c5768) } + +var fileDescriptor_86c534389d2c5768 = []byte{ + // 735 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x56, 0xcd, 0x4f, 0x13, 0x4d, + 0x18, 0xef, 0xbc, 0x6d, 0x79, 0x61, 0x4a, 0xc8, 0xdb, 0xa1, 0x2f, 0xd6, 0x6a, 0xba, 0x64, 0x4d, + 0x08, 0x91, 0xb0, 0x0d, 0x78, 0x31, 0x3d, 0xd9, 0x05, 0x41, 0x82, 0x24, 0x66, 0xf5, 0x64, 0x62, + 0x36, 0xfb, 0x31, 0xdd, 0x6e, 0xda, 0xdd, 0xd9, 0xec, 0x6c, 0x11, 0x12, 0xff, 0x00, 0xe3, 0x89, + 0xa3, 0x47, 0xce, 0x9e, 0x34, 0xf1, 0x8f, 0x20, 0x9e, 0x38, 0x7a, 0x02, 0x03, 0x89, 0xf1, 0x6c, + 0xbc, 0x68, 0x3c, 0x98, 0x9d, 0x99, 0xa5, 0x0b, 0x65, 0x0d, 0xe8, 0xc9, 0x78, 0x69, 0x76, 0x9e, + 0x3c, 0xbf, 0x8f, 0xe7, 0x63, 0x77, 0x0a, 0xaf, 0x6f, 0x35, 0xda, 0x18, 0x3b, 0xa1, 0xe1, 0x47, + 0x8d, 0x68, 0x3b, 0xc0, 0x94, 0xff, 0x2a, 0x41, 0x48, 0x22, 0x82, 0xaa, 0x16, 0xa1, 0x1e, 0xa1, + 0x3a, 0xb5, 0xbb, 0xca, 0x96, 0x92, 0x24, 0x2a, 0x9b, 0x0b, 0xb5, 0xb9, 0xa8, 0xe3, 0x86, 0xb6, + 0x1e, 0x18, 0x61, 0xb4, 0xdd, 0x60, 0xc9, 0x0d, 0x9e, 0x3b, 0x9f, 0x3e, 0x70, 0x9a, 0xda, 0xcc, + 0x70, 0xb2, 0x43, 0x1c, 0x32, 0x78, 0x12, 0x79, 0xe5, 0x21, 0x07, 0x35, 0xc9, 0x21, 0xc4, 0xe9, + 0x61, 0x8e, 0x32, 0xfb, 0xed, 0x46, 0xe4, 0x7a, 0x98, 0x46, 0x86, 0x17, 0xf0, 0x04, 0xf9, 0x0b, + 0x80, 0xe3, 0x2b, 0x18, 0xb7, 0x7a, 0x3d, 0xf2, 0xd4, 0xf0, 0x2d, 0x8c, 0x9e, 0xc0, 0x49, 0xd3, + 0xa0, 0xae, 0xa5, 0xb7, 0x31, 0xd6, 0x8d, 0x24, 0x5c, 0x05, 0xd3, 0x60, 0xb6, 0xb4, 0x38, 0xa7, + 0x64, 0x55, 0xa4, 0xa8, 0x31, 0x28, 0xcd, 0x74, 0x2f, 0xa7, 0x95, 0xcd, 0xb3, 0x41, 0xd4, 0x86, + 0x53, 0x01, 0x0e, 0x5d, 0x62, 0x0f, 0x29, 0xfc, 0xc3, 0x14, 0x94, 0x6c, 0x85, 0x07, 0x02, 0x77, + 0x46, 0xa4, 0x12, 0x9c, 0x13, 0x6f, 0x4e, 0x7d, 0xda, 0x95, 0xc0, 0xbb, 0xb7, 0xf3, 0x13, 0x37, + 0xd3, 0xe1, 0x35, 0xb5, 0x08, 0xf3, 0xb4, 0xef, 0xc9, 0xdf, 0x01, 0xac, 0x6c, 0x50, 0x67, 0x35, + 0xe6, 0x3e, 0xe5, 0x6f, 0x1d, 0xfe, 0xcb, 0x04, 0x71, 0xc8, 0x4a, 0x1e, 0x57, 0x17, 0xbe, 0x1d, + 0x48, 0xf3, 0x8e, 0x1b, 0x75, 0xfa, 0xa6, 0x62, 0x11, 0x4f, 0x4c, 0x26, 0x99, 0x16, 0xb5, 0xbb, + 0xa2, 0xdf, 0x2d, 0xcb, 0x6a, 0xd9, 0x76, 0x88, 0x29, 0xd5, 0x12, 0x86, 0x01, 0x19, 0xaf, 0xee, + 0x77, 0xc8, 0x30, 0x5a, 0x86, 0x63, 0x83, 0x66, 0xe5, 0x59, 0xb3, 0x66, 0xb2, 0x9b, 0x95, 0x2e, + 0x4a, 0x1b, 0x00, 0x9b, 0x85, 0xe7, 0xbb, 0x52, 0x4e, 0x7e, 0x03, 0xe0, 0xff, 0x1b, 0xd4, 0xd1, + 0xf0, 0x26, 0xe9, 0xe2, 0x3f, 0xa3, 0x7e, 0xf9, 0x23, 0x80, 0xe5, 0xa1, 0x25, 0x43, 0xcf, 0x60, + 0x89, 0x06, 0xd8, 0xb7, 0xf5, 0x9e, 0xeb, 0xb9, 0x51, 0x15, 0x4c, 0xe7, 0x67, 0x4b, 0x8b, 0x93, + 0xe9, 0xbe, 0x6c, 0x2e, 0x28, 0x4b, 0xc4, 0xf5, 0xd5, 0x95, 0xbd, 0x03, 0x29, 0xf7, 0xf9, 0x40, + 0x42, 0xdb, 0x86, 0xd7, 0x6b, 0xca, 0x29, 0x94, 0xfc, 0xea, 0x50, 0x9a, 0xbd, 0x80, 0xab, 0x98, + 0x86, 0x6a, 0x90, 0x21, 0xef, 0xc7, 0x40, 0xb4, 0x06, 0x21, 0xde, 0x0a, 0xdc, 0xd0, 0x88, 0x5c, + 0xe2, 0x8b, 0x0d, 0xbe, 0x91, 0x3d, 0x94, 0xbb, 0x71, 0x2e, 0xa6, 0xad, 0x48, 0x2d, 0xc4, 0x66, + 0xb4, 0x14, 0xb8, 0x39, 0x1a, 0x0f, 0x26, 0x5e, 0x5a, 0xf9, 0x75, 0x01, 0x56, 0xce, 0xdb, 0x75, + 0xb4, 0x0a, 0x8b, 0xec, 0x85, 0xfa, 0x85, 0x97, 0x51, 0x08, 0x72, 0x3c, 0xba, 0x03, 0x47, 0xf8, + 0x4b, 0x23, 0x2c, 0xcb, 0xd9, 0x4c, 0xcb, 0x7d, 0xee, 0x4f, 0x10, 0x08, 0x1c, 0xda, 0x01, 0x10, + 0xf1, 0x47, 0x3d, 0xdd, 0xfe, 0x7c, 0x76, 0xfb, 0x37, 0x44, 0xfb, 0xaf, 0xf2, 0xf6, 0x0f, 0x83, + 0x2f, 0x37, 0x85, 0xff, 0x38, 0xc1, 0xc3, 0xc1, 0x2c, 0x5e, 0x00, 0x28, 0x82, 0xba, 0x65, 0xf8, + 0x9c, 0xb9, 0x5a, 0xc8, 0x36, 0xb4, 0x2e, 0x0c, 0x5d, 0x39, 0x65, 0xe8, 0x04, 0x7a, 0x39, 0x3b, + 0x13, 0x1c, 0xbe, 0x64, 0xf8, 0xcc, 0x11, 0xb2, 0xe0, 0xb8, 0x20, 0x0c, 0x31, 0xc5, 0x51, 0xb5, + 0x78, 0xf1, 0xd5, 0xb8, 0x26, 0x7c, 0x4d, 0x9e, 0xf2, 0xc5, 0x68, 0x64, 0xad, 0xc4, 0x8f, 0x5a, + 0x7c, 0x4a, 0xad, 0x8c, 0x09, 0x47, 0x93, 0x41, 0xa1, 0x26, 0x2c, 0x5a, 0x3d, 0x62, 0x75, 0xc5, + 0x96, 0xd4, 0x14, 0x7e, 0x05, 0x28, 0xc9, 0x15, 0xa0, 0x3c, 0x4a, 0xae, 0x00, 0x75, 0x34, 0x96, + 0x7a, 0x79, 0x28, 0x01, 0x8d, 0x43, 0x50, 0x05, 0x16, 0x4d, 0x86, 0x8d, 0xf7, 0x22, 0xaf, 0xf1, + 0x43, 0xb3, 0xc0, 0x34, 0x2c, 0x38, 0x76, 0x62, 0x12, 0xdd, 0x86, 0x85, 0xf8, 0x26, 0xb9, 0xa8, + 0xc6, 0x4e, 0xac, 0xc1, 0x10, 0x68, 0x0a, 0x8e, 0x74, 0xb0, 0xeb, 0x74, 0x22, 0xa1, 0x21, 0x4e, + 0x42, 0xe4, 0x2b, 0x80, 0xe5, 0xf4, 0xde, 0xb2, 0x0f, 0xf4, 0xdf, 0xf1, 0x51, 0x56, 0x57, 0xf7, + 0x8e, 0xea, 0x60, 0xff, 0xa8, 0x0e, 0x3e, 0x1c, 0xd5, 0xc1, 0xce, 0x71, 0x3d, 0xb7, 0x7f, 0x5c, + 0xcf, 0xbd, 0x3f, 0xae, 0xe7, 0x1e, 0xff, 0xdc, 0xdd, 0xd9, 0xbf, 0x20, 0xe6, 0x08, 0x1b, 0xc3, + 0xad, 0x1f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x60, 0xf1, 0xd1, 0x21, 0x9d, 0x08, 0x00, 0x00, +} + +func (this *FeeAllowance) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*FeeAllowance) + if !ok { + that2, ok := that.(FeeAllowance) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if that1.Sum == nil { + if this.Sum != nil { + return false + } + } else if this.Sum == nil { + return false + } else if !this.Sum.Equal(that1.Sum) { + return false + } + return true +} +func (this *FeeAllowance_BasicFeeAllowance) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*FeeAllowance_BasicFeeAllowance) + if !ok { + that2, ok := that.(FeeAllowance_BasicFeeAllowance) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.BasicFeeAllowance.Equal(that1.BasicFeeAllowance) { + return false + } + return true +} +func (this *FeeAllowance_PeriodicFeeAllowance) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*FeeAllowance_PeriodicFeeAllowance) + if !ok { + that2, ok := that.(FeeAllowance_PeriodicFeeAllowance) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.PeriodicFeeAllowance.Equal(that1.PeriodicFeeAllowance) { + return false + } + return true +} +func (this *BasicFeeAllowance) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*BasicFeeAllowance) + if !ok { + that2, ok := that.(BasicFeeAllowance) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if len(this.SpendLimit) != len(that1.SpendLimit) { + return false + } + for i := range this.SpendLimit { + if !this.SpendLimit[i].Equal(&that1.SpendLimit[i]) { + return false + } + } + if !this.Expiration.Equal(&that1.Expiration) { + return false + } + return true +} +func (this *PeriodicFeeAllowance) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*PeriodicFeeAllowance) + if !ok { + that2, ok := that.(PeriodicFeeAllowance) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.Basic.Equal(&that1.Basic) { + return false + } + if !this.Period.Equal(&that1.Period) { + return false + } + if len(this.PeriodSpendLimit) != len(that1.PeriodSpendLimit) { + return false + } + for i := range this.PeriodSpendLimit { + if !this.PeriodSpendLimit[i].Equal(&that1.PeriodSpendLimit[i]) { + return false + } + } + if len(this.PeriodCanSpend) != len(that1.PeriodCanSpend) { + return false + } + for i := range this.PeriodCanSpend { + if !this.PeriodCanSpend[i].Equal(&that1.PeriodCanSpend[i]) { + return false + } + } + if !this.PeriodReset.Equal(&that1.PeriodReset) { + return false + } + return true +} +func (this *Duration) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Duration) + if !ok { + that2, ok := that.(Duration) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Clock != that1.Clock { + return false + } + if this.Block != that1.Block { + return false + } + return true +} +func (this *ExpiresAt) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*ExpiresAt) + if !ok { + that2, ok := that.(ExpiresAt) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.Time.Equal(that1.Time) { + return false + } + if this.Height != that1.Height { + return false + } + return true +} +func (this *FeeAllowance) GetFeeAllowanceI() FeeAllowanceI { + if x := this.GetBasicFeeAllowance(); x != nil { + return x + } + if x := this.GetPeriodicFeeAllowance(); x != nil { + return x + } + return nil +} + +func (this *FeeAllowance) SetFeeAllowanceI(value FeeAllowanceI) error { + if value == nil { + this.Sum = nil + return nil + } + switch vt := value.(type) { + case *BasicFeeAllowance: + this.Sum = &FeeAllowance_BasicFeeAllowance{vt} + return nil + case *PeriodicFeeAllowance: + this.Sum = &FeeAllowance_PeriodicFeeAllowance{vt} + return nil + } + return fmt.Errorf("can't encode value of type %T as message FeeAllowance", value) +} + +func (m *FeeAllowance) 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 *FeeAllowance) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FeeAllowance) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Sum != nil { + { + size := m.Sum.Size() + i -= size + if _, err := m.Sum.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + return len(dAtA) - i, nil +} + +func (m *FeeAllowance_BasicFeeAllowance) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FeeAllowance_BasicFeeAllowance) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.BasicFeeAllowance != nil { + { + size, err := m.BasicFeeAllowance.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} +func (m *FeeAllowance_PeriodicFeeAllowance) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FeeAllowance_PeriodicFeeAllowance) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.PeriodicFeeAllowance != nil { + { + size, err := m.PeriodicFeeAllowance.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + return len(dAtA) - i, nil +} +func (m *MsgGrantFeeAllowance) 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 *MsgGrantFeeAllowance) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgGrantFeeAllowance) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Allowance != nil { + { + size, err := m.Allowance.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Grantee) > 0 { + i -= len(m.Grantee) + copy(dAtA[i:], m.Grantee) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Grantee))) + i-- + dAtA[i] = 0x12 + } + if len(m.Granter) > 0 { + i -= len(m.Granter) + copy(dAtA[i:], m.Granter) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Granter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgRevokeFeeAllowance) 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 *MsgRevokeFeeAllowance) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRevokeFeeAllowance) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Grantee) > 0 { + i -= len(m.Grantee) + copy(dAtA[i:], m.Grantee) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Grantee))) + i-- + dAtA[i] = 0x12 + } + if len(m.Granter) > 0 { + i -= len(m.Granter) + copy(dAtA[i:], m.Granter) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Granter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *BasicFeeAllowance) 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 *BasicFeeAllowance) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BasicFeeAllowance) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Expiration.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.SpendLimit) > 0 { + for iNdEx := len(m.SpendLimit) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.SpendLimit[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *PeriodicFeeAllowance) 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 *PeriodicFeeAllowance) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PeriodicFeeAllowance) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.PeriodReset.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if len(m.PeriodCanSpend) > 0 { + for iNdEx := len(m.PeriodCanSpend) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PeriodCanSpend[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.PeriodSpendLimit) > 0 { + for iNdEx := len(m.PeriodSpendLimit) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PeriodSpendLimit[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + { + size, err := m.Period.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size, err := m.Basic.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *Duration) 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 *Duration) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Duration) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Block != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Block)) + i-- + dAtA[i] = 0x10 + } + n8, err8 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.Clock, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.Clock):]) + if err8 != nil { + return 0, err8 + } + i -= n8 + i = encodeVarintTypes(dAtA, i, uint64(n8)) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *ExpiresAt) 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 *ExpiresAt) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExpiresAt) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Height != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x10 + } + n9, err9 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Time):]) + if err9 != nil { + return 0, err9 + } + i -= n9 + i = encodeVarintTypes(dAtA, i, uint64(n9)) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *FeeAllowanceGrant) 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 *FeeAllowanceGrant) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FeeAllowanceGrant) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Allowance != nil { + { + size, err := m.Allowance.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Grantee) > 0 { + i -= len(m.Grantee) + copy(dAtA[i:], m.Grantee) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Grantee))) + i-- + dAtA[i] = 0x12 + } + if len(m.Granter) > 0 { + i -= len(m.Granter) + copy(dAtA[i:], m.Granter) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Granter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintTypes(dAtA []byte, offset int, v uint64) int { + offset -= sovTypes(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *FeeAllowance) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Sum != nil { + n += m.Sum.Size() + } + return n +} + +func (m *FeeAllowance_BasicFeeAllowance) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BasicFeeAllowance != nil { + l = m.BasicFeeAllowance.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} +func (m *FeeAllowance_PeriodicFeeAllowance) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.PeriodicFeeAllowance != nil { + l = m.PeriodicFeeAllowance.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} +func (m *MsgGrantFeeAllowance) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Granter) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.Grantee) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + if m.Allowance != nil { + l = m.Allowance.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + +func (m *MsgRevokeFeeAllowance) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Granter) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.Grantee) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + +func (m *BasicFeeAllowance) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.SpendLimit) > 0 { + for _, e := range m.SpendLimit { + l = e.Size() + n += 1 + l + sovTypes(uint64(l)) + } + } + l = m.Expiration.Size() + n += 1 + l + sovTypes(uint64(l)) + return n +} + +func (m *PeriodicFeeAllowance) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Basic.Size() + n += 1 + l + sovTypes(uint64(l)) + l = m.Period.Size() + n += 1 + l + sovTypes(uint64(l)) + if len(m.PeriodSpendLimit) > 0 { + for _, e := range m.PeriodSpendLimit { + l = e.Size() + n += 1 + l + sovTypes(uint64(l)) + } + } + if len(m.PeriodCanSpend) > 0 { + for _, e := range m.PeriodCanSpend { + l = e.Size() + n += 1 + l + sovTypes(uint64(l)) + } + } + l = m.PeriodReset.Size() + n += 1 + l + sovTypes(uint64(l)) + return n +} + +func (m *Duration) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.Clock) + n += 1 + l + sovTypes(uint64(l)) + if m.Block != 0 { + n += 1 + sovTypes(uint64(m.Block)) + } + return n +} + +func (m *ExpiresAt) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Time) + n += 1 + l + sovTypes(uint64(l)) + if m.Height != 0 { + n += 1 + sovTypes(uint64(m.Height)) + } + return n +} + +func (m *FeeAllowanceGrant) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Granter) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.Grantee) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + if m.Allowance != nil { + l = m.Allowance.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + +func sovTypes(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTypes(x uint64) (n int) { + return sovTypes(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *FeeAllowance) 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 ErrIntOverflowTypes + } + 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: FeeAllowance: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FeeAllowance: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BasicFeeAllowance", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &BasicFeeAllowance{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &FeeAllowance_BasicFeeAllowance{v} + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PeriodicFeeAllowance", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &PeriodicFeeAllowance{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &FeeAllowance_PeriodicFeeAllowance{v} + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgGrantFeeAllowance) 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 ErrIntOverflowTypes + } + 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: MsgGrantFeeAllowance: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgGrantFeeAllowance: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Granter", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Granter = append(m.Granter[:0], dAtA[iNdEx:postIndex]...) + if m.Granter == nil { + m.Granter = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Grantee", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Grantee = append(m.Grantee[:0], dAtA[iNdEx:postIndex]...) + if m.Grantee == nil { + m.Grantee = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Allowance", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Allowance == nil { + m.Allowance = &FeeAllowance{} + } + if err := m.Allowance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgRevokeFeeAllowance) 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 ErrIntOverflowTypes + } + 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: MsgRevokeFeeAllowance: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRevokeFeeAllowance: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Granter", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Granter = append(m.Granter[:0], dAtA[iNdEx:postIndex]...) + if m.Granter == nil { + m.Granter = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Grantee", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Grantee = append(m.Grantee[:0], dAtA[iNdEx:postIndex]...) + if m.Grantee == nil { + m.Grantee = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BasicFeeAllowance) 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 ErrIntOverflowTypes + } + 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: BasicFeeAllowance: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BasicFeeAllowance: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SpendLimit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SpendLimit = append(m.SpendLimit, types.Coin{}) + if err := m.SpendLimit[len(m.SpendLimit)-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 Expiration", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Expiration.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PeriodicFeeAllowance) 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 ErrIntOverflowTypes + } + 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: PeriodicFeeAllowance: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PeriodicFeeAllowance: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Basic", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Basic.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Period", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Period.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PeriodSpendLimit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PeriodSpendLimit = append(m.PeriodSpendLimit, types.Coin{}) + if err := m.PeriodSpendLimit[len(m.PeriodSpendLimit)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PeriodCanSpend", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PeriodCanSpend = append(m.PeriodCanSpend, types.Coin{}) + if err := m.PeriodCanSpend[len(m.PeriodCanSpend)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PeriodReset", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.PeriodReset.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Duration) 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 ErrIntOverflowTypes + } + 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: Duration: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Duration: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Clock", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&m.Clock, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Block", wireType) + } + m.Block = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Block |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExpiresAt) 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 ErrIntOverflowTypes + } + 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: ExpiresAt: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExpiresAt: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Time, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FeeAllowanceGrant) 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 ErrIntOverflowTypes + } + 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: FeeAllowanceGrant: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FeeAllowanceGrant: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Granter", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Granter = append(m.Granter[:0], dAtA[iNdEx:postIndex]...) + if m.Granter == nil { + m.Granter = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Grantee", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Grantee = append(m.Grantee[:0], dAtA[iNdEx:postIndex]...) + if m.Grantee == nil { + m.Grantee = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Allowance", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Allowance == nil { + m.Allowance = &FeeAllowance{} + } + if err := m.Allowance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTypes(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, ErrIntOverflowTypes + } + 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, ErrIntOverflowTypes + } + 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, ErrIntOverflowTypes + } + 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, ErrInvalidLengthTypes + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTypes + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTypes + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTypes = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTypes = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTypes = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/feegrant/types/types.proto b/x/feegrant/types/types.proto new file mode 100644 index 000000000000..69eab22c1e70 --- /dev/null +++ b/x/feegrant/types/types.proto @@ -0,0 +1,105 @@ +syntax = "proto3"; +package cosmos_sdk.x.feegrant.v1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/feegrant/types"; + +import "third_party/proto/cosmos-proto/cosmos.proto"; +import "third_party/proto/gogoproto/gogo.proto"; +import "types/types.proto"; +import "google/protobuf/timestamp.proto"; + +// FeeAllowance defines the application-level fee allowance to be used in +// feegrant module +message FeeAllowance { + option (gogoproto.equal) = true; + option (cosmos_proto.interface_type) = "*FeeAllowanceI"; + + // sum defines a set of all acceptable concrete feeallowance implementations. + oneof sum{ + BasicFeeAllowance basic_fee_allowance= 1; + PeriodicFeeAllowance periodic_fee_allowance= 2; + } +} +// MsgGrantFeeAllowance adds permission for Grantee to spend up to Allowance +// of fees from the account of Granter. +message MsgGrantFeeAllowance{ + option (gogoproto.goproto_getters) = false; + + bytes granter = 1 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; + bytes grantee = 2 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; + FeeAllowance allowance = 3; +} + +// MsgRevokeFeeAllowance removes any existing FeeAllowance from Granter to Grantee. +message MsgRevokeFeeAllowance{ + bytes granter = 1 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; + bytes grantee = 2 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; +} + +// BasicFeeAllowance implements FeeAllowance with a one-time grant of tokens +// that optionally expires. The delegatee can use up to SpendLimit to cover fees. +message BasicFeeAllowance{ + option (gogoproto.equal) = true; + option (gogoproto.goproto_getters) = false; + + repeated cosmos_sdk.v1.Coin spend_limit = 1 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.moretags) = "yaml:\"spend_limit\"" + ]; + ExpiresAt expiration = 2 [(gogoproto.nullable) = false]; +} + +// PeriodicFeeAllowance extends FeeAllowance to allow for both a maximum cap, +// as well as a limit per time period. +message PeriodicFeeAllowance{ + option (gogoproto.equal) = true; + option (gogoproto.goproto_getters) = false; + + BasicFeeAllowance basic = 1[(gogoproto.nullable) = false]; + Duration period = 2[(gogoproto.nullable) = false]; + repeated cosmos_sdk.v1.Coin period_spend_limit = 3 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.moretags) = "yaml:\"period_spend_limit\"" + ]; + repeated cosmos_sdk.v1.Coin period_can_spend = 4 [ + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", + (gogoproto.moretags) = "yaml:\"period_can_spend\"" + ]; + + ExpiresAt period_reset = 5 [(gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"period_reset\""]; +} + +// Duration is a repeating unit of either clock time or number of blocks. +// This is designed to be added to an ExpiresAt struct. +message Duration{ + option (gogoproto.equal) = true; + google.protobuf.Timestamp clock = 1[(gogoproto.stdduration) = true, + (gogoproto.nullable) = false + ]; + int64 block = 2; +} + +// ExpiresAt is a point in time where something expires. +// It may be *either* block time or block height +message ExpiresAt{ + option (gogoproto.equal) = true; + google.protobuf.Timestamp time = 1 [(gogoproto.stdtime) = true, + (gogoproto.nullable) = false + ]; + int64 height = 2; +} + + + +// FeeAllowanceGrant is stored in the KVStore to record a grant with full context +message FeeAllowanceGrant{ + option (gogoproto.goproto_getters) = false; + + bytes granter = 1 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; + bytes grantee = 2 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"]; + FeeAllowance allowance = 3; +} \ No newline at end of file