x/feegrant remove height base expiration (#9206)

* remove height from proto files

* remove PrepareForExport

* fix basic fee

* fix periodic fee

* fix errors

* fix error

* fix errors

* add tests

* review changes

* fix errors

* fix tests

* fix lint error

* Update x/feegrant/types/basic_fee.go

Co-authored-by: technicallyty <48813565+technicallyty@users.noreply.github.com>

* fix errors

* fix keeper tests

* Update x/feegrant/keeper/keeper_test.go

Co-authored-by: Marie Gauthier <marie.gauthier63@gmail.com>

* review changes

* review changes

* fix tests

* run make proto-gen

* fix errors

* Update x/feegrant/keeper/keeper_test.go

Co-authored-by: atheeshp <59333759+atheeshp@users.noreply.github.com>

* Update x/feegrant/keeper/keeper_test.go

* update ADR

* add test

* review changes

* review changes

Co-authored-by: technicallyty <48813565+technicallyty@users.noreply.github.com>
Co-authored-by: Marie Gauthier <marie.gauthier63@gmail.com>
Co-authored-by: atheeshp <59333759+atheeshp@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
MD Aleem 2021-05-05 21:59:22 +05:30 committed by GitHub
parent 68d461052b
commit 1e1c812de2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 358 additions and 1676 deletions

View File

@ -3,6 +3,7 @@
## Changelog
- 2020/08/18: Initial Draft
- 2021/05/05: Removed height based expiration support and simplified naming.
## Status
@ -38,87 +39,76 @@ Fee allowances are defined by the extensible `FeeAllowanceI` interface:
```go
type FeeAllowanceI {
// 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)
Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (remove bool, err error)
// Accept can use fee payment requested as well as timestamp 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)
Accept(ctx sdk.Context, fee sdk.Coins, msgs []sdk.Msg) (remove bool, err error)
// ValidateBasic should evaluate this FeeAllowance for internal consistency.
// Don't allow negative amounts, or negative periods for example.
ValidateBasic() error
}
```
Two basic fee allowance types, `BasicFeeAllowance` and `PeriodicFeeAllowance` are defined to support known use cases:
Two basic fee allowance types, `BasicAllowance` and `PeriodicAllowance` are defined to support known use cases:
```proto
// BasicFeeAllowance implements FeeAllowance with a one-time grant of tokens
// BasicAllowance implements FeeAllowanceI with a one-time grant of tokens
// that optionally expires. The delegatee can use up to SpendLimit to cover fees.
message BasicFeeAllowance {
// spend_limit specifies the maximum amount of tokens that can be spent
// by this allowance 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.
repeated cosmos_sdk.v1.Coin spend_limit = 1;
message BasicAllowance {
// spend_limit specifies the maximum amount of tokens that can be spent
// by this allowance 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.
repeated cosmos_sdk.v1.Coin spend_limit = 1;
// expires_at specifies an optional time when this allowance expires
ExpiresAt expiration = 2;
// expiration specifies an optional time when this allowance expires
google.protobuf.Timestamp expiration = 2;
}
// PeriodicFeeAllowance extends FeeAllowance to allow for both a maximum cap,
// PeriodicAllowance extends FeeAllowanceI to allow for both a maximum cap,
// as well as a limit per time period.
message PeriodicFeeAllowance {
BasicFeeAllowance basic = 1;
message PeriodicAllowance {
BasicAllowance basic = 1;
// period specifies the time duration in which period_spend_limit coins can
// be spent before that allowance is reset
Duration period = 2;
// period_spend_limit specifies the maximum number of coins that can be spent
// in the period
repeated cosmos_sdk.v1.Coin period_spend_limit = 3;
// period specifies the time duration in which period_spend_limit coins can
// be spent before that allowance is reset
google.protobuf.Duration period = 2;
// period_can_spend is the number of coins left to be spent before the period_reset time
repeated cosmos_sdk.v1.Coin period_can_spend = 4;
// period_reset 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
ExpiresAt period_reset = 5;
}
// period_spend_limit specifies the maximum number of coins that can be spent
// in the period
repeated cosmos_sdk.v1.Coin period_spend_limit = 3;
// ExpiresAt is a point in time where something expires.
// It may be *either* block time or block height
message ExpiresAt {
oneof sum {
google.protobuf.Timestamp time = 1;
uint64 height = 2;
}
}
// period_can_spend is the number of coins left to be spent before the period_reset time
repeated cosmos_sdk.v1.Coin period_can_spend = 4;
// Duration is a repeating unit of either clock time or number of blocks.
message Duration {
oneof sum {
google.protobuf.Duration duration = 1;
uint64 blocks = 2;
}
// period_reset 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
google.protobuf.Timestamp period_reset = 5;
}
```
Allowances can be granted and revoked using `MsgGrantFeeAllowance` and `MsgRevokeFeeAllowance`:
Allowances can be granted and revoked using `MsgGrantAllowance` and `MsgRevokeAllowance`:
```proto
message MsgGrantFeeAllowance {
// MsgGrantAllowance adds permission for Grantee to spend up to Allowance
// of fees from the account of Granter.
message MsgGrantAllowance {
string granter = 1;
string grantee = 2;
google.protobuf.Any allowance = 3;
}
// MsgRevokeFeeAllowance removes any existing FeeAllowance from Granter to Grantee.
message MsgRevokeFeeAllowance {
// MsgRevokeAllowance removes any existing FeeAllowance from Granter to Grantee.
message MsgRevokeAllowance {
string granter = 1;
string grantee = 2;
}

View File

@ -309,8 +309,6 @@
- [cosmos/feegrant/v1beta1/feegrant.proto](#cosmos/feegrant/v1beta1/feegrant.proto)
- [AllowedMsgAllowance](#cosmos.feegrant.v1beta1.AllowedMsgAllowance)
- [BasicAllowance](#cosmos.feegrant.v1beta1.BasicAllowance)
- [Duration](#cosmos.feegrant.v1beta1.Duration)
- [ExpiresAt](#cosmos.feegrant.v1beta1.ExpiresAt)
- [Grant](#cosmos.feegrant.v1beta1.Grant)
- [PeriodicAllowance](#cosmos.feegrant.v1beta1.PeriodicAllowance)
@ -4571,41 +4569,7 @@ that optionally expires. The grantee can use up to SpendLimit to cover fees.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `spend_limit` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | spend_limit specifies the maximum amount of tokens that can be spent by this allowance 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. |
| `expiration` | [ExpiresAt](#cosmos.feegrant.v1beta1.ExpiresAt) | | expiration specifies an optional time when this allowance expires |
<a name="cosmos.feegrant.v1beta1.Duration"></a>
### Duration
Duration is a span of a clock time or number of blocks.
This is designed to be added to an ExpiresAt struct.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `duration` | [google.protobuf.Duration](#google.protobuf.Duration) | | |
| `blocks` | [uint64](#uint64) | | |
<a name="cosmos.feegrant.v1beta1.ExpiresAt"></a>
### ExpiresAt
ExpiresAt is a point in time where something expires.
It may be *either* block time or block height
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | |
| `height` | [int64](#int64) | | |
| `expiration` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | expiration specifies an optional time when this allowance expires |
@ -4639,10 +4603,10 @@ as well as a limit per time period.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `basic` | [BasicAllowance](#cosmos.feegrant.v1beta1.BasicAllowance) | | basic specifies a struct of `BasicAllowance` |
| `period` | [Duration](#cosmos.feegrant.v1beta1.Duration) | | period specifies the time duration in which period_spend_limit coins can be spent before that allowance is reset |
| `period` | [google.protobuf.Duration](#google.protobuf.Duration) | | period specifies the time duration in which period_spend_limit coins can be spent before that allowance is reset |
| `period_spend_limit` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | period_spend_limit specifies the maximum number of coins that can be spent in the period |
| `period_can_spend` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | period_can_spend is the number of coins left to be spent before the period_reset time |
| `period_reset` | [ExpiresAt](#cosmos.feegrant.v1beta1.ExpiresAt) | | period_reset 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 |
| `period_reset` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | period_reset 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 |

View File

@ -22,7 +22,7 @@ message BasicAllowance {
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
// expiration specifies an optional time when this allowance expires
ExpiresAt expiration = 2 [(gogoproto.nullable) = false];
google.protobuf.Timestamp expiration = 2 [(gogoproto.stdtime) = true];
}
// PeriodicAllowance extends Allowance to allow for both a maximum cap,
@ -35,7 +35,7 @@ message PeriodicAllowance {
// period specifies the time duration in which period_spend_limit coins can
// be spent before that allowance is reset
Duration period = 2 [(gogoproto.nullable) = false];
google.protobuf.Duration period = 2 [(gogoproto.stdduration) = true, (gogoproto.nullable) = false];
// period_spend_limit specifies the maximum number of coins that can be spent
// in the period
@ -49,7 +49,7 @@ message PeriodicAllowance {
// period_reset 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
ExpiresAt period_reset = 5 [(gogoproto.nullable) = false];
google.protobuf.Timestamp period_reset = 5 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
}
// AllowedMsgAllowance creates allowance only for specified message types.
@ -64,26 +64,6 @@ message AllowedMsgAllowance {
repeated string allowed_messages = 2;
}
// Duration is a span of a clock time or number of blocks.
// This is designed to be added to an ExpiresAt struct.
message Duration {
// sum is the oneof that represents either duration or block
oneof sum {
google.protobuf.Duration duration = 1 [(gogoproto.stdduration) = true];
uint64 blocks = 2;
}
}
// ExpiresAt is a point in time where something expires.
// It may be *either* block time or block height
message ExpiresAt {
// sum is the oneof that represents either time or height
oneof sum {
google.protobuf.Timestamp time = 1 [(gogoproto.stdtime) = true];
int64 height = 2;
}
}
// Grant is stored in the KVStore to record a grant with full context
message Grant {
// granter is the address of the user granting an allowance of their funds.

View File

@ -107,7 +107,7 @@ Examples:
if err != nil {
return err
}
basic.Expiration = types.ExpiresAtTime(expiresAtTime)
basic.Expiration = &expiresAtTime
}
var grant types.FeeAllowanceI
@ -131,15 +131,15 @@ Examples:
}
if periodClock > 0 && periodLimit != nil {
periodReset := time.Now().Add(time.Duration(periodClock) * time.Second)
periodReset := getPeriodReset(periodClock)
if exp != "" && periodReset.Sub(expiresAtTime) > 0 {
return fmt.Errorf("period(%d) cannot reset after expiration(%v)", periodClock, exp)
}
periodic := types.PeriodicAllowance{
Basic: basic,
Period: types.ClockDuration(time.Duration(periodClock) * time.Second),
PeriodReset: types.ExpiresAtTime(periodReset),
Period: getPeriod(periodClock),
PeriodReset: getPeriodReset(periodClock),
PeriodSpendLimit: periodLimit,
PeriodCanSpend: periodLimit,
}
@ -230,3 +230,11 @@ Example:
flags.AddTxFlagsToCmd(cmd)
return cmd
}
func getPeriodReset(duration int64) time.Time {
return time.Now().Add(getPeriod(duration))
}
func getPeriod(duration int64) time.Duration {
return time.Duration(duration) * time.Second
}

View File

@ -31,19 +31,12 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, data *types.GenesisState) err
return nil
}
// 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 expiry at 5000 and current is 4000, they export with
// expiry of 1000. Every FeeAllowance has a method `PrepareForExport` that allows
// them to perform any changes needed prior to export.
// ExportGenesis will dump the contents of the keeper into a serializable GenesisState.
func ExportGenesis(ctx sdk.Context, k keeper.Keeper) (*types.GenesisState, error) {
time, height := ctx.BlockTime(), ctx.BlockHeight()
var grants []types.Grant
err := k.IterateAllFeeAllowances(ctx, func(grant types.Grant) bool {
grants = append(grants, grant.PrepareForExport(time, height))
grants = append(grants, grant)
return false
})

View File

@ -39,9 +39,10 @@ var (
func (suite *GenesisTestSuite) TestImportExportGenesis() {
coins := sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1_000)))
now := suite.ctx.BlockHeader().Time
oneYear := now.AddDate(1, 0, 0)
msgSrvr := keeper.NewMsgServerImpl(suite.keeper)
allowance := &types.BasicAllowance{SpendLimit: coins, Expiration: types.ExpiresAtTime(now.AddDate(1, 0, 0))}
allowance := &types.BasicAllowance{SpendLimit: coins, Expiration: &oneYear}
err := suite.keeper.GrantAllowance(suite.ctx, granterAddr, granteeAddr, allowance)
suite.Require().NoError(err)

View File

@ -149,9 +149,10 @@ func (suite *KeeperTestSuite) TestFeeAllowances() {
}
func grantFeeAllowance(suite *KeeperTestSuite) {
exp := suite.sdkCtx.BlockTime().AddDate(1, 0, 0)
err := suite.app.FeeGrantKeeper.GrantAllowance(suite.sdkCtx, suite.addrs[0], suite.addrs[1], &types.BasicAllowance{
SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 555)),
Expiration: types.ExpiresAtHeight(334455),
Expiration: &exp,
})
suite.Require().NoError(err)
}

View File

@ -3,7 +3,6 @@ package keeper_test
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/suite"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
@ -46,14 +45,15 @@ func (suite *KeeperTestSuite) SetupTest() {
func (suite *KeeperTestSuite) TestKeeperCrud() {
// some helpers
eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123))
exp := suite.sdkCtx.BlockTime().AddDate(1, 0, 0)
basic := &types.BasicAllowance{
SpendLimit: suite.atom,
Expiration: types.ExpiresAtHeight(334455),
Expiration: &exp,
}
basic2 := &types.BasicAllowance{
SpendLimit: eth,
Expiration: types.ExpiresAtHeight(172436),
Expiration: &exp,
}
// let's set up some initial state here
@ -149,24 +149,20 @@ func (suite *KeeperTestSuite) TestKeeperCrud() {
func (suite *KeeperTestSuite) TestUseGrantedFee() {
eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123))
blockTime := suite.sdkCtx.BlockTime()
oneYear := blockTime.AddDate(1, 0, 0)
future := &types.BasicAllowance{
SpendLimit: suite.atom,
Expiration: types.ExpiresAtHeight(5678),
}
expired := &types.BasicAllowance{
SpendLimit: eth,
Expiration: types.ExpiresAtHeight(55),
Expiration: &oneYear,
}
// for testing limits of the contract
hugeAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 9999))
_ = hugeAtom
smallAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 1))
_ = smallAtom
futureAfterSmall := &types.BasicAllowance{
SpendLimit: sdk.NewCoins(sdk.NewInt64Coin("atom", 554)),
Expiration: types.ExpiresAtHeight(5678),
Expiration: &oneYear,
}
// then lots of queries
@ -184,13 +180,6 @@ func (suite *KeeperTestSuite) TestUseGrantedFee() {
allowed: true,
final: nil,
},
"expired and removed": {
granter: suite.addrs[0],
grantee: suite.addrs[2],
fee: eth,
allowed: false,
final: nil,
},
"too high": {
granter: suite.addrs[0],
grantee: suite.addrs[1],
@ -210,16 +199,9 @@ func (suite *KeeperTestSuite) TestUseGrantedFee() {
for name, tc := range cases {
tc := tc
suite.Run(name, func() {
// let's set up some initial state here
// addr -> addr2 (future)
// addr -> addr3 (expired)
err := suite.keeper.GrantAllowance(suite.sdkCtx, suite.addrs[0], suite.addrs[1], future)
suite.Require().NoError(err)
err = suite.keeper.GrantAllowance(suite.sdkCtx, suite.addrs[0], suite.addrs[3], expired)
suite.Require().NoError(err)
err = suite.keeper.UseGrantedFees(suite.sdkCtx, tc.granter, tc.grantee, tc.fee, []sdk.Msg{})
if tc.allowed {
suite.NoError(err)
@ -228,22 +210,43 @@ func (suite *KeeperTestSuite) TestUseGrantedFee() {
}
loaded, _ := suite.keeper.GetAllowance(suite.sdkCtx, tc.granter, tc.grantee)
suite.Equal(tc.final, loaded)
})
}
expired := &types.BasicAllowance{
SpendLimit: eth,
Expiration: &blockTime,
}
// creating expired feegrant
ctx := suite.sdkCtx.WithBlockTime(oneYear)
err := suite.keeper.GrantAllowance(ctx, suite.addrs[0], suite.addrs[2], expired)
suite.Require().NoError(err)
// expect error: feegrant expired
err = suite.keeper.UseGrantedFees(ctx, suite.addrs[0], suite.addrs[2], eth, []sdk.Msg{})
suite.Error(err)
suite.Contains(err.Error(), "fee allowance expired")
// verify: feegrant is revoked
_, err = suite.keeper.GetAllowance(ctx, suite.addrs[0], suite.addrs[2])
suite.Error(err)
suite.Contains(err.Error(), "fee-grant not found")
}
func (suite *KeeperTestSuite) TestIterateGrants() {
eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123))
exp := suite.sdkCtx.BlockTime().AddDate(1, 0, 0)
allowance := &types.BasicAllowance{
SpendLimit: suite.atom,
Expiration: types.ExpiresAtHeight(5678),
Expiration: &exp,
}
allowance1 := &types.BasicAllowance{
SpendLimit: eth,
Expiration: types.ExpiresAtTime(suite.sdkCtx.BlockTime().Add(24 * time.Hour)),
Expiration: &exp,
}
suite.keeper.GrantAllowance(suite.sdkCtx, suite.addrs[0], suite.addrs[1], allowance)

View File

@ -7,6 +7,8 @@ import (
)
func (suite *KeeperTestSuite) TestGrantFeeAllowance() {
oneYear := suite.sdkCtx.BlockTime().AddDate(1, 0, 0)
testCases := []struct {
name string
req func() *types.MsgGrantAllowance
@ -46,7 +48,7 @@ func (suite *KeeperTestSuite) TestGrantFeeAllowance() {
func() *types.MsgGrantAllowance {
any, err := codectypes.NewAnyWithValue(&types.BasicAllowance{
SpendLimit: suite.atom,
Expiration: types.ExpiresAtTime(suite.sdkCtx.BlockTime().AddDate(1, 0, 0)),
Expiration: &oneYear,
})
suite.Require().NoError(err)
return &types.MsgGrantAllowance{
@ -63,7 +65,7 @@ func (suite *KeeperTestSuite) TestGrantFeeAllowance() {
func() *types.MsgGrantAllowance {
any, err := codectypes.NewAnyWithValue(&types.BasicAllowance{
SpendLimit: suite.atom,
Expiration: types.ExpiresAtTime(suite.sdkCtx.BlockTime().AddDate(1, 0, 0)),
Expiration: &oneYear,
})
suite.Require().NoError(err)
return &types.MsgGrantAllowance{
@ -81,7 +83,7 @@ func (suite *KeeperTestSuite) TestGrantFeeAllowance() {
any, err := codectypes.NewAnyWithValue(&types.PeriodicAllowance{
Basic: types.BasicAllowance{
SpendLimit: suite.atom,
Expiration: types.ExpiresAtTime(suite.sdkCtx.BlockTime().AddDate(1, 0, 0)),
Expiration: &oneYear,
},
})
suite.Require().NoError(err)
@ -100,7 +102,7 @@ func (suite *KeeperTestSuite) TestGrantFeeAllowance() {
any, err := codectypes.NewAnyWithValue(&types.PeriodicAllowance{
Basic: types.BasicAllowance{
SpendLimit: suite.atom,
Expiration: types.ExpiresAtTime(suite.sdkCtx.BlockTime().AddDate(1, 0, 0)),
Expiration: &oneYear,
},
})
suite.Require().NoError(err)
@ -126,6 +128,7 @@ func (suite *KeeperTestSuite) TestGrantFeeAllowance() {
}
func (suite *KeeperTestSuite) TestRevokeFeeAllowance() {
oneYear := suite.sdkCtx.BlockTime().AddDate(1, 0, 0)
testCases := []struct {
name string
@ -179,7 +182,7 @@ func (suite *KeeperTestSuite) TestRevokeFeeAllowance() {
any, err := codectypes.NewAnyWithValue(&types.PeriodicAllowance{
Basic: types.BasicAllowance{
SpendLimit: suite.atom,
Expiration: types.ExpiresAtTime(suite.sdkCtx.BlockTime().AddDate(1, 0, 0)),
Expiration: &oneYear,
},
})
suite.Require().NoError(err)

View File

@ -43,7 +43,7 @@ func generateRandomAllowances(granter, grantee sdk.AccAddress, r *rand.Rand) typ
periodicAllowance, err := types.NewGrant(granter, grantee, &types.PeriodicAllowance{
Basic: basic,
PeriodSpendLimit: periodSpendLimit,
Period: types.ClockDuration(time.Hour),
Period: time.Hour,
})
if err != nil {
panic(err)

View File

@ -3,7 +3,6 @@ package simulation
import (
"context"
"math/rand"
"time"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
@ -91,9 +90,10 @@ func SimulateMsgGrantFeeAllowance(ak types.AccountKeeper, bk types.BankKeeper, k
return simtypes.NoOpMsg(types.ModuleName, TypeMsgGrantFeeAllowance, "unable to grant empty coins as SpendLimit"), nil, nil
}
oneYear := ctx.BlockTime().AddDate(1, 0, 0)
msg, err := types.NewMsgGrantAllowance(&types.BasicAllowance{
SpendLimit: spendableCoins,
Expiration: types.ExpiresAtTime(ctx.BlockTime().Add(30 * time.Hour)),
Expiration: &oneYear,
}, granter.Address, grantee.Address)
if err != nil {

View File

@ -31,7 +31,9 @@ func (suite *SimTestSuite) SetupTest() {
checkTx := false
app := simapp.Setup(checkTx)
suite.app = app
suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{})
suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{
Time: time.Now(),
})
suite.protoCdc = codec.NewProtoCodec(suite.app.InterfaceRegistry())
}
@ -139,13 +141,14 @@ func (suite *SimTestSuite) TestSimulateMsgRevokeFeeAllowance() {
granter, grantee := accounts[0], accounts[1]
oneYear := ctx.BlockTime().AddDate(1, 0, 0)
err := app.FeeGrantKeeper.GrantAllowance(
ctx,
granter.Address,
grantee.Address,
&types.BasicAllowance{
SpendLimit: feeCoins,
Expiration: types.ExpiresAtTime(ctx.BlockTime().Add(30 * time.Hour)),
Expiration: &oneYear,
},
)
require.NoError(err)

View File

@ -1,15 +1,13 @@
package types
import (
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
var _ FeeAllowanceI = (*BasicAllowance)(nil)
// Accept can use fee payment requested as well as timestamp/height of the current block
// Accept can use fee payment requested as well as timestamp 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.
//
@ -20,10 +18,7 @@ var _ FeeAllowanceI = (*BasicAllowance)(nil)
// 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 *BasicAllowance) Accept(ctx sdk.Context, fee sdk.Coins, _ []sdk.Msg) (bool, error) {
blockTime := ctx.BlockTime()
blockHeight := ctx.BlockHeight()
if a.Expiration.IsExpired(&blockTime, blockHeight) {
if a.Expiration != nil && a.Expiration.Before(ctx.BlockTime()) {
return true, sdkerrors.Wrap(ErrFeeLimitExpired, "basic allowance")
}
@ -40,16 +35,6 @@ func (a *BasicAllowance) Accept(ctx sdk.Context, fee sdk.Coins, _ []sdk.Msg) (bo
return false, 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 *BasicAllowance) PrepareForExport(dumpTime time.Time, dumpHeight int64) FeeAllowanceI {
return &BasicAllowance{
SpendLimit: a.SpendLimit,
Expiration: a.Expiration.PrepareForExport(dumpTime, dumpHeight),
}
}
// ValidateBasic implements FeeAllowance and enforces basic sanity checks
func (a BasicAllowance) ValidateBasic() error {
if a.SpendLimit != nil {
@ -60,5 +45,10 @@ func (a BasicAllowance) ValidateBasic() error {
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "spend limit must be positive")
}
}
return a.Expiration.ValidateBasic()
if a.Expiration != nil && a.Expiration.Unix() < 0 {
return sdkerrors.Wrap(ErrInvalidDuration, "expiration time cannot be negative")
}
return nil
}

View File

@ -16,24 +16,37 @@ import (
func TestBasicFeeValidAllow(t *testing.T) {
app := simapp.Setup(false)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
badTime := ctx.BlockTime().AddDate(0, 0, -1)
allowace := &types.BasicAllowance{
Expiration: &badTime,
}
require.Error(t, allowace.ValidateBasic())
ctx = app.BaseApp.NewContext(false, tmproto.Header{
Time: time.Now(),
})
eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 10))
atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555))
smallAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 43))
bigAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 1000))
leftAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 512))
now := ctx.BlockTime()
oneHour := now.Add(1 * time.Hour)
cases := map[string]struct {
allowance *types.BasicAllowance
// all other checks are ignored if valid=false
fee sdk.Coins
blockHeight int64
accept bool
remove bool
remains sdk.Coins
fee sdk.Coins
blockTime time.Time
valid bool
accept bool
remove bool
remains sdk.Coins
}{
"empty": {
allowance: &types.BasicAllowance{},
accept: true,
allowance: &types.BasicAllowance{},
accept: true,
},
"small fee without expire": {
allowance: &types.BasicAllowance{
@ -62,48 +75,53 @@ func TestBasicFeeValidAllow(t *testing.T) {
"non-expired": {
allowance: &types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtHeight(100),
Expiration: &oneHour,
},
fee: smallAtom,
blockHeight: 85,
accept: true,
remove: false,
remains: leftAtom,
valid: true,
fee: smallAtom,
blockTime: now,
accept: true,
remove: false,
remains: leftAtom,
},
"expired": {
allowance: &types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtHeight(100),
Expiration: &now,
},
fee: smallAtom,
blockHeight: 121,
accept: false,
remove: true,
valid: true,
fee: smallAtom,
blockTime: oneHour,
accept: false,
remove: true,
},
"fee more than allowed": {
allowance: &types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtHeight(100),
Expiration: &oneHour,
},
fee: bigAtom,
blockHeight: 85,
accept: false,
valid: true,
fee: bigAtom,
blockTime: now,
accept: false,
},
"with out spend limit": {
allowance: &types.BasicAllowance{
Expiration: types.ExpiresAtHeight(100),
Expiration: &oneHour,
},
fee: bigAtom,
blockHeight: 85,
accept: true,
valid: true,
fee: bigAtom,
blockTime: now,
accept: true,
},
"expired no spend limit": {
allowance: &types.BasicAllowance{
Expiration: types.ExpiresAtHeight(100),
Expiration: &now,
},
fee: bigAtom,
blockHeight: 120,
accept: false,
valid: true,
fee: bigAtom,
blockTime: oneHour,
accept: false,
},
}
@ -113,7 +131,7 @@ func TestBasicFeeValidAllow(t *testing.T) {
err := tc.allowance.ValidateBasic()
require.NoError(t, err)
ctx := app.BaseApp.NewContext(false, tmproto.Header{}).WithBlockHeight(tc.blockHeight)
ctx := app.BaseApp.NewContext(false, tmproto.Header{}).WithBlockTime(tc.blockTime)
// now try to deduct
removed, err := tc.allowance.Accept(ctx, tc.fee, []sdk.Msg{})
@ -130,138 +148,3 @@ func TestBasicFeeValidAllow(t *testing.T) {
})
}
}
func TestBasicFeeAllowTime(t *testing.T) {
app := simapp.Setup(false)
eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 10))
atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555))
smallAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 43))
bigAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 1000))
leftAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 512))
now := time.Now()
oneHour := now.Add(1 * time.Hour)
cases := map[string]struct {
allow *types.BasicAllowance
// all other checks are ignored if valid=false
fee sdk.Coins
blockTime time.Time
valid bool
accept bool
remove bool
remains sdk.Coins
}{
"empty": {
allow: &types.BasicAllowance{},
valid: true,
accept: true,
},
"small fee without expire": {
allow: &types.BasicAllowance{
SpendLimit: atom,
},
valid: true,
fee: smallAtom,
accept: true,
remove: false,
remains: leftAtom,
},
"all fee without expire": {
allow: &types.BasicAllowance{
SpendLimit: smallAtom,
},
valid: true,
fee: smallAtom,
accept: true,
remove: true,
},
"wrong fee": {
allow: &types.BasicAllowance{
SpendLimit: smallAtom,
},
valid: true,
fee: eth,
accept: false,
},
"non-expired": {
allow: &types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtTime(oneHour),
},
valid: true,
fee: smallAtom,
blockTime: now,
accept: true,
remove: false,
remains: leftAtom,
},
"expired": {
allow: &types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtTime(now),
},
valid: true,
fee: smallAtom,
blockTime: oneHour,
accept: false,
remove: true,
},
"fee more than allowed": {
allow: &types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtTime(oneHour),
},
valid: true,
fee: bigAtom,
blockTime: now,
accept: false,
},
"without spend limit": {
allow: &types.BasicAllowance{
Expiration: types.ExpiresAtTime(oneHour),
},
valid: true,
fee: bigAtom,
blockTime: now,
accept: true,
},
"expired no spend limit": {
allow: &types.BasicAllowance{
Expiration: types.ExpiresAtTime(now),
},
valid: true,
fee: bigAtom,
blockTime: oneHour,
accept: false,
},
}
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)
ctx := app.BaseApp.NewContext(false, tmproto.Header{}).WithBlockTime(tc.blockTime)
// now try to deduct
remove, err := tc.allow.Accept(ctx, tc.fee, []sdk.Msg{})
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)
}
})
}
}

View File

@ -3,7 +3,6 @@ package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
auth "github.com/cosmos/cosmos-sdk/x/auth/types"
// supply "github.com/cosmos/cosmos-sdk/x/supply/exported"
)
// AccountKeeper defines the expected auth Account Keeper (noalias)

View File

@ -1,138 +0,0 @@
package types
import (
"time"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
// ExpiresAtTime creates an expiration at the given time
func ExpiresAtTime(t time.Time) ExpiresAt {
return ExpiresAt{
Sum: &ExpiresAt_Time{
Time: &t,
},
}
}
// ExpiresAtHeight creates an expiration at the given height
func ExpiresAtHeight(h int64) ExpiresAt {
return ExpiresAt{
&ExpiresAt_Height{
Height: h,
},
}
}
// ValidateBasic performs basic sanity checks.
// Note that empty expiration is allowed
func (e ExpiresAt) ValidateBasic() error {
if e.HasDefinedTime() && e.GetHeight() != 0 {
return sdkerrors.Wrap(ErrInvalidDuration, "both time and height are set")
}
if e.GetHeight() < 0 {
return sdkerrors.Wrap(ErrInvalidDuration, "negative height")
}
return nil
}
// Undefined returns true for an uninitialized struct
func (e ExpiresAt) Undefined() bool {
return (e.GetTime() == nil || e.GetTime().Unix() <= 0) && e.GetHeight() == 0
}
// HasDefinedTime returns true if `ExpiresAt` has valid time
func (e ExpiresAt) HasDefinedTime() bool {
t := e.GetTime()
return t != nil && t.Unix() > 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.HasDefinedTime() {
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.
//
// Note a "zero" ExpiresAt is never expired
func (e ExpiresAt) IsExpired(t *time.Time, h int64) bool {
if e.HasDefinedTime() && t.After(*e.GetTime()) {
return true
}
return e.GetHeight() != 0 && h >= e.GetHeight()
}
// IsCompatible returns true iff the two use the same units.
// If false, they cannot be added.
func (e ExpiresAt) IsCompatible(d Duration) bool {
if e.HasDefinedTime() {
return d.GetDuration() != nil && d.GetDuration().Seconds() > float64(0)
}
return d.GetBlocks() > 0
}
// Step will increase the expiration point by one Duration
// It returns an error if the Duration is incompatible
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.HasDefinedTime() {
return ExpiresAtTime(e.GetTime().Add(*d.GetDuration())), nil
}
return ExpiresAtHeight(e.GetHeight() + int64(d.GetBlocks())), nil
}
// MustStep is like Step, but panics on error
func (e ExpiresAt) MustStep(d Duration) ExpiresAt {
res, err := e.Step(d)
if err != nil {
panic(err)
}
return res
}
// 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.GetHeight() != 0 {
return ExpiresAtHeight(e.GetHeight() - dumpHeight)
}
return ExpiresAt{}
}
// ClockDuration creates an Duration by clock time
func ClockDuration(d time.Duration) Duration {
return Duration{Sum: &Duration_Duration{
Duration: &d,
}}
}
// BlockDuration creates an Duration by block height
func BlockDuration(h uint64) Duration {
return Duration{Sum: &Duration_Blocks{
Blocks: h,
}}
}
// ValidateBasic performs basic sanity checks
// Note that exactly one must be set and it must be positive
func (d Duration) ValidateBasic() error {
if d.GetBlocks() == 0 && d.GetDuration() == nil {
return sdkerrors.Wrap(ErrInvalidDuration, "neither time and height are set")
}
if d.GetBlocks() != 0 && d.GetDuration() != nil && d.GetDuration().Seconds() != float64(0) {
return sdkerrors.Wrap(ErrInvalidDuration, "both time and height are set")
}
if d.GetDuration() != nil && d.GetDuration().Seconds() < 0 {
return sdkerrors.Wrap(ErrInvalidDuration, "negative clock step")
}
return nil
}

View File

@ -1,153 +0,0 @@
package types_test
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/x/feegrant/types"
)
func TestExpiresAt(t *testing.T) {
now := time.Now()
cases := map[string]struct {
expires types.ExpiresAt
zero bool
before types.ExpiresAt
after types.ExpiresAt
}{
"basic": {
expires: types.ExpiresAtHeight(100),
before: types.ExpiresAtHeight(50),
after: types.ExpiresAtHeight(122),
},
"zero": {
expires: types.ExpiresAt{},
zero: true,
before: types.ExpiresAtHeight(1),
},
"match height": {
expires: types.ExpiresAtHeight(1000),
before: types.ExpiresAtHeight(999),
after: types.ExpiresAtHeight(1000),
},
"match time": {
expires: types.ExpiresAtTime(now),
before: types.ExpiresAtTime(now.Add(-1 * time.Second)),
after: types.ExpiresAtTime(now.Add(1 * time.Second)),
},
}
for name, stc := range cases {
tc := stc // to make scopelint happy
t.Run(name, func(t *testing.T) {
err := tc.expires.ValidateBasic()
assert.Equal(t, tc.zero, tc.expires.Undefined())
require.NoError(t, err)
if !tc.before.Undefined() {
assert.Equal(t, false, tc.expires.IsExpired(tc.before.GetTime(), tc.before.GetHeight()))
}
if !tc.after.Undefined() {
assert.Equal(t, true, tc.expires.IsExpired(tc.after.GetTime(), tc.after.GetHeight()))
}
})
}
}
func TestDurationValid(t *testing.T) {
now := time.Now()
cases := map[string]struct {
period types.Duration
valid bool
compatible types.ExpiresAt
incompatible types.ExpiresAt
}{
"basic height": {
period: types.BlockDuration(100),
valid: true,
compatible: types.ExpiresAtHeight(50),
incompatible: types.ExpiresAtTime(now),
},
"basic time": {
period: types.ClockDuration(time.Hour),
valid: true,
compatible: types.ExpiresAtTime(now),
incompatible: types.ExpiresAtHeight(50),
},
"zero": {
period: types.Duration{},
valid: false,
},
"negative clock": {
period: types.ClockDuration(-1 * time.Hour),
valid: false,
},
}
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 {
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 TestDurationStep(t *testing.T) {
now := time.Now()
cases := map[string]struct {
expires types.ExpiresAt
period types.Duration
valid bool
result types.ExpiresAt
}{
"add height": {
expires: types.ExpiresAtHeight(789),
period: types.BlockDuration(100),
valid: true,
result: types.ExpiresAtHeight(889),
},
"add time": {
expires: types.ExpiresAtTime(now),
period: types.ClockDuration(time.Hour),
valid: true,
result: types.ExpiresAtTime(now.Add(time.Hour)),
},
"mismatch": {
expires: types.ExpiresAtHeight(789),
period: types.ClockDuration(time.Hour),
valid: false,
},
}
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)
err = tc.expires.ValidateBasic()
require.NoError(t, err)
next, err := tc.expires.Step(tc.period)
if !tc.valid {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tc.result, next)
})
}
}

View File

@ -40,7 +40,7 @@ type BasicAllowance struct {
// empty, there is no spend limit and any amount of coins can be spent.
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"`
// expiration specifies an optional time when this allowance expires
Expiration ExpiresAt `protobuf:"bytes,2,opt,name=expiration,proto3" json:"expiration"`
Expiration *time.Time `protobuf:"bytes,2,opt,name=expiration,proto3,stdtime" json:"expiration,omitempty"`
}
func (m *BasicAllowance) Reset() { *m = BasicAllowance{} }
@ -83,11 +83,11 @@ func (m *BasicAllowance) GetSpendLimit() github_com_cosmos_cosmos_sdk_types.Coin
return nil
}
func (m *BasicAllowance) GetExpiration() ExpiresAt {
func (m *BasicAllowance) GetExpiration() *time.Time {
if m != nil {
return m.Expiration
}
return ExpiresAt{}
return nil
}
// PeriodicAllowance extends Allowance to allow for both a maximum cap,
@ -97,7 +97,7 @@ type PeriodicAllowance struct {
Basic BasicAllowance `protobuf:"bytes,1,opt,name=basic,proto3" json:"basic"`
// period specifies the time duration in which period_spend_limit coins can
// be spent before that allowance is reset
Period Duration `protobuf:"bytes,2,opt,name=period,proto3" json:"period"`
Period time.Duration `protobuf:"bytes,2,opt,name=period,proto3,stdduration" json:"period"`
// period_spend_limit specifies the maximum number of coins that can be spent
// in the 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"`
@ -106,7 +106,7 @@ type PeriodicAllowance struct {
// period_reset 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 ExpiresAt `protobuf:"bytes,5,opt,name=period_reset,json=periodReset,proto3" json:"period_reset"`
PeriodReset time.Time `protobuf:"bytes,5,opt,name=period_reset,json=periodReset,proto3,stdtime" json:"period_reset"`
}
func (m *PeriodicAllowance) Reset() { *m = PeriodicAllowance{} }
@ -149,11 +149,11 @@ func (m *PeriodicAllowance) GetBasic() BasicAllowance {
return BasicAllowance{}
}
func (m *PeriodicAllowance) GetPeriod() Duration {
func (m *PeriodicAllowance) GetPeriod() time.Duration {
if m != nil {
return m.Period
}
return Duration{}
return 0
}
func (m *PeriodicAllowance) GetPeriodSpendLimit() github_com_cosmos_cosmos_sdk_types.Coins {
@ -170,11 +170,11 @@ func (m *PeriodicAllowance) GetPeriodCanSpend() github_com_cosmos_cosmos_sdk_typ
return nil
}
func (m *PeriodicAllowance) GetPeriodReset() ExpiresAt {
func (m *PeriodicAllowance) GetPeriodReset() time.Time {
if m != nil {
return m.PeriodReset
}
return ExpiresAt{}
return time.Time{}
}
// AllowedMsgAllowance creates allowance only for specified message types.
@ -218,184 +218,6 @@ func (m *AllowedMsgAllowance) XXX_DiscardUnknown() {
var xxx_messageInfo_AllowedMsgAllowance proto.InternalMessageInfo
// Duration is a span of a clock time or number of blocks.
// This is designed to be added to an ExpiresAt struct.
type Duration struct {
// sum is the oneof that represents either duration or block
//
// Types that are valid to be assigned to Sum:
// *Duration_Duration
// *Duration_Blocks
Sum isDuration_Sum `protobuf_oneof:"sum"`
}
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_7279582900c30aea, []int{3}
}
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
type isDuration_Sum interface {
isDuration_Sum()
MarshalTo([]byte) (int, error)
Size() int
}
type Duration_Duration struct {
Duration *time.Duration `protobuf:"bytes,1,opt,name=duration,proto3,oneof,stdduration" json:"duration,omitempty"`
}
type Duration_Blocks struct {
Blocks uint64 `protobuf:"varint,2,opt,name=blocks,proto3,oneof" json:"blocks,omitempty"`
}
func (*Duration_Duration) isDuration_Sum() {}
func (*Duration_Blocks) isDuration_Sum() {}
func (m *Duration) GetSum() isDuration_Sum {
if m != nil {
return m.Sum
}
return nil
}
func (m *Duration) GetDuration() *time.Duration {
if x, ok := m.GetSum().(*Duration_Duration); ok {
return x.Duration
}
return nil
}
func (m *Duration) GetBlocks() uint64 {
if x, ok := m.GetSum().(*Duration_Blocks); ok {
return x.Blocks
}
return 0
}
// XXX_OneofWrappers is for the internal use of the proto package.
func (*Duration) XXX_OneofWrappers() []interface{} {
return []interface{}{
(*Duration_Duration)(nil),
(*Duration_Blocks)(nil),
}
}
// ExpiresAt is a point in time where something expires.
// It may be *either* block time or block height
type ExpiresAt struct {
// sum is the oneof that represents either time or height
//
// Types that are valid to be assigned to Sum:
// *ExpiresAt_Time
// *ExpiresAt_Height
Sum isExpiresAt_Sum `protobuf_oneof:"sum"`
}
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_7279582900c30aea, []int{4}
}
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
type isExpiresAt_Sum interface {
isExpiresAt_Sum()
MarshalTo([]byte) (int, error)
Size() int
}
type ExpiresAt_Time struct {
Time *time.Time `protobuf:"bytes,1,opt,name=time,proto3,oneof,stdtime" json:"time,omitempty"`
}
type ExpiresAt_Height struct {
Height int64 `protobuf:"varint,2,opt,name=height,proto3,oneof" json:"height,omitempty"`
}
func (*ExpiresAt_Time) isExpiresAt_Sum() {}
func (*ExpiresAt_Height) isExpiresAt_Sum() {}
func (m *ExpiresAt) GetSum() isExpiresAt_Sum {
if m != nil {
return m.Sum
}
return nil
}
func (m *ExpiresAt) GetTime() *time.Time {
if x, ok := m.GetSum().(*ExpiresAt_Time); ok {
return x.Time
}
return nil
}
func (m *ExpiresAt) GetHeight() int64 {
if x, ok := m.GetSum().(*ExpiresAt_Height); ok {
return x.Height
}
return 0
}
// XXX_OneofWrappers is for the internal use of the proto package.
func (*ExpiresAt) XXX_OneofWrappers() []interface{} {
return []interface{}{
(*ExpiresAt_Time)(nil),
(*ExpiresAt_Height)(nil),
}
}
// Grant is stored in the KVStore to record a grant with full context
type Grant struct {
// granter is the address of the user granting an allowance of their funds.
@ -410,7 +232,7 @@ func (m *Grant) Reset() { *m = Grant{} }
func (m *Grant) String() string { return proto.CompactTextString(m) }
func (*Grant) ProtoMessage() {}
func (*Grant) Descriptor() ([]byte, []int) {
return fileDescriptor_7279582900c30aea, []int{5}
return fileDescriptor_7279582900c30aea, []int{3}
}
func (m *Grant) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -464,8 +286,6 @@ func init() {
proto.RegisterType((*BasicAllowance)(nil), "cosmos.feegrant.v1beta1.BasicAllowance")
proto.RegisterType((*PeriodicAllowance)(nil), "cosmos.feegrant.v1beta1.PeriodicAllowance")
proto.RegisterType((*AllowedMsgAllowance)(nil), "cosmos.feegrant.v1beta1.AllowedMsgAllowance")
proto.RegisterType((*Duration)(nil), "cosmos.feegrant.v1beta1.Duration")
proto.RegisterType((*ExpiresAt)(nil), "cosmos.feegrant.v1beta1.ExpiresAt")
proto.RegisterType((*Grant)(nil), "cosmos.feegrant.v1beta1.Grant")
}
@ -474,48 +294,43 @@ func init() {
}
var fileDescriptor_7279582900c30aea = []byte{
// 645 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xbf, 0x6f, 0xd3, 0x40,
0x14, 0xb6, 0xeb, 0xa4, 0x34, 0x17, 0x28, 0xed, 0x51, 0x84, 0xdb, 0xc1, 0x29, 0x19, 0x20, 0x0c,
0xb5, 0x69, 0x91, 0x18, 0x2a, 0x21, 0x54, 0x97, 0xd2, 0x22, 0xa8, 0x84, 0x0c, 0x13, 0x4b, 0x74,
0xb6, 0xaf, 0xae, 0xa9, 0xed, 0xb3, 0x7c, 0x17, 0x68, 0x56, 0x26, 0xc6, 0x8e, 0x4c, 0x88, 0x99,
0x99, 0x3f, 0xa2, 0x62, 0xaa, 0x98, 0x90, 0x90, 0x28, 0x6a, 0x46, 0xfe, 0x09, 0xe4, 0xbb, 0xb3,
0x1d, 0x12, 0x82, 0x04, 0xea, 0x64, 0xbf, 0x7b, 0xef, 0xfb, 0xbe, 0xf7, 0xeb, 0x0e, 0xdc, 0xf0,
0x08, 0x8d, 0x09, 0xb5, 0xf6, 0x30, 0x0e, 0x32, 0x94, 0x30, 0xeb, 0xd5, 0xaa, 0x8b, 0x19, 0x5a,
0x2d, 0x0f, 0xcc, 0x34, 0x23, 0x8c, 0xc0, 0x6b, 0x22, 0xce, 0x2c, 0x8f, 0x65, 0xdc, 0xd2, 0x42,
0x40, 0x02, 0xc2, 0x63, 0xac, 0xfc, 0x4f, 0x84, 0x2f, 0x2d, 0x06, 0x84, 0x04, 0x11, 0xb6, 0xb8,
0xe5, 0xf6, 0xf6, 0x2c, 0x94, 0xf4, 0x0b, 0x97, 0x60, 0xea, 0x0a, 0x8c, 0xa4, 0x15, 0x2e, 0x43,
0x26, 0xe3, 0x22, 0x8a, 0xcb, 0x44, 0x3c, 0x12, 0x26, 0xd2, 0xdf, 0x1a, 0x65, 0x65, 0x61, 0x8c,
0x29, 0x43, 0x71, 0x5a, 0x10, 0x8c, 0x06, 0xf8, 0xbd, 0x0c, 0xb1, 0x90, 0x48, 0x82, 0xf6, 0x37,
0x15, 0xcc, 0xda, 0x88, 0x86, 0xde, 0x46, 0x14, 0x91, 0xd7, 0x28, 0xf1, 0x30, 0x8c, 0x40, 0x93,
0xa6, 0x38, 0xf1, 0xbb, 0x51, 0x18, 0x87, 0x4c, 0x57, 0x97, 0xb5, 0x4e, 0x73, 0x6d, 0xd1, 0x94,
0x79, 0xe5, 0x99, 0x14, 0xa5, 0x9a, 0x9b, 0x24, 0x4c, 0xec, 0xdb, 0xc7, 0xdf, 0x5b, 0xca, 0xc7,
0xd3, 0x56, 0x27, 0x08, 0xd9, 0x7e, 0xcf, 0x35, 0x3d, 0x12, 0xcb, 0x22, 0xe4, 0x67, 0x85, 0xfa,
0x07, 0x16, 0xeb, 0xa7, 0x98, 0x72, 0x00, 0x75, 0x00, 0xe7, 0x7f, 0x92, 0xd3, 0xc3, 0x1d, 0x00,
0xf0, 0x61, 0x1a, 0x8a, 0xa4, 0xf4, 0xa9, 0x65, 0xb5, 0xd3, 0x5c, 0x6b, 0x9b, 0x13, 0x7a, 0x6b,
0x6e, 0xe5, 0xa1, 0x98, 0x6e, 0x30, 0xbb, 0x96, 0xab, 0x3a, 0x43, 0xd8, 0xf5, 0xf9, 0x2f, 0x9f,
0x56, 0x2e, 0x3d, 0xc4, 0xb8, 0xac, 0xe4, 0x51, 0xfb, 0xa7, 0x06, 0xe6, 0x9f, 0xe2, 0x2c, 0x24,
0xfe, 0x70, 0x81, 0x9b, 0xa0, 0xee, 0xe6, 0x25, 0xeb, 0x2a, 0x57, 0xbb, 0x39, 0x51, 0xed, 0xf7,
0xc6, 0x48, 0x49, 0x81, 0x85, 0xf7, 0xc1, 0x74, 0xca, 0x99, 0x65, 0xce, 0xd7, 0x27, 0xb2, 0x3c,
0x90, 0x1d, 0x97, 0x78, 0x09, 0x83, 0x7d, 0x00, 0xc5, 0x5f, 0x77, 0xb8, 0xdb, 0xda, 0xf9, 0x77,
0x7b, 0x4e, 0xc8, 0x3c, 0xab, 0x7a, 0xde, 0x03, 0xf2, 0xac, 0xeb, 0xa1, 0x44, 0xc8, 0xeb, 0xb5,
0xf3, 0x17, 0x9e, 0x15, 0x22, 0x9b, 0x28, 0xe1, 0xda, 0xf0, 0x31, 0xb8, 0x28, 0x65, 0x33, 0x4c,
0x31, 0xd3, 0xeb, 0xff, 0x38, 0xec, 0xa6, 0x40, 0x3b, 0x39, 0xf8, 0x4f, 0xd3, 0x7e, 0xaf, 0x82,
0x2b, 0xdc, 0xc4, 0xfe, 0x2e, 0x0d, 0xaa, 0x79, 0x6f, 0x81, 0x06, 0x2a, 0x0c, 0x39, 0xf3, 0x05,
0x53, 0xdc, 0x0b, 0xb3, 0xb8, 0x17, 0xe6, 0x46, 0xd2, 0xb7, 0xe7, 0x3f, 0x8f, 0x72, 0x3a, 0x15,
0x12, 0xde, 0x02, 0x73, 0x48, 0xb0, 0x77, 0x63, 0x4c, 0x29, 0x0a, 0x30, 0xd5, 0xa7, 0x96, 0xb5,
0x4e, 0xc3, 0xb9, 0x2c, 0xcf, 0x77, 0xe5, 0xf1, 0xfa, 0xd5, 0xb7, 0x1f, 0x5a, 0xca, 0x78, 0x82,
0x2f, 0xc1, 0x4c, 0xb1, 0x0c, 0xf0, 0x1e, 0x98, 0x29, 0xae, 0xa2, 0xcc, 0x69, 0x71, 0x2c, 0xa7,
0x6a, 0x73, 0xde, 0x9d, 0xb6, 0xd4, 0x1d, 0xc5, 0x29, 0x21, 0x50, 0x07, 0xd3, 0x6e, 0x44, 0xbc,
0x03, 0xca, 0xd7, 0xaf, 0xb6, 0xa3, 0x38, 0xd2, 0xb6, 0xeb, 0x40, 0xa3, 0xbd, 0xb8, 0xed, 0x83,
0x46, 0xd9, 0x3f, 0x78, 0x17, 0xd4, 0xf2, 0x87, 0x41, 0x0a, 0x2d, 0x8d, 0x09, 0x3d, 0x2f, 0x5e,
0x0d, 0xbb, 0x76, 0x24, 0x94, 0x78, 0x7c, 0xae, 0xb2, 0x8f, 0xc3, 0x60, 0x9f, 0x71, 0x15, 0x2d,
0x57, 0x11, 0x76, 0xa1, 0xf2, 0x46, 0x05, 0xf5, 0xed, 0x7c, 0x68, 0x50, 0x07, 0x17, 0xf8, 0xf4,
0x70, 0xc6, 0x55, 0x1a, 0x4e, 0x61, 0x56, 0x1e, 0xcc, 0x59, 0x4a, 0xcf, 0xc8, 0x60, 0xb4, 0xff,
0x1d, 0x8c, 0xbd, 0x7d, 0x7c, 0x66, 0xa8, 0x27, 0x67, 0x86, 0xfa, 0xe3, 0xcc, 0x50, 0x8f, 0x06,
0x86, 0x72, 0x32, 0x30, 0x94, 0xaf, 0x03, 0x43, 0x79, 0xb1, 0xf2, 0xd7, 0x5d, 0x3d, 0xac, 0xde,
0x78, 0xbe, 0xb6, 0xee, 0x34, 0x17, 0xbd, 0xf3, 0x2b, 0x00, 0x00, 0xff, 0xff, 0x5a, 0x4c, 0xb0,
0x35, 0x03, 0x06, 0x00, 0x00,
// 564 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0x3f, 0x6f, 0xd3, 0x40,
0x14, 0x8f, 0x9b, 0xa4, 0x90, 0x0b, 0x94, 0xc6, 0x14, 0xe1, 0x64, 0x70, 0xa2, 0x0e, 0x10, 0x86,
0x9c, 0x69, 0xd9, 0xca, 0x42, 0x1d, 0x20, 0x42, 0xa2, 0x12, 0x32, 0x4c, 0x2c, 0xd1, 0xd9, 0x79,
0x35, 0x27, 0x62, 0x9f, 0xe5, 0xbb, 0x40, 0xb3, 0x32, 0x31, 0x76, 0x64, 0x42, 0xcc, 0xcc, 0x7c,
0x88, 0x8a, 0xa9, 0x82, 0x85, 0x89, 0xa2, 0xe4, 0x8b, 0x20, 0xdf, 0x9d, 0x93, 0x90, 0xf0, 0x47,
0x42, 0x9d, 0xe2, 0xbb, 0xf7, 0x7e, 0xff, 0xde, 0x3b, 0x05, 0xdd, 0x08, 0x18, 0x8f, 0x18, 0x77,
0x0e, 0x01, 0xc2, 0x94, 0xc4, 0xc2, 0x79, 0xb5, 0xe3, 0x83, 0x20, 0x3b, 0xb3, 0x0b, 0x9c, 0xa4,
0x4c, 0x30, 0xf3, 0xba, 0xea, 0xc3, 0xb3, 0x6b, 0xdd, 0xd7, 0xd8, 0x0a, 0x59, 0xc8, 0x64, 0x8f,
0x93, 0x7d, 0xa9, 0xf6, 0x46, 0x3d, 0x64, 0x2c, 0x1c, 0x82, 0x23, 0x4f, 0xfe, 0xe8, 0xd0, 0x21,
0xf1, 0x38, 0x2f, 0x29, 0xa6, 0xbe, 0xc2, 0x68, 0x5a, 0x55, 0xb2, 0xb5, 0x19, 0x9f, 0x70, 0x98,
0x19, 0x09, 0x18, 0x8d, 0x75, 0xbd, 0xb9, 0xcc, 0x2a, 0x68, 0x04, 0x5c, 0x90, 0x28, 0xc9, 0x09,
0x96, 0x1b, 0x06, 0xa3, 0x94, 0x08, 0xca, 0x34, 0xc1, 0xf6, 0x57, 0x03, 0x6d, 0xb8, 0x84, 0xd3,
0x60, 0x7f, 0x38, 0x64, 0xaf, 0x49, 0x1c, 0x80, 0x39, 0x44, 0x55, 0x9e, 0x40, 0x3c, 0xe8, 0x0f,
0x69, 0x44, 0x85, 0x65, 0xb4, 0x8a, 0xed, 0xea, 0x6e, 0x1d, 0x6b, 0x5f, 0x99, 0x93, 0x3c, 0x2a,
0xee, 0x32, 0x1a, 0xbb, 0xb7, 0x4f, 0xbe, 0x37, 0x0b, 0x1f, 0xcf, 0x9a, 0xed, 0x90, 0x8a, 0x17,
0x23, 0x1f, 0x07, 0x2c, 0xd2, 0x21, 0xf4, 0x4f, 0x87, 0x0f, 0x5e, 0x3a, 0x62, 0x9c, 0x00, 0x97,
0x00, 0xee, 0x21, 0xc9, 0xff, 0x38, 0xa3, 0x37, 0xef, 0x21, 0x04, 0x47, 0x09, 0x55, 0xa6, 0xac,
0xb5, 0x96, 0xd1, 0xae, 0xee, 0x36, 0xb0, 0x72, 0x8d, 0x73, 0xd7, 0xf8, 0x59, 0x1e, 0xcb, 0x2d,
0x1d, 0x9f, 0x35, 0x0d, 0x6f, 0x01, 0xb3, 0x57, 0xfb, 0xf2, 0xa9, 0x73, 0xf9, 0x21, 0xc0, 0x2c,
0xc1, 0xa3, 0xed, 0x69, 0x11, 0xd5, 0x9e, 0x40, 0x4a, 0xd9, 0x60, 0x31, 0x58, 0x17, 0x95, 0xfd,
0x2c, 0xaa, 0x65, 0x48, 0x95, 0x9b, 0xf8, 0x0f, 0x1b, 0xc4, 0xbf, 0x0e, 0xc4, 0x2d, 0x65, 0x01,
0x3d, 0x85, 0x35, 0xef, 0xa2, 0xf5, 0x44, 0x32, 0x6b, 0xaf, 0xf5, 0x15, 0xaf, 0xf7, 0xf5, 0x84,
0xdd, 0x8b, 0x19, 0xee, 0x5d, 0x66, 0x57, 0x43, 0xcc, 0x31, 0x32, 0xd5, 0x57, 0x7f, 0x71, 0xc2,
0xc5, 0xf3, 0x9f, 0xf0, 0xa6, 0x92, 0x79, 0x3a, 0x9f, 0xf3, 0x08, 0xe9, 0xbb, 0x7e, 0x40, 0x62,
0x25, 0x6f, 0x95, 0xce, 0x5f, 0x78, 0x43, 0x89, 0x74, 0x49, 0x2c, 0xb5, 0xcd, 0x1e, 0xba, 0xa4,
0x65, 0x53, 0xe0, 0x20, 0xac, 0xf2, 0x3f, 0x17, 0x2c, 0xa7, 0x26, 0x97, 0x5c, 0x55, 0x48, 0x2f,
0x03, 0xfe, 0x6e, 0xcb, 0xef, 0x0d, 0x74, 0x55, 0x1e, 0x61, 0x70, 0xc0, 0xc3, 0xf9, 0x9e, 0x1f,
0xa0, 0x0a, 0xc9, 0x0f, 0x7a, 0xd7, 0x5b, 0x2b, 0x82, 0xfb, 0xf1, 0xd8, 0xad, 0x7d, 0x5e, 0xe6,
0xf4, 0xe6, 0x48, 0xf3, 0x16, 0xda, 0x24, 0x8a, 0xbd, 0x1f, 0x01, 0xe7, 0x24, 0x04, 0x6e, 0xad,
0xb5, 0x8a, 0xed, 0x8a, 0x77, 0x45, 0xdf, 0x1f, 0xe8, 0xeb, 0xbd, 0x6b, 0x6f, 0x3f, 0x34, 0x0b,
0xab, 0x06, 0xdf, 0x18, 0xa8, 0xdc, 0xcb, 0x5e, 0x96, 0x69, 0xa1, 0x0b, 0xf2, 0x89, 0x41, 0x2a,
0x0d, 0x55, 0xbc, 0xfc, 0x38, 0xaf, 0x80, 0x7c, 0x50, 0xb3, 0xca, 0x52, 0x8c, 0xe2, 0xff, 0xc6,
0x70, 0x7b, 0x27, 0x13, 0xdb, 0x38, 0x9d, 0xd8, 0xc6, 0x8f, 0x89, 0x6d, 0x1c, 0x4f, 0xed, 0xc2,
0xe9, 0xd4, 0x2e, 0x7c, 0x9b, 0xda, 0x85, 0xe7, 0x9d, 0xbf, 0x6e, 0xf5, 0x68, 0xfe, 0x0f, 0x28,
0x17, 0xec, 0xaf, 0x4b, 0xd1, 0x3b, 0x3f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x34, 0xef, 0x7e, 0x35,
0x21, 0x05, 0x00, 0x00,
}
func (m *BasicAllowance) Marshal() (dAtA []byte, err error) {
@ -538,16 +353,16 @@ func (m *BasicAllowance) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
{
size, err := m.Expiration.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
if m.Expiration != nil {
n1, err1 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Expiration, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Expiration):])
if err1 != nil {
return 0, err1
}
i -= size
i = encodeVarintFeegrant(dAtA, i, uint64(size))
i -= n1
i = encodeVarintFeegrant(dAtA, i, uint64(n1))
i--
dAtA[i] = 0x12
}
i--
dAtA[i] = 0x12
if len(m.SpendLimit) > 0 {
for iNdEx := len(m.SpendLimit) - 1; iNdEx >= 0; iNdEx-- {
{
@ -585,14 +400,12 @@ func (m *PeriodicAllowance) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
{
size, err := m.PeriodReset.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintFeegrant(dAtA, i, uint64(size))
n2, err2 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.PeriodReset, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.PeriodReset):])
if err2 != nil {
return 0, err2
}
i -= n2
i = encodeVarintFeegrant(dAtA, i, uint64(n2))
i--
dAtA[i] = 0x2a
if len(m.PeriodCanSpend) > 0 {
@ -623,14 +436,12 @@ func (m *PeriodicAllowance) MarshalToSizedBuffer(dAtA []byte) (int, error) {
dAtA[i] = 0x1a
}
}
{
size, err := m.Period.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintFeegrant(dAtA, i, uint64(size))
n3, err3 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.Period, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.Period):])
if err3 != nil {
return 0, err3
}
i -= n3
i = encodeVarintFeegrant(dAtA, i, uint64(n3))
i--
dAtA[i] = 0x12
{
@ -690,132 +501,6 @@ func (m *AllowedMsgAllowance) MarshalToSizedBuffer(dAtA []byte) (int, error) {
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.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 *Duration_Duration) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Duration_Duration) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
if m.Duration != nil {
n6, err6 := github_com_gogo_protobuf_types.StdDurationMarshalTo(*m.Duration, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(*m.Duration):])
if err6 != nil {
return 0, err6
}
i -= n6
i = encodeVarintFeegrant(dAtA, i, uint64(n6))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *Duration_Blocks) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Duration_Blocks) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
i = encodeVarintFeegrant(dAtA, i, uint64(m.Blocks))
i--
dAtA[i] = 0x10
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.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 *ExpiresAt_Time) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *ExpiresAt_Time) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
if m.Time != nil {
n7, err7 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Time):])
if err7 != nil {
return 0, err7
}
i -= n7
i = encodeVarintFeegrant(dAtA, i, uint64(n7))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *ExpiresAt_Height) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *ExpiresAt_Height) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
i = encodeVarintFeegrant(dAtA, i, uint64(m.Height))
i--
dAtA[i] = 0x10
return len(dAtA) - i, nil
}
func (m *Grant) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@ -888,8 +573,10 @@ func (m *BasicAllowance) Size() (n int) {
n += 1 + l + sovFeegrant(uint64(l))
}
}
l = m.Expiration.Size()
n += 1 + l + sovFeegrant(uint64(l))
if m.Expiration != nil {
l = github_com_gogo_protobuf_types.SizeOfStdTime(*m.Expiration)
n += 1 + l + sovFeegrant(uint64(l))
}
return n
}
@ -901,7 +588,7 @@ func (m *PeriodicAllowance) Size() (n int) {
_ = l
l = m.Basic.Size()
n += 1 + l + sovFeegrant(uint64(l))
l = m.Period.Size()
l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.Period)
n += 1 + l + sovFeegrant(uint64(l))
if len(m.PeriodSpendLimit) > 0 {
for _, e := range m.PeriodSpendLimit {
@ -915,7 +602,7 @@ func (m *PeriodicAllowance) Size() (n int) {
n += 1 + l + sovFeegrant(uint64(l))
}
}
l = m.PeriodReset.Size()
l = github_com_gogo_protobuf_types.SizeOfStdTime(m.PeriodReset)
n += 1 + l + sovFeegrant(uint64(l))
return n
}
@ -939,72 +626,6 @@ func (m *AllowedMsgAllowance) Size() (n int) {
return n
}
func (m *Duration) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Sum != nil {
n += m.Sum.Size()
}
return n
}
func (m *Duration_Duration) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Duration != nil {
l = github_com_gogo_protobuf_types.SizeOfStdDuration(*m.Duration)
n += 1 + l + sovFeegrant(uint64(l))
}
return n
}
func (m *Duration_Blocks) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
n += 1 + sovFeegrant(uint64(m.Blocks))
return n
}
func (m *ExpiresAt) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Sum != nil {
n += m.Sum.Size()
}
return n
}
func (m *ExpiresAt_Time) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Time != nil {
l = github_com_gogo_protobuf_types.SizeOfStdTime(*m.Time)
n += 1 + l + sovFeegrant(uint64(l))
}
return n
}
func (m *ExpiresAt_Height) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
n += 1 + sovFeegrant(uint64(m.Height))
return n
}
func (m *Grant) Size() (n int) {
if m == nil {
return 0
@ -1124,7 +745,10 @@ func (m *BasicAllowance) Unmarshal(dAtA []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := m.Expiration.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
if m.Expiration == nil {
m.Expiration = new(time.Time)
}
if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(m.Expiration, dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
@ -1240,7 +864,7 @@ func (m *PeriodicAllowance) Unmarshal(dAtA []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := m.Period.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&m.Period, dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
@ -1341,7 +965,7 @@ func (m *PeriodicAllowance) Unmarshal(dAtA []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := m.PeriodReset.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.PeriodReset, dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
@ -1484,216 +1108,6 @@ func (m *AllowedMsgAllowance) Unmarshal(dAtA []byte) error {
}
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 ErrIntOverflowFeegrant
}
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 Duration", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowFeegrant
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthFeegrant
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthFeegrant
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
v := new(time.Duration)
if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(v, dAtA[iNdEx:postIndex]); err != nil {
return err
}
m.Sum = &Duration_Duration{v}
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Blocks", wireType)
}
var v uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowFeegrant
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Sum = &Duration_Blocks{v}
default:
iNdEx = preIndex
skippy, err := skipFeegrant(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthFeegrant
}
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 ErrIntOverflowFeegrant
}
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 ErrIntOverflowFeegrant
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthFeegrant
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthFeegrant
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
v := new(time.Time)
if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(v, dAtA[iNdEx:postIndex]); err != nil {
return err
}
m.Sum = &ExpiresAt_Time{v}
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType)
}
var v int64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowFeegrant
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int64(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Sum = &ExpiresAt_Height{v}
default:
iNdEx = preIndex
skippy, err := skipFeegrant(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthFeegrant
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Grant) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0

View File

@ -1,15 +1,13 @@
package types
import (
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// FeeAllowance implementations are tied to a given fee delegator and delegatee,
// and are used to enforce fee grant limits.
type FeeAllowanceI interface {
// Accept can use fee payment requested as well as timestamp/height of the current block
// Accept can use fee payment requested as well as timestamp 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.
//
@ -21,11 +19,6 @@ type FeeAllowanceI interface {
// (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseGrantedFees)
Accept(ctx sdk.Context, fee sdk.Coins, msgs []sdk.Msg) (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) FeeAllowanceI
// ValidateBasic should evaluate this FeeAllowance for internal consistency.
// Don't allow negative amounts, or negative periods for example.
ValidateBasic() error

View File

@ -1,8 +1,6 @@
package types
import (
"time"
"github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@ -88,23 +86,6 @@ func (a *AllowedMsgAllowance) allMsgTypesAllowed(ctx sdk.Context, msgs []sdk.Msg
return true
}
// 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 *AllowedMsgAllowance) PrepareForExport(dumpTime time.Time, dumpHeight int64) FeeAllowanceI {
allowance, err := a.GetAllowance()
if err != nil {
panic("failed to get allowance")
}
f, err := NewAllowedMsgAllowance(allowance.PrepareForExport(dumpTime, dumpHeight), a.AllowedMessages)
if err != nil {
panic("failed to export filtered fee allowance")
}
return f
}
// ValidateBasic implements FeeAllowance and enforces basic sanity checks
func (a *AllowedMsgAllowance) ValidateBasic() error {
if a.Allowance == nil {

View File

@ -1,8 +1,6 @@
package types
import (
"time"
"github.com/gogo/protobuf/proto"
"github.com/cosmos/cosmos-sdk/codec/types"
@ -70,34 +68,3 @@ func (a Grant) UnpackInterfaces(unpacker types.AnyUnpacker) error {
var allowance FeeAllowanceI
return unpacker.UnpackAny(a.Allowance, &allowance)
}
// 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 Grant) PrepareForExport(dumpTime time.Time, dumpHeight int64) Grant {
f, err := a.GetGrant()
if err != nil {
return Grant{}
}
feegrant := f.PrepareForExport(dumpTime, dumpHeight)
if feegrant == nil {
return Grant{}
}
granter, err := sdk.AccAddressFromBech32(a.Granter)
if err != nil {
return Grant{}
}
grantee, err := sdk.AccAddressFromBech32(a.Grantee)
if err != nil {
return Grant{}
}
grant, err := NewGrant(granter, grantee, feegrant)
if err != nil {
return Grant{}
}
return grant
}

View File

@ -5,6 +5,7 @@ import (
"time"
"github.com/stretchr/testify/require"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -18,57 +19,56 @@ func TestGrant(t *testing.T) {
addr2, err := sdk.AccAddressFromBech32("cosmos1p9qh4ldfd6n0qehujsal4k7g0e37kel90rc4ts")
require.NoError(t, err)
atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555))
ctx := app.BaseApp.NewContext(false, tmproto.Header{
Time: time.Now(),
})
now := ctx.BlockTime()
oneYear := now.AddDate(1, 0, 0)
zeroAtoms := sdk.NewCoins(sdk.NewInt64Coin("atom", 0))
cdc := app.AppCodec()
cases := map[string]struct {
granter sdk.AccAddress
grantee sdk.AccAddress
limit sdk.Coins
expires types.ExpiresAt
valid bool
limit sdk.Coins
expires time.Time
valid bool
}{
"good": {
granter: addr2,
grantee: addr,
limit: atom,
expires: types.ExpiresAtHeight(100),
valid: true,
limit: atom,
expires: oneYear,
valid: true,
},
"no grantee": {
granter: addr2,
grantee: nil,
limit: atom,
expires: types.ExpiresAtHeight(100),
valid: false,
limit: atom,
expires: oneYear,
valid: false,
},
"no granter": {
granter: nil,
grantee: addr,
limit: atom,
expires: types.ExpiresAtHeight(100),
valid: false,
limit: atom,
expires: oneYear,
valid: false,
},
"self-grant": {
granter: addr2,
grantee: addr2,
limit: atom,
expires: types.ExpiresAtHeight(100),
valid: false,
},
"bad height": {
granter: addr2,
grantee: addr,
limit: atom,
expires: types.ExpiresAtHeight(-100),
valid: false,
limit: atom,
expires: oneYear,
valid: false,
},
"zero allowance": {
granter: addr2,
grantee: addr,
limit: zeroAtoms,
expires: types.ExpiresAtTime(time.Now().Add(3 * time.Hour)),
valid: false,
limit: zeroAtoms,
expires: oneYear,
valid: false,
},
}
@ -77,7 +77,7 @@ func TestGrant(t *testing.T) {
t.Run(name, func(t *testing.T) {
grant, err := types.NewGrant(tc.granter, tc.grantee, &types.BasicAllowance{
SpendLimit: tc.limit,
Expiration: tc.expires,
Expiration: &tc.expires,
})
require.NoError(t, err)
err = grant.ValidateBasic()
@ -97,6 +97,7 @@ func TestGrant(t *testing.T) {
err = loaded.ValidateBasic()
require.NoError(t, err)
require.Equal(t, grant, loaded)
})
}

View File

@ -1,13 +1,14 @@
package types_test
import (
"testing"
"time"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/feegrant/types"
"github.com/stretchr/testify/require"
"testing"
"time"
)
func TestMsgGrantFeeAllowance(t *testing.T) {
@ -15,43 +16,45 @@ func TestMsgGrantFeeAllowance(t *testing.T) {
addr, _ := sdk.AccAddressFromBech32("cosmos1aeuqja06474dfrj7uqsvukm6rael982kk89mqr")
addr2, _ := sdk.AccAddressFromBech32("cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl")
atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555))
threeHours := time.Now().Add(3 * time.Hour)
basic := &types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtTime(time.Now().Add(3 * time.Hour)),
Expiration: &threeHours,
}
cases := map[string]struct {
grantee sdk.AccAddress
granter sdk.AccAddress
grant *types.BasicAllowance
valid bool
grant *types.BasicAllowance
valid bool
}{
"valid":{
"valid": {
grantee: addr,
granter: addr2,
grant: basic,
valid: true,
grant: basic,
valid: true,
},
"no grantee": {
granter: addr2,
grantee: sdk.AccAddress{},
grant: basic,
valid: false,
grant: basic,
valid: false,
},
"no granter": {
granter: sdk.AccAddress{},
grantee: addr,
grant: basic,
valid: false,
grant: basic,
valid: false,
},
"grantee == granter":{
"grantee == granter": {
grantee: addr,
granter: addr,
grant: basic,
valid: false,
grant: basic,
valid: false,
},
}
for _,tc := range cases {
for _, tc := range cases {
msg, err := types.NewMsgGrantAllowance(tc.grant, tc.granter, tc.grantee)
require.NoError(t, err)
err = msg.ValidateBasic()
@ -78,43 +81,45 @@ func TestMsgRevokeFeeAllowance(t *testing.T) {
addr, _ := sdk.AccAddressFromBech32("cosmos1aeuqja06474dfrj7uqsvukm6rael982kk89mqr")
addr2, _ := sdk.AccAddressFromBech32("cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl")
atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555))
threeHours := time.Now().Add(3 * time.Hour)
basic := &types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtTime(time.Now().Add(3 * time.Hour)),
Expiration: &threeHours,
}
cases := map[string]struct {
grantee sdk.AccAddress
granter sdk.AccAddress
grant *types.BasicAllowance
valid bool
grant *types.BasicAllowance
valid bool
}{
"valid":{
"valid": {
grantee: addr,
granter: addr2,
grant: basic,
valid: true,
grant: basic,
valid: true,
},
"no grantee": {
granter: addr2,
grantee: sdk.AccAddress{},
grant: basic,
valid: false,
grant: basic,
valid: false,
},
"no granter": {
granter: sdk.AccAddress{},
grantee: addr,
grant: basic,
valid: false,
grant: basic,
valid: false,
},
"grantee == granter":{
"grantee == granter": {
grantee: addr,
granter: addr,
grant: basic,
valid: false,
grant: basic,
valid: false,
},
}
for _,tc := range cases {
for _, tc := range cases {
msg := types.NewMsgRevokeAllowance(tc.granter, tc.grantee)
err := msg.ValidateBasic()
if tc.valid {

View File

@ -9,7 +9,7 @@ import (
var _ FeeAllowanceI = (*PeriodicAllowance)(nil)
// Accept can use fee payment requested as well as timestamp/height of the current block
// Accept can use fee payment requested as well as timestamp 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.
//
@ -21,13 +21,12 @@ var _ FeeAllowanceI = (*PeriodicAllowance)(nil)
// (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseGrantedFees)
func (a *PeriodicAllowance) Accept(ctx sdk.Context, fee sdk.Coins, _ []sdk.Msg) (bool, error) {
blockTime := ctx.BlockTime()
blockHeight := ctx.BlockHeight()
if a.Basic.Expiration.IsExpired(&blockTime, blockHeight) {
if a.Basic.Expiration != nil && blockTime.After(*a.Basic.Expiration) {
return true, sdkerrors.Wrap(ErrFeeLimitExpired, "absolute limit")
}
a.tryResetPeriod(blockTime, blockHeight)
a.tryResetPeriod(blockTime)
// deduct from both the current period and the max amount
var isNeg bool
@ -54,8 +53,8 @@ func (a *PeriodicAllowance) Accept(ctx sdk.Context, fee sdk.Coins, _ []sdk.Msg)
// 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 *PeriodicAllowance) tryResetPeriod(blockTime time.Time, blockHeight int64) {
if !a.PeriodReset.Undefined() && !a.PeriodReset.IsExpired(&blockTime, blockHeight) {
func (a *PeriodicAllowance) tryResetPeriod(blockTime time.Time) {
if blockTime.Before(a.PeriodReset) {
return
}
// set CanSpend to the lesser of PeriodSpendLimit and the TotalLimit
@ -67,26 +66,9 @@ func (a *PeriodicAllowance) tryResetPeriod(blockTime time.Time, blockHeight int6
// 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 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 *PeriodicAllowance) PrepareForExport(dumpTime time.Time, dumpHeight int64) FeeAllowanceI {
return &PeriodicAllowance{
Basic: BasicAllowance{
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),
a.PeriodReset = a.PeriodReset.Add(a.Period)
if blockTime.After(a.PeriodReset) {
a.PeriodReset = blockTime.Add(a.Period)
}
}
@ -116,8 +98,9 @@ func (a PeriodicAllowance) ValidateBasic() error {
}
// check times
if err := a.Period.ValidateBasic(); err != nil {
return err
if a.Period.Seconds() < 0 {
return sdkerrors.Wrap(ErrInvalidDuration, "negative clock step")
}
return a.PeriodReset.ValidateBasic()
return nil
}

View File

@ -15,209 +15,20 @@ import (
func TestPeriodicFeeValidAllow(t *testing.T) {
app := simapp.Setup(false)
ctx := app.BaseApp.NewContext(false, tmproto.Header{
Time: time.Now(),
})
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))
eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 1))
cases := map[string]struct {
allowance types.PeriodicAllowance
// all other checks are ignored if valid=false
fee sdk.Coins
blockHeight int64
valid bool
accept bool
remove bool
remains sdk.Coins
remainsPeriod sdk.Coins
periodReset types.ExpiresAt
}{
"empty": {
allowance: types.PeriodicAllowance{},
valid: false,
},
"only basic": {
allowance: types.PeriodicAllowance{
Basic: types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtHeight(100),
},
},
valid: false,
},
"empty basic": {
allowance: types.PeriodicAllowance{
Period: types.BlockDuration(10),
PeriodSpendLimit: smallAtom,
PeriodReset: types.ExpiresAtHeight(70),
},
blockHeight: 75,
valid: true,
accept: true,
remove: false,
periodReset: types.ExpiresAtHeight(80),
},
"mismatched currencies": {
allowance: types.PeriodicAllowance{
Basic: types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtHeight(100),
},
Period: types.BlockDuration(10),
PeriodSpendLimit: eth,
},
valid: false,
},
"first time": {
allowance: types.PeriodicAllowance{
Basic: types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtHeight(100),
},
Period: types.BlockDuration(10),
PeriodSpendLimit: smallAtom,
},
valid: true,
fee: smallAtom,
blockHeight: 75,
accept: true,
remove: false,
remainsPeriod: nil,
remains: leftAtom,
periodReset: types.ExpiresAtHeight(85),
},
"same period": {
allowance: types.PeriodicAllowance{
Basic: types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtHeight(100),
},
Period: types.BlockDuration(10),
PeriodReset: types.ExpiresAtHeight(80),
PeriodSpendLimit: leftAtom,
PeriodCanSpend: smallAtom,
},
valid: true,
fee: smallAtom,
blockHeight: 75,
accept: true,
remove: false,
remainsPeriod: nil,
remains: leftAtom,
periodReset: types.ExpiresAtHeight(80),
},
"step one period": {
allowance: types.PeriodicAllowance{
Basic: types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtHeight(100),
},
Period: types.BlockDuration(10),
PeriodReset: types.ExpiresAtHeight(70),
PeriodSpendLimit: leftAtom,
},
valid: true,
fee: leftAtom,
blockHeight: 75,
accept: true,
remove: false,
remainsPeriod: nil,
remains: smallAtom,
periodReset: types.ExpiresAtHeight(80), // one step from last reset, not now
},
"step limited by global allowance": {
allowance: types.PeriodicAllowance{
Basic: types.BasicAllowance{
SpendLimit: smallAtom,
Expiration: types.ExpiresAtHeight(100),
},
Period: types.BlockDuration(10),
PeriodReset: types.ExpiresAtHeight(70),
PeriodSpendLimit: atom,
},
valid: true,
fee: oneAtom,
blockHeight: 75,
accept: true,
remove: false,
remainsPeriod: smallAtom.Sub(oneAtom),
remains: smallAtom.Sub(oneAtom),
periodReset: types.ExpiresAtHeight(80), // one step from last reset, not now
},
"expired": {
allowance: types.PeriodicAllowance{
Basic: types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtHeight(100),
},
Period: types.BlockDuration(10),
PeriodSpendLimit: smallAtom,
},
valid: true,
fee: smallAtom,
blockHeight: 101,
accept: false,
remove: true,
},
"over period limit": {
allowance: types.PeriodicAllowance{
Basic: types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtHeight(100),
},
Period: types.BlockDuration(10),
PeriodReset: types.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.allowance.ValidateBasic()
if !tc.valid {
require.Error(t, err)
return
}
require.NoError(t, err)
ctx := app.BaseApp.NewContext(false, tmproto.Header{}).WithBlockHeight(tc.blockHeight)
// now try to deduct
removed, err := tc.allowance.Accept(ctx, tc.fee, []sdk.Msg{})
if !tc.accept {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tc.remove, removed)
if !removed {
assert.Equal(t, tc.remains, tc.allowance.Basic.SpendLimit)
assert.Equal(t, tc.remainsPeriod, tc.allowance.PeriodCanSpend)
assert.Equal(t, tc.periodReset, tc.allowance.PeriodReset)
}
})
}
}
func TestPeriodicFeeValidAllowTime(t *testing.T) {
app := simapp.Setup(false)
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))
eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 1))
now := time.Now()
now := ctx.BlockTime()
oneHour := now.Add(1 * time.Hour)
twoHours := now.Add(2 * time.Hour)
tenMinutes := time.Duration(10) * time.Minute
cases := map[string]struct {
allow types.PeriodicAllowance
@ -229,7 +40,7 @@ func TestPeriodicFeeValidAllowTime(t *testing.T) {
remove bool
remains sdk.Coins
remainsPeriod sdk.Coins
periodReset types.ExpiresAt
periodReset time.Time
}{
"empty": {
allow: types.PeriodicAllowance{},
@ -239,30 +50,30 @@ func TestPeriodicFeeValidAllowTime(t *testing.T) {
allow: types.PeriodicAllowance{
Basic: types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtTime(oneHour),
Expiration: &oneHour,
},
},
valid: false,
},
"empty basic": {
allow: types.PeriodicAllowance{
Period: types.ClockDuration(time.Duration(10) * time.Minute),
Period: tenMinutes,
PeriodSpendLimit: smallAtom,
PeriodReset: types.ExpiresAtTime(now.Add(30 * time.Minute)),
PeriodReset: now.Add(30 * time.Minute),
},
blockTime: now,
valid: true,
accept: true,
remove: false,
periodReset: types.ExpiresAtTime(now.Add(30 * time.Minute)),
periodReset: now.Add(30 * time.Minute),
},
"mismatched currencies": {
allow: types.PeriodicAllowance{
Basic: types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtTime(oneHour),
Expiration: &oneHour,
},
Period: types.ClockDuration(10 * time.Minute),
Period: tenMinutes,
PeriodSpendLimit: eth,
},
valid: false,
@ -271,10 +82,10 @@ func TestPeriodicFeeValidAllowTime(t *testing.T) {
allow: types.PeriodicAllowance{
Basic: types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtTime(now.Add(2 * time.Hour)),
Expiration: &twoHours,
},
Period: types.ClockDuration(10),
PeriodReset: types.ExpiresAtTime(now.Add(1 * time.Hour)),
Period: tenMinutes,
PeriodReset: now.Add(1 * time.Hour),
PeriodSpendLimit: leftAtom,
PeriodCanSpend: smallAtom,
},
@ -285,16 +96,16 @@ func TestPeriodicFeeValidAllowTime(t *testing.T) {
remove: false,
remainsPeriod: nil,
remains: leftAtom,
periodReset: types.ExpiresAtTime(now.Add(1 * time.Hour)),
periodReset: now.Add(1 * time.Hour),
},
"step one period": {
allow: types.PeriodicAllowance{
Basic: types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtTime(now.Add(2 * time.Hour)),
Expiration: &twoHours,
},
Period: types.ClockDuration(10 * time.Minute),
PeriodReset: types.ExpiresAtTime(now),
Period: tenMinutes,
PeriodReset: now,
PeriodSpendLimit: leftAtom,
},
valid: true,
@ -304,16 +115,16 @@ func TestPeriodicFeeValidAllowTime(t *testing.T) {
remove: false,
remainsPeriod: nil,
remains: smallAtom,
periodReset: types.ExpiresAtTime(oneHour.Add(10 * time.Minute)), // one step from last reset, not now
periodReset: oneHour.Add(10 * time.Minute), // one step from last reset, not now
},
"step limited by global allowance": {
allow: types.PeriodicAllowance{
Basic: types.BasicAllowance{
SpendLimit: smallAtom,
Expiration: types.ExpiresAtTime(now.Add(2 * time.Hour)),
Expiration: &twoHours,
},
Period: types.ClockDuration(10 * time.Minute),
PeriodReset: types.ExpiresAtTime(now),
Period: tenMinutes,
PeriodReset: now,
PeriodSpendLimit: atom,
},
valid: true,
@ -323,15 +134,15 @@ func TestPeriodicFeeValidAllowTime(t *testing.T) {
remove: false,
remainsPeriod: smallAtom.Sub(oneAtom),
remains: smallAtom.Sub(oneAtom),
periodReset: types.ExpiresAtTime(oneHour.Add(10 * time.Minute)), // one step from last reset, not now
periodReset: oneHour.Add(10 * time.Minute), // one step from last reset, not now
},
"expired": {
allow: types.PeriodicAllowance{
Basic: types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtTime(now),
Expiration: &now,
},
Period: types.ClockDuration(time.Hour),
Period: time.Hour,
PeriodSpendLimit: smallAtom,
},
valid: true,
@ -344,10 +155,10 @@ func TestPeriodicFeeValidAllowTime(t *testing.T) {
allow: types.PeriodicAllowance{
Basic: types.BasicAllowance{
SpendLimit: atom,
Expiration: types.ExpiresAtHeight(100),
Expiration: &now,
},
Period: types.ClockDuration(time.Hour),
PeriodReset: types.ExpiresAtTime(now.Add(1 * time.Hour)),
Period: time.Hour,
PeriodReset: now.Add(1 * time.Hour),
PeriodSpendLimit: leftAtom,
PeriodCanSpend: smallAtom,
},