diff --git a/proto/cosmos/feegrant/v1beta1/tx.proto b/proto/cosmos/feegrant/v1beta1/tx.proto index 37e505c4e9a2..a45eec3ba368 100644 --- a/proto/cosmos/feegrant/v1beta1/tx.proto +++ b/proto/cosmos/feegrant/v1beta1/tx.proto @@ -20,6 +20,11 @@ service Msg { // RevokeAllowance revokes any fee allowance of granter's account that // has been granted to the grantee. rpc RevokeAllowance(MsgRevokeAllowance) returns (MsgRevokeAllowanceResponse); + + // PruneAllowances prunes expired fee allowances, currently up to 75 at a time. + // + // Since cosmos-sdk 0.50 + rpc PruneAllowances(MsgPruneAllowances) returns (MsgPruneAllowancesResponse); } // MsgGrantAllowance adds permission for Grantee to spend up to Allowance @@ -55,3 +60,18 @@ message MsgRevokeAllowance { // MsgRevokeAllowanceResponse defines the Msg/RevokeAllowanceResponse response type. message MsgRevokeAllowanceResponse {} + +// MsgPruneAllowances prunes expired fee allowances. +// +// Since cosmos-sdk 0.50 +message MsgPruneAllowances { + option (cosmos.msg.v1.signer) = "pruner"; + + // pruner is the address of the user pruning expired allowances. + string pruner = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; +} + +// MsgPruneAllowancesResponse defines the Msg/PruneAllowancesResponse response type. +// +// Since cosmos-sdk 0.50 +message MsgPruneAllowancesResponse {} \ No newline at end of file diff --git a/x/feegrant/CHANGELOG.md b/x/feegrant/CHANGELOG.md index 9dabfb6fa994..07efab6565ec 100644 --- a/x/feegrant/CHANGELOG.md +++ b/x/feegrant/CHANGELOG.md @@ -27,6 +27,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +* [#18047](https://github.com/cosmos/cosmos-sdk/pull/18047) Added a limit of 200 grants pruned per EndBlock and the method PruneAllowances that prunes 75 expired grants on every run. * [#14649](https://github.com/cosmos/cosmos-sdk/pull/14649) The `x/feegrant` module is extracted to have a separate go.mod file which allows it to be a standalone module. ### API Breaking Changes @@ -34,3 +35,4 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [#15606](https://github.com/cosmos/cosmos-sdk/pull/15606) `NewKeeper` now takes a `KVStoreService` instead of a `StoreKey` and methods in the `Keeper` now take a `context.Context` instead of a `sdk.Context`. * [#15347](https://github.com/cosmos/cosmos-sdk/pull/15347) Remove global bech32 usage in keeper. * [#15347](https://github.com/cosmos/cosmos-sdk/pull/15347) `ValidateBasic` is treated as a no op now with with acceptance of RFC001 +* [#17869](https://github.com/cosmos/cosmos-sdk/pull/17869) `NewGrant`, `NewMsgGrantAllowance` & `NewMsgRevokeAllowance` takes strings instead of `sdk.AccAddress` diff --git a/x/feegrant/README.md b/x/feegrant/README.md index 9fcd1e476d6f..07524449a862 100644 --- a/x/feegrant/README.md +++ b/x/feegrant/README.md @@ -206,6 +206,14 @@ The feegrant module emits the following events: | message | granter | {granterAddress} | | message | grantee | {granteeAddress} | +### Prune fee allowances + +| Type | Attribute Key | Attribute Value | +| ------- | ------------- | ---------------- | +| message | action | prune_feegrant | +| message | pruner | {prunerAddress} | + + ## Client ### CLI diff --git a/x/feegrant/basic_fee_test.go b/x/feegrant/basic_fee_test.go index 00e5ce5cc367..46e34704c6e0 100644 --- a/x/feegrant/basic_fee_test.go +++ b/x/feegrant/basic_fee_test.go @@ -27,9 +27,7 @@ func TestBasicFeeValidAllow(t *testing.T) { } require.Error(t, allowace.ValidateBasic()) - ctx = ctx.WithBlockHeader(cmtproto.Header{ - Time: time.Now(), - }) + ctx = ctx.WithBlockHeader(cmtproto.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)) diff --git a/x/feegrant/client/cli/tx.go b/x/feegrant/client/cli/tx.go index 9ff9ed96b1bc..9f25df680c13 100644 --- a/x/feegrant/client/cli/tx.go +++ b/x/feegrant/client/cli/tx.go @@ -75,12 +75,17 @@ Examples: return err } - grantee, err := ac.StringToBytes(args[1]) + grantee := args[1] + _, err = ac.StringToBytes(grantee) if err != nil { return err } granter := clientCtx.GetFromAddress() + granterStr, err := ac.BytesToString(granter) + if err != nil { + return err + } sl, err := cmd.Flags().GetString(FlagSpendLimit) if err != nil { return err @@ -168,7 +173,7 @@ Examples: } } - msg, err := feegrant.NewMsgGrantAllowance(grant, granter, grantee) + msg, err := feegrant.NewMsgGrantAllowance(grant, granterStr, grantee) if err != nil { return err } diff --git a/x/feegrant/client/cli/tx_test.go b/x/feegrant/client/cli/tx_test.go index dc9b8d2abfb9..d9cdf6ad63ef 100644 --- a/x/feegrant/client/cli/tx_test.go +++ b/x/feegrant/client/cli/tx_test.go @@ -93,7 +93,13 @@ func (s *CLITestSuite) SetupSuite() { s.createGrant(granter, grantee) - grant, err := feegrant.NewGrant(granter, grantee, &feegrant.BasicAllowance{ + ac := codecaddress.NewBech32Codec("cosmos") + granteeStr, err := ac.BytesToString(grantee) + s.Require().NoError(err) + granterStr, err := ac.BytesToString(granter) + s.Require().NoError(err) + + grant, err := feegrant.NewGrant(granterStr, granteeStr, &feegrant.BasicAllowance{ SpendLimit: sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(100))), }) s.Require().NoError(err) diff --git a/x/feegrant/events.go b/x/feegrant/events.go index d5f047612370..70cb6990f0cd 100644 --- a/x/feegrant/events.go +++ b/x/feegrant/events.go @@ -6,7 +6,9 @@ const ( EventTypeRevokeFeeGrant = "revoke_feegrant" EventTypeSetFeeGrant = "set_feegrant" EventTypeUpdateFeeGrant = "update_feegrant" + EventTypePruneFeeGrant = "prune_feegrant" AttributeKeyGranter = "granter" AttributeKeyGrantee = "grantee" + AttributeKeyPruner = "pruner" ) diff --git a/x/feegrant/filtered_fee_test.go b/x/feegrant/filtered_fee_test.go index 4e864fde74f8..9a2d0f43e1f8 100644 --- a/x/feegrant/filtered_fee_test.go +++ b/x/feegrant/filtered_fee_test.go @@ -12,6 +12,7 @@ import ( "cosmossdk.io/x/feegrant" "cosmossdk.io/x/feegrant/module" + addresscodec "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" @@ -33,6 +34,8 @@ func TestFilteredFeeValidAllow(t *testing.T) { now := ctx.BlockTime() oneHour := now.Add(1 * time.Hour) + ac := addresscodec.NewBech32Codec("cosmos") + // msg we will call in the all cases call := banktypes.MsgSend{} cases := map[string]struct { @@ -146,7 +149,9 @@ func TestFilteredFeeValidAllow(t *testing.T) { var granter, grantee sdk.AccAddress allowance, err := feegrant.NewAllowedMsgAllowance(tc.allowance, tc.msgs) require.NoError(t, err) - grant, err := feegrant.NewGrant(granter, grantee, allowance) + granterStr, err := ac.BytesToString(granter) + require.NoError(t, err) + granteeStr, err := ac.BytesToString(grantee) require.NoError(t, err) // now try to deduct @@ -166,8 +171,8 @@ func TestFilteredFeeValidAllow(t *testing.T) { // create a new updated grant newGrant, err := feegrant.NewGrant( - sdk.AccAddress(grant.Granter), - sdk.AccAddress(grant.Grantee), + granterStr, + granteeStr, allowance) require.NoError(t, err) diff --git a/x/feegrant/go.mod b/x/feegrant/go.mod index 2a8bb4710ee0..b060b5b3724c 100644 --- a/x/feegrant/go.mod +++ b/x/feegrant/go.mod @@ -4,6 +4,7 @@ go 1.21 require ( cosmossdk.io/api v0.7.2 + cosmossdk.io/collections v0.4.0 cosmossdk.io/core v0.11.0 cosmossdk.io/depinject v1.0.0-alpha.4 cosmossdk.io/errors v1.0.0 @@ -26,7 +27,6 @@ require ( ) require ( - cosmossdk.io/collections v0.4.0 // indirect cosmossdk.io/x/tx v0.11.0 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect @@ -41,10 +41,10 @@ require ( github.com/chzyer/readline v1.5.1 // indirect github.com/cockroachdb/errors v1.11.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect - github.com/cockroachdb/pebble v0.0.0-20230817233644-564b068800e0 // indirect + github.com/cockroachdb/pebble v0.0.0-20230824192853-9bb0864bdb98 // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect - github.com/cometbft/cometbft-db v0.7.0 // indirect + github.com/cometbft/cometbft-db v0.8.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/cosmos-db v1.0.0 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect @@ -53,7 +53,7 @@ require ( github.com/cosmos/ics23/go v0.10.0 // indirect github.com/cosmos/ledger-cosmos-go v0.13.2 // indirect github.com/danieljoos/wincred v1.1.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v2 v2.2007.4 // indirect @@ -61,7 +61,7 @@ require ( github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.5.0 // indirect - github.com/emicklei/dot v1.5.0 // indirect + github.com/emicklei/dot v1.6.0 // indirect github.com/fatih/color v1.15.0 // indirect github.com/felixge/httpsnoop v1.0.2 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect @@ -85,7 +85,7 @@ require ( github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-metrics v0.5.1 // indirect - github.com/hashicorp/go-plugin v1.4.10 // indirect + github.com/hashicorp/go-plugin v1.5.2 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect @@ -95,7 +95,7 @@ require ( github.com/improbable-eng/grpc-web v0.15.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/compress v1.17.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/lib/pq v1.10.7 // indirect @@ -112,36 +112,38 @@ require ( github.com/mtibben/percent v0.2.1 // indirect github.com/oasisprotocol/curve25519-voi v0.0.0-20230110094441-db37f07504ce // indirect github.com/oklog/run v1.1.0 // indirect - github.com/pelletier/go-toml/v2 v2.0.8 // indirect - github.com/petermattis/goid v0.0.0-20230518223814-80aa455d8761 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/petermattis/goid v0.0.0-20230808133559-b036b712a89b // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.16.0 // indirect - github.com/prometheus/client_model v0.4.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.17.0 // indirect + github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.11.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/rs/cors v1.8.3 // indirect - github.com/rs/zerolog v1.30.0 // indirect + github.com/rs/zerolog v1.31.0 // indirect + github.com/sagikazarmark/locafero v0.3.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect - github.com/spf13/afero v1.9.5 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.10.0 // indirect github.com/spf13/cast v1.5.1 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.16.0 // indirect - github.com/subosito/gotenv v1.4.2 // indirect + github.com/spf13/viper v1.17.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect - github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect github.com/tendermint/go-amino v0.16.0 // indirect - github.com/tidwall/btree v1.6.0 // indirect + github.com/tidwall/btree v1.7.0 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect go.etcd.io/bbolt v1.3.7 // indirect + go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.14.0 // indirect - golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/net v0.17.0 // indirect - golang.org/x/sync v0.3.0 // indirect + golang.org/x/sync v0.4.0 // indirect golang.org/x/sys v0.13.0 // indirect golang.org/x/term v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect diff --git a/x/feegrant/go.sum b/x/feegrant/go.sum index 9fd5ae62a4cd..d338dc2d957d 100644 --- a/x/feegrant/go.sum +++ b/x/feegrant/go.sum @@ -157,8 +157,8 @@ github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZ github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v0.0.0-20230817233644-564b068800e0 h1:M4A5LioEhkZ/s+m0g0pWgiLBQr83p0jWnQUo320Qy+A= -github.com/cockroachdb/pebble v0.0.0-20230817233644-564b068800e0/go.mod h1:EDjiaAXc0FXiRmxDzcu1wIEJ093ohHMUWxrI6iku0XA= +github.com/cockroachdb/pebble v0.0.0-20230824192853-9bb0864bdb98 h1:Y7g+YeGJ+1Ni31uOplgf7mi+1X+Em5PzIx9WMPq/2zY= +github.com/cockroachdb/pebble v0.0.0-20230824192853-9bb0864bdb98/go.mod h1:EDjiaAXc0FXiRmxDzcu1wIEJ093ohHMUWxrI6iku0XA= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= @@ -166,8 +166,8 @@ github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1: github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/cometbft/cometbft v0.38.0 h1:ogKnpiPX7gxCvqTEF4ly25/wAxUqf181t30P3vqdpdc= github.com/cometbft/cometbft v0.38.0/go.mod h1:5Jz0Z8YsHSf0ZaAqGvi/ifioSdVFPtEGrm8Y9T/993k= -github.com/cometbft/cometbft-db v0.7.0 h1:uBjbrBx4QzU0zOEnU8KxoDl18dMNgDh+zZRUE0ucsbo= -github.com/cometbft/cometbft-db v0.7.0/go.mod h1:yiKJIm2WKrt6x8Cyxtq9YTEcIMPcEe4XPxhgX59Fzf0= +github.com/cometbft/cometbft-db v0.8.0 h1:vUMDaH3ApkX8m0KZvOFFy9b5DZHBAjsnEuo9AKVZpjo= +github.com/cometbft/cometbft-db v0.8.0/go.mod h1:6ASCP4pfhmrCBpfk01/9E1SI29nD3HfVHrY4PG8x5c0= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -205,8 +205,9 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= @@ -236,8 +237,8 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/emicklei/dot v1.5.0 h1:tc9eKdCBTgoR68vJ6OcgMtI0SdrGDwLPPVaPA6XhX50= -github.com/emicklei/dot v1.5.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/emicklei/dot v1.6.0 h1:vUzuoVE8ipzS7QkES4UfxdpCwdU2U97m2Pb2tQCoYRY= +github.com/emicklei/dot v1.6.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -247,12 +248,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= -github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= -github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= -github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= -github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= -github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= @@ -449,8 +444,8 @@ github.com/hashicorp/go-metrics v0.5.1 h1:rfPwUqFU6uZXNvGl4hzjY8LEBsqFVU4si1H9/H github.com/hashicorp/go-metrics v0.5.1/go.mod h1:KEjodfebIOuBYSAe/bHTm+HChmKSxAOXPBieMLYozDE= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-plugin v1.4.10 h1:xUbmA4jC6Dq163/fWcp8P3JuHilrHHMLNRxzGQJ9hNk= -github.com/hashicorp/go-plugin v1.4.10/go.mod h1:6/1TEzT0eQznvI/gV2CM29DLSkAK/e58mUWKVsPaph0= +github.com/hashicorp/go-plugin v1.5.2 h1:aWv8eimFqWlsEiMrYZdPYl+FdHaBJSN4AWwGWfT1G2Y= +github.com/hashicorp/go-plugin v1.5.2/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= @@ -516,8 +511,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -645,12 +640,12 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= -github.com/petermattis/goid v0.0.0-20230518223814-80aa455d8761 h1:W04oB3d0J01W5jgYRGKsV8LCM6g9EkCvPkZcmFuy0OE= -github.com/petermattis/goid v0.0.0-20230518223814-80aa455d8761/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/petermattis/goid v0.0.0-20230808133559-b036b712a89b h1:vab8deKC4QoIfm9fJM59iuNz1ELGsuLoYYpiF+pHiG8= +github.com/petermattis/goid v0.0.0-20230808133559-b036b712a89b/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= @@ -662,8 +657,9 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= @@ -671,16 +667,16 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= -github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= @@ -710,12 +706,16 @@ github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo= github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= -github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w= +github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= +github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ= +github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= @@ -731,12 +731,14 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= @@ -745,15 +747,13 @@ github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tL github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= -github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= +github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -771,19 +771,16 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= -github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= -github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= -github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= -github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= -github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= +github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= @@ -826,6 +823,8 @@ go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= @@ -855,8 +854,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -881,8 +880,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -952,8 +951,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1029,6 +1028,7 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1109,8 +1109,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E= -golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/x/feegrant/grant.go b/x/feegrant/grant.go index 56029b9ae21c..0fb85dd5a226 100644 --- a/x/feegrant/grant.go +++ b/x/feegrant/grant.go @@ -6,14 +6,13 @@ import ( errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/codec/types" - sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) var _ types.UnpackInterfacesMessage = &Grant{} // NewGrant creates a new FeeAllowanceGrant. -func NewGrant(granter, grantee sdk.AccAddress, feeAllowance FeeAllowanceI) (Grant, error) { +func NewGrant(granter, grantee string, feeAllowance FeeAllowanceI) (Grant, error) { msg, ok := feeAllowance.(proto.Message) if !ok { return Grant{}, errorsmod.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", feeAllowance) @@ -25,8 +24,8 @@ func NewGrant(granter, grantee sdk.AccAddress, feeAllowance FeeAllowanceI) (Gran } return Grant{ - Granter: granter.String(), - Grantee: grantee.String(), + Granter: granter, + Grantee: grantee, Allowance: any, }, nil } diff --git a/x/feegrant/grant_test.go b/x/feegrant/grant_test.go index 31b9fb62542f..d03a751031cb 100644 --- a/x/feegrant/grant_test.go +++ b/x/feegrant/grant_test.go @@ -82,7 +82,11 @@ func TestGrant(t *testing.T) { for name, tc := range cases { tc := tc t.Run(name, func(t *testing.T) { - grant, err := feegrant.NewGrant(tc.granter, tc.grantee, &feegrant.BasicAllowance{ + granterStr, err := addressCodec.BytesToString(tc.granter) + require.NoError(t, err) + granteeStr, err := addressCodec.BytesToString(tc.grantee) + require.NoError(t, err) + grant, err := feegrant.NewGrant(granterStr, granteeStr, &feegrant.BasicAllowance{ SpendLimit: tc.limit, Expiration: &tc.expires, }) diff --git a/x/feegrant/keeper/genesis_test.go b/x/feegrant/keeper/genesis_test.go index 776a51e2872b..90105db77add 100644 --- a/x/feegrant/keeper/genesis_test.go +++ b/x/feegrant/keeper/genesis_test.go @@ -39,6 +39,7 @@ type genesisFixture struct { } func initFixture(t *testing.T) *genesisFixture { + t.Helper() key := storetypes.NewKVStoreKey(feegrant.StoreKey) testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test")) encCfg := moduletestutil.MakeTestEncodingConfig(module.AppModuleBasic{}) @@ -58,7 +59,6 @@ func TestImportExportGenesis(t *testing.T) { f := initFixture(t) f.accountKeeper.EXPECT().GetAccount(gomock.Any(), granteeAddr).Return(authtypes.NewBaseAccountWithAddress(granteeAddr)).AnyTimes() - f.accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec("cosmos")).AnyTimes() coins := sdk.NewCoins(sdk.NewCoin("foo", math.NewInt(1_000))) now := f.ctx.BlockHeader().Time @@ -72,10 +72,15 @@ func TestImportExportGenesis(t *testing.T) { genesis, err := f.feegrantKeeper.ExportGenesis(f.ctx) assert.NilError(t, err) + granter, err := f.accountKeeper.AddressCodec().BytesToString(granterAddr.Bytes()) + assert.NilError(t, err) + grantee, err := f.accountKeeper.AddressCodec().BytesToString(granteeAddr.Bytes()) + assert.NilError(t, err) + // revoke fee allowance _, err = msgSrvr.RevokeAllowance(f.ctx, &feegrant.MsgRevokeAllowance{ - Granter: granterAddr.String(), - Grantee: granteeAddr.String(), + Granter: granter, + Grantee: grantee, }) assert.NilError(t, err) @@ -91,6 +96,13 @@ func TestInitGenesis(t *testing.T) { any, err := codectypes.NewAnyWithValue(&testdata.Dog{}) assert.NilError(t, err) + ac := address.NewBech32Codec("cosmos") + + granter, err := ac.BytesToString(granterAddr.Bytes()) + assert.NilError(t, err) + grantee, err := ac.BytesToString(granteeAddr.Bytes()) + assert.NilError(t, err) + testCases := []struct { name string feeAllowances []feegrant.Grant @@ -101,7 +113,7 @@ func TestInitGenesis(t *testing.T) { []feegrant.Grant{ { Granter: "invalid granter", - Grantee: granteeAddr.String(), + Grantee: grantee, }, }, true, @@ -110,7 +122,7 @@ func TestInitGenesis(t *testing.T) { "invalid grantee", []feegrant.Grant{ { - Granter: granterAddr.String(), + Granter: granter, Grantee: "invalid grantee", }, }, @@ -120,8 +132,8 @@ func TestInitGenesis(t *testing.T) { "invalid allowance", []feegrant.Grant{ { - Granter: granterAddr.String(), - Grantee: granteeAddr.String(), + Granter: granter, + Grantee: grantee, Allowance: any, }, }, diff --git a/x/feegrant/keeper/grpc_query.go b/x/feegrant/keeper/grpc_query.go index 7dde41517154..0bc3db1839cc 100644 --- a/x/feegrant/keeper/grpc_query.go +++ b/x/feegrant/keeper/grpc_query.go @@ -1,18 +1,16 @@ package keeper import ( - "bytes" "context" "github.com/cosmos/gogoproto/proto" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "cosmossdk.io/store/prefix" + "cosmossdk.io/collections" "cosmossdk.io/x/feegrant" codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/runtime" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" ) @@ -20,7 +18,7 @@ import ( var _ feegrant.QueryServer = Keeper{} // Allowance returns granted allowance to the grantee by the granter. -func (q Keeper) Allowance(c context.Context, req *feegrant.QueryAllowanceRequest) (*feegrant.QueryAllowanceResponse, error) { +func (q Keeper) Allowance(ctx context.Context, req *feegrant.QueryAllowanceRequest) (*feegrant.QueryAllowanceResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "invalid request") } @@ -35,8 +33,6 @@ func (q Keeper) Allowance(c context.Context, req *feegrant.QueryAllowanceRequest return nil, err } - ctx := sdk.UnwrapSDKContext(c) - feeAllowance, err := q.GetAllowance(ctx, granterAddr, granteeAddr) if err != nil { return nil, status.Errorf(codes.Internal, err.Error()) @@ -72,23 +68,16 @@ func (q Keeper) Allowances(c context.Context, req *feegrant.QueryAllowancesReque return nil, err } - ctx := sdk.UnwrapSDKContext(c) - var grants []*feegrant.Grant - store := q.storeService.OpenKVStore(ctx) - grantsStore := prefix.NewStore(runtime.KVStoreAdapter(store), feegrant.FeeAllowancePrefixByGrantee(granteeAddr)) - - pageRes, err := query.Paginate(grantsStore, req.Pagination, func(key, value []byte) error { - var grant feegrant.Grant - - if err := q.cdc.Unmarshal(value, &grant); err != nil { - return err - } - - grants = append(grants, &grant) - return nil - }) + _, pageRes, err := query.CollectionFilteredPaginate(c, q.FeeAllowance, req.Pagination, + func(key collections.Pair[sdk.AccAddress, sdk.AccAddress], grant feegrant.Grant) (include bool, err error) { + grants = append(grants, &grant) + return true, nil + }, func(_ collections.Pair[sdk.AccAddress, sdk.AccAddress], value feegrant.Grant) (*feegrant.Grant, error) { + return &value, nil + }, query.WithCollectionPaginationPairPrefix[sdk.AccAddress, sdk.AccAddress](granteeAddr), + ) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } @@ -107,21 +96,20 @@ func (q Keeper) AllowancesByGranter(c context.Context, req *feegrant.QueryAllowa return nil, err } - ctx := sdk.UnwrapSDKContext(c) - - store := q.storeService.OpenKVStore(ctx) - prefixStore := prefix.NewStore(runtime.KVStoreAdapter(store), feegrant.FeeAllowanceKeyPrefix) - grants, pageRes, err := query.GenericFilteredPaginate(q.cdc, prefixStore, req.Pagination, func(key []byte, grant *feegrant.Grant) (*feegrant.Grant, error) { - // ParseAddressesFromFeeAllowanceKey expects the full key including the prefix. - granter, _ := feegrant.ParseAddressesFromFeeAllowanceKey(append(feegrant.FeeAllowanceKeyPrefix, key...)) - if !bytes.Equal(granter, granterAddr) { - return nil, nil - } - - return grant, nil - }, func() *feegrant.Grant { - return &feegrant.Grant{} - }) + var grants []*feegrant.Grant + _, pageRes, err := query.CollectionFilteredPaginate(c, q.FeeAllowance, req.Pagination, + func(key collections.Pair[sdk.AccAddress, sdk.AccAddress], grant feegrant.Grant) (include bool, err error) { + if !sdk.AccAddress(granterAddr).Equals(key.K2()) { + return false, nil + } + + grants = append(grants, &grant) + return true, nil + }, + func(_ collections.Pair[sdk.AccAddress, sdk.AccAddress], grant feegrant.Grant) (*feegrant.Grant, error) { + return &grant, nil + }, + ) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } diff --git a/x/feegrant/keeper/grpc_query_test.go b/x/feegrant/keeper/grpc_query_test.go index 25afdd1f3d94..225edf7ac678 100644 --- a/x/feegrant/keeper/grpc_query_test.go +++ b/x/feegrant/keeper/grpc_query_test.go @@ -30,7 +30,7 @@ func (suite *KeeperTestSuite) TestFeeAllowance() { "fail: invalid granter", &feegrant.QueryAllowanceRequest{ Granter: invalidGranter, - Grantee: suite.addrs[0].String(), + Grantee: suite.encodedAddrs[0], }, true, func() {}, @@ -39,7 +39,7 @@ func (suite *KeeperTestSuite) TestFeeAllowance() { { "fail: invalid grantee", &feegrant.QueryAllowanceRequest{ - Granter: suite.addrs[0].String(), + Granter: suite.encodedAddrs[0], Grantee: invalidGrantee, }, true, @@ -49,8 +49,8 @@ func (suite *KeeperTestSuite) TestFeeAllowance() { { "fail: no grants", &feegrant.QueryAllowanceRequest{ - Granter: suite.addrs[0].String(), - Grantee: suite.addrs[1].String(), + Granter: suite.encodedAddrs[0], + Grantee: suite.encodedAddrs[1], }, true, func() {}, @@ -69,16 +69,16 @@ func (suite *KeeperTestSuite) TestFeeAllowance() { { "valid query: expect single grant", &feegrant.QueryAllowanceRequest{ - Granter: suite.addrs[0].String(), - Grantee: suite.addrs[1].String(), + Granter: suite.encodedAddrs[0], + Grantee: suite.encodedAddrs[1], }, false, func() { suite.grantFeeAllowance(suite.addrs[0], suite.addrs[1]) }, func(response *feegrant.QueryAllowanceResponse) { - suite.Require().Equal(response.Allowance.Granter, suite.addrs[0].String()) - suite.Require().Equal(response.Allowance.Grantee, suite.addrs[1].String()) + suite.Require().Equal(response.Allowance.Granter, suite.encodedAddrs[0]) + suite.Require().Equal(response.Allowance.Grantee, suite.encodedAddrs[1]) }, }, } @@ -124,7 +124,7 @@ func (suite *KeeperTestSuite) TestFeeAllowances() { { "no grants", &feegrant.QueryAllowancesRequest{ - Grantee: suite.addrs[1].String(), + Grantee: suite.encodedAddrs[1], }, false, func() {}, @@ -135,7 +135,7 @@ func (suite *KeeperTestSuite) TestFeeAllowances() { { "valid query: expect single grant", &feegrant.QueryAllowancesRequest{ - Grantee: suite.addrs[1].String(), + Grantee: suite.encodedAddrs[1], }, false, func() { @@ -143,8 +143,8 @@ func (suite *KeeperTestSuite) TestFeeAllowances() { }, func(resp *feegrant.QueryAllowancesResponse) { suite.Require().Equal(len(resp.Allowances), 1) - suite.Require().Equal(resp.Allowances[0].Granter, suite.addrs[0].String()) - suite.Require().Equal(resp.Allowances[0].Grantee, suite.addrs[1].String()) + suite.Require().Equal(resp.Allowances[0].Granter, suite.encodedAddrs[0]) + suite.Require().Equal(resp.Allowances[0].Grantee, suite.encodedAddrs[1]) }, }, } @@ -190,7 +190,7 @@ func (suite *KeeperTestSuite) TestFeeAllowancesByGranter() { { "no grants", &feegrant.QueryAllowancesByGranterRequest{ - Granter: suite.addrs[0].String(), + Granter: suite.encodedAddrs[0], }, false, func() {}, @@ -201,7 +201,7 @@ func (suite *KeeperTestSuite) TestFeeAllowancesByGranter() { { "valid query: expect single grant", &feegrant.QueryAllowancesByGranterRequest{ - Granter: suite.addrs[0].String(), + Granter: suite.encodedAddrs[0], }, false, func() { @@ -212,8 +212,8 @@ func (suite *KeeperTestSuite) TestFeeAllowancesByGranter() { }, func(resp *feegrant.QueryAllowancesByGranterResponse) { suite.Require().Equal(len(resp.Allowances), 1) - suite.Require().Equal(resp.Allowances[0].Granter, suite.addrs[0].String()) - suite.Require().Equal(resp.Allowances[0].Grantee, suite.addrs[1].String()) + suite.Require().Equal(resp.Allowances[0].Granter, suite.encodedAddrs[0]) + suite.Require().Equal(resp.Allowances[0].Grantee, suite.encodedAddrs[1]) suite.Require().Equal(resp.Pagination.Total, uint64(1)) }, }, diff --git a/x/feegrant/keeper/keeper.go b/x/feegrant/keeper/keeper.go index aa491665366d..1bb0c9dd264f 100644 --- a/x/feegrant/keeper/keeper.go +++ b/x/feegrant/keeper/keeper.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "cosmossdk.io/collections" "cosmossdk.io/core/store" errorsmod "cosmossdk.io/errors" "cosmossdk.io/log" @@ -24,16 +25,37 @@ type Keeper struct { cdc codec.BinaryCodec storeService store.KVStoreService authKeeper feegrant.AccountKeeper + Schema collections.Schema + // FeeAllowance key: grantee+granter | value: Grant + FeeAllowance collections.Map[collections.Pair[sdk.AccAddress, sdk.AccAddress], feegrant.Grant] + // FeeAllowanceQueue key: expiration time+grantee+granter | value: bool + FeeAllowanceQueue collections.Map[collections.Triple[time.Time, sdk.AccAddress, sdk.AccAddress], bool] } var _ ante.FeegrantKeeper = &Keeper{} // NewKeeper creates a feegrant Keeper func NewKeeper(cdc codec.BinaryCodec, storeService store.KVStoreService, ak feegrant.AccountKeeper) Keeper { + sb := collections.NewSchemaBuilder(storeService) + return Keeper{ cdc: cdc, storeService: storeService, authKeeper: ak, + FeeAllowance: collections.NewMap( + sb, + feegrant.FeeAllowanceKeyPrefix, + "allowances", + collections.PairKeyCodec(sdk.LengthPrefixedAddressKey(sdk.AccAddressKey), sdk.LengthPrefixedAddressKey(sdk.AccAddressKey)), //nolint: staticcheck // sdk.LengthPrefixedAddressKey is needed to retain state compatibility + codec.CollValue[feegrant.Grant](cdc), + ), + FeeAllowanceQueue: collections.NewMap( + sb, + feegrant.FeeAllowanceQueueKeyPrefix, + "allowances_queue", + collections.TripleKeyCodec(sdk.TimeKey, sdk.LengthPrefixedAddressKey(sdk.AccAddressKey), sdk.LengthPrefixedAddressKey(sdk.AccAddressKey)), //nolint: staticcheck // sdk.LengthPrefixedAddressKey is needed to retain state compatibility + collections.BoolValue, + ), } } @@ -56,9 +78,6 @@ func (k Keeper) GrantAllowance(ctx context.Context, granter, grantee sdk.AccAddr k.authKeeper.SetAccount(ctx, granteeAcc) } - store := k.storeService.OpenKVStore(ctx) - key := feegrant.FeeAllowanceKey(granter, grantee) - exp, err := feeAllowance.ExpiresAt() if err != nil { return err @@ -66,35 +85,36 @@ func (k Keeper) GrantAllowance(ctx context.Context, granter, grantee sdk.AccAddr // expiration shouldn't be in the past. sdkCtx := sdk.UnwrapSDKContext(ctx) - if exp != nil && exp.Before(sdkCtx.BlockTime()) { + if exp != nil && exp.Before(sdkCtx.HeaderInfo().Time) { return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "expiration is before current block time") } // if expiry is not nil, add the new key to pruning queue. if exp != nil { - // `key` formed here with the prefix of `FeeAllowanceKeyPrefix` (which is `0x00`) - // remove the 1st byte and reuse the remaining key as it is - err = k.addToFeeAllowanceQueue(ctx, key[1:], exp) + err = k.FeeAllowanceQueue.Set(ctx, collections.Join3(*exp, grantee, granter), true) if err != nil { return err } } - grant, err := feegrant.NewGrant(granter, grantee, feeAllowance) + granterStr, err := k.authKeeper.AddressCodec().BytesToString(granter) if err != nil { return err } - - bz, err := k.cdc.Marshal(&grant) + granteeStr, err := k.authKeeper.AddressCodec().BytesToString(grantee) if err != nil { return err } - err = store.Set(key, bz) + grant, err := feegrant.NewGrant(granterStr, granteeStr, feeAllowance) if err != nil { return err } + if err := k.FeeAllowance.Set(ctx, collections.Join(grantee, granter), grant); err != nil { + return err + } + sdkCtx.EventManager().EmitEvent( sdk.NewEvent( feegrant.EventTypeSetFeeGrant, @@ -108,29 +128,29 @@ func (k Keeper) GrantAllowance(ctx context.Context, granter, grantee sdk.AccAddr // UpdateAllowance updates the existing grant. func (k Keeper) UpdateAllowance(ctx context.Context, granter, grantee sdk.AccAddress, feeAllowance feegrant.FeeAllowanceI) error { - store := k.storeService.OpenKVStore(ctx) - key := feegrant.FeeAllowanceKey(granter, grantee) - - _, err := k.getGrant(ctx, granter, grantee) + _, err := k.GetAllowance(ctx, granter, grantee) if err != nil { return err } - grant, err := feegrant.NewGrant(granter, grantee, feeAllowance) + granterStr, err := k.authKeeper.AddressCodec().BytesToString(granter) if err != nil { return err } - - bz, err := k.cdc.Marshal(&grant) + granteeStr, err := k.authKeeper.AddressCodec().BytesToString(grantee) if err != nil { return err } - err = store.Set(key, bz) + grant, err := feegrant.NewGrant(granterStr, granteeStr, feeAllowance) if err != nil { return err } + if err := k.FeeAllowance.Set(ctx, collections.Join(grantee, granter), grant); err != nil { + return err + } + sdk.UnwrapSDKContext(ctx).EventManager().EmitEvent( sdk.NewEvent( feegrant.EventTypeUpdateFeeGrant, @@ -149,10 +169,7 @@ func (k Keeper) revokeAllowance(ctx context.Context, granter, grantee sdk.AccAdd return err } - store := k.storeService.OpenKVStore(ctx) - key := feegrant.FeeAllowanceKey(granter, grantee) - err = store.Delete(key) - if err != nil { + if err := k.FeeAllowance.Remove(ctx, collections.Join(grantee, granter)); err != nil { return err } @@ -162,16 +179,25 @@ func (k Keeper) revokeAllowance(ctx context.Context, granter, grantee sdk.AccAdd } if exp != nil { - if err := store.Delete(feegrant.FeeAllowancePrefixQueue(exp, feegrant.FeeAllowanceKey(grantee, granter)[1:])); err != nil { + if err := k.FeeAllowanceQueue.Remove(ctx, collections.Join3(*exp, grantee, granter)); err != nil { return err } } + granterStr, err := k.authKeeper.AddressCodec().BytesToString(granter) + if err != nil { + return err + } + granteeStr, err := k.authKeeper.AddressCodec().BytesToString(grantee) + if err != nil { + return err + } + sdk.UnwrapSDKContext(ctx).EventManager().EmitEvent( sdk.NewEvent( feegrant.EventTypeRevokeFeeGrant, - sdk.NewAttribute(feegrant.AttributeKeyGranter, granter.String()), - sdk.NewAttribute(feegrant.AttributeKeyGrantee, grantee.String()), + sdk.NewAttribute(feegrant.AttributeKeyGranter, granterStr), + sdk.NewAttribute(feegrant.AttributeKeyGrantee, granteeStr), ), ) return nil @@ -181,7 +207,7 @@ func (k Keeper) revokeAllowance(ctx context.Context, granter, grantee sdk.AccAdd // If there is none, it returns nil, nil. // Returns an error on parsing issues func (k Keeper) GetAllowance(ctx context.Context, granter, grantee sdk.AccAddress) (feegrant.FeeAllowanceI, error) { - grant, err := k.getGrant(ctx, granter, grantee) + grant, err := k.FeeAllowance.Get(ctx, collections.Join(grantee, granter)) if err != nil { return nil, err } @@ -189,27 +215,6 @@ func (k Keeper) GetAllowance(ctx context.Context, granter, grantee sdk.AccAddres return grant.GetGrant() } -// getGrant returns entire grant between both accounts -func (k Keeper) getGrant(ctx context.Context, granter, grantee sdk.AccAddress) (*feegrant.Grant, error) { - store := k.storeService.OpenKVStore(ctx) - key := feegrant.FeeAllowanceKey(granter, grantee) - bz, err := store.Get(key) - if err != nil { - return nil, err - } - - if len(bz) == 0 { - return nil, sdkerrors.ErrNotFound.Wrap("fee-grant not found") - } - - var feegrant feegrant.Grant - if err := k.cdc.Unmarshal(bz, &feegrant); err != nil { - return nil, err - } - - return &feegrant, nil -} - // 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 @@ -218,14 +223,11 @@ func (k Keeper) IterateAllFeeAllowances(ctx context.Context, cb func(grant feegr iter := storetypes.KVStorePrefixIterator(runtime.KVStoreAdapter(store), feegrant.FeeAllowanceKeyPrefix) defer iter.Close() - stop := false - for ; iter.Valid() && !stop; iter.Next() { - bz := iter.Value() - var feeGrant feegrant.Grant - if err := k.cdc.Unmarshal(bz, &feeGrant); err != nil { - return err - } - stop = cb(feeGrant) + err := k.FeeAllowance.Walk(ctx, nil, func(key collections.Pair[sdk.AccAddress, sdk.AccAddress], grant feegrant.Grant) (stop bool, err error) { + return cb(grant), nil + }) + if err != nil { + return err } return nil @@ -233,35 +235,35 @@ func (k Keeper) IterateAllFeeAllowances(ctx context.Context, cb func(grant feegr // UseGrantedFees will try to pay the given fee from the granter's account as requested by the grantee func (k Keeper) UseGrantedFees(ctx context.Context, granter, grantee sdk.AccAddress, fee sdk.Coins, msgs []sdk.Msg) error { - f, err := k.getGrant(ctx, granter, grantee) + grant, err := k.GetAllowance(ctx, granter, grantee) if err != nil { return err } - grant, err := f.GetGrant() + granterStr, err := k.authKeeper.AddressCodec().BytesToString(granter) + if err != nil { + return err + } + granteeStr, err := k.authKeeper.AddressCodec().BytesToString(grantee) if err != nil { return err } remove, err := grant.Accept(ctx, fee, msgs) - if remove { // Ignoring the `revokeFeeAllowance` error, because the user has enough grants to perform this transaction. - k.revokeAllowance(ctx, granter, grantee) + _ = k.revokeAllowance(ctx, granter, grantee) if err != nil { return err } - - emitUseGrantEvent(ctx, granter.String(), grantee.String()) + emitUseGrantEvent(ctx, granterStr, granteeStr) return nil } - if err != nil { return err } - - emitUseGrantEvent(ctx, granter.String(), grantee.String()) + emitUseGrantEvent(ctx, granterStr, granteeStr) // if fee allowance is accepted, store the updated state of the allowance return k.UpdateAllowance(ctx, granter, grantee, grant) @@ -316,32 +318,34 @@ func (k Keeper) ExportGenesis(ctx context.Context) (*feegrant.GenesisState, erro }, err } -func (k Keeper) addToFeeAllowanceQueue(ctx context.Context, grantKey []byte, exp *time.Time) error { - store := k.storeService.OpenKVStore(ctx) - return store.Set(feegrant.FeeAllowancePrefixQueue(exp, grantKey), []byte{}) -} - // RemoveExpiredAllowances iterates grantsByExpiryQueue and deletes the expired grants. -func (k Keeper) RemoveExpiredAllowances(ctx context.Context) error { - exp := sdk.UnwrapSDKContext(ctx).BlockTime() - store := k.storeService.OpenKVStore(ctx) - iterator, err := store.Iterator(feegrant.FeeAllowanceQueueKeyPrefix, storetypes.InclusiveEndBytes(feegrant.AllowanceByExpTimeKey(&exp))) - if err != nil { - return err - } - defer iterator.Close() +func (k Keeper) RemoveExpiredAllowances(ctx context.Context, limit int) error { + exp := sdk.UnwrapSDKContext(ctx).HeaderInfo().Time + rng := collections.NewPrefixUntilTripleRange[time.Time, sdk.AccAddress, sdk.AccAddress](exp) + count := 0 - for ; iterator.Valid(); iterator.Next() { - err = store.Delete(iterator.Key()) - if err != nil { - return err + err := k.FeeAllowanceQueue.Walk(ctx, rng, func(key collections.Triple[time.Time, sdk.AccAddress, sdk.AccAddress], value bool) (stop bool, err error) { + grantee, granter := key.K2(), key.K3() + + if err := k.FeeAllowance.Remove(ctx, collections.Join(grantee, granter)); err != nil { + return true, err } - granter, grantee := feegrant.ParseAddressesFromFeeAllowanceQueueKey(iterator.Key()) - err = store.Delete(feegrant.FeeAllowanceKey(granter, grantee)) - if err != nil { - return err + if err := k.FeeAllowanceQueue.Remove(ctx, key); err != nil { + return true, err + } + + // limit the amount of iterations to avoid taking too much time + count++ + if count == limit { + return true, nil } + + return false, nil + }) + if err != nil { + return err } + return nil } diff --git a/x/feegrant/keeper/keeper_test.go b/x/feegrant/keeper/keeper_test.go index de527485fefd..272618a7121a 100644 --- a/x/feegrant/keeper/keeper_test.go +++ b/x/feegrant/keeper/keeper_test.go @@ -6,6 +6,7 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/suite" + "cosmossdk.io/core/header" sdkmath "cosmossdk.io/math" storetypes "cosmossdk.io/store/types" "cosmossdk.io/x/feegrant" @@ -27,6 +28,7 @@ type KeeperTestSuite struct { ctx sdk.Context addrs []sdk.AccAddress + encodedAddrs []string msgSrvr feegrant.MsgServer atom sdk.Coins feegrantKeeper keeper.Keeper @@ -38,7 +40,7 @@ func TestKeeperTestSuite(t *testing.T) { } func (suite *KeeperTestSuite) SetupTest() { - suite.addrs = simtestutil.CreateIncrementalAccounts(4) + suite.addrs = simtestutil.CreateIncrementalAccounts(20) key := storetypes.NewKVStoreKey(feegrant.StoreKey) testCtx := testutil.DefaultContextWithDB(suite.T(), key, storetypes.NewTransientStoreKey("transient_test")) encCfg := moduletestutil.MakeTestEncodingConfig(module.AppModuleBasic{}) @@ -46,12 +48,17 @@ func (suite *KeeperTestSuite) SetupTest() { // setup gomock and initialize some globally expected executions ctrl := gomock.NewController(suite.T()) suite.accountKeeper = feegranttestutil.NewMockAccountKeeper(ctrl) - suite.accountKeeper.EXPECT().GetAccount(gomock.Any(), suite.addrs[0]).Return(authtypes.NewBaseAccountWithAddress(suite.addrs[0])).AnyTimes() - suite.accountKeeper.EXPECT().GetAccount(gomock.Any(), suite.addrs[1]).Return(authtypes.NewBaseAccountWithAddress(suite.addrs[1])).AnyTimes() - suite.accountKeeper.EXPECT().GetAccount(gomock.Any(), suite.addrs[2]).Return(authtypes.NewBaseAccountWithAddress(suite.addrs[2])).AnyTimes() - suite.accountKeeper.EXPECT().GetAccount(gomock.Any(), suite.addrs[3]).Return(authtypes.NewBaseAccountWithAddress(suite.addrs[3])).AnyTimes() + for i := 0; i < len(suite.addrs); i++ { + suite.accountKeeper.EXPECT().GetAccount(gomock.Any(), suite.addrs[i]).Return(authtypes.NewBaseAccountWithAddress(suite.addrs[i])).AnyTimes() + } - suite.accountKeeper.EXPECT().AddressCodec().Return(codecaddress.NewBech32Codec("cosmos")).AnyTimes() + ac := codecaddress.NewBech32Codec("cosmos") + suite.accountKeeper.EXPECT().AddressCodec().Return(ac).AnyTimes() + for _, addr := range suite.addrs { + str, err := ac.BytesToString(addr) + suite.Require().NoError(err) + suite.encodedAddrs = append(suite.encodedAddrs, str) + } suite.feegrantKeeper = keeper.NewKeeper(encCfg.Codec, runtime.NewKVStoreService(key), suite.accountKeeper) suite.ctx = testCtx.Ctx @@ -62,8 +69,8 @@ func (suite *KeeperTestSuite) SetupTest() { func (suite *KeeperTestSuite) TestKeeperCrud() { // some helpers eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123)) - exp := suite.ctx.BlockTime().AddDate(1, 0, 0) - exp2 := suite.ctx.BlockTime().AddDate(2, 0, 0) + exp := suite.ctx.HeaderInfo().Time.AddDate(1, 0, 0) + exp2 := suite.ctx.HeaderInfo().Time.AddDate(2, 0, 0) basic := &feegrant.BasicAllowance{ SpendLimit: suite.atom, Expiration: &exp, @@ -106,21 +113,21 @@ func (suite *KeeperTestSuite) TestKeeperCrud() { suite.Require().Error(err) // remove some, overwrite other - _, err = suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{Granter: suite.addrs[0].String(), Grantee: suite.addrs[1].String()}) + _, err = suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{Granter: suite.encodedAddrs[0], Grantee: suite.encodedAddrs[1]}) suite.Require().NoError(err) - _, err = suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{Granter: suite.addrs[0].String(), Grantee: suite.addrs[2].String()}) + _, err = suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{Granter: suite.encodedAddrs[0], Grantee: suite.encodedAddrs[2]}) suite.Require().NoError(err) // revoke non-exist fee allowance - _, err = suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{Granter: suite.addrs[0].String(), Grantee: suite.addrs[2].String()}) + _, err = suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{Granter: suite.encodedAddrs[0], Grantee: suite.encodedAddrs[2]}) suite.Require().Error(err) err = suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[0], suite.addrs[2], basic) suite.Require().NoError(err) // revoke an existing grant and grant again with different allowance. - _, err = suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{Granter: suite.addrs[1].String(), Grantee: suite.addrs[2].String()}) + _, err = suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{Granter: suite.encodedAddrs[1], Grantee: suite.encodedAddrs[2]}) suite.Require().NoError(err) err = suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[1], suite.addrs[2], basic3) @@ -182,13 +189,13 @@ func (suite *KeeperTestSuite) TestKeeperCrud() { _, err = suite.feegrantKeeper.GetAllowance(suite.ctx, suite.addrs[3], accAddr) suite.Require().NoError(err) - _, err = suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{Granter: suite.addrs[3].String(), Grantee: address}) + _, err = suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{Granter: suite.encodedAddrs[3], Grantee: address}) suite.Require().NoError(err) } func (suite *KeeperTestSuite) TestUseGrantedFee() { eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123)) - blockTime := suite.ctx.BlockTime() + blockTime := suite.ctx.HeaderInfo().Time oneYear := blockTime.AddDate(1, 0, 0) future := &feegrant.BasicAllowance{ @@ -229,8 +236,8 @@ func (suite *KeeperTestSuite) TestUseGrantedFee() { final: future, postRun: func() { _, err := suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{ - Granter: suite.addrs[0].String(), - Grantee: suite.addrs[1].String(), + Granter: suite.encodedAddrs[0], + Grantee: suite.encodedAddrs[1], }) suite.Require().NoError(err) }, @@ -243,8 +250,8 @@ func (suite *KeeperTestSuite) TestUseGrantedFee() { final: futureAfterSmall, postRun: func() { _, err := suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{ - Granter: suite.addrs[0].String(), - Grantee: suite.addrs[1].String(), + Granter: suite.encodedAddrs[0], + Grantee: suite.encodedAddrs[1], }) suite.Require().NoError(err) }, @@ -281,7 +288,7 @@ func (suite *KeeperTestSuite) TestUseGrantedFee() { suite.Require().NoError(err) // waiting for future blocks, allowance to be pruned. - ctx := suite.ctx.WithBlockTime(oneYear) + ctx := suite.ctx.WithHeaderInfo(header.Info{Time: oneYear}) // expect error: feegrant expired err = suite.feegrantKeeper.UseGrantedFees(ctx, suite.addrs[0], suite.addrs[2], eth, []sdk.Msg{}) @@ -291,12 +298,12 @@ func (suite *KeeperTestSuite) TestUseGrantedFee() { // verify: feegrant is revoked _, err = suite.feegrantKeeper.GetAllowance(ctx, suite.addrs[0], suite.addrs[2]) suite.Error(err) - suite.Contains(err.Error(), "fee-grant not found") + suite.Contains(err.Error(), "not found") } func (suite *KeeperTestSuite) TestIterateGrants() { eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123)) - exp := suite.ctx.BlockTime().AddDate(1, 0, 0) + exp := suite.ctx.HeaderInfo().Time.AddDate(1, 0, 0) allowance := &feegrant.BasicAllowance{ SpendLimit: suite.atom, @@ -308,19 +315,22 @@ func (suite *KeeperTestSuite) TestIterateGrants() { Expiration: &exp, } - suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[0], suite.addrs[1], allowance) - suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[2], suite.addrs[1], allowance1) - - suite.feegrantKeeper.IterateAllFeeAllowances(suite.ctx, func(grant feegrant.Grant) bool { - suite.Require().Equal(suite.addrs[1].String(), grant.Grantee) - suite.Require().Contains([]string{suite.addrs[0].String(), suite.addrs[2].String()}, grant.Granter) + err := suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[0], suite.addrs[1], allowance) + suite.Require().NoError(err) + err = suite.feegrantKeeper.GrantAllowance(suite.ctx, suite.addrs[2], suite.addrs[1], allowance1) + suite.Require().NoError(err) + err = suite.feegrantKeeper.IterateAllFeeAllowances(suite.ctx, func(grant feegrant.Grant) bool { + suite.Require().Equal(suite.encodedAddrs[1], grant.Grantee) + suite.Require().Contains([]string{suite.encodedAddrs[0], suite.encodedAddrs[2]}, grant.Granter) return true }) + suite.Require().NoError(err) } func (suite *KeeperTestSuite) TestPruneGrants() { eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 123)) - now := suite.ctx.BlockTime() + now := suite.ctx.HeaderInfo().Time + oneDay := now.AddDate(0, 0, 1) oneYearExpiry := now.AddDate(1, 0, 0) testCases := []struct { @@ -334,29 +344,40 @@ func (suite *KeeperTestSuite) TestPruneGrants() { postRun func() }{ { - name: "grant not pruned from state", - ctx: suite.ctx, - granter: suite.addrs[0], - grantee: suite.addrs[1], + name: "grant pruned from state after a block: error", + ctx: suite.ctx, + granter: suite.addrs[0], + grantee: suite.addrs[1], + expErrMsg: "not found", allowance: &feegrant.BasicAllowance{ SpendLimit: suite.atom, Expiration: &now, }, }, { - name: "grant pruned from state after a block: error", - ctx: suite.ctx.WithBlockTime(now.AddDate(0, 0, 1)), - granter: suite.addrs[2], - grantee: suite.addrs[1], + name: "grant not pruned from state before expiration: no error", + ctx: suite.ctx, + granter: suite.addrs[2], + grantee: suite.addrs[1], + allowance: &feegrant.BasicAllowance{ + SpendLimit: eth, + Expiration: &oneDay, + }, + }, + { + name: "grant pruned from state after a day: error", + ctx: suite.ctx.WithHeaderInfo(header.Info{Time: now.AddDate(0, 0, 1)}), + granter: suite.addrs[1], + grantee: suite.addrs[0], expErrMsg: "not found", allowance: &feegrant.BasicAllowance{ SpendLimit: eth, - Expiration: &now, + Expiration: &oneDay, }, }, { name: "grant not pruned from state after a day: no error", - ctx: suite.ctx.WithBlockTime(now.AddDate(0, 0, 1)), + ctx: suite.ctx.WithHeaderInfo(header.Info{Time: now.AddDate(0, 0, 1)}), granter: suite.addrs[1], grantee: suite.addrs[0], allowance: &feegrant.BasicAllowance{ @@ -366,7 +387,7 @@ func (suite *KeeperTestSuite) TestPruneGrants() { }, { name: "grant pruned from state after a year: error", - ctx: suite.ctx.WithBlockTime(now.AddDate(1, 0, 1)), + ctx: suite.ctx.WithHeaderInfo(header.Info{Time: now.AddDate(1, 0, 0)}), granter: suite.addrs[1], grantee: suite.addrs[2], expErrMsg: "not found", @@ -377,12 +398,11 @@ func (suite *KeeperTestSuite) TestPruneGrants() { }, { name: "no expiry: no error", - ctx: suite.ctx.WithBlockTime(now.AddDate(1, 0, 0)), + ctx: suite.ctx.WithHeaderInfo(header.Info{Time: now.AddDate(1, 0, 0)}), granter: suite.addrs[1], grantee: suite.addrs[2], allowance: &feegrant.BasicAllowance{ SpendLimit: eth, - Expiration: &oneYearExpiry, }, }, } @@ -395,14 +415,18 @@ func (suite *KeeperTestSuite) TestPruneGrants() { } err := suite.feegrantKeeper.GrantAllowance(suite.ctx, tc.granter, tc.grantee, tc.allowance) suite.NoError(err) - suite.feegrantKeeper.RemoveExpiredAllowances(tc.ctx) + err = suite.feegrantKeeper.RemoveExpiredAllowances(tc.ctx, 5) + suite.NoError(err) + grant, err := suite.feegrantKeeper.GetAllowance(tc.ctx, tc.granter, tc.grantee) if tc.expErrMsg != "" { suite.Error(err) suite.Contains(err.Error(), tc.expErrMsg) } else { + suite.NoError(err) suite.NotNil(grant) } + if tc.postRun != nil { tc.postRun() } diff --git a/x/feegrant/keeper/msg_server.go b/x/feegrant/keeper/msg_server.go index 1b15208da6ff..f3c5319123ac 100644 --- a/x/feegrant/keeper/msg_server.go +++ b/x/feegrant/keeper/msg_server.go @@ -26,13 +26,11 @@ func NewMsgServerImpl(k Keeper) feegrant.MsgServer { var _ feegrant.MsgServer = msgServer{} // GrantAllowance grants an allowance from the granter's funds to be used by the grantee. -func (k msgServer) GrantAllowance(goCtx context.Context, msg *feegrant.MsgGrantAllowance) (*feegrant.MsgGrantAllowanceResponse, error) { +func (k msgServer) GrantAllowance(ctx context.Context, msg *feegrant.MsgGrantAllowance) (*feegrant.MsgGrantAllowanceResponse, error) { if strings.EqualFold(msg.Grantee, msg.Granter) { return nil, errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "cannot self-grant fee authorization") } - ctx := sdk.UnwrapSDKContext(goCtx) - grantee, err := k.authKeeper.AddressCodec().StringToBytes(msg.Grantee) if err != nil { return nil, err @@ -65,13 +63,11 @@ func (k msgServer) GrantAllowance(goCtx context.Context, msg *feegrant.MsgGrantA } // RevokeAllowance revokes a fee allowance between a granter and grantee. -func (k msgServer) RevokeAllowance(goCtx context.Context, msg *feegrant.MsgRevokeAllowance) (*feegrant.MsgRevokeAllowanceResponse, error) { +func (k msgServer) RevokeAllowance(ctx context.Context, msg *feegrant.MsgRevokeAllowance) (*feegrant.MsgRevokeAllowanceResponse, error) { if msg.Grantee == msg.Granter { return nil, errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "addresses must be different") } - ctx := sdk.UnwrapSDKContext(goCtx) - grantee, err := k.authKeeper.AddressCodec().StringToBytes(msg.Grantee) if err != nil { return nil, err @@ -89,3 +85,22 @@ func (k msgServer) RevokeAllowance(goCtx context.Context, msg *feegrant.MsgRevok return &feegrant.MsgRevokeAllowanceResponse{}, nil } + +// PruneAllowances removes expired allowances from the store. +func (k msgServer) PruneAllowances(ctx context.Context, req *feegrant.MsgPruneAllowances) (*feegrant.MsgPruneAllowancesResponse, error) { + // 75 is an arbitrary value, we can change it later if needed + err := k.RemoveExpiredAllowances(ctx, 75) + if err != nil { + return nil, err + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + sdkCtx.EventManager().EmitEvent( + sdk.NewEvent( + feegrant.EventTypePruneFeeGrant, + sdk.NewAttribute(feegrant.AttributeKeyPruner, req.Pruner), + ), + ) + + return &feegrant.MsgPruneAllowancesResponse{}, nil +} diff --git a/x/feegrant/keeper/msg_server_test.go b/x/feegrant/keeper/msg_server_test.go index ea1a6e523aae..e368df88b66a 100644 --- a/x/feegrant/keeper/msg_server_test.go +++ b/x/feegrant/keeper/msg_server_test.go @@ -5,17 +5,20 @@ import ( "github.com/golang/mock/gomock" + "cosmossdk.io/collections" + "cosmossdk.io/core/header" "cosmossdk.io/x/feegrant" codecaddress "github.com/cosmos/cosmos-sdk/codec/address" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) func (suite *KeeperTestSuite) TestGrantAllowance() { - ctx := suite.ctx.WithBlockTime(time.Now()) - oneYear := ctx.BlockTime().AddDate(1, 0, 0) - yesterday := ctx.BlockTime().AddDate(0, 0, -1) + ctx := suite.ctx.WithHeaderInfo(header.Info{Time: time.Now()}) + oneYear := ctx.HeaderInfo().Time.AddDate(1, 0, 0) + yesterday := ctx.HeaderInfo().Time.AddDate(0, 0, -1) addressCodec := codecaddress.NewBech32Codec("cosmos") @@ -33,7 +36,7 @@ func (suite *KeeperTestSuite) TestGrantAllowance() { invalid := "invalid-granter" return &feegrant.MsgGrantAllowance{ Granter: invalid, - Grantee: suite.addrs[1].String(), + Grantee: suite.encodedAddrs[1], Allowance: any, } }, @@ -47,7 +50,7 @@ func (suite *KeeperTestSuite) TestGrantAllowance() { suite.Require().NoError(err) invalid := "invalid-grantee" return &feegrant.MsgGrantAllowance{ - Granter: suite.addrs[0].String(), + Granter: suite.encodedAddrs[0], Grantee: invalid, Allowance: any, } @@ -78,7 +81,7 @@ func (suite *KeeperTestSuite) TestGrantAllowance() { suite.Require().NoError(err) return &feegrant.MsgGrantAllowance{ - Granter: suite.addrs[0].String(), + Granter: suite.encodedAddrs[0], Grantee: grantee, Allowance: any, } @@ -95,8 +98,8 @@ func (suite *KeeperTestSuite) TestGrantAllowance() { }) suite.Require().NoError(err) return &feegrant.MsgGrantAllowance{ - Granter: suite.addrs[0].String(), - Grantee: suite.addrs[1].String(), + Granter: suite.encodedAddrs[0], + Grantee: suite.encodedAddrs[1], Allowance: any, } }, @@ -112,8 +115,8 @@ func (suite *KeeperTestSuite) TestGrantAllowance() { }) suite.Require().NoError(err) return &feegrant.MsgGrantAllowance{ - Granter: suite.addrs[0].String(), - Grantee: suite.addrs[1].String(), + Granter: suite.encodedAddrs[0], + Grantee: suite.encodedAddrs[1], Allowance: any, } }, @@ -129,8 +132,8 @@ func (suite *KeeperTestSuite) TestGrantAllowance() { }) suite.Require().NoError(err) return &feegrant.MsgGrantAllowance{ - Granter: suite.addrs[0].String(), - Grantee: suite.addrs[1].String(), + Granter: suite.encodedAddrs[0], + Grantee: suite.encodedAddrs[1], Allowance: any, } }, @@ -149,8 +152,8 @@ func (suite *KeeperTestSuite) TestGrantAllowance() { }) suite.Require().NoError(err) return &feegrant.MsgGrantAllowance{ - Granter: suite.addrs[1].String(), - Grantee: suite.addrs[2].String(), + Granter: suite.encodedAddrs[1], + Grantee: suite.encodedAddrs[2], Allowance: any, } }, @@ -169,8 +172,8 @@ func (suite *KeeperTestSuite) TestGrantAllowance() { }) suite.Require().NoError(err) return &feegrant.MsgGrantAllowance{ - Granter: suite.addrs[1].String(), - Grantee: suite.addrs[2].String(), + Granter: suite.encodedAddrs[1], + Grantee: suite.encodedAddrs[2], Allowance: any, } }, @@ -190,7 +193,8 @@ func (suite *KeeperTestSuite) TestGrantAllowance() { } func (suite *KeeperTestSuite) TestRevokeAllowance() { - oneYear := suite.ctx.BlockTime().AddDate(1, 0, 0) + suite.ctx = suite.ctx.WithHeaderInfo(header.Info{Time: time.Now()}) + oneYear := suite.ctx.HeaderInfo().Time.AddDate(1, 0, 0) testCases := []struct { name string @@ -203,7 +207,7 @@ func (suite *KeeperTestSuite) TestRevokeAllowance() { "error: invalid granter", &feegrant.MsgRevokeAllowance{ Granter: invalidGranter, - Grantee: suite.addrs[1].String(), + Grantee: suite.encodedAddrs[1], }, func() {}, true, @@ -212,7 +216,7 @@ func (suite *KeeperTestSuite) TestRevokeAllowance() { { "error: invalid grantee", &feegrant.MsgRevokeAllowance{ - Granter: suite.addrs[0].String(), + Granter: suite.encodedAddrs[0], Grantee: invalidGrantee, }, func() {}, @@ -222,26 +226,26 @@ func (suite *KeeperTestSuite) TestRevokeAllowance() { { "error: fee allowance not found", &feegrant.MsgRevokeAllowance{ - Granter: suite.addrs[0].String(), - Grantee: suite.addrs[1].String(), + Granter: suite.encodedAddrs[0], + Grantee: suite.encodedAddrs[1], }, func() {}, true, - "fee-grant not found", + "not found", }, { "success: revoke fee allowance", &feegrant.MsgRevokeAllowance{ - Granter: suite.addrs[0].String(), - Grantee: suite.addrs[1].String(), + Granter: suite.encodedAddrs[0], + Grantee: suite.encodedAddrs[1], }, func() { // removing fee allowance from previous tests if exists - suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{ - Granter: suite.addrs[0].String(), - Grantee: suite.addrs[1].String(), + _, err := suite.msgSrvr.RevokeAllowance(suite.ctx, &feegrant.MsgRevokeAllowance{ + Granter: suite.encodedAddrs[0], + Grantee: suite.encodedAddrs[1], }) - + suite.Require().Error(err) any, err := codectypes.NewAnyWithValue(&feegrant.PeriodicAllowance{ Basic: feegrant.BasicAllowance{ SpendLimit: suite.atom, @@ -251,8 +255,8 @@ func (suite *KeeperTestSuite) TestRevokeAllowance() { }) suite.Require().NoError(err) req := &feegrant.MsgGrantAllowance{ - Granter: suite.addrs[0].String(), - Grantee: suite.addrs[1].String(), + Granter: suite.encodedAddrs[0], + Grantee: suite.encodedAddrs[1], Allowance: any, } _, err = suite.msgSrvr.GrantAllowance(suite.ctx, req) @@ -264,12 +268,12 @@ func (suite *KeeperTestSuite) TestRevokeAllowance() { { "error: check fee allowance revoked", &feegrant.MsgRevokeAllowance{ - Granter: suite.addrs[0].String(), - Grantee: suite.addrs[1].String(), + Granter: suite.encodedAddrs[0], + Grantee: suite.encodedAddrs[1], }, func() {}, true, - "fee-grant not found", + "not found", }, } @@ -284,3 +288,67 @@ func (suite *KeeperTestSuite) TestRevokeAllowance() { }) } } + +func (suite *KeeperTestSuite) TestPruneAllowances() { + ctx := suite.ctx.WithHeaderInfo(header.Info{Time: time.Now()}) + oneYear := ctx.HeaderInfo().Time.AddDate(1, 0, 0) + + // We create 76 allowances, all expiring in one year + count := 0 + for i := 0; i < len(suite.encodedAddrs); i++ { + for j := 0; j < len(suite.encodedAddrs); j++ { + if count == 76 { + break + } + if suite.encodedAddrs[i] == suite.encodedAddrs[j] { + continue + } + + any, err := codectypes.NewAnyWithValue(&feegrant.BasicAllowance{ + SpendLimit: suite.atom, + Expiration: &oneYear, + }) + suite.Require().NoError(err) + req := &feegrant.MsgGrantAllowance{ + Granter: suite.encodedAddrs[i], + Grantee: suite.encodedAddrs[j], + Allowance: any, + } + + _, err = suite.msgSrvr.GrantAllowance(ctx, req) + if err != nil { + // do not fail, just try with another pair + continue + } + + count++ + } + } + + // we have 76 allowances + count = 0 + err := suite.feegrantKeeper.FeeAllowance.Walk(ctx, nil, func(key collections.Pair[types.AccAddress, types.AccAddress], value feegrant.Grant) (stop bool, err error) { + count++ + return false, nil + }) + suite.Require().NoError(err) + suite.Require().Equal(76, count) + + // after a year and one day passes, they are all expired + oneYearAndADay := ctx.HeaderInfo().Time.AddDate(1, 0, 1) + ctx = suite.ctx.WithHeaderInfo(header.Info{Time: oneYearAndADay}) + + // we prune them, but currently only 75 will be pruned + _, err = suite.msgSrvr.PruneAllowances(ctx, &feegrant.MsgPruneAllowances{}) + suite.Require().NoError(err) + + // we have 1 allowance left + count = 0 + err = suite.feegrantKeeper.FeeAllowance.Walk(ctx, nil, func(key collections.Pair[types.AccAddress, types.AccAddress], value feegrant.Grant) (stop bool, err error) { + count++ + + return false, nil + }) + suite.Require().NoError(err) + suite.Require().Equal(1, count) +} diff --git a/x/feegrant/key.go b/x/feegrant/key.go index 066ddf5c8997..837435128c73 100644 --- a/x/feegrant/key.go +++ b/x/feegrant/key.go @@ -1,10 +1,7 @@ package feegrant import ( - time "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/address" + "cosmossdk.io/collections" ) const ( @@ -24,72 +21,9 @@ const ( var ( // FeeAllowanceKeyPrefix is the set of the kvstore for fee allowance data // - 0x00: allowance - FeeAllowanceKeyPrefix = []byte{0x00} + FeeAllowanceKeyPrefix = collections.NewPrefix(0) // FeeAllowanceQueueKeyPrefix is the set of the kvstore for fee allowance keys data // - 0x01: - FeeAllowanceQueueKeyPrefix = []byte{0x01} + FeeAllowanceQueueKeyPrefix = collections.NewPrefix(1) ) - -// 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 -// -// Key format: -// - <0x00> -func FeeAllowanceKey(granter, grantee sdk.AccAddress) []byte { - return append(FeeAllowancePrefixByGrantee(grantee), address.MustLengthPrefix(granter.Bytes())...) -} - -// FeeAllowancePrefixByGrantee returns a prefix to scan for all grants to this given address. -// -// Key format: -// - <0x00> -func FeeAllowancePrefixByGrantee(grantee sdk.AccAddress) []byte { - return append(FeeAllowanceKeyPrefix, address.MustLengthPrefix(grantee.Bytes())...) -} - -// FeeAllowancePrefixQueue is the canonical key to store grant key. -// -// Key format: -// - <0x01> -func FeeAllowancePrefixQueue(exp *time.Time, key []byte) []byte { - allowanceByExpTimeKey := AllowanceByExpTimeKey(exp) - return append(allowanceByExpTimeKey, key...) -} - -// AllowanceByExpTimeKey returns a key with `FeeAllowanceQueueKeyPrefix`, expiry -// -// Key format: -// - <0x01> -func AllowanceByExpTimeKey(exp *time.Time) []byte { - // no need of appending len(exp_bytes) here, `FormatTimeBytes` gives const length everytime. - return append(FeeAllowanceQueueKeyPrefix, sdk.FormatTimeBytes(*exp)...) -} - -// ParseAddressesFromFeeAllowanceKey extracts and returns the granter, grantee from the given key. -func ParseAddressesFromFeeAllowanceKey(key []byte) (granter, grantee []byte) { - // key is of format: - // 0x00 - granterAddrLen, granterAddrLenEndIndex := sdk.ParseLengthPrefixedBytes(key, 1, 1) // ignore key[0] since it is a prefix key - grantee, granterAddrEndIndex := sdk.ParseLengthPrefixedBytes(key, granterAddrLenEndIndex+1, int(granterAddrLen[0])) - - granteeAddrLen, granteeAddrLenEndIndex := sdk.ParseLengthPrefixedBytes(key, granterAddrEndIndex+1, 1) - granter, _ = sdk.ParseLengthPrefixedBytes(key, granteeAddrLenEndIndex+1, int(granteeAddrLen[0])) - - return granter, grantee -} - -// ParseAddressesFromFeeAllowanceQueueKey extracts and returns the granter, grantee from the given key. -func ParseAddressesFromFeeAllowanceQueueKey(key []byte) (granter, grantee []byte) { - lenTime := len(sdk.FormatTimeBytes(time.Now())) - - // key is of format: - // <0x01> - granterAddrLen, granterAddrLenEndIndex := sdk.ParseLengthPrefixedBytes(key, 1+lenTime, 1) // ignore key[0] since it is a prefix key - grantee, granterAddrEndIndex := sdk.ParseLengthPrefixedBytes(key, granterAddrLenEndIndex+1, int(granterAddrLen[0])) - - granteeAddrLen, granteeAddrLenEndIndex := sdk.ParseLengthPrefixedBytes(key, granterAddrEndIndex+1, 1) - granter, _ = sdk.ParseLengthPrefixedBytes(key, granteeAddrLenEndIndex+1, int(granteeAddrLen[0])) - - return granter, grantee -} diff --git a/x/feegrant/key_test.go b/x/feegrant/key_test.go deleted file mode 100644 index 45eae077d781..000000000000 --- a/x/feegrant/key_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package feegrant_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - - "cosmossdk.io/x/feegrant" - - codecaddress "github.com/cosmos/cosmos-sdk/codec/address" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -func TestMarshalAndUnmarshalFeegrantKey(t *testing.T) { - addressCodec := codecaddress.NewBech32Codec("cosmos") - grantee, err := addressCodec.StringToBytes("cosmos1qk93t4j0yyzgqgt6k5qf8deh8fq6smpn3ntu3x") - require.NoError(t, err) - granter, err := addressCodec.StringToBytes("cosmos1p9qh4ldfd6n0qehujsal4k7g0e37kel90rc4ts") - require.NoError(t, err) - - key := feegrant.FeeAllowanceKey(granter, grantee) - require.Len(t, key, len(grantee)+len(granter)+3) - require.Equal(t, feegrant.FeeAllowancePrefixByGrantee(grantee), key[:len(grantee)+2]) - - g1, g2 := feegrant.ParseAddressesFromFeeAllowanceKey(key) - require.Equal(t, granter, g1) - require.Equal(t, grantee, g2) -} - -func TestMarshalAndUnmarshalFeegrantKeyQueueKey(t *testing.T) { - addressCodec := codecaddress.NewBech32Codec("cosmos") - grantee, err := addressCodec.StringToBytes("cosmos1qk93t4j0yyzgqgt6k5qf8deh8fq6smpn3ntu3x") - require.NoError(t, err) - granter, err := addressCodec.StringToBytes("cosmos1p9qh4ldfd6n0qehujsal4k7g0e37kel90rc4ts") - require.NoError(t, err) - - exp := time.Now() - expBytes := sdk.FormatTimeBytes(exp) - - key := feegrant.FeeAllowancePrefixQueue(&exp, feegrant.FeeAllowanceKey(granter, grantee)[1:]) - require.Len(t, key, len(grantee)+len(granter)+3+len(expBytes)) - - granter1, grantee1 := feegrant.ParseAddressesFromFeeAllowanceQueueKey(key) - require.Equal(t, granter, granter1) - require.Equal(t, grantee, grantee1) -} diff --git a/x/feegrant/migrations/v2/store.go b/x/feegrant/migrations/v2/store.go index 48a8d3e3398b..a90f83531abf 100644 --- a/x/feegrant/migrations/v2/store.go +++ b/x/feegrant/migrations/v2/store.go @@ -37,11 +37,14 @@ func addAllowancesByExpTimeQueue(ctx context.Context, store store.KVStore, cdc c if exp != nil { // store key is not changed in 0.46 key := iterator.Key() - if exp.Before(types.UnwrapSDKContext(ctx).BlockTime()) { + if exp.Before(types.UnwrapSDKContext(ctx).HeaderInfo().Time) { prefixStore.Delete(key) } else { grantByExpTimeQueueKey := FeeAllowancePrefixQueue(exp, key) - store.Set(grantByExpTimeQueueKey, []byte{}) + err = store.Set(grantByExpTimeQueueKey, []byte{}) + if err != nil { + return err + } } } } diff --git a/x/feegrant/migrations/v2/store_test.go b/x/feegrant/migrations/v2/store_test.go index 7b2ad1517160..ab8ad26fe010 100644 --- a/x/feegrant/migrations/v2/store_test.go +++ b/x/feegrant/migrations/v2/store_test.go @@ -6,12 +6,14 @@ import ( "github.com/stretchr/testify/require" + "cosmossdk.io/core/header" sdkmath "cosmossdk.io/math" storetypes "cosmossdk.io/store/types" "cosmossdk.io/x/feegrant" v2 "cosmossdk.io/x/feegrant/migrations/v2" "cosmossdk.io/x/feegrant/module" + addresscodec "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/runtime" "github.com/cosmos/cosmos-sdk/testutil" @@ -22,6 +24,7 @@ import ( func TestMigration(t *testing.T) { encodingConfig := moduletestutil.MakeTestEncodingConfig(module.AppModuleBasic{}) cdc := encodingConfig.Codec + ac := addresscodec.NewBech32Codec("cosmos") feegrantKey := storetypes.NewKVStoreKey(v2.ModuleName) ctx := testutil.DefaultContext(feegrantKey, storetypes.NewTransientStoreKey("transient_test")) @@ -31,7 +34,7 @@ func TestMigration(t *testing.T) { grantee2 := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) spendLimit := sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(1000))) - now := ctx.BlockTime() + now := ctx.HeaderInfo().Time oneDay := now.AddDate(0, 0, 1) twoDays := now.AddDate(0, 0, 2) @@ -67,7 +70,11 @@ func TestMigration(t *testing.T) { store := ctx.KVStore(feegrantKey) for _, grant := range grants { - newGrant, err := feegrant.NewGrant(grant.granter, grant.grantee, &feegrant.BasicAllowance{ + granterStr, err := ac.BytesToString(grant.granter) + require.NoError(t, err) + granteeStr, err := ac.BytesToString(grant.grantee) + require.NoError(t, err) + newGrant, err := feegrant.NewGrant(granterStr, granteeStr, &feegrant.BasicAllowance{ SpendLimit: grant.spendLimit, Expiration: grant.expiration, }) @@ -79,7 +86,7 @@ func TestMigration(t *testing.T) { store.Set(v2.FeeAllowanceKey(grant.granter, grant.grantee), bz) } - ctx = ctx.WithBlockTime(now.Add(30 * time.Hour)) + ctx = ctx.WithHeaderInfo(header.Info{Time: now.Add(30 * time.Hour)}) require.NoError(t, v2.MigrateStore(ctx, runtime.NewKVStoreService(feegrantKey), cdc)) store = ctx.KVStore(feegrantKey) diff --git a/x/feegrant/module/abci.go b/x/feegrant/module/abci.go index 4dbab056a9a3..6f14eee3137c 100644 --- a/x/feegrant/module/abci.go +++ b/x/feegrant/module/abci.go @@ -1,14 +1,12 @@ package module import ( - "cosmossdk.io/x/feegrant/keeper" + "context" - sdk "github.com/cosmos/cosmos-sdk/types" + "cosmossdk.io/x/feegrant/keeper" ) -func EndBlocker(ctx sdk.Context, k keeper.Keeper) { - err := k.RemoveExpiredAllowances(ctx) - if err != nil { - panic(err) - } +func EndBlocker(ctx context.Context, k keeper.Keeper) error { + // 200 is an arbitrary value, we can change it later if needed + return k.RemoveExpiredAllowances(ctx, 200) } diff --git a/x/feegrant/module/abci_test.go b/x/feegrant/module/abci_test.go index 6e9b1f81ac1a..34280ef68a88 100644 --- a/x/feegrant/module/abci_test.go +++ b/x/feegrant/module/abci_test.go @@ -6,6 +6,7 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" + "cosmossdk.io/core/header" sdkmath "cosmossdk.io/math" storetypes "cosmossdk.io/store/types" "cosmossdk.io/x/feegrant" @@ -34,7 +35,7 @@ func TestFeegrantPruning(t *testing.T) { granter3 := addrs[2] grantee := addrs[3] spendLimit := sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(1000))) - now := testCtx.Ctx.BlockTime() + now := testCtx.Ctx.HeaderInfo().Time oneDay := now.AddDate(0, 0, 1) ctrl := gomock.NewController(t) @@ -43,12 +44,12 @@ func TestFeegrantPruning(t *testing.T) { accountKeeper.EXPECT().GetAccount(gomock.Any(), granter1).Return(authtypes.NewBaseAccountWithAddress(granter1)).AnyTimes() accountKeeper.EXPECT().GetAccount(gomock.Any(), granter2).Return(authtypes.NewBaseAccountWithAddress(granter2)).AnyTimes() accountKeeper.EXPECT().GetAccount(gomock.Any(), granter3).Return(authtypes.NewBaseAccountWithAddress(granter3)).AnyTimes() - - accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec("cosmos")).AnyTimes() + ac := address.NewBech32Codec("cosmos") + accountKeeper.EXPECT().AddressCodec().Return(ac).AnyTimes() feegrantKeeper := keeper.NewKeeper(encCfg.Codec, runtime.NewKVStoreService(key), accountKeeper) - feegrantKeeper.GrantAllowance( + err := feegrantKeeper.GrantAllowance( testCtx.Ctx, granter1, grantee, @@ -56,7 +57,9 @@ func TestFeegrantPruning(t *testing.T) { Expiration: &now, }, ) - feegrantKeeper.GrantAllowance( + require.NoError(t, err) + + err = feegrantKeeper.GrantAllowance( testCtx.Ctx, granter2, grantee, @@ -64,7 +67,9 @@ func TestFeegrantPruning(t *testing.T) { SpendLimit: spendLimit, }, ) - feegrantKeeper.GrantAllowance( + require.NoError(t, err) + + err = feegrantKeeper.GrantAllowance( testCtx.Ctx, granter3, grantee, @@ -72,25 +77,28 @@ func TestFeegrantPruning(t *testing.T) { Expiration: &oneDay, }, ) + require.NoError(t, err) queryHelper := baseapp.NewQueryServerTestHelper(testCtx.Ctx, encCfg.InterfaceRegistry) feegrant.RegisterQueryServer(queryHelper, feegrantKeeper) queryClient := feegrant.NewQueryClient(queryHelper) - module.EndBlocker(testCtx.Ctx, feegrantKeeper) + require.NoError(t, module.EndBlocker(testCtx.Ctx, feegrantKeeper)) + granteeStr, err := ac.BytesToString(grantee) + require.NoError(t, err) res, err := queryClient.Allowances(testCtx.Ctx.Context(), &feegrant.QueryAllowancesRequest{ - Grantee: grantee.String(), + Grantee: granteeStr, }) require.NoError(t, err) require.NotNil(t, res) - require.Len(t, res.Allowances, 3) + require.Len(t, res.Allowances, 2) - testCtx.Ctx = testCtx.Ctx.WithBlockTime(now.AddDate(0, 0, 2)) - module.EndBlocker(testCtx.Ctx, feegrantKeeper) + testCtx.Ctx = testCtx.Ctx.WithHeaderInfo(header.Info{Time: now.AddDate(0, 0, 2)}) + require.NoError(t, module.EndBlocker(testCtx.Ctx, feegrantKeeper)) res, err = queryClient.Allowances(testCtx.Ctx.Context(), &feegrant.QueryAllowancesRequest{ - Grantee: grantee.String(), + Grantee: granteeStr, }) require.NoError(t, err) require.NotNil(t, res) diff --git a/x/feegrant/module/autocli.go b/x/feegrant/module/autocli.go index 3ba095376ab1..818c3be9c3e6 100644 --- a/x/feegrant/module/autocli.go +++ b/x/feegrant/module/autocli.go @@ -63,6 +63,13 @@ You can find the fee-grant of a granter and grantee.`), {ProtoField: "grantee"}, }, }, + { + RpcMethod: "PruneAllowances", + Use: "prune", + Short: "Prune expired allowances", + Long: "Prune up to 75 expired allowances in order to reduce the size of the store when the number of expired allowances is large.", + Example: fmt.Sprintf(`$ %s tx feegrant prune --from [mykey]`, version.AppName), + }, }, EnhanceCustomCommand: true, }, diff --git a/x/feegrant/module/module.go b/x/feegrant/module/module.go index 82103c7a81cd..913c8601796b 100644 --- a/x/feegrant/module/module.go +++ b/x/feegrant/module/module.go @@ -162,9 +162,7 @@ func (AppModule) ConsensusVersion() uint64 { return 2 } // EndBlock returns the end blocker for the feegrant module. It returns no validator // updates. func (am AppModule) EndBlock(ctx context.Context) error { - c := sdk.UnwrapSDKContext(ctx) - EndBlocker(c, am.keeper) - return nil + return EndBlocker(ctx, am.keeper) } func init() { @@ -205,6 +203,6 @@ func (am AppModule) RegisterStoreDecoder(sdr simtypes.StoreDecoderRegistry) { func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { return simulation.WeightedOperations( am.registry, simState.AppParams, simState.Cdc, simState.TxConfig, - am.accountKeeper, am.bankKeeper, am.keeper, am.ac, + am.accountKeeper, am.bankKeeper, am.keeper, am.accountKeeper.AddressCodec(), ) } diff --git a/x/feegrant/msgs.go b/x/feegrant/msgs.go index b0c4fcf09d47..c049c73d4f3f 100644 --- a/x/feegrant/msgs.go +++ b/x/feegrant/msgs.go @@ -16,7 +16,7 @@ var ( ) // NewMsgGrantAllowance creates a new MsgGrantAllowance. -func NewMsgGrantAllowance(feeAllowance FeeAllowanceI, granter, grantee sdk.AccAddress) (*MsgGrantAllowance, error) { +func NewMsgGrantAllowance(feeAllowance FeeAllowanceI, granter, grantee string) (*MsgGrantAllowance, error) { msg, ok := feeAllowance.(proto.Message) if !ok { return nil, errorsmod.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", msg) @@ -27,8 +27,8 @@ func NewMsgGrantAllowance(feeAllowance FeeAllowanceI, granter, grantee sdk.AccAd } return &MsgGrantAllowance{ - Granter: granter.String(), - Grantee: grantee.String(), + Granter: granter, + Grantee: grantee, Allowance: any, }, nil } @@ -51,6 +51,6 @@ func (msg MsgGrantAllowance) UnpackInterfaces(unpacker types.AnyUnpacker) error // NewMsgRevokeAllowance returns a message to revoke a fee allowance for a given // granter and grantee -func NewMsgRevokeAllowance(granter, grantee sdk.AccAddress) MsgRevokeAllowance { - return MsgRevokeAllowance{Granter: granter.String(), Grantee: grantee.String()} +func NewMsgRevokeAllowance(granter, grantee string) MsgRevokeAllowance { + return MsgRevokeAllowance{Granter: granter, Grantee: grantee} } diff --git a/x/feegrant/periodic_fee_test.go b/x/feegrant/periodic_fee_test.go index 911973c60486..1b7f942f72fc 100644 --- a/x/feegrant/periodic_fee_test.go +++ b/x/feegrant/periodic_fee_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "cosmossdk.io/core/header" storetypes "cosmossdk.io/store/types" "cosmossdk.io/x/feegrant" @@ -19,7 +19,7 @@ func TestPeriodicFeeValidAllow(t *testing.T) { key := storetypes.NewKVStoreKey(feegrant.StoreKey) testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test")) - ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: time.Now()}) + ctx := testCtx.Ctx.WithHeaderInfo(header.Info{Time: time.Now()}) atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555)) smallAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 43)) @@ -28,7 +28,7 @@ func TestPeriodicFeeValidAllow(t *testing.T) { eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 1)) emptyCoins := sdk.Coins{} - now := ctx.BlockTime() + now := ctx.HeaderInfo().Time oneHour := now.Add(1 * time.Hour) twoHours := now.Add(2 * time.Hour) tenMinutes := time.Duration(10) * time.Minute @@ -197,7 +197,7 @@ func TestPeriodicFeeValidAllow(t *testing.T) { } require.NoError(t, err) - ctx := testCtx.Ctx.WithBlockTime(tc.blockTime) + ctx := testCtx.Ctx.WithHeaderInfo(header.Info{Time: tc.blockTime}) // now try to deduct remove, err := tc.allow.Accept(ctx, tc.fee, []sdk.Msg{}) if !tc.accept { diff --git a/x/feegrant/simulation/decoder_test.go b/x/feegrant/simulation/decoder_test.go index e1f1bfddb46a..6033f9e75143 100644 --- a/x/feegrant/simulation/decoder_test.go +++ b/x/feegrant/simulation/decoder_test.go @@ -11,6 +11,7 @@ import ( "cosmossdk.io/x/feegrant/module" "cosmossdk.io/x/feegrant/simulation" + addresscodec "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/kv" @@ -27,8 +28,14 @@ func TestDecodeStore(t *testing.T) { encodingConfig := moduletestutil.MakeTestEncodingConfig(module.AppModuleBasic{}) cdc := encodingConfig.Codec dec := simulation.NewDecodeStore(cdc) + ac := addresscodec.NewBech32Codec("cosmos") - grant, err := feegrant.NewGrant(granterAddr, granteeAddr, &feegrant.BasicAllowance{ + granterStr, err := ac.BytesToString(granterAddr) + require.NoError(t, err) + granteeStr, err := ac.BytesToString(granteeAddr) + require.NoError(t, err) + + grant, err := feegrant.NewGrant(granterStr, granteeStr, &feegrant.BasicAllowance{ SpendLimit: sdk.NewCoins(sdk.NewCoin("foo", sdkmath.NewInt(100))), }) diff --git a/x/feegrant/simulation/genesis.go b/x/feegrant/simulation/genesis.go index 607111bc975c..14066e803326 100644 --- a/x/feegrant/simulation/genesis.go +++ b/x/feegrant/simulation/genesis.go @@ -18,12 +18,12 @@ func genFeeGrants(r *rand.Rand, accounts []simtypes.Account) []feegrant.Grant { for i := 0; i < len(accounts)-1; i++ { granter := accounts[i].Address grantee := accounts[i+1].Address - allowances[i] = generateRandomAllowances(granter, grantee, r) + allowances[i] = generateRandomAllowances(granter.String(), grantee.String(), r) // TODO decouple this from call .String() } return allowances } -func generateRandomAllowances(granter, grantee sdk.AccAddress, r *rand.Rand) feegrant.Grant { +func generateRandomAllowances(granter, grantee string, r *rand.Rand) feegrant.Grant { allowances := make([]feegrant.Grant, 3) spendLimit := sdk.NewCoins(sdk.NewCoin("stake", math.NewInt(100))) periodSpendLimit := sdk.NewCoins(sdk.NewCoin("stake", math.NewInt(10))) diff --git a/x/feegrant/simulation/operations.go b/x/feegrant/simulation/operations.go index 488ab82e8c98..78bb2950f345 100644 --- a/x/feegrant/simulation/operations.go +++ b/x/feegrant/simulation/operations.go @@ -83,7 +83,16 @@ func SimulateMsgGrantAllowance( ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { granter, _ := simtypes.RandomAcc(r, accs) grantee, _ := simtypes.RandomAcc(r, accs) - if grantee.Address.String() == granter.Address.String() { + granterStr, err := ak.AddressCodec().BytesToString(granter.Address) + if err != nil { + return simtypes.OperationMsg{}, nil, err + } + granteeStr, err := ak.AddressCodec().BytesToString(grantee.Address) + if err != nil { + return simtypes.OperationMsg{}, nil, err + } + + if granteeStr == granterStr { return simtypes.NoOpMsg(feegrant.ModuleName, TypeMsgGrantAllowance, "grantee and granter cannot be same"), nil, nil } @@ -98,11 +107,11 @@ func SimulateMsgGrantAllowance( return simtypes.NoOpMsg(feegrant.ModuleName, TypeMsgGrantAllowance, "unable to grant empty coins as SpendLimit"), nil, nil } - oneYear := ctx.BlockTime().AddDate(1, 0, 0) + oneYear := ctx.HeaderInfo().Time.AddDate(1, 0, 0) msg, err := feegrant.NewMsgGrantAllowance(&feegrant.BasicAllowance{ SpendLimit: spendableCoins, Expiration: &oneYear, - }, granter.Address, grantee.Address) + }, granterStr, granteeStr) if err != nil { return simtypes.NoOpMsg(feegrant.ModuleName, TypeMsgGrantAllowance, err.Error()), nil, err } @@ -138,9 +147,10 @@ func SimulateMsgRevokeAllowance( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { hasGrant := false + var granterAddr sdk.AccAddress var granteeAddr sdk.AccAddress - k.IterateAllFeeAllowances(ctx, func(grant feegrant.Grant) bool { + err := k.IterateAllFeeAllowances(ctx, func(grant feegrant.Grant) bool { granter, err := ac.StringToBytes(grant.Granter) if err != nil { panic(err) @@ -154,6 +164,9 @@ func SimulateMsgRevokeAllowance( hasGrant = true return true }) + if err != nil { + return simtypes.OperationMsg{}, nil, err + } if !hasGrant { return simtypes.NoOpMsg(feegrant.ModuleName, TypeMsgRevokeAllowance, "no grants"), nil, nil @@ -167,7 +180,15 @@ func SimulateMsgRevokeAllowance( account := ak.GetAccount(ctx, granter.Address) spendableCoins := bk.SpendableCoins(ctx, account.GetAddress()) - msg := feegrant.NewMsgRevokeAllowance(granterAddr, granteeAddr) + granterStr, err := ak.AddressCodec().BytesToString(granterAddr) + if err != nil { + return simtypes.NoOpMsg(feegrant.ModuleName, TypeMsgRevokeAllowance, err.Error()), nil, err + } + granteeStr, err := ak.AddressCodec().BytesToString(granteeAddr) + if err != nil { + return simtypes.NoOpMsg(feegrant.ModuleName, TypeMsgRevokeAllowance, err.Error()), nil, err + } + msg := feegrant.NewMsgRevokeAllowance(granterStr, granteeStr) txCtx := simulation.OperationInput{ R: r, diff --git a/x/feegrant/simulation/operations_test.go b/x/feegrant/simulation/operations_test.go index 4198d39d55f0..008517e7d02f 100644 --- a/x/feegrant/simulation/operations_test.go +++ b/x/feegrant/simulation/operations_test.go @@ -10,6 +10,7 @@ import ( "github.com/cosmos/gogoproto/proto" "github.com/stretchr/testify/suite" + "cosmossdk.io/core/header" "cosmossdk.io/depinject" "cosmossdk.io/log" "cosmossdk.io/x/feegrant" @@ -35,7 +36,6 @@ import ( _ "github.com/cosmos/cosmos-sdk/x/consensus" _ "github.com/cosmos/cosmos-sdk/x/genutil" _ "github.com/cosmos/cosmos-sdk/x/mint" - _ "github.com/cosmos/cosmos-sdk/x/params" _ "github.com/cosmos/cosmos-sdk/x/staking" ) @@ -63,7 +63,6 @@ func (suite *SimTestSuite) SetupTest() { configurator.StakingModule(), configurator.TxModule(), configurator.ConsensusModule(), - configurator.ParamsModule(), configurator.GenutilModule(), configurator.FeegrantModule(), ), @@ -99,8 +98,6 @@ func (suite *SimTestSuite) getTestingAccounts(r *rand.Rand, n int) []simtypes.Ac func (suite *SimTestSuite) TestWeightedOperations() { require := suite.Require() - suite.ctx.WithChainID("test-chain") - appParams := make(simtypes.AppParams) weightedOps := simulation.WeightedOperations( @@ -131,7 +128,7 @@ func (suite *SimTestSuite) TestWeightedOperations() { } for i, w := range weightedOps { - operationMsg, _, err := w.Op()(r, suite.app.BaseApp, suite.ctx, accs, suite.ctx.ChainID()) + operationMsg, _, err := w.Op()(r, suite.app.BaseApp, suite.ctx.WithHeaderInfo(header.Info{Time: time.Now()}), accs, suite.ctx.ChainID()) require.NoError(err) // the following checks are very much dependent from the ordering of the output given @@ -150,22 +147,26 @@ func (suite *SimTestSuite) TestSimulateMsgGrantAllowance() { s := rand.NewSource(1) r := rand.New(s) accounts := suite.getTestingAccounts(r, 3) + addr1, err := suite.accountKeeper.AddressCodec().BytesToString(accounts[1].Address) + require.NoError(err) + addr2, err := suite.accountKeeper.AddressCodec().BytesToString(accounts[2].Address) + require.NoError(err) // new block - _, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: app.LastBlockHeight() + 1}) + _, err = app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: app.LastBlockHeight() + 1}) require.NoError(err) // execute operation op := simulation.SimulateMsgGrantAllowance(codec.NewProtoCodec(suite.interfaceRegistry), suite.txConfig, suite.accountKeeper, suite.bankKeeper, suite.feegrantKeeper) - operationMsg, futureOperations, err := op(r, app.BaseApp, ctx, accounts, "") + operationMsg, futureOperations, err := op(r, app.BaseApp, ctx.WithHeaderInfo(header.Info{Time: time.Now()}), accounts, "") require.NoError(err) var msg feegrant.MsgGrantAllowance err = proto.Unmarshal(operationMsg.Msg, &msg) require.NoError(err) require.True(operationMsg.OK) - require.Equal(accounts[2].Address.String(), msg.Granter) - require.Equal(accounts[1].Address.String(), msg.Grantee) + require.Equal(addr2, msg.Granter) + require.Equal(addr1, msg.Grantee) require.Len(futureOperations, 0) } @@ -178,15 +179,15 @@ func (suite *SimTestSuite) TestSimulateMsgRevokeAllowance() { accounts := suite.getTestingAccounts(r, 3) // begin a new block - app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: suite.app.LastBlockHeight() + 1, Hash: suite.app.LastCommitID().Hash}) - + _, err := app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: suite.app.LastBlockHeight() + 1, Hash: suite.app.LastCommitID().Hash}) + require.NoError(err) feeAmt := sdk.TokensFromConsensusPower(200000, sdk.DefaultPowerReduction) feeCoins := sdk.NewCoins(sdk.NewCoin("foo", feeAmt)) granter, grantee := accounts[0], accounts[1] - oneYear := ctx.BlockTime().AddDate(1, 0, 0) - err := suite.feegrantKeeper.GrantAllowance( + oneYear := ctx.HeaderInfo().Time.AddDate(1, 0, 0) + err = suite.feegrantKeeper.GrantAllowance( ctx, granter.Address, grantee.Address, @@ -197,6 +198,11 @@ func (suite *SimTestSuite) TestSimulateMsgRevokeAllowance() { ) require.NoError(err) + granterStr, err := suite.accountKeeper.AddressCodec().BytesToString(accounts[0].Address) + require.NoError(err) + granteeStr, err := suite.accountKeeper.AddressCodec().BytesToString(accounts[1].Address) + require.NoError(err) + // execute operation op := simulation.SimulateMsgRevokeAllowance(codec.NewProtoCodec(suite.interfaceRegistry), suite.txConfig, suite.accountKeeper, suite.bankKeeper, suite.feegrantKeeper, codecaddress.NewBech32Codec("cosmos")) operationMsg, futureOperations, err := op(r, app.BaseApp, ctx, accounts, "") @@ -206,8 +212,8 @@ func (suite *SimTestSuite) TestSimulateMsgRevokeAllowance() { err = proto.Unmarshal(operationMsg.Msg, &msg) require.NoError(err) require.True(operationMsg.OK) - require.Equal(granter.Address.String(), msg.Granter) - require.Equal(grantee.Address.String(), msg.Grantee) + require.Equal(granterStr, msg.Granter) + require.Equal(granteeStr, msg.Grantee) require.Len(futureOperations, 0) } diff --git a/x/feegrant/tx.pb.go b/x/feegrant/tx.pb.go index f0dcf66635f2..7bc84761b388 100644 --- a/x/feegrant/tx.pb.go +++ b/x/feegrant/tx.pb.go @@ -225,17 +225,102 @@ func (m *MsgRevokeAllowanceResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgRevokeAllowanceResponse proto.InternalMessageInfo +// MsgPruneAllowances prunes expired fee allowances. +type MsgPruneAllowances struct { + // pruner is the address of the user pruning expired allowances. + Pruner string `protobuf:"bytes,1,opt,name=pruner,proto3" json:"pruner,omitempty"` +} + +func (m *MsgPruneAllowances) Reset() { *m = MsgPruneAllowances{} } +func (m *MsgPruneAllowances) String() string { return proto.CompactTextString(m) } +func (*MsgPruneAllowances) ProtoMessage() {} +func (*MsgPruneAllowances) Descriptor() ([]byte, []int) { + return fileDescriptor_dd44ad7946dad783, []int{4} +} +func (m *MsgPruneAllowances) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgPruneAllowances) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgPruneAllowances.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 *MsgPruneAllowances) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgPruneAllowances.Merge(m, src) +} +func (m *MsgPruneAllowances) XXX_Size() int { + return m.Size() +} +func (m *MsgPruneAllowances) XXX_DiscardUnknown() { + xxx_messageInfo_MsgPruneAllowances.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgPruneAllowances proto.InternalMessageInfo + +func (m *MsgPruneAllowances) GetPruner() string { + if m != nil { + return m.Pruner + } + return "" +} + +// MsgPruneAllowancesResponse defines the Msg/PruneAllowancesResponse response type. +type MsgPruneAllowancesResponse struct { +} + +func (m *MsgPruneAllowancesResponse) Reset() { *m = MsgPruneAllowancesResponse{} } +func (m *MsgPruneAllowancesResponse) String() string { return proto.CompactTextString(m) } +func (*MsgPruneAllowancesResponse) ProtoMessage() {} +func (*MsgPruneAllowancesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_dd44ad7946dad783, []int{5} +} +func (m *MsgPruneAllowancesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgPruneAllowancesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgPruneAllowancesResponse.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 *MsgPruneAllowancesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgPruneAllowancesResponse.Merge(m, src) +} +func (m *MsgPruneAllowancesResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgPruneAllowancesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgPruneAllowancesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgPruneAllowancesResponse proto.InternalMessageInfo + func init() { proto.RegisterType((*MsgGrantAllowance)(nil), "cosmos.feegrant.v1beta1.MsgGrantAllowance") proto.RegisterType((*MsgGrantAllowanceResponse)(nil), "cosmos.feegrant.v1beta1.MsgGrantAllowanceResponse") proto.RegisterType((*MsgRevokeAllowance)(nil), "cosmos.feegrant.v1beta1.MsgRevokeAllowance") proto.RegisterType((*MsgRevokeAllowanceResponse)(nil), "cosmos.feegrant.v1beta1.MsgRevokeAllowanceResponse") + proto.RegisterType((*MsgPruneAllowances)(nil), "cosmos.feegrant.v1beta1.MsgPruneAllowances") + proto.RegisterType((*MsgPruneAllowancesResponse)(nil), "cosmos.feegrant.v1beta1.MsgPruneAllowancesResponse") } func init() { proto.RegisterFile("cosmos/feegrant/v1beta1/tx.proto", fileDescriptor_dd44ad7946dad783) } var fileDescriptor_dd44ad7946dad783 = []byte{ - // 409 bytes of a gzipped FileDescriptorProto + // 455 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x48, 0xce, 0x2f, 0xce, 0xcd, 0x2f, 0xd6, 0x4f, 0x4b, 0x4d, 0x4d, 0x2f, 0x4a, 0xcc, 0x2b, 0xd1, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, 0x2f, 0xa9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x87, 0xa8, @@ -254,14 +339,17 @@ var fileDescriptor_dd44ad7946dad783 = []byte{ 0xb2, 0xf5, 0x31, 0x7c, 0xad, 0x24, 0xcd, 0x25, 0x89, 0x21, 0x18, 0x94, 0x5a, 0x5c, 0x90, 0x9f, 0x57, 0x9c, 0xaa, 0xb4, 0x86, 0x91, 0x4b, 0xc8, 0xb7, 0x38, 0x3d, 0x28, 0xb5, 0x2c, 0x3f, 0x3b, 0x95, 0xee, 0x21, 0x65, 0xa5, 0x87, 0xee, 0x15, 0x59, 0x54, 0xaf, 0xa0, 0xb9, 0x4b, 0x49, 0x86, - 0x4b, 0x0a, 0x53, 0x14, 0xe6, 0x19, 0xa3, 0xcf, 0x8c, 0x5c, 0xcc, 0xbe, 0xc5, 0xe9, 0x42, 0x05, - 0x5c, 0x7c, 0x68, 0x31, 0xaf, 0xa5, 0x87, 0x2b, 0x94, 0x31, 0x82, 0x46, 0xca, 0x88, 0x78, 0xb5, - 0x30, 0x9b, 0x85, 0x8a, 0xb9, 0xf8, 0xd1, 0x83, 0x50, 0x1b, 0x9f, 0x31, 0x68, 0x8a, 0xa5, 0x8c, - 0x49, 0x50, 0x0c, 0xb3, 0x54, 0x8a, 0xb5, 0xe1, 0xf9, 0x06, 0x2d, 0x46, 0x27, 0xc3, 0x13, 0x8f, - 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, - 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x82, 0xe6, 0x98, 0xe2, 0x94, 0x6c, 0xbd, 0xcc, - 0x7c, 0xfd, 0x0a, 0x78, 0x56, 0x4d, 0x62, 0x03, 0xa7, 0x42, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, - 0xff, 0x3f, 0x13, 0x6c, 0x2e, 0xc4, 0x03, 0x00, 0x00, + 0x4b, 0x0a, 0x53, 0x14, 0xee, 0x99, 0x60, 0xb0, 0x5f, 0x02, 0x8a, 0x4a, 0xf3, 0x10, 0x92, 0xc5, + 0x42, 0x06, 0x5c, 0x6c, 0x05, 0x20, 0x21, 0xc2, 0x5e, 0x81, 0xaa, 0xb3, 0xe2, 0x06, 0xb9, 0x0a, + 0xca, 0x81, 0x5a, 0x89, 0x66, 0x28, 0xcc, 0x4a, 0xa3, 0x17, 0x4c, 0x5c, 0xcc, 0xbe, 0xc5, 0xe9, + 0x42, 0x05, 0x5c, 0x7c, 0x68, 0x89, 0x4d, 0x4b, 0x0f, 0x57, 0xc4, 0x62, 0xc4, 0x86, 0x94, 0x11, + 0xf1, 0x6a, 0x61, 0x36, 0x0b, 0x15, 0x73, 0xf1, 0xa3, 0xc7, 0x9a, 0x36, 0x3e, 0x63, 0xd0, 0x14, + 0x4b, 0x19, 0x93, 0xa0, 0x18, 0xd9, 0x52, 0xf4, 0xe0, 0xc5, 0x6b, 0x29, 0x9a, 0x62, 0xfc, 0x96, + 0xe2, 0x08, 0x63, 0x29, 0xd6, 0x86, 0xe7, 0x1b, 0xb4, 0x18, 0x9d, 0x0c, 0x4f, 0x3c, 0x92, 0x63, + 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, + 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0x0a, 0x5a, 0x32, 0x14, 0xa7, 0x64, 0xeb, 0x65, 0xe6, 0xeb, + 0x57, 0xc0, 0x8b, 0xa4, 0x24, 0x36, 0x70, 0x6e, 0x33, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x8d, + 0x04, 0xdd, 0xf4, 0xac, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -282,6 +370,8 @@ type MsgClient interface { // RevokeAllowance revokes any fee allowance of granter's account that // has been granted to the grantee. RevokeAllowance(ctx context.Context, in *MsgRevokeAllowance, opts ...grpc.CallOption) (*MsgRevokeAllowanceResponse, error) + // PruneAllowances prunes expired fee allowances. + PruneAllowances(ctx context.Context, in *MsgPruneAllowances, opts ...grpc.CallOption) (*MsgPruneAllowancesResponse, error) } type msgClient struct { @@ -310,6 +400,15 @@ func (c *msgClient) RevokeAllowance(ctx context.Context, in *MsgRevokeAllowance, return out, nil } +func (c *msgClient) PruneAllowances(ctx context.Context, in *MsgPruneAllowances, opts ...grpc.CallOption) (*MsgPruneAllowancesResponse, error) { + out := new(MsgPruneAllowancesResponse) + err := c.cc.Invoke(ctx, "/cosmos.feegrant.v1beta1.Msg/PruneAllowances", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { // GrantAllowance grants fee allowance to the grantee on the granter's @@ -318,6 +417,8 @@ type MsgServer interface { // RevokeAllowance revokes any fee allowance of granter's account that // has been granted to the grantee. RevokeAllowance(context.Context, *MsgRevokeAllowance) (*MsgRevokeAllowanceResponse, error) + // PruneAllowances prunes expired fee allowances. + PruneAllowances(context.Context, *MsgPruneAllowances) (*MsgPruneAllowancesResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -330,6 +431,9 @@ func (*UnimplementedMsgServer) GrantAllowance(ctx context.Context, req *MsgGrant func (*UnimplementedMsgServer) RevokeAllowance(ctx context.Context, req *MsgRevokeAllowance) (*MsgRevokeAllowanceResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method RevokeAllowance not implemented") } +func (*UnimplementedMsgServer) PruneAllowances(ctx context.Context, req *MsgPruneAllowances) (*MsgPruneAllowancesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PruneAllowances not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -371,6 +475,24 @@ func _Msg_RevokeAllowance_Handler(srv interface{}, ctx context.Context, dec func return interceptor(ctx, in, info, handler) } +func _Msg_PruneAllowances_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgPruneAllowances) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).PruneAllowances(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.feegrant.v1beta1.Msg/PruneAllowances", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).PruneAllowances(ctx, req.(*MsgPruneAllowances)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "cosmos.feegrant.v1beta1.Msg", HandlerType: (*MsgServer)(nil), @@ -383,6 +505,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "RevokeAllowance", Handler: _Msg_RevokeAllowance_Handler, }, + { + MethodName: "PruneAllowances", + Handler: _Msg_PruneAllowances_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "cosmos/feegrant/v1beta1/tx.proto", @@ -520,6 +646,59 @@ func (m *MsgRevokeAllowanceResponse) MarshalToSizedBuffer(dAtA []byte) (int, err return len(dAtA) - i, nil } +func (m *MsgPruneAllowances) 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 *MsgPruneAllowances) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgPruneAllowances) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Pruner) > 0 { + i -= len(m.Pruner) + copy(dAtA[i:], m.Pruner) + i = encodeVarintTx(dAtA, i, uint64(len(m.Pruner))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgPruneAllowancesResponse) 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 *MsgPruneAllowancesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgPruneAllowancesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func encodeVarintTx(dAtA []byte, offset int, v uint64) int { offset -= sovTx(v) base := offset @@ -587,6 +766,28 @@ func (m *MsgRevokeAllowanceResponse) Size() (n int) { return n } +func (m *MsgPruneAllowances) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Pruner) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgPruneAllowancesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func sovTx(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -957,6 +1158,138 @@ func (m *MsgRevokeAllowanceResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgPruneAllowances) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgPruneAllowances: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgPruneAllowances: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pruner", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Pruner = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgPruneAllowancesResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgPruneAllowancesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgPruneAllowancesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTx(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0