diff --git a/.golangci.yml b/.golangci.yml
index a79f7d7e44..556a02e597 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -20,12 +20,10 @@ linters:
- gosimple
- govet
- ineffassign
- - interfacer
- - maligned
- misspell
- nakedret
- prealloc
- - scopelint
+ - exportloopref
- staticcheck
- structcheck
- stylecheck
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5ef8631696..63275b5125 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -53,7 +53,6 @@ Ref: https://keepachangelog.com/en/1.0.0/
* updated the keyring display structure (it uses protobuf JSON serialization) - the output is more verbose.
* Renamed `MarshalAny` and `UnmarshalAny` to `MarshalInterface` and `UnmarshalInterface` respectively. These functions must take an interface as parameter (not a concrete type nor `Any` object). Underneath they use `Any` wrapping for correct protobuf serialization.
* CLI: removed `--text` flag from `show-node-id` command; the text format for public keys is not used any more - instead we use ProtoJSON.
-* [\#9026](https://github.com/cosmos/cosmos-sdk/pull/9026) The `tx sign` and `tx sign-batch` CLI commands use SIGN_MODE_DIRECT by default for local pubkeys. For multisigs and ledger keys, the default LEGACY_AMINO_JSON is kept.
### API Breaking Changes
@@ -106,13 +105,34 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Bug Fixes
-* (gRPC) [\#9015](https://github.com/cosmos/cosmos-sdk/pull/9015) Fix invalid status code when accessing gRPC endpoints.
* (gRPC) [\#8945](https://github.com/cosmos/cosmos-sdk/pull/8945) gRPC reflection now works correctly.
* (keyring) [#\8635](https://github.com/cosmos/cosmos-sdk/issues/8635) Remove hardcoded default passphrase value on `NewMnemonic`
* (x/bank) [\#8434](https://github.com/cosmos/cosmos-sdk/pull/8434) Fix legacy REST API `GET /bank/total` and `GET /bank/total/{denom}` in swagger
* (x/slashing) [\#8427](https://github.com/cosmos/cosmos-sdk/pull/8427) Fix query signing infos command
* (server) [\#8399](https://github.com/cosmos/cosmos-sdk/pull/8399) fix gRPC-web flag default value
-* [\#9026](https://github.com/cosmos/cosmos-sdk/pull/9026) Fix bug of `gentx` command not working with ledger keys.
+
+### Deprecated
+
+* (grpc) [\#8926](https://github.com/cosmos/cosmos-sdk/pull/8926) The `tx` field in `SimulateRequest` has been deprecated, prefer to pass `tx_bytes` instead.
+
+## [v0.42.4](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.42.4) - 2021-04-08
+
+### Client Breaking Changes
+
+* [\#9026](https://github.com/cosmos/cosmos-sdk/pull/9026) By default, the `tx sign` and `tx sign-batch` CLI commands use SIGN_MODE_DIRECT to sign transactions for local pubkeys. For multisigs and ledger keys, the default LEGACY_AMINO_JSON is used.
+
+### Bug Fixes
+
+* (gRPC) [\#9015](https://github.com/cosmos/cosmos-sdk/pull/9015) Fix invalid status code when accessing gRPC endpoints.
+* [\#9026](https://github.com/cosmos/cosmos-sdk/pull/9026) Fixed the bug that caused the `gentx` command to fail for Ledger keys.
+
+### Improvements
+
+* [\#9081](https://github.com/cosmos/cosmos-sdk/pull/9081) Upgrade Tendermint to v0.34.9 that includes a security issue fix for Tendermint light clients.
+
+## [v0.42.3](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.42.3) - 2021-03-24
+
+This release fixes a security vulnerability identified in x/bank.
## [v0.42.2](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.42.2) - 2021-03-19
@@ -131,10 +151,6 @@ Ref: https://keepachangelog.com/en/1.0.0/
This release fixes security vulnerability identified in the simapp.
-### Deprecated
-
-* (grpc) [\#8926](https://github.com/cosmos/cosmos-sdk/pull/8926) The `tx` field in `SimulateRequest` has been deprecated, prefer to pass `tx_bytes` instead.
-
## [v0.42.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.42.0) - 2021-03-08
**IMPORTANT**: This release contains an important security fix for all non Cosmos Hub chains running Stargate version of the Cosmos SDK (>0.40). Non-hub chains should not be using any version of the SDK in the v0.40.x or v0.41.x release series. See [#8461](https://github.com/cosmos/cosmos-sdk/pull/8461) for more details.
diff --git a/cosmovisor/upgrade.go b/cosmovisor/upgrade.go
index 3057597f7d..7e8091d611 100644
--- a/cosmovisor/upgrade.go
+++ b/cosmovisor/upgrade.go
@@ -12,6 +12,7 @@ import (
"strings"
"github.com/hashicorp/go-getter"
+ "github.com/otiai10/copy"
)
// DoUpgrade will be called after the log message has been parsed and the process has terminated.
@@ -62,10 +63,19 @@ func DownloadBinary(cfg *Config, info *UpgradeInfo) error {
if err != nil {
dirPath := cfg.UpgradeDir(info.Name)
err = getter.Get(dirPath, url)
+ if err != nil {
+ return err
+ }
+ err = EnsureBinary(binPath)
+ // copy binary to binPath from dirPath if zipped directory don't contain bin directory to wrap the binary
+ if err != nil {
+ err = copy.Copy(filepath.Join(dirPath, cfg.Name), binPath)
+ if err != nil {
+ return err
+ }
+ }
}
- if err != nil {
- return err
- }
+
// if it is successful, let's ensure the binary is executable
return MarkExecutable(binPath)
}
diff --git a/docs/core/proto-docs.md b/docs/core/proto-docs.md
index d0709133a1..0044d66896 100644
--- a/docs/core/proto-docs.md
+++ b/docs/core/proto-docs.md
@@ -307,6 +307,7 @@
- [Msg](#cosmos.evidence.v1beta1.Msg)
- [cosmos/feegrant/v1beta1/feegrant.proto](#cosmos/feegrant/v1beta1/feegrant.proto)
+ - [AllowedMsgFeeAllowance](#cosmos.feegrant.v1beta1.AllowedMsgFeeAllowance)
- [BasicFeeAllowance](#cosmos.feegrant.v1beta1.BasicFeeAllowance)
- [Duration](#cosmos.feegrant.v1beta1.Duration)
- [ExpiresAt](#cosmos.feegrant.v1beta1.ExpiresAt)
@@ -4543,6 +4544,22 @@ Msg defines the evidence Msg service.
+
+
+### AllowedMsgFeeAllowance
+AllowedMsgFeeAllowance creates allowance only for specified message types.
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| `allowance` | [google.protobuf.Any](#google.protobuf.Any) | | allowance can be any of basic and filtered fee allowance. |
+| `allowed_messages` | [string](#string) | repeated | allowed_messages are the messages for which the grantee has the access. |
+
+
+
+
+
+
### BasicFeeAllowance
diff --git a/go.mod b/go.mod
index 55ad2b4272..302b631a92 100644
--- a/go.mod
+++ b/go.mod
@@ -30,7 +30,7 @@ require (
github.com/jhump/protoreflect v1.8.2
github.com/magiconair/properties v1.8.5
github.com/mattn/go-isatty v0.0.12
- github.com/otiai10/copy v1.5.0
+ github.com/otiai10/copy v1.5.1
github.com/pelletier/go-toml v1.8.1 // indirect
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.10.0
@@ -49,7 +49,7 @@ require (
github.com/tendermint/cosmos-rosetta-gateway v0.3.0-rc2.0.20210304154332-87d6ca4410df
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15
github.com/tendermint/go-amino v0.16.0
- github.com/tendermint/tendermint v0.34.8
+ github.com/tendermint/tendermint v0.34.9
github.com/tendermint/tm-db v0.6.4
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f
diff --git a/go.sum b/go.sum
index 879ac8e520..8cc2fdc748 100644
--- a/go.sum
+++ b/go.sum
@@ -284,6 +284,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64=
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us=
+github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
@@ -507,8 +509,8 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
-github.com/otiai10/copy v1.5.0 h1:SoXDGnlTUZoqB/wSuj/Y5L6T5i6iN4YRAcMCd+JnLNU=
-github.com/otiai10/copy v1.5.0/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E=
+github.com/otiai10/copy v1.5.1 h1:a/cs2E1/1V0az8K5nblbl+ymEa4E11AfaOLMar8V34w=
+github.com/otiai10/copy v1.5.1/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI=
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
@@ -678,8 +680,8 @@ github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoM
github.com/tendermint/tendermint v0.34.0-rc4/go.mod h1:yotsojf2C1QBOw4dZrTcxbyxmPUrT4hNuOQWX9XUwB4=
github.com/tendermint/tendermint v0.34.0-rc6/go.mod h1:ugzyZO5foutZImv0Iyx/gOFCX6mjJTgbLHTwi17VDVg=
github.com/tendermint/tendermint v0.34.0/go.mod h1:Aj3PIipBFSNO21r+Lq3TtzQ+uKESxkbA3yo/INM4QwQ=
-github.com/tendermint/tendermint v0.34.8 h1:PMWgUx47FrNTsfhxCWzoiIlVAC1SE9+WBlnsF9oQW0I=
-github.com/tendermint/tendermint v0.34.8/go.mod h1:JVuu3V1ZexOaZG8VJMRl8lnfrGw6hEB2TVnoUwKRbss=
+github.com/tendermint/tendermint v0.34.9 h1:9P2MXDEPOcPW0NBcHQ/HDSfvczZm+q5nUUw7AZ6f1Vc=
+github.com/tendermint/tendermint v0.34.9/go.mod h1:kl4Z1JwGx1I+u1SXIzMDy7Z3T8LiMeCAOnzNn6AIMT4=
github.com/tendermint/tm-db v0.6.2/go.mod h1:GYtQ67SUvATOcoY8/+x6ylk8Qo02BQyLrAs+yAcLvGI=
github.com/tendermint/tm-db v0.6.3/go.mod h1:lfA1dL9/Y/Y8wwyPp2NMLyn5P5Ptr/gvDFNWtrCWSf8=
github.com/tendermint/tm-db v0.6.4 h1:3N2jlnYQkXNQclQwd/eKV/NzlqPlfK21cpRRIx80XXQ=
diff --git a/proto/cosmos/feegrant/v1beta1/feegrant.proto b/proto/cosmos/feegrant/v1beta1/feegrant.proto
index 534de582d9..15382e56e3 100644
--- a/proto/cosmos/feegrant/v1beta1/feegrant.proto
+++ b/proto/cosmos/feegrant/v1beta1/feegrant.proto
@@ -52,6 +52,18 @@ message PeriodicFeeAllowance {
ExpiresAt period_reset = 5 [(gogoproto.nullable) = false];
}
+// AllowedMsgFeeAllowance creates allowance only for specified message types.
+message AllowedMsgFeeAllowance {
+ option (gogoproto.goproto_getters) = false;
+ option (cosmos_proto.implements_interface) = "FeeAllowanceI";
+
+ // allowance can be any of basic and filtered fee allowance.
+ google.protobuf.Any allowance = 1 [(cosmos_proto.accepts_interface) = "FeeAllowanceI"];
+
+ // allowed_messages are the messages for which the grantee has the access.
+ repeated string allowed_messages = 2;
+}
+
// Duration is a span of a clock time or number of blocks.
// This is designed to be added to an ExpiresAt struct.
message Duration {
diff --git a/store/types/listening_test.go b/store/types/listening_test.go
index 96c59ca752..acf17b5001 100644
--- a/store/types/listening_test.go
+++ b/store/types/listening_test.go
@@ -4,9 +4,10 @@ import (
"bytes"
"testing"
+ "github.com/stretchr/testify/require"
+
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
- "github.com/stretchr/testify/require"
)
func TestNewStoreKVPairWriteListener(t *testing.T) {
diff --git a/testutil/testdata/tx.go b/testutil/testdata/tx.go
index cb7ae9d370..4da80c9e4e 100644
--- a/testutil/testdata/tx.go
+++ b/testutil/testdata/tx.go
@@ -3,11 +3,12 @@ package testdata
import (
"encoding/json"
+ "github.com/stretchr/testify/require"
+
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256r1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
- "github.com/stretchr/testify/require"
)
// KeyTestPubAddr generates a new secp256k1 keypair.
diff --git a/types/msgservice/msg_service.go b/types/msgservice/msg_service.go
index 277ee3d157..ddffed943e 100644
--- a/types/msgservice/msg_service.go
+++ b/types/msgservice/msg_service.go
@@ -3,6 +3,7 @@ package msgservice
import (
"context"
"fmt"
+ "strings"
"github.com/gogo/protobuf/proto"
"google.golang.org/grpc"
@@ -42,3 +43,9 @@ func RegisterMsgServiceDesc(registry codectypes.InterfaceRegistry, sd *grpc.Serv
func noopInterceptor(_ context.Context, _ interface{}, _ *grpc.UnaryServerInfo, _ grpc.UnaryHandler) (interface{}, error) {
return nil, nil
}
+
+// IsServiceMsg checks if a type URL corresponds to a service method name,
+// i.e. /cosmos.bank.Msg/Send vs /cosmos.bank.MsgSend
+func IsServiceMsg(typeURL string) bool {
+ return strings.Count(typeURL, "/") >= 2
+}
diff --git a/types/query/pagination.go b/types/query/pagination.go
index 9d9431faba..dc7f678c56 100644
--- a/types/query/pagination.go
+++ b/types/query/pagination.go
@@ -7,8 +7,9 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
- "github.com/cosmos/cosmos-sdk/store/types"
db "github.com/tendermint/tm-db"
+
+ "github.com/cosmos/cosmos-sdk/store/types"
)
// DefaultLimit is the default `limit` for queries
diff --git a/types/tx/types.go b/types/tx/types.go
index a0b05b5dce..66fbb193ad 100644
--- a/types/tx/types.go
+++ b/types/tx/types.go
@@ -2,12 +2,12 @@ package tx
import (
"fmt"
- "strings"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+ "github.com/cosmos/cosmos-sdk/types/msgservice"
)
// MaxGasWanted defines the max gas allowed.
@@ -27,7 +27,7 @@ func (t *Tx) GetMsgs() []sdk.Msg {
res := make([]sdk.Msg, len(anys))
for i, any := range anys {
var msg sdk.Msg
- if isServiceMsg(any.TypeUrl) {
+ if msgservice.IsServiceMsg(any.TypeUrl) {
req := any.GetCachedValue()
if req == nil {
panic("Any cached value is nil. Transaction messages must be correctly packed Any values.")
@@ -183,7 +183,7 @@ func (m *TxBody) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
for _, any := range m.Messages {
// If the any's typeUrl contains 2 slashes, then we unpack the any into
// a ServiceMsg struct as per ADR-031.
- if isServiceMsg(any.TypeUrl) {
+ if msgservice.IsServiceMsg(any.TypeUrl) {
var req sdk.MsgRequest
err := unpacker.UnpackAny(any, &req)
if err != nil {
@@ -222,9 +222,3 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
registry.RegisterInterface("cosmos.tx.v1beta1.Tx", (*sdk.Tx)(nil))
registry.RegisterImplementations((*sdk.Tx)(nil), &Tx{})
}
-
-// isServiceMsg checks if a type URL corresponds to a service method name,
-// i.e. /cosmos.bank.Msg/Send vs /cosmos.bank.MsgSend
-func isServiceMsg(typeURL string) bool {
- return strings.Count(typeURL, "/") >= 2
-}
diff --git a/x/auth/ante/expected_keepers.go b/x/auth/ante/expected_keepers.go
index e9c2286f03..4dbbbd21c7 100644
--- a/x/auth/ante/expected_keepers.go
+++ b/x/auth/ante/expected_keepers.go
@@ -16,5 +16,5 @@ type AccountKeeper interface {
// FeegrantKeeper defines the expected feegrant keeper.
type FeegrantKeeper interface {
- UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins) error
+ UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins, msgs []sdk.Msg) error
}
diff --git a/x/auth/ante/fee.go b/x/auth/ante/fee.go
index 3cc6aee10c..e183887d0f 100644
--- a/x/auth/ante/fee.go
+++ b/x/auth/ante/fee.go
@@ -94,7 +94,7 @@ func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo
if dfd.feegrantKeeper == nil {
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "fee grants are not enabled")
} else if !feeGranter.Equals(feePayer) {
- err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, fee)
+ err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, fee, tx.GetMsgs())
if err != nil {
return ctx, sdkerrors.Wrapf(err, "%s not allowed to pay fees from %s", feeGranter, feePayer)
diff --git a/x/auth/keeper/keeper.go b/x/auth/keeper/keeper.go
index 3ead1674e7..62a10e26ba 100644
--- a/x/auth/keeper/keeper.go
+++ b/x/auth/keeper/keeper.go
@@ -206,7 +206,7 @@ func (ak AccountKeeper) GetModuleAccount(ctx sdk.Context, moduleName string) typ
}
// SetModuleAccount sets the module account to the auth account store
-func (ak AccountKeeper) SetModuleAccount(ctx sdk.Context, macc types.ModuleAccountI) { //nolint:interfacer
+func (ak AccountKeeper) SetModuleAccount(ctx sdk.Context, macc types.ModuleAccountI) {
ak.SetAccount(ctx, macc)
}
diff --git a/x/auth/keeper/migrations.go b/x/auth/keeper/migrations.go
new file mode 100644
index 0000000000..d3ad7a2f8c
--- /dev/null
+++ b/x/auth/keeper/migrations.go
@@ -0,0 +1,43 @@
+package keeper
+
+import (
+ "github.com/gogo/protobuf/grpc"
+
+ v043 "github.com/cosmos/cosmos-sdk/x/auth/legacy/v043"
+ "github.com/cosmos/cosmos-sdk/x/auth/types"
+
+ sdk "github.com/cosmos/cosmos-sdk/types"
+)
+
+// Migrator is a struct for handling in-place store migrations.
+type Migrator struct {
+ keeper AccountKeeper
+ queryServer grpc.Server
+}
+
+// NewMigrator returns a new Migrator.
+func NewMigrator(keeper AccountKeeper, queryServer grpc.Server) Migrator {
+ return Migrator{keeper: keeper, queryServer: queryServer}
+}
+
+// Migrate1to2 migrates from version 1 to 2.
+func (m Migrator) Migrate1to2(ctx sdk.Context) error {
+ var iterErr error
+
+ m.keeper.IterateAccounts(ctx, func(account types.AccountI) (stop bool) {
+ wb, err := v043.MigrateAccount(ctx, account, m.queryServer)
+ if err != nil {
+ iterErr = err
+ return true
+ }
+
+ if wb == nil {
+ return false
+ }
+
+ m.keeper.SetAccount(ctx, wb)
+ return false
+ })
+
+ return iterErr
+}
diff --git a/x/auth/legacy/v043/store.go b/x/auth/legacy/v043/store.go
new file mode 100644
index 0000000000..27db787f84
--- /dev/null
+++ b/x/auth/legacy/v043/store.go
@@ -0,0 +1,269 @@
+package v043
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/gogo/protobuf/grpc"
+ "github.com/gogo/protobuf/proto"
+ abci "github.com/tendermint/tendermint/abci/types"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ "github.com/cosmos/cosmos-sdk/baseapp"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+ "github.com/cosmos/cosmos-sdk/x/auth/types"
+ "github.com/cosmos/cosmos-sdk/x/auth/vesting/exported"
+ vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
+ banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+ stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
+)
+
+const (
+ delegatorDelegationPath = "/cosmos.staking.v1beta1.Query/DelegatorDelegations"
+ stakingParamsPath = "/cosmos.staking.v1beta1.Query/Params"
+ delegatorUnbondingDelegationsPath = "/cosmos.staking.v1beta1.Query/DelegatorUnbondingDelegations"
+ balancesPath = "/cosmos.bank.v1beta1.Query/AllBalances"
+)
+
+func migrateVestingAccounts(ctx sdk.Context, account types.AccountI, queryServer grpc.Server) (types.AccountI, error) {
+ bondDenom, err := getBondDenom(ctx, queryServer)
+
+ if err != nil {
+ return nil, err
+ }
+
+ asVesting, ok := account.(exported.VestingAccount)
+ if !ok {
+ return nil, nil
+ }
+
+ addr := account.GetAddress().String()
+ balance, err := getBalance(
+ ctx,
+ addr,
+ queryServer,
+ )
+
+ if err != nil {
+ return nil, err
+ }
+
+ delegations, err := getDelegatorDelegationsSum(
+ ctx,
+ addr,
+ queryServer,
+ )
+
+ if err != nil {
+ return nil, err
+ }
+
+ unbondingDelegations, err := getDelegatorUnbondingDelegationsSum(
+ ctx,
+ addr,
+ bondDenom,
+ queryServer,
+ )
+
+ if err != nil {
+ return nil, err
+ }
+
+ delegations = delegations.Add(unbondingDelegations...)
+
+ asVesting, ok = resetVestingDelegatedBalances(asVesting)
+ if !ok {
+ return nil, nil
+ }
+
+ // balance before any delegation includes balance of delegation
+ for _, coin := range delegations {
+ balance = balance.Add(coin)
+ }
+
+ asVesting.TrackDelegation(ctx.BlockTime(), balance, delegations)
+
+ return asVesting.(types.AccountI), nil
+}
+
+func resetVestingDelegatedBalances(evacct exported.VestingAccount) (exported.VestingAccount, bool) {
+ // reset `DelegatedVesting` and `DelegatedFree` to zero
+ df := sdk.NewCoins()
+ dv := sdk.NewCoins()
+
+ switch vacct := evacct.(type) {
+ case *vestingtypes.ContinuousVestingAccount:
+ vacct.DelegatedVesting = dv
+ vacct.DelegatedFree = df
+ return vacct, true
+ case *vestingtypes.DelayedVestingAccount:
+ vacct.DelegatedVesting = dv
+ vacct.DelegatedFree = df
+ return vacct, true
+ case *vestingtypes.PeriodicVestingAccount:
+ vacct.DelegatedVesting = dv
+ vacct.DelegatedFree = df
+ return vacct, true
+ default:
+ return nil, false
+ }
+}
+
+func getDelegatorDelegationsSum(ctx sdk.Context, address string, queryServer grpc.Server) (sdk.Coins, error) {
+ querier, ok := queryServer.(*baseapp.GRPCQueryRouter)
+ if !ok {
+ return nil, fmt.Errorf("unexpected type: %T wanted *baseapp.GRPCQueryRouter", queryServer)
+ }
+
+ queryFn := querier.Route(delegatorDelegationPath)
+
+ q := &stakingtypes.QueryDelegatorDelegationsRequest{
+ DelegatorAddr: address,
+ }
+
+ b, err := proto.Marshal(q)
+ if err != nil {
+ return nil, fmt.Errorf("cannot marshal staking type query request, %w", err)
+ }
+ req := abci.RequestQuery{
+ Data: b,
+ Path: delegatorDelegationPath,
+ }
+ resp, err := queryFn(ctx, req)
+ if err != nil {
+ e, ok := status.FromError(err)
+ if ok && e.Code() == codes.NotFound {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("staking query error, %w", err)
+ }
+
+ balance := new(stakingtypes.QueryDelegatorDelegationsResponse)
+ if err := proto.Unmarshal(resp.Value, balance); err != nil {
+ return nil, fmt.Errorf("unable to unmarshal delegator query delegations: %w", err)
+ }
+
+ res := sdk.NewCoins()
+ for _, i := range balance.DelegationResponses {
+ res = res.Add(i.Balance)
+ }
+
+ return res, nil
+}
+
+func getDelegatorUnbondingDelegationsSum(ctx sdk.Context, address, bondDenom string, queryServer grpc.Server) (sdk.Coins, error) {
+ querier, ok := queryServer.(*baseapp.GRPCQueryRouter)
+ if !ok {
+ return nil, fmt.Errorf("unexpected type: %T wanted *baseapp.GRPCQueryRouter", queryServer)
+ }
+
+ queryFn := querier.Route(delegatorUnbondingDelegationsPath)
+
+ q := &stakingtypes.QueryDelegatorUnbondingDelegationsRequest{
+ DelegatorAddr: address,
+ }
+
+ b, err := proto.Marshal(q)
+ if err != nil {
+ return nil, fmt.Errorf("cannot marshal staking type query request, %w", err)
+ }
+ req := abci.RequestQuery{
+ Data: b,
+ Path: delegatorUnbondingDelegationsPath,
+ }
+ resp, err := queryFn(ctx, req)
+ if err != nil && !errors.Is(err, sdkerrors.ErrNotFound) {
+ e, ok := status.FromError(err)
+ if ok && e.Code() == codes.NotFound {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("staking query error, %w", err)
+ }
+
+ balance := new(stakingtypes.QueryDelegatorUnbondingDelegationsResponse)
+ if err := proto.Unmarshal(resp.Value, balance); err != nil {
+ return nil, fmt.Errorf("unable to unmarshal delegator query delegations: %w", err)
+ }
+
+ res := sdk.NewCoins()
+ for _, i := range balance.UnbondingResponses {
+ for _, r := range i.Entries {
+ res = res.Add(sdk.NewCoin(bondDenom, r.Balance))
+ }
+ }
+
+ return res, nil
+}
+
+func getBalance(ctx sdk.Context, address string, queryServer grpc.Server) (sdk.Coins, error) {
+ querier, ok := queryServer.(*baseapp.GRPCQueryRouter)
+ if !ok {
+ return nil, fmt.Errorf("unexpected type: %T wanted *baseapp.GRPCQueryRouter", queryServer)
+ }
+
+ queryFn := querier.Route(balancesPath)
+
+ q := &banktypes.QueryAllBalancesRequest{
+ Address: address,
+ Pagination: nil,
+ }
+ b, err := proto.Marshal(q)
+ if err != nil {
+ return nil, fmt.Errorf("cannot marshal bank type query request, %w", err)
+ }
+
+ req := abci.RequestQuery{
+ Data: b,
+ Path: balancesPath,
+ }
+ resp, err := queryFn(ctx, req)
+ if err != nil {
+ return nil, fmt.Errorf("bank query error, %w", err)
+ }
+ balance := new(banktypes.QueryAllBalancesResponse)
+ if err := proto.Unmarshal(resp.Value, balance); err != nil {
+ return nil, fmt.Errorf("unable to unmarshal bank balance response: %w", err)
+ }
+ return balance.Balances, nil
+}
+
+func getBondDenom(ctx sdk.Context, queryServer grpc.Server) (string, error) {
+ querier, ok := queryServer.(*baseapp.GRPCQueryRouter)
+ if !ok {
+ return "", fmt.Errorf("unexpected type: %T wanted *baseapp.GRPCQueryRouter", queryServer)
+ }
+
+ queryFn := querier.Route(stakingParamsPath)
+
+ q := &stakingtypes.QueryParamsRequest{}
+
+ b, err := proto.Marshal(q)
+ if err != nil {
+ return "", fmt.Errorf("cannot marshal staking params query request, %w", err)
+ }
+ req := abci.RequestQuery{
+ Data: b,
+ Path: stakingParamsPath,
+ }
+
+ resp, err := queryFn(ctx, req)
+ if err != nil {
+ return "", fmt.Errorf("staking query error, %w", err)
+ }
+
+ params := new(stakingtypes.QueryParamsResponse)
+ if err := proto.Unmarshal(resp.Value, params); err != nil {
+ return "", fmt.Errorf("unable to unmarshal delegator query delegations: %w", err)
+ }
+
+ return params.Params.BondDenom, nil
+}
+
+// MigrateAccount migrates vesting account to make the DelegatedVesting and DelegatedFree fields correctly
+// track delegations.
+// References: https://github.com/cosmos/cosmos-sdk/issues/8601, https://github.com/cosmos/cosmos-sdk/issues/8812
+func MigrateAccount(ctx sdk.Context, account types.AccountI, queryServer grpc.Server) (types.AccountI, error) {
+ return migrateVestingAccounts(ctx, account, queryServer)
+}
diff --git a/x/auth/legacy/v043/store_test.go b/x/auth/legacy/v043/store_test.go
new file mode 100644
index 0000000000..acd06fe878
--- /dev/null
+++ b/x/auth/legacy/v043/store_test.go
@@ -0,0 +1,687 @@
+package v043_test
+
+import (
+ "fmt"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
+
+ "github.com/cosmos/cosmos-sdk/simapp"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
+ authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+ "github.com/cosmos/cosmos-sdk/x/auth/vesting/exported"
+ "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
+ "github.com/cosmos/cosmos-sdk/x/staking"
+ stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
+ stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
+)
+
+func TestMigrateVestingAccounts(t *testing.T) {
+ testCases := []struct {
+ name string
+ prepareFunc func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress)
+ garbageFunc func(ctx sdk.Context, vesting exported.VestingAccount, app *simapp.SimApp) error
+ tokenAmount int64
+ expVested int64
+ expFree int64
+ blockTime int64
+ }{
+ {
+ "delayed vesting has vested, multiple delegations less than the total account balance",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(200)))
+ delayedAccount := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().Unix())
+
+ ctx = ctx.WithBlockTime(ctx.BlockTime().AddDate(1, 0, 0))
+
+ app.AccountKeeper.SetAccount(ctx, delayedAccount)
+
+ _, err := app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(100), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ _, err = app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(100), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ _, err = app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(100), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ },
+ cleartTrackingFields,
+ 300,
+ 0,
+ 300,
+ 0,
+ },
+ {
+ "delayed vesting has vested, single delegations which exceed the vested amount",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(200)))
+ delayedAccount := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().Unix())
+
+ ctx = ctx.WithBlockTime(ctx.BlockTime().AddDate(1, 0, 0))
+
+ app.AccountKeeper.SetAccount(ctx, delayedAccount)
+
+ _, err := app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(300), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ },
+ cleartTrackingFields,
+ 300,
+ 0,
+ 300,
+ 0,
+ },
+ {
+ "delayed vesting has vested, multiple delegations which exceed the vested amount",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(200)))
+ delayedAccount := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().Unix())
+
+ ctx = ctx.WithBlockTime(ctx.BlockTime().AddDate(1, 0, 0))
+
+ app.AccountKeeper.SetAccount(ctx, delayedAccount)
+
+ _, err := app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(100), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ _, err = app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(100), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ _, err = app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(100), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ },
+ cleartTrackingFields,
+ 300,
+ 0,
+ 300,
+ 0,
+ },
+ {
+ "delayed vesting has not vested, single delegations which exceed the vested amount",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(200)))
+ delayedAccount := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().AddDate(1, 0, 0).Unix())
+
+ app.AccountKeeper.SetAccount(ctx, delayedAccount)
+
+ _, err := app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(300), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ },
+ cleartTrackingFields,
+ 300,
+ 200,
+ 100,
+ 0,
+ },
+ {
+ "delayed vesting has not vested, multiple delegations which exceed the vested amount",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(200)))
+ delayedAccount := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().AddDate(1, 0, 0).Unix())
+
+ app.AccountKeeper.SetAccount(ctx, delayedAccount)
+
+ _, err := app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(100), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ _, err = app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(100), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ _, err = app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(100), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ },
+ cleartTrackingFields,
+ 300,
+ 200,
+ 100,
+ 0,
+ },
+ {
+ "not end time",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(300)))
+ delayedAccount := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().AddDate(1, 0, 0).Unix())
+
+ app.AccountKeeper.SetAccount(ctx, delayedAccount)
+
+ _, err := app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(100), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ _, err = app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(100), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ _, err = app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(100), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ },
+ cleartTrackingFields,
+ 300,
+ 300,
+ 0,
+ 0,
+ },
+ {
+ "delayed vesting has not vested, single delegation greater than the total account balance",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(300)))
+ delayedAccount := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().AddDate(1, 0, 0).Unix())
+
+ app.AccountKeeper.SetAccount(ctx, delayedAccount)
+
+ _, err := app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(300), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ },
+ cleartTrackingFields,
+ 300,
+ 300,
+ 0,
+ 0,
+ },
+ {
+ "delayed vesting has vested, single delegation greater than the total account balance",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(300)))
+ delayedAccount := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().Unix())
+
+ ctx = ctx.WithBlockTime(ctx.BlockTime().AddDate(1, 0, 0))
+
+ app.AccountKeeper.SetAccount(ctx, delayedAccount)
+
+ _, err := app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(300), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ },
+ cleartTrackingFields,
+ 300,
+ 0,
+ 300,
+ 0,
+ },
+ {
+ "continuous vesting, start time after blocktime",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+
+ startTime := ctx.BlockTime().AddDate(1, 0, 0).Unix()
+ endTime := ctx.BlockTime().AddDate(2, 0, 0).Unix()
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(300)))
+ delayedAccount := types.NewContinuousVestingAccount(baseAccount, vestedCoins, startTime, endTime)
+
+ ctx = ctx.WithBlockTime(ctx.BlockTime().AddDate(1, 0, 0))
+
+ app.AccountKeeper.SetAccount(ctx, delayedAccount)
+
+ _, err := app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(300), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ },
+ cleartTrackingFields,
+ 300,
+ 300,
+ 0,
+ 0,
+ },
+ {
+ "continuous vesting, start time passed but not ended",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+
+ startTime := ctx.BlockTime().AddDate(-1, 0, 0).Unix()
+ endTime := ctx.BlockTime().AddDate(2, 0, 0).Unix()
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(300)))
+ delayedAccount := types.NewContinuousVestingAccount(baseAccount, vestedCoins, startTime, endTime)
+
+ ctx = ctx.WithBlockTime(ctx.BlockTime().AddDate(1, 0, 0))
+
+ app.AccountKeeper.SetAccount(ctx, delayedAccount)
+
+ _, err := app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(300), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ },
+ cleartTrackingFields,
+ 300,
+ 200,
+ 100,
+ 0,
+ },
+ {
+ "continuous vesting, start time and endtime passed",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+
+ startTime := ctx.BlockTime().AddDate(-2, 0, 0).Unix()
+ endTime := ctx.BlockTime().AddDate(-1, 0, 0).Unix()
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(300)))
+ delayedAccount := types.NewContinuousVestingAccount(baseAccount, vestedCoins, startTime, endTime)
+
+ ctx = ctx.WithBlockTime(ctx.BlockTime().AddDate(1, 0, 0))
+
+ app.AccountKeeper.SetAccount(ctx, delayedAccount)
+
+ _, err := app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(300), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ },
+ cleartTrackingFields,
+ 300,
+ 0,
+ 300,
+ 0,
+ },
+ {
+ "periodic vesting account, yet to be vested, some rewards delegated",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(100)))
+
+ start := ctx.BlockTime().Unix() + int64(time.Hour/time.Second)
+
+ periods := []types.Period{
+ {
+ Length: int64((24 * time.Hour) / time.Second),
+ Amount: vestedCoins,
+ },
+ }
+
+ account := types.NewPeriodicVestingAccount(baseAccount, vestedCoins, start, periods)
+
+ app.AccountKeeper.SetAccount(ctx, account)
+
+ _, err := app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(150), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ },
+ cleartTrackingFields,
+ 300,
+ 100,
+ 50,
+ 0,
+ },
+ {
+ "periodic vesting account, nothing has vested yet",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+ /*
+ Test case:
+ - periodic vesting account starts at time 1601042400
+ - account balance and original vesting: 3666666670000
+ - nothing has vested, we put the block time slightly after start time
+ - expected vested: original vesting amount
+ - expected free: zero
+ - we're delegating the full original vesting
+ */
+ startTime := int64(1601042400)
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(3666666670000)))
+ periods := []types.Period{
+ {
+ Length: 31536000,
+ Amount: sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(1833333335000))),
+ },
+ {
+ Length: 15638400,
+ Amount: sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(916666667500))),
+ },
+ {
+ Length: 15897600,
+ Amount: sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(916666667500))),
+ },
+ }
+
+ delayedAccount := types.NewPeriodicVestingAccount(baseAccount, vestedCoins, startTime, periods)
+
+ app.AccountKeeper.SetAccount(ctx, delayedAccount)
+
+ // delegation of the original vesting
+ _, err := app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(3666666670000), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ },
+ cleartTrackingFields,
+ 3666666670000,
+ 3666666670000,
+ 0,
+ 1601042400 + 1,
+ },
+ {
+ "periodic vesting account, all has vested",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+ /*
+ Test case:
+ - periodic vesting account starts at time 1601042400
+ - account balance and original vesting: 3666666670000
+ - all has vested, so we set the block time at initial time + sum of all periods times + 1 => 1601042400 + 31536000 + 15897600 + 15897600 + 1
+ - expected vested: zero
+ - expected free: original vesting amount
+ - we're delegating the full original vesting
+ */
+ startTime := int64(1601042400)
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(3666666670000)))
+ periods := []types.Period{
+ {
+ Length: 31536000,
+ Amount: sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(1833333335000))),
+ },
+ {
+ Length: 15638400,
+ Amount: sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(916666667500))),
+ },
+ {
+ Length: 15897600,
+ Amount: sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(916666667500))),
+ },
+ }
+
+ delayedAccount := types.NewPeriodicVestingAccount(baseAccount, vestedCoins, startTime, periods)
+
+ ctx = ctx.WithBlockTime(time.Unix(1601042400+31536000+15897600+15897600+1, 0))
+
+ app.AccountKeeper.SetAccount(ctx, delayedAccount)
+
+ // delegation of the original vesting
+ _, err := app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(3666666670000), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ },
+ cleartTrackingFields,
+ 3666666670000,
+ 0,
+ 3666666670000,
+ 1601042400 + 31536000 + 15897600 + 15897600 + 1,
+ },
+ {
+ "periodic vesting account, first period has vested",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+ /*
+ Test case:
+ - periodic vesting account starts at time 1601042400
+ - account balance and original vesting: 3666666670000
+ - first period have vested, so we set the block time at initial time + time of the first periods + 1 => 1601042400 + 31536000 + 1
+ - expected vested: original vesting - first period amount
+ - expected free: first period amount
+ - we're delegating the full original vesting
+ */
+ startTime := int64(1601042400)
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(3666666670000)))
+ periods := []types.Period{
+ {
+ Length: 31536000,
+ Amount: sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(1833333335000))),
+ },
+ {
+ Length: 15638400,
+ Amount: sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(916666667500))),
+ },
+ {
+ Length: 15897600,
+ Amount: sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(916666667500))),
+ },
+ }
+
+ delayedAccount := types.NewPeriodicVestingAccount(baseAccount, vestedCoins, startTime, periods)
+
+ ctx = ctx.WithBlockTime(time.Unix(1601042400+31536000+1, 0))
+
+ app.AccountKeeper.SetAccount(ctx, delayedAccount)
+
+ // delegation of the original vesting
+ _, err := app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(3666666670000), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ },
+ cleartTrackingFields,
+ 3666666670000,
+ 3666666670000 - 1833333335000,
+ 1833333335000,
+ 1601042400 + 31536000 + 1,
+ },
+ {
+ "periodic vesting account, first 2 period has vested",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+ /*
+ Test case:
+ - periodic vesting account starts at time 1601042400
+ - account balance and original vesting: 3666666670000
+ - first 2 periods have vested, so we set the block time at initial time + time of the two periods + 1 => 1601042400 + 31536000 + 15638400 + 1
+ - expected vested: original vesting - (sum of the first two periods amounts)
+ - expected free: sum of the first two periods
+ - we're delegating the full original vesting
+ */
+ startTime := int64(1601042400)
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(3666666670000)))
+ periods := []types.Period{
+ {
+ Length: 31536000,
+ Amount: sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(1833333335000))),
+ },
+ {
+ Length: 15638400,
+ Amount: sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(916666667500))),
+ },
+ {
+ Length: 15897600,
+ Amount: sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(916666667500))),
+ },
+ }
+
+ delayedAccount := types.NewPeriodicVestingAccount(baseAccount, vestedCoins, startTime, periods)
+
+ ctx = ctx.WithBlockTime(time.Unix(1601042400+31536000+15638400+1, 0))
+
+ app.AccountKeeper.SetAccount(ctx, delayedAccount)
+
+ // delegation of the original vesting
+ _, err := app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(3666666670000), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+ },
+ cleartTrackingFields,
+ 3666666670000,
+ 3666666670000 - 1833333335000 - 916666667500,
+ 1833333335000 + 916666667500,
+ 1601042400 + 31536000 + 15638400 + 1,
+ },
+ {
+ "vesting account has unbonding delegations in place",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(300)))
+
+ delayedAccount := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().AddDate(10, 0, 0).Unix())
+
+ app.AccountKeeper.SetAccount(ctx, delayedAccount)
+
+ // delegation of the original vesting
+ _, err := app.StakingKeeper.Delegate(ctx, delegatorAddr, sdk.NewInt(300), stakingtypes.Unbonded, validator, true)
+ require.NoError(t, err)
+
+ ctx = ctx.WithBlockTime(ctx.BlockTime().AddDate(1, 0, 0))
+
+ valAddr, err := sdk.ValAddressFromBech32(validator.OperatorAddress)
+ require.NoError(t, err)
+
+ // un-delegation of the original vesting
+ _, err = app.StakingKeeper.Undelegate(ctx, delegatorAddr, valAddr, sdk.NewDecFromInt(sdk.NewInt(300)))
+ require.NoError(t, err)
+ },
+ cleartTrackingFields,
+ 450,
+ 300,
+ 0,
+ 0,
+ },
+ {
+ "vesting account has never delegated anything",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(300)))
+
+ delayedAccount := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().AddDate(10, 0, 0).Unix())
+
+ app.AccountKeeper.SetAccount(ctx, delayedAccount)
+ },
+ cleartTrackingFields,
+ 450,
+ 0,
+ 0,
+ 0,
+ },
+ {
+ "vesting account has no delegation but dirty DelegatedFree and DelegatedVesting fields",
+ func(app *simapp.SimApp, ctx sdk.Context, validator stakingtypes.Validator, delegatorAddr sdk.AccAddress) {
+ baseAccount := authtypes.NewBaseAccountWithAddress(delegatorAddr)
+ vestedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(300)))
+
+ delayedAccount := types.NewDelayedVestingAccount(baseAccount, vestedCoins, ctx.BlockTime().AddDate(10, 0, 0).Unix())
+
+ app.AccountKeeper.SetAccount(ctx, delayedAccount)
+ },
+ dirtyTrackingFields,
+ 450,
+ 0,
+ 0,
+ 0,
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ app := simapp.Setup(false)
+ ctx := app.BaseApp.NewContext(false, tmproto.Header{
+ Time: time.Now(),
+ })
+
+ addrs := simapp.AddTestAddrs(app, ctx, 1, sdk.NewInt(tc.tokenAmount))
+ delegatorAddr := addrs[0]
+
+ _, valAddr := createValidator(t, ctx, app, tc.tokenAmount*2)
+ validator, found := app.StakingKeeper.GetValidator(ctx, valAddr)
+ require.True(t, found)
+
+ tc.prepareFunc(app, ctx, validator, delegatorAddr)
+
+ if tc.blockTime != 0 {
+ ctx = ctx.WithBlockTime(time.Unix(tc.blockTime, 0))
+ }
+
+ // We introduce the bug
+ savedAccount := app.AccountKeeper.GetAccount(ctx, delegatorAddr)
+ vestingAccount, ok := savedAccount.(exported.VestingAccount)
+ require.True(t, ok)
+ require.NoError(t, tc.garbageFunc(ctx, vestingAccount, app))
+
+ m := authkeeper.NewMigrator(app.AccountKeeper, app.GRPCQueryRouter())
+ require.NoError(t, m.Migrate1to2(ctx))
+
+ var expVested sdk.Coins
+ var expFree sdk.Coins
+
+ if tc.expVested != 0 {
+ expVested = sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(tc.expVested)))
+ }
+
+ if tc.expFree != 0 {
+ expFree = sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(tc.expFree)))
+ }
+
+ trackingCorrected(
+ ctx,
+ t,
+ app.AccountKeeper,
+ savedAccount.GetAddress(),
+ expVested,
+ expFree,
+ )
+ })
+ }
+
+}
+
+func trackingCorrected(ctx sdk.Context, t *testing.T, ak authkeeper.AccountKeeper, addr sdk.AccAddress, expDelVesting sdk.Coins, expDelFree sdk.Coins) {
+ t.Helper()
+ baseAccount := ak.GetAccount(ctx, addr)
+ vDA, ok := baseAccount.(exported.VestingAccount)
+ require.True(t, ok)
+
+ vestedOk := expDelVesting.IsEqual(vDA.GetDelegatedVesting())
+ freeOk := expDelFree.IsEqual(vDA.GetDelegatedFree())
+ require.True(t, vestedOk, vDA.GetDelegatedVesting().String())
+ require.True(t, freeOk, vDA.GetDelegatedFree().String())
+}
+
+func cleartTrackingFields(ctx sdk.Context, vesting exported.VestingAccount, app *simapp.SimApp) error {
+ switch t := vesting.(type) {
+ case *types.DelayedVestingAccount:
+ t.DelegatedFree = nil
+ t.DelegatedVesting = nil
+ app.AccountKeeper.SetAccount(ctx, t)
+ case *types.ContinuousVestingAccount:
+ t.DelegatedFree = nil
+ t.DelegatedVesting = nil
+ app.AccountKeeper.SetAccount(ctx, t)
+ case *types.PeriodicVestingAccount:
+ t.DelegatedFree = nil
+ t.DelegatedVesting = nil
+ app.AccountKeeper.SetAccount(ctx, t)
+ default:
+ return fmt.Errorf("expected vesting account, found %t", t)
+ }
+
+ return nil
+}
+
+func dirtyTrackingFields(ctx sdk.Context, vesting exported.VestingAccount, app *simapp.SimApp) error {
+ dirt := sdk.NewCoins(sdk.NewInt64Coin("stake", 42))
+
+ switch t := vesting.(type) {
+ case *types.DelayedVestingAccount:
+ t.DelegatedFree = dirt
+ t.DelegatedVesting = dirt
+ app.AccountKeeper.SetAccount(ctx, t)
+ case *types.ContinuousVestingAccount:
+ t.DelegatedFree = dirt
+ t.DelegatedVesting = dirt
+ app.AccountKeeper.SetAccount(ctx, t)
+ case *types.PeriodicVestingAccount:
+ t.DelegatedFree = dirt
+ t.DelegatedVesting = dirt
+ app.AccountKeeper.SetAccount(ctx, t)
+ default:
+ return fmt.Errorf("expected vesting account, found %t", t)
+ }
+
+ return nil
+}
+
+func createValidator(t *testing.T, ctx sdk.Context, app *simapp.SimApp, powers int64) (sdk.AccAddress, sdk.ValAddress) {
+ valTokens := sdk.TokensFromConsensusPower(powers)
+ addrs := simapp.AddTestAddrsIncremental(app, ctx, 1, valTokens)
+ valAddrs := simapp.ConvertAddrsToValAddrs(addrs)
+ pks := simapp.CreateTestPubKeys(1)
+ cdc := simapp.MakeTestEncodingConfig().Marshaler
+
+ app.StakingKeeper = stakingkeeper.NewKeeper(
+ cdc,
+ app.GetKey(stakingtypes.StoreKey),
+ app.AccountKeeper,
+ app.BankKeeper,
+ app.GetSubspace(stakingtypes.ModuleName),
+ )
+
+ val1, err := stakingtypes.NewValidator(valAddrs[0], pks[0], stakingtypes.Description{})
+ require.NoError(t, err)
+
+ app.StakingKeeper.SetValidator(ctx, val1)
+ require.NoError(t, app.StakingKeeper.SetValidatorByConsAddr(ctx, val1))
+ app.StakingKeeper.SetNewValidatorByPowerIndex(ctx, val1)
+
+ _, err = app.StakingKeeper.Delegate(ctx, addrs[0], valTokens, stakingtypes.Unbonded, val1, true)
+ require.NoError(t, err)
+
+ _ = staking.EndBlocker(ctx, app.StakingKeeper)
+
+ return addrs[0], valAddrs[0]
+}
diff --git a/x/auth/module.go b/x/auth/module.go
index ff053d8c7c..f0c26cd97e 100644
--- a/x/auth/module.go
+++ b/x/auth/module.go
@@ -129,6 +129,11 @@ func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sd
// module-specific GRPC queries.
func (am AppModule) RegisterServices(cfg module.Configurator) {
types.RegisterQueryServer(cfg.QueryServer(), am.accountKeeper)
+ m := keeper.NewMigrator(am.accountKeeper, cfg.QueryServer())
+ err := cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2)
+ if err != nil {
+ panic(err)
+ }
}
// InitGenesis performs genesis initialization for the auth module. It returns
@@ -148,7 +153,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json
}
// ConsensusVersion implements AppModule/ConsensusVersion.
-func (AppModule) ConsensusVersion() uint64 { return 1 }
+func (AppModule) ConsensusVersion() uint64 { return 2 }
// BeginBlock returns the begin blocker for the auth module.
func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {}
diff --git a/x/auth/tx/service.go b/x/auth/tx/service.go
index a4c0706a13..a0deedae6a 100644
--- a/x/auth/tx/service.go
+++ b/x/auth/tx/service.go
@@ -6,7 +6,7 @@ import (
"strings"
gogogrpc "github.com/gogo/protobuf/grpc"
- "github.com/golang/protobuf/proto" //nolint:staticcheck
+ "github.com/golang/protobuf/proto" // nolint: staticcheck
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
diff --git a/x/auth/tx/service_test.go b/x/auth/tx/service_test.go
index d7244fb86f..68c474facc 100644
--- a/x/auth/tx/service_test.go
+++ b/x/auth/tx/service_test.go
@@ -8,6 +8,8 @@ import (
"strings"
"testing"
+ "github.com/stretchr/testify/suite"
+
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
clienttx "github.com/cosmos/cosmos-sdk/client/tx"
@@ -28,7 +30,6 @@ import (
authtest "github.com/cosmos/cosmos-sdk/x/auth/client/testutil"
bankcli "github.com/cosmos/cosmos-sdk/x/bank/client/testutil"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
- "github.com/stretchr/testify/suite"
)
type IntegrationTestSuite struct {
diff --git a/x/authz/client/cli/tx_test.go b/x/authz/client/cli/tx_test.go
index 179feca333..b7dad15e65 100644
--- a/x/authz/client/cli/tx_test.go
+++ b/x/authz/client/cli/tx_test.go
@@ -156,8 +156,8 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() {
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours),
},
- &sdk.TxResponse{}, 29,
- false,
+ nil, 0,
+ true,
},
{
"failed with error both validators not allowed",
diff --git a/x/authz/exported/authorizations.go b/x/authz/exported/authorizations.go
index d7cca5ae79..32fea0b0b3 100644
--- a/x/authz/exported/authorizations.go
+++ b/x/authz/exported/authorizations.go
@@ -3,8 +3,6 @@ package exported
import (
"github.com/gogo/protobuf/proto"
- tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
-
sdk "github.com/cosmos/cosmos-sdk/types"
)
@@ -17,5 +15,9 @@ type Authorization interface {
// Accept determines whether this grant permits the provided sdk.ServiceMsg to be performed, and if
// so provides an upgraded authorization instance.
- Accept(msg sdk.ServiceMsg, block tmproto.Header) (updated Authorization, delete bool, err error)
+ Accept(ctx sdk.Context, msg sdk.ServiceMsg) (updated Authorization, delete bool, err error)
+
+ // ValidateBasic does a simple validation check that
+ // doesn't require access to any other information.
+ ValidateBasic() error
}
diff --git a/x/authz/keeper/keeper.go b/x/authz/keeper/keeper.go
index 33d6248f71..dcbee170cb 100644
--- a/x/authz/keeper/keeper.go
+++ b/x/authz/keeper/keeper.go
@@ -87,7 +87,7 @@ func (k Keeper) DispatchActions(ctx sdk.Context, grantee sdk.AccAddress, service
if authorization == nil {
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "authorization not found")
}
- updated, del, err := authorization.Accept(serviceMsg, ctx.BlockHeader())
+ updated, del, err := authorization.Accept(ctx, serviceMsg)
if err != nil {
return nil, err
}
diff --git a/x/authz/simulation/operations.go b/x/authz/simulation/operations.go
index 959b0c06fe..8fef468b06 100644
--- a/x/authz/simulation/operations.go
+++ b/x/authz/simulation/operations.go
@@ -96,8 +96,12 @@ func SimulateMsgGrantAuthorization(ak types.AccountKeeper, bk types.BankKeeper,
}
blockTime := ctx.BlockTime()
+ spendLimit := spendableCoins.Sub(fees)
+ if spendLimit == nil {
+ return simtypes.NoOpMsg(types.ModuleName, TypeMsgGrantAuthorization, "spend limit is nil"), nil, nil
+ }
msg, err := types.NewMsgGrantAuthorization(granter.Address, grantee.Address,
- banktype.NewSendAuthorization(spendableCoins.Sub(fees)), blockTime.AddDate(1, 0, 0))
+ banktype.NewSendAuthorization(spendLimit), blockTime.AddDate(1, 0, 0))
if err != nil {
return simtypes.NoOpMsg(types.ModuleName, TypeMsgGrantAuthorization, err.Error()), nil, err
@@ -249,7 +253,7 @@ func SimulateMsgExecuteAuthorized(ak types.AccountKeeper, bk types.BankKeeper, k
msg := types.NewMsgExecAuthorized(grantee.Address, []sdk.ServiceMsg{execMsg})
sendGrant := targetGrant.Authorization.GetCachedValue().(*banktype.SendAuthorization)
- _, _, err = sendGrant.Accept(execMsg, ctx.BlockHeader())
+ _, _, err = sendGrant.Accept(ctx, execMsg)
if err != nil {
return simtypes.NoOpMsg(types.ModuleName, TypeMsgExecDelegated, err.Error()), nil, nil
}
diff --git a/x/authz/types/generic_authorization.go b/x/authz/types/generic_authorization.go
index 672260c67f..101f3acf16 100644
--- a/x/authz/types/generic_authorization.go
+++ b/x/authz/types/generic_authorization.go
@@ -1,9 +1,9 @@
package types
import (
- tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
-
sdk "github.com/cosmos/cosmos-sdk/types"
+ sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+ "github.com/cosmos/cosmos-sdk/types/msgservice"
"github.com/cosmos/cosmos-sdk/x/authz/exported"
)
@@ -19,11 +19,19 @@ func NewGenericAuthorization(methodName string) *GenericAuthorization {
}
// MethodName implements Authorization.MethodName.
-func (cap GenericAuthorization) MethodName() string {
- return cap.MessageName
+func (authorization GenericAuthorization) MethodName() string {
+ return authorization.MessageName
}
// Accept implements Authorization.Accept.
-func (cap GenericAuthorization) Accept(msg sdk.ServiceMsg, block tmproto.Header) (updated exported.Authorization, delete bool, err error) {
- return &cap, false, nil
+func (authorization GenericAuthorization) Accept(ctx sdk.Context, msg sdk.ServiceMsg) (updated exported.Authorization, delete bool, err error) {
+ return &authorization, false, nil
+}
+
+// ValidateBasic implements Authorization.ValidateBasic.
+func (authorization GenericAuthorization) ValidateBasic() error {
+ if !msgservice.IsServiceMsg(authorization.MessageName) {
+ return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, " %s is not a valid service msg", authorization.MessageName)
+ }
+ return nil
}
diff --git a/x/authz/types/generic_authorization_test.go b/x/authz/types/generic_authorization_test.go
new file mode 100644
index 0000000000..e3330d1c65
--- /dev/null
+++ b/x/authz/types/generic_authorization_test.go
@@ -0,0 +1,20 @@
+package types_test
+
+import (
+ "testing"
+
+ "github.com/cosmos/cosmos-sdk/x/authz/types"
+ banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+ "github.com/stretchr/testify/require"
+)
+
+func TestGenericAuthorization(t *testing.T) {
+ t.Log("verify ValidateBasic returns error for non-service msg")
+ authorization := types.NewGenericAuthorization(banktypes.TypeMsgSend)
+ require.Error(t, authorization.ValidateBasic())
+
+ t.Log("verify ValidateBasic returns nil for service msg")
+ authorization = types.NewGenericAuthorization(banktypes.SendAuthorization{}.MethodName())
+ require.NoError(t, authorization.ValidateBasic())
+ require.Equal(t, banktypes.SendAuthorization{}.MethodName(), authorization.MessageName)
+}
diff --git a/x/authz/types/msgs.go b/x/authz/types/msgs.go
index 8fa3bdd1f0..0b72d1a7e8 100644
--- a/x/authz/types/msgs.go
+++ b/x/authz/types/msgs.go
@@ -63,7 +63,11 @@ func (msg MsgGrantAuthorizationRequest) ValidateBasic() error {
return sdkerrors.Wrap(ErrInvalidExpirationTime, "Time can't be in the past")
}
- return nil
+ authorization, ok := msg.Authorization.GetCachedValue().(exported.Authorization)
+ if !ok {
+ return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expected %T, got %T", (exported.Authorization)(nil), msg.Authorization.GetCachedValue())
+ }
+ return authorization.ValidateBasic()
}
// GetGrantAuthorization returns the cache value from the MsgGrantAuthorization.Authorization if present.
diff --git a/x/bank/keeper/keeper.go b/x/bank/keeper/keeper.go
index 34eb434c6f..a5b05b044a 100644
--- a/x/bank/keeper/keeper.go
+++ b/x/bank/keeper/keeper.go
@@ -446,6 +446,7 @@ func (k BaseKeeper) trackDelegation(ctx sdk.Context, addr sdk.AccAddress, balanc
if ok {
// TODO: return error on account.TrackDelegation
vacc.TrackDelegation(ctx.BlockHeader().Time, balance, amt)
+ k.ak.SetAccount(ctx, acc)
}
return nil
@@ -461,6 +462,7 @@ func (k BaseKeeper) trackUndelegation(ctx sdk.Context, addr sdk.AccAddress, amt
if ok {
// TODO: return error on account.TrackUndelegation
vacc.TrackUndelegation(amt)
+ k.ak.SetAccount(ctx, acc)
}
return nil
diff --git a/x/bank/keeper/keeper_test.go b/x/bank/keeper/keeper_test.go
index 37a5f606f9..15672f4556 100644
--- a/x/bank/keeper/keeper_test.go
+++ b/x/bank/keeper/keeper_test.go
@@ -1,10 +1,12 @@
package keeper_test
import (
- "github.com/cosmos/cosmos-sdk/types/query"
"testing"
"time"
+ "github.com/cosmos/cosmos-sdk/types/query"
+
+ "github.com/cosmos/cosmos-sdk/x/auth/vesting/exported"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
"github.com/stretchr/testify/suite"
@@ -905,6 +907,12 @@ func (suite *IntegrationTestSuite) TestDelegateCoins() {
// require the ability for a vesting account to delegate
suite.Require().NoError(app.BankKeeper.DelegateCoins(ctx, addr1, addrModule, delCoins))
suite.Require().Equal(delCoins, app.BankKeeper.GetAllBalances(ctx, addr1))
+
+ // require that delegated vesting amount is equal to what was delegated with DelegateCoins
+ acc = app.AccountKeeper.GetAccount(ctx, addr1)
+ vestingAcc, ok := acc.(exported.VestingAccount)
+ suite.Require().True(ok)
+ suite.Require().Equal(delCoins, vestingAcc.GetDelegatedVesting())
}
func (suite *IntegrationTestSuite) TestDelegateCoins_Invalid() {
@@ -979,6 +987,12 @@ func (suite *IntegrationTestSuite) TestUndelegateCoins() {
suite.Require().Equal(origCoins, app.BankKeeper.GetAllBalances(ctx, addr1))
suite.Require().True(app.BankKeeper.GetAllBalances(ctx, addrModule).Empty())
+
+ // require that delegated vesting amount is completely empty, since they were completely undelegated
+ acc = app.AccountKeeper.GetAccount(ctx, addr1)
+ vestingAcc, ok := acc.(exported.VestingAccount)
+ suite.Require().True(ok)
+ suite.Require().Empty(vestingAcc.GetDelegatedVesting())
}
func (suite *IntegrationTestSuite) TestUndelegateCoins_Invalid() {
diff --git a/x/bank/types/send_authorization.go b/x/bank/types/send_authorization.go
index ccd755c1fc..82d1bf1e63 100644
--- a/x/bank/types/send_authorization.go
+++ b/x/bank/types/send_authorization.go
@@ -3,8 +3,6 @@ package types
import (
"reflect"
- tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
-
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authz "github.com/cosmos/cosmos-sdk/x/authz/exported"
@@ -27,7 +25,7 @@ func (authorization SendAuthorization) MethodName() string {
}
// Accept implements Authorization.Accept.
-func (authorization SendAuthorization) Accept(msg sdk.ServiceMsg, block tmproto.Header) (updated authz.Authorization, delete bool, err error) {
+func (authorization SendAuthorization) Accept(ctx sdk.Context, msg sdk.ServiceMsg) (updated authz.Authorization, delete bool, err error) {
if reflect.TypeOf(msg.Request) == reflect.TypeOf(&MsgSend{}) {
msg, ok := msg.Request.(*MsgSend)
if ok {
@@ -44,3 +42,14 @@ func (authorization SendAuthorization) Accept(msg sdk.ServiceMsg, block tmproto.
}
return nil, false, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "type mismatch")
}
+
+// ValidateBasic implements Authorization.ValidateBasic.
+func (authorization SendAuthorization) ValidateBasic() error {
+ if authorization.SpendLimit == nil {
+ return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "spend limit cannot be nil")
+ }
+ if !authorization.SpendLimit.IsAllPositive() {
+ return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "spend limit cannot be negitive")
+ }
+ return nil
+}
diff --git a/x/bank/types/send_authorization_test.go b/x/bank/types/send_authorization_test.go
new file mode 100644
index 0000000000..055a0b1fb0
--- /dev/null
+++ b/x/bank/types/send_authorization_test.go
@@ -0,0 +1,64 @@
+package types_test
+
+import (
+ "testing"
+
+ "github.com/cosmos/cosmos-sdk/simapp"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ "github.com/cosmos/cosmos-sdk/x/bank/types"
+ "github.com/stretchr/testify/require"
+ tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
+)
+
+var (
+ coins1000 = sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1000)))
+ coins500 = sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(500)))
+ fromAddr = sdk.AccAddress("_____from _____")
+ toAddr = sdk.AccAddress("_______to________")
+)
+
+func TestSendAuthorization(t *testing.T) {
+ app := simapp.Setup(false)
+ ctx := app.BaseApp.NewContext(false, tmproto.Header{})
+ authorization := types.NewSendAuthorization(coins1000)
+
+ t.Log("verify authorization returns valid method name")
+ require.Equal(t, authorization.MethodName(), "/cosmos.bank.v1beta1.Msg/Send")
+ require.NoError(t, authorization.ValidateBasic())
+ send := types.NewMsgSend(fromAddr, toAddr, coins1000)
+ srvMsg := sdk.ServiceMsg{
+ MethodName: "/cosmos.bank.v1beta1.Msg/Send",
+ Request: send,
+ }
+ require.NoError(t, authorization.ValidateBasic())
+
+ t.Log("verify updated authorization returns nil")
+ updated, del, err := authorization.Accept(ctx, srvMsg)
+ require.NoError(t, err)
+ require.True(t, del)
+ require.Nil(t, updated)
+
+ authorization = types.NewSendAuthorization(coins1000)
+ require.Equal(t, authorization.MethodName(), "/cosmos.bank.v1beta1.Msg/Send")
+ require.NoError(t, authorization.ValidateBasic())
+ send = types.NewMsgSend(fromAddr, toAddr, coins500)
+ srvMsg = sdk.ServiceMsg{
+ MethodName: "/cosmos.bank.v1beta1.Msg/Send",
+ Request: send,
+ }
+ require.NoError(t, authorization.ValidateBasic())
+ updated, del, err = authorization.Accept(ctx, srvMsg)
+
+ t.Log("verify updated authorization returns remaining spent limit")
+ require.NoError(t, err)
+ require.False(t, del)
+ require.NotNil(t, updated)
+ sendAuth := types.NewSendAuthorization(coins500)
+ require.Equal(t, sendAuth.String(), updated.String())
+
+ t.Log("expect updated authorization nil after spending remaining amount")
+ updated, del, err = updated.Accept(ctx, srvMsg)
+ require.NoError(t, err)
+ require.True(t, del)
+ require.Nil(t, updated)
+}
diff --git a/x/distribution/types/msg.go b/x/distribution/types/msg.go
index 8dc72081e9..09e9994c16 100644
--- a/x/distribution/types/msg.go
+++ b/x/distribution/types/msg.go
@@ -1,4 +1,3 @@
-//nolint
package types
import (
diff --git a/x/feegrant/client/cli/cli_test.go b/x/feegrant/client/cli/cli_test.go
index 66827e6148..787a342d18 100644
--- a/x/feegrant/client/cli/cli_test.go
+++ b/x/feegrant/client/cli/cli_test.go
@@ -13,6 +13,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
+ "github.com/cosmos/cosmos-sdk/testutil"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
"github.com/cosmos/cosmos-sdk/testutil/network"
sdk "github.com/cosmos/cosmos-sdk/types"
@@ -40,7 +41,7 @@ func (s *IntegrationTestSuite) SetupSuite() {
}
cfg := network.DefaultConfig()
- cfg.NumValidators = 2
+ cfg.NumValidators = 3
s.cfg = cfg
s.network = network.New(s.T(), cfg)
@@ -137,7 +138,7 @@ func (s *IntegrationTestSuite) TestCmdGetFeeGrant() {
grantee.String(),
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
},
- "no fee allowance found",
+ "no allowance",
true, nil, nil,
},
{
@@ -169,9 +170,13 @@ func (s *IntegrationTestSuite) TestCmdGetFeeGrant() {
s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
s.Require().Equal(tc.respType.Grantee, tc.respType.Grantee)
s.Require().Equal(tc.respType.Granter, tc.respType.Granter)
+ grant, err := tc.respType.GetFeeGrant()
+ s.Require().NoError(err)
+ grant1, err1 := tc.resp.GetFeeGrant()
+ s.Require().NoError(err1)
s.Require().Equal(
- tc.respType.GetFeeGrant().(*types.BasicFeeAllowance).SpendLimit,
- tc.resp.GetFeeGrant().(*types.BasicFeeAllowance).SpendLimit,
+ grant.(*types.BasicFeeAllowance).SpendLimit,
+ grant1.(*types.BasicFeeAllowance).SpendLimit,
)
}
})
@@ -617,6 +622,181 @@ func (s *IntegrationTestSuite) TestTxWithFeeGrant() {
s.Require().Equal(uint32(0), resp.Code)
}
+func (s *IntegrationTestSuite) TestFilteredFeeAllowance() {
+ val := s.network.Validators[0]
+
+ granter := val.Address
+ info, _, err := val.ClientCtx.Keyring.NewMnemonic("grantee1", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1)
+ s.Require().NoError(err)
+ grantee := sdk.AccAddress(info.GetPubKey().Address())
+
+ clientCtx := val.ClientCtx
+
+ commonFlags := []string{
+ fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
+ fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
+ fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
+ }
+ spendLimit := sdk.NewCoin("stake", sdk.NewInt(1000))
+
+ allowMsgs := "/cosmos.gov.v1beta1.Msg/SubmitProposal"
+
+ testCases := []struct {
+ name string
+ args []string
+ expectErr bool
+ respType proto.Message
+ expectedCode uint32
+ }{
+ {
+ "wrong granter",
+ append(
+ []string{
+ "wrong granter",
+ "cosmos1nph3cfzk6trsmfxkeu943nvach5qw4vwstnvkl",
+ fmt.Sprintf("--%s=%s", cli.FlagAllowedMsgs, allowMsgs),
+ fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, spendLimit.String()),
+ fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
+ },
+ commonFlags...,
+ ),
+ true, &sdk.TxResponse{}, 0,
+ },
+ {
+ "wrong grantee",
+ append(
+ []string{
+ granter.String(),
+ "wrong grantee",
+ fmt.Sprintf("--%s=%s", cli.FlagAllowedMsgs, allowMsgs),
+ fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, spendLimit.String()),
+ fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
+ },
+ commonFlags...,
+ ),
+ true, &sdk.TxResponse{}, 0,
+ },
+ {
+ "valid filter fee grant",
+ append(
+ []string{
+ granter.String(),
+ grantee.String(),
+ fmt.Sprintf("--%s=%s", cli.FlagAllowedMsgs, allowMsgs),
+ fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, spendLimit.String()),
+ fmt.Sprintf("--%s=%s", flags.FlagFrom, granter),
+ },
+ commonFlags...,
+ ),
+ false, &sdk.TxResponse{}, 0,
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+
+ s.Run(tc.name, func() {
+ cmd := cli.NewCmdFeeGrant()
+ out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
+
+ if tc.expectErr {
+ s.Require().Error(err)
+ } else {
+ s.Require().NoError(err)
+ s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
+
+ txResp := tc.respType.(*sdk.TxResponse)
+ s.Require().Equal(tc.expectedCode, txResp.Code, out.String())
+ }
+ })
+ }
+
+ args := []string{
+ granter.String(),
+ grantee.String(),
+ fmt.Sprintf("--%s=json", tmcli.OutputFlag),
+ }
+
+ // get filtered fee allowance and check info
+ cmd := cli.GetCmdQueryFeeGrant()
+ out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, args)
+ s.Require().NoError(err)
+
+ resp := &types.FeeAllowanceGrant{}
+
+ s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), resp), out.String())
+ s.Require().Equal(resp.Grantee, resp.Grantee)
+ s.Require().Equal(resp.Granter, resp.Granter)
+
+ grant, err := resp.GetFeeGrant()
+ s.Require().NoError(err)
+
+ filteredFeeGrant, err := grant.(*types.AllowedMsgFeeAllowance).GetAllowance()
+ s.Require().NoError(err)
+
+ s.Require().Equal(
+ filteredFeeGrant.(*types.BasicFeeAllowance).SpendLimit.String(),
+ spendLimit.String(),
+ )
+
+ // exec filtered fee allowance
+ cases := []struct {
+ name string
+ malleate func() (testutil.BufferWriter, error)
+ expectErr bool
+ respType proto.Message
+ expectedCode uint32
+ }{
+ {
+ "valid tx",
+ func() (testutil.BufferWriter, error) {
+ return govtestutil.MsgSubmitProposal(val.ClientCtx, grantee.String(),
+ "Text Proposal", "No desc", govtypes.ProposalTypeText,
+ fmt.Sprintf("--%s=%s", flags.FlagFeeAccount, granter.String()),
+ )
+ },
+ false,
+ &sdk.TxResponse{},
+ 0,
+ },
+ {
+ "should fail with unauthorized msgs",
+ func() (testutil.BufferWriter, error) {
+ args := append(
+ []string{
+ grantee.String(),
+ "cosmos14cm33pvnrv2497tyt8sp9yavhmw83nwej3m0e8",
+ fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"),
+ fmt.Sprintf("--%s=%s", flags.FlagFeeAccount, granter),
+ },
+ commonFlags...,
+ )
+ cmd := cli.NewCmdFeeGrant()
+ return clitestutil.ExecTestCLICmd(clientCtx, cmd, args)
+ },
+ false, &sdk.TxResponse{}, 7,
+ },
+ }
+
+ for _, tc := range cases {
+ tc := tc
+
+ s.Run(tc.name, func() {
+ out, err := tc.malleate()
+
+ if tc.expectErr {
+ s.Require().Error(err)
+ } else {
+ s.Require().NoError(err)
+ s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String())
+
+ txResp := tc.respType.(*sdk.TxResponse)
+ s.Require().Equal(tc.expectedCode, txResp.Code, out.String())
+ }
+ })
+ }
+}
+
func TestIntegrationTestSuite(t *testing.T) {
suite.Run(t, new(IntegrationTestSuite))
}
diff --git a/x/feegrant/client/cli/tx.go b/x/feegrant/client/cli/tx.go
index 49702e0c78..c355ca4b5a 100644
--- a/x/feegrant/client/cli/tx.go
+++ b/x/feegrant/client/cli/tx.go
@@ -22,6 +22,7 @@ const (
FlagPeriod = "period"
FlagPeriodLimit = "period-limit"
FlagSpendLimit = "spend-limit"
+ FlagAllowedMsgs = "allowed-messages"
)
// GetTxCmd returns the transaction commands for this module
@@ -55,8 +56,10 @@ func NewCmdFeeGrant() *cobra.Command {
Examples:
%s tx %s grant cosmos1skjw... cosmos1skjw... --spend-limit 100stake --expiration 36000 or
-%s tx %s grant cosmos1skjw... cosmos1skjw... --spend-limit 100stake --period 3600 --period-limit 10stake --expiration 36000
- `, version.AppName, types.ModuleName, version.AppName, types.ModuleName,
+%s tx %s grant cosmos1skjw... cosmos1skjw... --spend-limit 100stake --period 3600 --period-limit 10stake --expiration 36000 or
+%s tx %s grant cosmos1skjw... cosmos1skjw... --spend-limit 100stake --expiration 36000
+ --allowed-messages "/cosmos.gov.v1beta1.Msg/SubmitProposal,/cosmos.gov.v1beta1.Msg/Vote"
+ `, version.AppName, types.ModuleName, version.AppName, types.ModuleName, version.AppName, types.ModuleName,
),
),
Args: cobra.ExactArgs(2),
@@ -143,6 +146,18 @@ Examples:
}
}
+ allowedMsgs, err := cmd.Flags().GetStringSlice(FlagAllowedMsgs)
+ if err != nil {
+ return err
+ }
+
+ if len(allowedMsgs) > 0 {
+ grant, err = types.NewAllowedMsgFeeAllowance(grant, allowedMsgs)
+ if err != nil {
+ return err
+ }
+ }
+
msg, err := types.NewMsgGrantFeeAllowance(grant, granter, grantee)
if err != nil {
return err
@@ -160,10 +175,11 @@ Examples:
}
flags.AddTxFlagsToCmd(cmd)
+ cmd.Flags().StringSlice(FlagAllowedMsgs, []string{}, "Set of allowed messages for fee allowance")
cmd.Flags().Int64(FlagExpiration, 0, "The second unit of time duration which the grant is active for the user")
cmd.Flags().String(FlagSpendLimit, "", "Spend limit specifies the max limit can be used, if not mentioned there is no limit")
cmd.Flags().Int64(FlagPeriod, 0, "period specifies the time duration in which period_spend_limit coins can be spent before that allowance is reset")
- cmd.Flags().String(FlagPeriodLimit, "", "// period limit specifies the maximum number of coins that can be spent in the period")
+ cmd.Flags().String(FlagPeriodLimit, "", "period limit specifies the maximum number of coins that can be spent in the period")
return cmd
}
diff --git a/x/feegrant/client/rest/grpc_query_test.go b/x/feegrant/client/rest/grpc_query_test.go
index 5dac57a8fb..a8e83716af 100644
--- a/x/feegrant/client/rest/grpc_query_test.go
+++ b/x/feegrant/client/rest/grpc_query_test.go
@@ -92,7 +92,7 @@ func (s *IntegrationTestSuite) TestQueryFeeAllowance() {
"fail: no grants",
fmt.Sprintf("%s/cosmos/feegrant/v1beta1/fee_allowance/%s/%s", baseURL, val.Address.String(), s.grantee.String()),
true,
- "no fee allowance found",
+ "no allowance",
func() {},
func(types.QueryFeeAllowanceResponse) {},
},
diff --git a/x/feegrant/genesis.go b/x/feegrant/genesis.go
index f65cfdb578..5fd23608fc 100644
--- a/x/feegrant/genesis.go
+++ b/x/feegrant/genesis.go
@@ -12,7 +12,11 @@ type GenesisState []types.FeeAllowanceGrant
// ValidateBasic ensures all grants in the genesis state are valid
func (g GenesisState) ValidateBasic() error {
for _, f := range g {
- err := f.GetFeeGrant().ValidateBasic()
+ grant, err := f.GetFeeGrant()
+ if err != nil {
+ return err
+ }
+ err = grant.ValidateBasic()
if err != nil {
return err
}
@@ -32,7 +36,12 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, data *types.GenesisState) {
panic(err)
}
- err = k.GrantFeeAllowance(ctx, granter, grantee, f.GetFeeGrant())
+ grant, err := f.GetFeeGrant()
+ if err != nil {
+ panic(err)
+ }
+
+ err = k.GrantFeeAllowance(ctx, granter, grantee, grant)
if err != nil {
panic(err)
}
diff --git a/x/feegrant/keeper/grpc_query.go b/x/feegrant/keeper/grpc_query.go
index 0fce084f90..d8e6c49138 100644
--- a/x/feegrant/keeper/grpc_query.go
+++ b/x/feegrant/keeper/grpc_query.go
@@ -34,9 +34,9 @@ func (q Keeper) FeeAllowance(c context.Context, req *types.QueryFeeAllowanceRequ
ctx := sdk.UnwrapSDKContext(c)
- feeAllowance := q.GetFeeAllowance(ctx, granterAddr, granteeAddr)
- if feeAllowance == nil {
- return nil, status.Errorf(codes.NotFound, "no fee allowance found")
+ feeAllowance, err := q.GetFeeAllowance(ctx, granterAddr, granteeAddr)
+ if err != nil {
+ return nil, status.Errorf(codes.Internal, err.Error())
}
msg, ok := feeAllowance.(proto.Message)
diff --git a/x/feegrant/keeper/keeper.go b/x/feegrant/keeper/keeper.go
index b8f30194ff..7abb87bd57 100644
--- a/x/feegrant/keeper/keeper.go
+++ b/x/feegrant/keeper/keeper.go
@@ -95,10 +95,10 @@ func (k Keeper) RevokeFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddr
// GetFeeAllowance returns the allowance between the granter and grantee.
// If there is none, it returns nil, nil.
// Returns an error on parsing issues
-func (k Keeper) GetFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) types.FeeAllowanceI {
+func (k Keeper) GetFeeAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) (types.FeeAllowanceI, error) {
grant, found := k.GetFeeGrant(ctx, granter, grantee)
if !found {
- return nil
+ return nil, sdkerrors.Wrapf(types.ErrNoAllowance, "grant missing")
}
return grant.GetFeeGrant()
@@ -161,13 +161,18 @@ func (k Keeper) IterateAllFeeAllowances(ctx sdk.Context, cb func(types.FeeAllowa
}
// UseGrantedFees will try to pay the given fee from the granter's account as requested by the grantee
-func (k Keeper) UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins) error {
- grant, found := k.GetFeeGrant(ctx, granter, grantee)
- if !found || grant.GetFeeGrant() == nil {
+func (k Keeper) UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins, msgs []sdk.Msg) error {
+ f, found := k.GetFeeGrant(ctx, granter, grantee)
+ if !found {
return sdkerrors.Wrapf(types.ErrNoAllowance, "grant missing")
}
- remove, err := grant.GetFeeGrant().Accept(fee, ctx.BlockTime(), ctx.BlockHeight())
+ grant, err := f.GetFeeGrant()
+ if err != nil {
+ return err
+ }
+
+ remove, err := grant.Accept(ctx, fee, msgs)
if err == nil {
ctx.EventManager().EmitEvent(
sdk.NewEvent(
@@ -189,5 +194,5 @@ func (k Keeper) UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress,
}
// if we accepted, store the updated state of the allowance
- return k.GrantFeeAllowance(ctx, granter, grantee, grant.GetFeeGrant())
+ return k.GrantFeeAllowance(ctx, granter, grantee, grant)
}
diff --git a/x/feegrant/keeper/keeper_test.go b/x/feegrant/keeper/keeper_test.go
index c14e141d31..b9a1f57019 100644
--- a/x/feegrant/keeper/keeper_test.go
+++ b/x/feegrant/keeper/keeper_test.go
@@ -109,7 +109,8 @@ func (suite *KeeperTestSuite) TestKeeperCrud() {
for name, tc := range cases {
tc := tc
suite.Run(name, func() {
- allow := k.GetFeeAllowance(ctx, tc.granter, tc.grantee)
+ allow, _ := k.GetFeeAllowance(ctx, tc.granter, tc.grantee)
+
if tc.allowance == nil {
suite.Nil(allow)
return
@@ -242,14 +243,14 @@ func (suite *KeeperTestSuite) TestUseGrantedFee() {
err = k.GrantFeeAllowance(ctx, suite.addrs[0], suite.addrs[3], expired)
suite.Require().NoError(err)
- err = k.UseGrantedFees(ctx, tc.granter, tc.grantee, tc.fee)
+ err = k.UseGrantedFees(ctx, tc.granter, tc.grantee, tc.fee, []sdk.Msg{})
if tc.allowed {
suite.NoError(err)
} else {
suite.Error(err)
}
- loaded := k.GetFeeAllowance(ctx, tc.granter, tc.grantee)
+ loaded, _ := k.GetFeeAllowance(ctx, tc.granter, tc.grantee)
suite.Equal(tc.final, loaded)
})
diff --git a/x/feegrant/keeper/msg_server.go b/x/feegrant/keeper/msg_server.go
index 3cd96e79ba..881e0f8b3f 100644
--- a/x/feegrant/keeper/msg_server.go
+++ b/x/feegrant/keeper/msg_server.go
@@ -37,12 +37,16 @@ func (k msgServer) GrantFeeAllowance(goCtx context.Context, msg *types.MsgGrantF
}
// Checking for duplicate entry
- f := k.Keeper.GetFeeAllowance(ctx, granter, grantee)
- if f != nil {
+ if f, _ := k.Keeper.GetFeeAllowance(ctx, granter, grantee); f != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "fee allowance already exists")
}
- err = k.Keeper.GrantFeeAllowance(ctx, granter, grantee, msg.GetFeeAllowanceI())
+ allowance, err := msg.GetFeeAllowanceI()
+ if err != nil {
+ return nil, err
+ }
+
+ err = k.Keeper.GrantFeeAllowance(ctx, granter, grantee, allowance)
if err != nil {
return nil, err
}
diff --git a/x/feegrant/simulation/operations.go b/x/feegrant/simulation/operations.go
index 54821873b6..6e53738b2d 100644
--- a/x/feegrant/simulation/operations.go
+++ b/x/feegrant/simulation/operations.go
@@ -71,8 +71,7 @@ func SimulateMsgGrantFeeAllowance(ak types.AccountKeeper, bk types.BankKeeper, k
return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgGrantFeeAllowance, "grantee and granter cannot be same"), nil, nil
}
- f := k.GetFeeAllowance(ctx, granter.Address, grantee.Address)
- if f != nil {
+ if f, _ := k.GetFeeAllowance(ctx, granter.Address, grantee.Address); f != nil {
return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgGrantFeeAllowance, "fee allowance exists"), nil, nil
}
diff --git a/x/feegrant/types/basic_fee.go b/x/feegrant/types/basic_fee.go
index 6d97068051..00054cb04b 100644
--- a/x/feegrant/types/basic_fee.go
+++ b/x/feegrant/types/basic_fee.go
@@ -19,7 +19,10 @@ var _ FeeAllowanceI = (*BasicFeeAllowance)(nil)
//
// If remove is true (regardless of the error), the FeeAllowance will be deleted from storage
// (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseGrantedFees)
-func (a *BasicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (bool, error) {
+func (a *BasicFeeAllowance) Accept(ctx sdk.Context, fee sdk.Coins, _ []sdk.Msg) (bool, error) {
+ blockTime := ctx.BlockTime()
+ blockHeight := ctx.BlockHeight()
+
if a.Expiration.IsExpired(&blockTime, blockHeight) {
return true, sdkerrors.Wrap(ErrFeeLimitExpired, "basic allowance")
}
diff --git a/x/feegrant/types/basic_fee_test.go b/x/feegrant/types/basic_fee_test.go
index dd3f09590f..25a5eb755c 100644
--- a/x/feegrant/types/basic_fee_test.go
+++ b/x/feegrant/types/basic_fee_test.go
@@ -2,16 +2,19 @@ package types_test
import (
"testing"
- "time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
+ "github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/feegrant/types"
)
func TestBasicFeeValidAllow(t *testing.T) {
+ app := simapp.Setup(false)
+
eth := sdk.NewCoins(sdk.NewInt64Coin("eth", 10))
atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555))
smallAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 43))
@@ -22,7 +25,6 @@ func TestBasicFeeValidAllow(t *testing.T) {
allow *types.BasicFeeAllowance
// all other checks are ignored if valid=false
fee sdk.Coins
- blockTime time.Time
blockHeight int64
valid bool
accept bool
@@ -124,8 +126,10 @@ func TestBasicFeeValidAllow(t *testing.T) {
}
require.NoError(t, err)
+ ctx := app.BaseApp.NewContext(false, tmproto.Header{}).WithBlockHeight(tc.blockHeight)
+
// now try to deduct
- remove, err := tc.allow.Accept(tc.fee, tc.blockTime, tc.blockHeight)
+ remove, err := tc.allow.Accept(ctx, tc.fee, []sdk.Msg{})
if !tc.accept {
require.Error(t, err)
return
diff --git a/x/feegrant/types/codec.go b/x/feegrant/types/codec.go
index 686187cd75..5557d96ac3 100644
--- a/x/feegrant/types/codec.go
+++ b/x/feegrant/types/codec.go
@@ -18,6 +18,7 @@ func RegisterInterfaces(registry types.InterfaceRegistry) {
(*FeeAllowanceI)(nil),
&BasicFeeAllowance{},
&PeriodicFeeAllowance{},
+ &AllowedMsgFeeAllowance{},
)
msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
diff --git a/x/feegrant/types/errors.go b/x/feegrant/types/errors.go
index 183f681ca5..d3234f8cd2 100644
--- a/x/feegrant/types/errors.go
+++ b/x/feegrant/types/errors.go
@@ -18,4 +18,8 @@ var (
ErrInvalidDuration = sdkerrors.Register(DefaultCodespace, 4, "invalid duration")
// ErrNoAllowance error if there is no allowance for that pair
ErrNoAllowance = sdkerrors.Register(DefaultCodespace, 5, "no allowance")
+ // ErrNoMessages error if there is no message
+ ErrNoMessages = sdkerrors.Register(DefaultCodespace, 6, "allowed messages are empty")
+ // ErrMessageNotAllowed error if message is not allowed
+ ErrMessageNotAllowed = sdkerrors.Register(DefaultCodespace, 7, "message not allowed")
)
diff --git a/x/feegrant/types/feegrant.pb.go b/x/feegrant/types/feegrant.pb.go
index 28f53e140d..f28e47d7ff 100644
--- a/x/feegrant/types/feegrant.pb.go
+++ b/x/feegrant/types/feegrant.pb.go
@@ -177,6 +177,47 @@ func (m *PeriodicFeeAllowance) GetPeriodReset() ExpiresAt {
return ExpiresAt{}
}
+// AllowedMsgFeeAllowance creates allowance only for specified message types.
+type AllowedMsgFeeAllowance struct {
+ // allowance can be any of basic and filtered fee allowance.
+ Allowance *types1.Any `protobuf:"bytes,1,opt,name=allowance,proto3" json:"allowance,omitempty"`
+ // allowed_messages are the messages for which the grantee has the access.
+ AllowedMessages []string `protobuf:"bytes,2,rep,name=allowed_messages,json=allowedMessages,proto3" json:"allowed_messages,omitempty"`
+}
+
+func (m *AllowedMsgFeeAllowance) Reset() { *m = AllowedMsgFeeAllowance{} }
+func (m *AllowedMsgFeeAllowance) String() string { return proto.CompactTextString(m) }
+func (*AllowedMsgFeeAllowance) ProtoMessage() {}
+func (*AllowedMsgFeeAllowance) Descriptor() ([]byte, []int) {
+ return fileDescriptor_7279582900c30aea, []int{2}
+}
+func (m *AllowedMsgFeeAllowance) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *AllowedMsgFeeAllowance) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_AllowedMsgFeeAllowance.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 *AllowedMsgFeeAllowance) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_AllowedMsgFeeAllowance.Merge(m, src)
+}
+func (m *AllowedMsgFeeAllowance) XXX_Size() int {
+ return m.Size()
+}
+func (m *AllowedMsgFeeAllowance) XXX_DiscardUnknown() {
+ xxx_messageInfo_AllowedMsgFeeAllowance.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_AllowedMsgFeeAllowance proto.InternalMessageInfo
+
// Duration is a span of a clock time or number of blocks.
// This is designed to be added to an ExpiresAt struct.
type Duration struct {
@@ -192,7 +233,7 @@ func (m *Duration) Reset() { *m = Duration{} }
func (m *Duration) String() string { return proto.CompactTextString(m) }
func (*Duration) ProtoMessage() {}
func (*Duration) Descriptor() ([]byte, []int) {
- return fileDescriptor_7279582900c30aea, []int{2}
+ return fileDescriptor_7279582900c30aea, []int{3}
}
func (m *Duration) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -281,7 +322,7 @@ func (m *ExpiresAt) Reset() { *m = ExpiresAt{} }
func (m *ExpiresAt) String() string { return proto.CompactTextString(m) }
func (*ExpiresAt) ProtoMessage() {}
func (*ExpiresAt) Descriptor() ([]byte, []int) {
- return fileDescriptor_7279582900c30aea, []int{3}
+ return fileDescriptor_7279582900c30aea, []int{4}
}
func (m *ExpiresAt) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -366,7 +407,7 @@ func (m *FeeAllowanceGrant) Reset() { *m = FeeAllowanceGrant{} }
func (m *FeeAllowanceGrant) String() string { return proto.CompactTextString(m) }
func (*FeeAllowanceGrant) ProtoMessage() {}
func (*FeeAllowanceGrant) Descriptor() ([]byte, []int) {
- return fileDescriptor_7279582900c30aea, []int{4}
+ return fileDescriptor_7279582900c30aea, []int{5}
}
func (m *FeeAllowanceGrant) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -419,6 +460,7 @@ func (m *FeeAllowanceGrant) GetAllowance() *types1.Any {
func init() {
proto.RegisterType((*BasicFeeAllowance)(nil), "cosmos.feegrant.v1beta1.BasicFeeAllowance")
proto.RegisterType((*PeriodicFeeAllowance)(nil), "cosmos.feegrant.v1beta1.PeriodicFeeAllowance")
+ proto.RegisterType((*AllowedMsgFeeAllowance)(nil), "cosmos.feegrant.v1beta1.AllowedMsgFeeAllowance")
proto.RegisterType((*Duration)(nil), "cosmos.feegrant.v1beta1.Duration")
proto.RegisterType((*ExpiresAt)(nil), "cosmos.feegrant.v1beta1.ExpiresAt")
proto.RegisterType((*FeeAllowanceGrant)(nil), "cosmos.feegrant.v1beta1.FeeAllowanceGrant")
@@ -429,45 +471,48 @@ func init() {
}
var fileDescriptor_7279582900c30aea = []byte{
- // 599 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0x4f, 0x6e, 0xd3, 0x40,
- 0x14, 0xc6, 0x6d, 0x9c, 0x96, 0x76, 0x02, 0x88, 0x8c, 0x22, 0xe1, 0x64, 0xe1, 0x94, 0x2c, 0x50,
- 0x84, 0x14, 0x9b, 0x16, 0x89, 0x05, 0x12, 0x42, 0x71, 0x69, 0x1b, 0x04, 0x0b, 0x64, 0x58, 0xb1,
- 0x89, 0x6c, 0x67, 0xea, 0x0c, 0xb5, 0x3d, 0x96, 0x67, 0x02, 0xcd, 0x25, 0x50, 0x97, 0x9c, 0x81,
- 0x35, 0x87, 0xa8, 0x58, 0x55, 0xac, 0x58, 0xb5, 0x28, 0x39, 0x01, 0x37, 0x40, 0xf3, 0xc7, 0x4e,
- 0x94, 0x10, 0x24, 0xa4, 0xae, 0xe2, 0x99, 0x79, 0xef, 0xfb, 0xbd, 0xf7, 0xbd, 0x99, 0x80, 0x07,
- 0x21, 0xa1, 0x09, 0xa1, 0xce, 0x31, 0x42, 0x51, 0xee, 0xa7, 0xcc, 0xf9, 0xb8, 0x1b, 0x20, 0xe6,
- 0xef, 0x96, 0x1b, 0x76, 0x96, 0x13, 0x46, 0xe0, 0x3d, 0x19, 0x67, 0x97, 0xdb, 0x2a, 0xae, 0x59,
- 0x8f, 0x48, 0x44, 0x44, 0x8c, 0xc3, 0xbf, 0x64, 0x78, 0xb3, 0x11, 0x11, 0x12, 0xc5, 0xc8, 0x11,
- 0xab, 0x60, 0x7c, 0xec, 0xf8, 0xe9, 0xa4, 0x38, 0x92, 0x4a, 0x03, 0x99, 0xa3, 0x64, 0xe5, 0x91,
- 0xa5, 0x8a, 0x09, 0x7c, 0x8a, 0xca, 0x42, 0x42, 0x82, 0x53, 0x75, 0xde, 0x5a, 0x56, 0x65, 0x38,
- 0x41, 0x94, 0xf9, 0x49, 0x56, 0x08, 0x2c, 0x07, 0x0c, 0xc7, 0xb9, 0xcf, 0x30, 0x51, 0x02, 0xed,
- 0x4b, 0x1d, 0xd4, 0x5c, 0x9f, 0xe2, 0xf0, 0x10, 0xa1, 0x5e, 0x1c, 0x93, 0x4f, 0x7e, 0x1a, 0x22,
- 0x18, 0x83, 0x2a, 0xcd, 0x50, 0x3a, 0x1c, 0xc4, 0x38, 0xc1, 0xcc, 0xd4, 0x77, 0x8c, 0x4e, 0x75,
- 0xaf, 0x61, 0xab, 0xd2, 0x78, 0x31, 0x45, 0xb7, 0xf6, 0x3e, 0xc1, 0xa9, 0xfb, 0xe8, 0xfc, 0xb2,
- 0xa5, 0x7d, 0xbd, 0x6a, 0x75, 0x22, 0xcc, 0x46, 0xe3, 0xc0, 0x0e, 0x49, 0xa2, 0xfa, 0x50, 0x3f,
- 0x5d, 0x3a, 0x3c, 0x71, 0xd8, 0x24, 0x43, 0x54, 0x24, 0x50, 0x0f, 0x08, 0xfd, 0xd7, 0x5c, 0x1e,
- 0xf6, 0x01, 0x40, 0xa7, 0x19, 0x96, 0x75, 0x99, 0x37, 0x76, 0xf4, 0x4e, 0x75, 0xaf, 0x6d, 0xaf,
- 0xb1, 0xd7, 0x3e, 0xe0, 0xa1, 0x88, 0xf6, 0x98, 0x5b, 0xe1, 0x54, 0x6f, 0x21, 0xf7, 0x69, 0xed,
- 0xc7, 0xb7, 0xee, 0xed, 0xc5, 0x4e, 0x5e, 0xb6, 0x7f, 0x1b, 0xa0, 0xfe, 0x06, 0xe5, 0x98, 0x0c,
- 0x97, 0x7a, 0x3c, 0x04, 0x1b, 0x01, 0x6f, 0xdc, 0xd4, 0x05, 0xf0, 0xe1, 0x5a, 0xe0, 0x8a, 0x3d,
- 0x0a, 0x2c, 0xd3, 0xe1, 0x73, 0xb0, 0x99, 0x09, 0x7d, 0x55, 0xf9, 0xfd, 0xb5, 0x42, 0x2f, 0x94,
- 0xf5, 0x2a, 0x5f, 0xa5, 0xc1, 0x09, 0x80, 0xf2, 0x6b, 0xb0, 0xe8, 0xb9, 0x71, 0xfd, 0x9e, 0xdf,
- 0x95, 0x98, 0xb7, 0x73, 0xe7, 0xc7, 0x40, 0xed, 0x0d, 0x42, 0x3f, 0x95, 0x78, 0xb3, 0x72, 0xfd,
- 0xe0, 0x3b, 0x12, 0xb2, 0xef, 0xa7, 0x82, 0x0d, 0x5f, 0x81, 0x5b, 0x0a, 0x9b, 0x23, 0x8a, 0x98,
- 0xb9, 0xf1, 0x9f, 0x23, 0xaf, 0xca, 0x6c, 0x8f, 0x27, 0xff, 0x6d, 0xe6, 0x1f, 0xc0, 0x56, 0xe1,
- 0x35, 0x7c, 0x06, 0xb6, 0x8a, 0x2b, 0xaf, 0x26, 0xdd, 0xb0, 0xe5, 0x9b, 0xb0, 0x8b, 0x37, 0xb1,
- 0x30, 0x98, 0x2f, 0x57, 0x2d, 0xbd, 0xaf, 0x79, 0x65, 0x0a, 0x34, 0xc1, 0x66, 0x10, 0x93, 0xf0,
- 0x84, 0x8a, 0xe9, 0x56, 0xfa, 0x9a, 0xa7, 0xd6, 0xee, 0x06, 0x30, 0xe8, 0x38, 0x69, 0x0f, 0xc1,
- 0x76, 0x59, 0x1e, 0x7c, 0x02, 0x2a, 0xfc, 0x01, 0x2a, 0x50, 0x73, 0x05, 0xf4, 0xae, 0x78, 0x9d,
- 0x6e, 0xe5, 0x4c, 0x92, 0x44, 0x3c, 0xa7, 0x8c, 0x10, 0x8e, 0x46, 0x4c, 0x50, 0x0c, 0x4e, 0x91,
- 0xeb, 0x82, 0xf2, 0x59, 0x07, 0xb5, 0xc5, 0x1e, 0x8f, 0xb8, 0x3f, 0xd0, 0x04, 0x37, 0x85, 0x51,
- 0x28, 0x17, 0xc4, 0x6d, 0xaf, 0x58, 0xce, 0x4f, 0x90, 0x50, 0x2c, 0x4f, 0x10, 0x3c, 0x00, 0xdb,
- 0x7e, 0xa1, 0x62, 0x1a, 0xa2, 0xce, 0xfa, 0x4a, 0x9d, 0xbd, 0x74, 0xe2, 0xd6, 0xbe, 0x2f, 0xfb,
- 0xea, 0xcd, 0x33, 0xdd, 0xa3, 0xf3, 0xa9, 0xa5, 0x5f, 0x4c, 0x2d, 0xfd, 0xd7, 0xd4, 0xd2, 0xcf,
- 0x66, 0x96, 0x76, 0x31, 0xb3, 0xb4, 0x9f, 0x33, 0x4b, 0x7b, 0xdf, 0xfd, 0xe7, 0xb5, 0x38, 0x9d,
- 0xff, 0xaf, 0x8a, 0x1b, 0x12, 0x6c, 0x0a, 0xe8, 0xe3, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x74,
- 0x27, 0xe6, 0x00, 0x77, 0x05, 0x00, 0x00,
+ // 650 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0x4d, 0x6f, 0xd3, 0x30,
+ 0x18, 0xc7, 0x93, 0xa5, 0x1b, 0xab, 0xcb, 0xcb, 0x6a, 0x0d, 0xc8, 0x76, 0x48, 0x47, 0x0f, 0xa8,
+ 0x20, 0x2d, 0x61, 0x43, 0xe2, 0x30, 0x09, 0xa1, 0x65, 0xec, 0x05, 0xc1, 0x24, 0x14, 0x38, 0x71,
+ 0xa9, 0x9c, 0xc4, 0x4b, 0xc3, 0x92, 0x38, 0x8a, 0x5d, 0x58, 0xbf, 0x01, 0x27, 0xb4, 0x23, 0x47,
+ 0xb8, 0x72, 0xe6, 0x43, 0x4c, 0x9c, 0x26, 0x4e, 0x9c, 0x36, 0xd4, 0x7e, 0x02, 0xbe, 0x01, 0x8a,
+ 0xed, 0xa4, 0xa5, 0xa5, 0x48, 0xa0, 0x9d, 0x92, 0xc7, 0x7e, 0x9e, 0xff, 0xef, 0x79, 0xb1, 0x0d,
+ 0x6e, 0x7b, 0x84, 0xc6, 0x84, 0x5a, 0x07, 0x18, 0x07, 0x19, 0x4a, 0x98, 0xf5, 0x66, 0xcd, 0xc5,
+ 0x0c, 0xad, 0x95, 0x0b, 0x66, 0x9a, 0x11, 0x46, 0xe0, 0x4d, 0xe1, 0x67, 0x96, 0xcb, 0xd2, 0x6f,
+ 0x79, 0x31, 0x20, 0x01, 0xe1, 0x3e, 0x56, 0xfe, 0x27, 0xdc, 0x97, 0x97, 0x02, 0x42, 0x82, 0x08,
+ 0x5b, 0xdc, 0x72, 0xbb, 0x07, 0x16, 0x4a, 0x7a, 0xc5, 0x96, 0x50, 0x6a, 0x8b, 0x18, 0x29, 0x2b,
+ 0xb6, 0x0c, 0x99, 0x8c, 0x8b, 0x28, 0x2e, 0x13, 0xf1, 0x48, 0x98, 0xc8, 0xfd, 0xc6, 0xb8, 0x2a,
+ 0x0b, 0x63, 0x4c, 0x19, 0x8a, 0xd3, 0x42, 0x60, 0xdc, 0xc1, 0xef, 0x66, 0x88, 0x85, 0x44, 0x0a,
+ 0x34, 0xcf, 0x54, 0x50, 0xb7, 0x11, 0x0d, 0xbd, 0x1d, 0x8c, 0x37, 0xa3, 0x88, 0xbc, 0x45, 0x89,
+ 0x87, 0x61, 0x04, 0x6a, 0x34, 0xc5, 0x89, 0xdf, 0x8e, 0xc2, 0x38, 0x64, 0xba, 0xba, 0xa2, 0xb5,
+ 0x6a, 0xeb, 0x4b, 0xa6, 0x4c, 0x2d, 0x4f, 0xa6, 0xa8, 0xd6, 0xdc, 0x22, 0x61, 0x62, 0xdf, 0x3b,
+ 0x39, 0x6b, 0x28, 0x9f, 0xcf, 0x1b, 0xad, 0x20, 0x64, 0x9d, 0xae, 0x6b, 0x7a, 0x24, 0x96, 0x75,
+ 0xc8, 0xcf, 0x2a, 0xf5, 0x0f, 0x2d, 0xd6, 0x4b, 0x31, 0xe5, 0x01, 0xd4, 0x01, 0x5c, 0xff, 0x59,
+ 0x2e, 0x0f, 0xf7, 0x00, 0xc0, 0x47, 0x69, 0x28, 0xf2, 0xd2, 0x67, 0x56, 0xd4, 0x56, 0x6d, 0xbd,
+ 0x69, 0x4e, 0x69, 0xaf, 0xb9, 0x9d, 0xbb, 0x62, 0xba, 0xc9, 0xec, 0x4a, 0x4e, 0x75, 0x46, 0x62,
+ 0x37, 0xea, 0xdf, 0xbe, 0xac, 0x5e, 0x19, 0xad, 0xe4, 0x49, 0xf3, 0xa7, 0x06, 0x16, 0x9f, 0xe3,
+ 0x2c, 0x24, 0xfe, 0x58, 0x8d, 0x3b, 0x60, 0xd6, 0xcd, 0x0b, 0xd7, 0x55, 0x0e, 0xbc, 0x3b, 0x15,
+ 0x38, 0xd1, 0x1e, 0x09, 0x16, 0xe1, 0xf0, 0x11, 0x98, 0x4b, 0xb9, 0xbe, 0xcc, 0xfc, 0xd6, 0x54,
+ 0xa1, 0xc7, 0xb2, 0xf5, 0x32, 0x5e, 0x86, 0xc1, 0x1e, 0x80, 0xe2, 0xaf, 0x3d, 0xda, 0x73, 0xed,
+ 0xe2, 0x7b, 0xbe, 0x20, 0x30, 0x2f, 0x86, 0x9d, 0xef, 0x02, 0xb9, 0xd6, 0xf6, 0x50, 0x22, 0xf0,
+ 0x7a, 0xe5, 0xe2, 0xc1, 0x57, 0x05, 0x64, 0x0b, 0x25, 0x9c, 0x0d, 0x9f, 0x82, 0xcb, 0x12, 0x9b,
+ 0x61, 0x8a, 0x99, 0x3e, 0xfb, 0x8f, 0x23, 0xaf, 0x89, 0x68, 0x27, 0x0f, 0xfe, 0xd3, 0xcc, 0x3f,
+ 0xa9, 0xe0, 0x06, 0x37, 0xb1, 0xbf, 0x4f, 0x83, 0xdf, 0xa6, 0xbe, 0x0d, 0xaa, 0xa8, 0x30, 0xe4,
+ 0xe4, 0x17, 0x4d, 0x71, 0x47, 0xcc, 0xe2, 0x8e, 0x98, 0x9b, 0x49, 0xcf, 0xae, 0x7f, 0x1d, 0x97,
+ 0x75, 0x86, 0x91, 0xf0, 0x0e, 0x58, 0x40, 0x02, 0xd0, 0x8e, 0x31, 0xa5, 0x28, 0xc0, 0x54, 0x9f,
+ 0x59, 0xd1, 0x5a, 0x55, 0xe7, 0x9a, 0x5c, 0xdf, 0x97, 0xcb, 0x1b, 0xd7, 0xdf, 0x7d, 0x6c, 0x28,
+ 0x93, 0x39, 0xbe, 0x06, 0xf3, 0xc5, 0x79, 0x80, 0x0f, 0xc1, 0x7c, 0x71, 0x2d, 0x65, 0x4e, 0x4b,
+ 0x13, 0x39, 0x0d, 0x0f, 0xcf, 0x87, 0xf3, 0x86, 0xba, 0xa7, 0x38, 0x65, 0x08, 0xd4, 0xc1, 0x9c,
+ 0x1b, 0x11, 0xef, 0x90, 0xf2, 0x13, 0x58, 0xd9, 0x53, 0x1c, 0x69, 0xdb, 0xb3, 0x40, 0xa3, 0xdd,
+ 0xb8, 0xe9, 0x83, 0x6a, 0xd9, 0x42, 0xf8, 0x00, 0x54, 0xf2, 0x47, 0x42, 0x82, 0x96, 0x27, 0x40,
+ 0x2f, 0x8b, 0x17, 0xc4, 0xae, 0x1c, 0x0b, 0x12, 0xf7, 0xcf, 0x29, 0x1d, 0x1c, 0x06, 0x1d, 0xc6,
+ 0x29, 0x5a, 0x4e, 0x11, 0x76, 0x41, 0x79, 0xaf, 0x82, 0xfa, 0x68, 0x8d, 0xbb, 0xf9, 0x0c, 0xa1,
+ 0x0e, 0x2e, 0xf1, 0x61, 0xe2, 0x8c, 0x13, 0xab, 0x4e, 0x61, 0x0e, 0x77, 0x30, 0x57, 0x2c, 0x77,
+ 0xc6, 0x86, 0xa4, 0xfd, 0xef, 0x90, 0xec, 0xdd, 0x93, 0xbe, 0xa1, 0x9e, 0xf6, 0x0d, 0xf5, 0x47,
+ 0xdf, 0x50, 0x8f, 0x07, 0x86, 0x72, 0x3a, 0x30, 0x94, 0xef, 0x03, 0x43, 0x79, 0xb5, 0xfa, 0xd7,
+ 0xa3, 0x7b, 0x34, 0x7c, 0xfb, 0xf9, 0x29, 0x76, 0xe7, 0x38, 0xf4, 0xfe, 0xaf, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xfc, 0x05, 0xa9, 0xc7, 0x1b, 0x06, 0x00, 0x00,
}
func (m *BasicFeeAllowance) Marshal() (dAtA []byte, err error) {
@@ -598,6 +643,50 @@ func (m *PeriodicFeeAllowance) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
+func (m *AllowedMsgFeeAllowance) 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 *AllowedMsgFeeAllowance) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *AllowedMsgFeeAllowance) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ if len(m.AllowedMessages) > 0 {
+ for iNdEx := len(m.AllowedMessages) - 1; iNdEx >= 0; iNdEx-- {
+ i -= len(m.AllowedMessages[iNdEx])
+ copy(dAtA[i:], m.AllowedMessages[iNdEx])
+ i = encodeVarintFeegrant(dAtA, i, uint64(len(m.AllowedMessages[iNdEx])))
+ i--
+ dAtA[i] = 0x12
+ }
+ }
+ if m.Allowance != nil {
+ {
+ size, err := m.Allowance.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintFeegrant(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0xa
+ }
+ return len(dAtA) - i, nil
+}
+
func (m *Duration) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@@ -638,12 +727,12 @@ func (m *Duration_Duration) MarshalTo(dAtA []byte) (int, error) {
func (m *Duration_Duration) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
if m.Duration != nil {
- n5, err5 := github_com_gogo_protobuf_types.StdDurationMarshalTo(*m.Duration, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(*m.Duration):])
- if err5 != nil {
- return 0, err5
+ n6, err6 := github_com_gogo_protobuf_types.StdDurationMarshalTo(*m.Duration, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(*m.Duration):])
+ if err6 != nil {
+ return 0, err6
}
- i -= n5
- i = encodeVarintFeegrant(dAtA, i, uint64(n5))
+ i -= n6
+ i = encodeVarintFeegrant(dAtA, i, uint64(n6))
i--
dAtA[i] = 0xa
}
@@ -701,12 +790,12 @@ func (m *ExpiresAt_Time) MarshalTo(dAtA []byte) (int, error) {
func (m *ExpiresAt_Time) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
if m.Time != nil {
- n6, err6 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Time):])
- if err6 != nil {
- return 0, err6
+ n7, err7 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Time):])
+ if err7 != nil {
+ return 0, err7
}
- i -= n6
- i = encodeVarintFeegrant(dAtA, i, uint64(n6))
+ i -= n7
+ i = encodeVarintFeegrant(dAtA, i, uint64(n7))
i--
dAtA[i] = 0xa
}
@@ -828,6 +917,25 @@ func (m *PeriodicFeeAllowance) Size() (n int) {
return n
}
+func (m *AllowedMsgFeeAllowance) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ if m.Allowance != nil {
+ l = m.Allowance.Size()
+ n += 1 + l + sovFeegrant(uint64(l))
+ }
+ if len(m.AllowedMessages) > 0 {
+ for _, s := range m.AllowedMessages {
+ l = len(s)
+ n += 1 + l + sovFeegrant(uint64(l))
+ }
+ }
+ return n
+}
+
func (m *Duration) Size() (n int) {
if m == nil {
return 0
@@ -1255,6 +1363,124 @@ func (m *PeriodicFeeAllowance) Unmarshal(dAtA []byte) error {
}
return nil
}
+func (m *AllowedMsgFeeAllowance) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowFeegrant
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: AllowedMsgFeeAllowance: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: AllowedMsgFeeAllowance: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Allowance", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowFeegrant
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthFeegrant
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthFeegrant
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.Allowance == nil {
+ m.Allowance = &types1.Any{}
+ }
+ if err := m.Allowance.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field AllowedMessages", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowFeegrant
+ }
+ 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 ErrInvalidLengthFeegrant
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthFeegrant
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.AllowedMessages = append(m.AllowedMessages, string(dAtA[iNdEx:postIndex]))
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipFeegrant(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthFeegrant
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
func (m *Duration) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
diff --git a/x/feegrant/types/fees.go b/x/feegrant/types/fees.go
index cc526628f5..d4b5348579 100644
--- a/x/feegrant/types/fees.go
+++ b/x/feegrant/types/fees.go
@@ -19,7 +19,7 @@ type FeeAllowanceI interface {
//
// If remove is true (regardless of the error), the FeeAllowance will be deleted from storage
// (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseGrantedFees)
- Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (remove bool, err error)
+ Accept(ctx sdk.Context, fee sdk.Coins, msgs []sdk.Msg) (remove bool, err error)
// If we export fee allowances the timing info will be quite off (eg. go from height 100000 to 0)
// This callback allows the fee-allowance to change it's state and return a copy that is adjusted
diff --git a/x/feegrant/types/filtered_fee.go b/x/feegrant/types/filtered_fee.go
new file mode 100644
index 0000000000..99cf5f1731
--- /dev/null
+++ b/x/feegrant/types/filtered_fee.go
@@ -0,0 +1,123 @@
+package types
+
+import (
+ "time"
+
+ "github.com/cosmos/cosmos-sdk/codec/types"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+ proto "github.com/gogo/protobuf/proto"
+)
+
+// TODO: Revisit this once we have propoer gas fee framework.
+// Tracking issues https://github.com/cosmos/cosmos-sdk/issues/9054, https://github.com/cosmos/cosmos-sdk/discussions/9072
+const (
+ gasCostPerIteration = uint64(10)
+)
+
+var _ FeeAllowanceI = (*AllowedMsgFeeAllowance)(nil)
+var _ types.UnpackInterfacesMessage = (*AllowedMsgFeeAllowance)(nil)
+
+// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces
+func (a *AllowedMsgFeeAllowance) UnpackInterfaces(unpacker types.AnyUnpacker) error {
+ var allowance FeeAllowanceI
+ return unpacker.UnpackAny(a.Allowance, &allowance)
+}
+
+// NewAllowedMsgFeeAllowance creates new filtered fee allowance.
+func NewAllowedMsgFeeAllowance(allowance FeeAllowanceI, allowedMsgs []string) (*AllowedMsgFeeAllowance, error) {
+ msg, ok := allowance.(proto.Message)
+ if !ok {
+ return nil, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", msg)
+ }
+ any, err := types.NewAnyWithValue(msg)
+ if err != nil {
+ return nil, err
+ }
+
+ return &AllowedMsgFeeAllowance{
+ Allowance: any,
+ AllowedMessages: allowedMsgs,
+ }, nil
+}
+
+// GetAllowance returns allowed fee allowance.
+func (a *AllowedMsgFeeAllowance) GetAllowance() (FeeAllowanceI, error) {
+ allowance, ok := a.Allowance.GetCachedValue().(FeeAllowanceI)
+ if !ok {
+ return nil, sdkerrors.Wrap(ErrNoAllowance, "failed to get allowance")
+ }
+
+ return allowance, nil
+}
+
+// Accept method checks for the filtered messages has valid expiry
+func (a *AllowedMsgFeeAllowance) Accept(ctx sdk.Context, fee sdk.Coins, msgs []sdk.Msg) (bool, error) {
+ if !a.allMsgTypesAllowed(ctx, msgs) {
+ return false, sdkerrors.Wrap(ErrMessageNotAllowed, "message does not exist in allowed messages")
+ }
+
+ allowance, err := a.GetAllowance()
+ if err != nil {
+ return false, err
+ }
+
+ return allowance.Accept(ctx, fee, msgs)
+}
+
+func (a *AllowedMsgFeeAllowance) allowedMsgsToMap(ctx sdk.Context) map[string]bool {
+ msgsMap := make(map[string]bool, len(a.AllowedMessages))
+ for _, msg := range a.AllowedMessages {
+ ctx.GasMeter().ConsumeGas(gasCostPerIteration, "check msg")
+ msgsMap[msg] = true
+ }
+
+ return msgsMap
+}
+
+func (a *AllowedMsgFeeAllowance) allMsgTypesAllowed(ctx sdk.Context, msgs []sdk.Msg) bool {
+ msgsMap := a.allowedMsgsToMap(ctx)
+
+ for _, msg := range msgs {
+ ctx.GasMeter().ConsumeGas(gasCostPerIteration, "check msg")
+ if !msgsMap[msg.Type()] {
+ return false
+ }
+ }
+
+ return true
+}
+
+// PrepareForExport will adjust the expiration based on export time. In particular,
+// it will subtract the dumpHeight from any height-based expiration to ensure that
+// the elapsed number of blocks this allowance is valid for is fixed.
+func (a *AllowedMsgFeeAllowance) PrepareForExport(dumpTime time.Time, dumpHeight int64) FeeAllowanceI {
+ allowance, err := a.GetAllowance()
+ if err != nil {
+ panic("failed to get allowance")
+ }
+
+ f, err := NewAllowedMsgFeeAllowance(allowance.PrepareForExport(dumpTime, dumpHeight), a.AllowedMessages)
+ if err != nil {
+ panic("failed to export filtered fee allowance")
+ }
+
+ return f
+}
+
+// ValidateBasic implements FeeAllowance and enforces basic sanity checks
+func (a *AllowedMsgFeeAllowance) ValidateBasic() error {
+ if a.Allowance == nil {
+ return sdkerrors.Wrap(ErrNoAllowance, "allowance should not be empty")
+ }
+ if len(a.AllowedMessages) == 0 {
+ return sdkerrors.Wrap(ErrNoMessages, "allowed messages shouldn't be empty")
+ }
+
+ allowance, err := a.GetAllowance()
+ if err != nil {
+ return err
+ }
+
+ return allowance.ValidateBasic()
+}
diff --git a/x/feegrant/types/genesis.go b/x/feegrant/types/genesis.go
index 57ade9b43d..0d72dc8b18 100644
--- a/x/feegrant/types/genesis.go
+++ b/x/feegrant/types/genesis.go
@@ -14,7 +14,11 @@ func NewGenesisState(entries []FeeAllowanceGrant) *GenesisState {
// ValidateGenesis ensures all grants in the genesis state are valid
func ValidateGenesis(data GenesisState) error {
for _, f := range data.FeeAllowances {
- err := f.GetFeeGrant().ValidateBasic()
+ grant, err := f.GetFeeGrant()
+ if err != nil {
+ return err
+ }
+ err = grant.ValidateBasic()
if err != nil {
return err
}
diff --git a/x/feegrant/types/grant.go b/x/feegrant/types/grant.go
index 97a18de529..1c92080be3 100644
--- a/x/feegrant/types/grant.go
+++ b/x/feegrant/types/grant.go
@@ -47,17 +47,22 @@ func (a FeeAllowanceGrant) ValidateBasic() error {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "cannot self-grant fee authorization")
}
- return a.GetFeeGrant().ValidateBasic()
+ f, err := a.GetFeeGrant()
+ if err != nil {
+ return err
+ }
+
+ return f.ValidateBasic()
}
// GetFeeGrant unpacks allowance
-func (a FeeAllowanceGrant) GetFeeGrant() FeeAllowanceI {
+func (a FeeAllowanceGrant) GetFeeGrant() (FeeAllowanceI, error) {
allowance, ok := a.Allowance.GetCachedValue().(FeeAllowanceI)
if !ok {
- return nil
+ return nil, sdkerrors.Wrap(ErrNoAllowance, "failed to get allowance")
}
- return allowance
+ return allowance, nil
}
// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces
@@ -69,7 +74,12 @@ func (a FeeAllowanceGrant) UnpackInterfaces(unpacker types.AnyUnpacker) error {
// PrepareForExport will make all needed changes to the allowance to prepare to be
// re-imported at height 0, and return a copy of this grant.
func (a FeeAllowanceGrant) PrepareForExport(dumpTime time.Time, dumpHeight int64) FeeAllowanceGrant {
- feegrant := a.GetFeeGrant().PrepareForExport(dumpTime, dumpHeight)
+ f, err := a.GetFeeGrant()
+ if err != nil {
+ return FeeAllowanceGrant{}
+ }
+
+ feegrant := f.PrepareForExport(dumpTime, dumpHeight)
if feegrant == nil {
return FeeAllowanceGrant{}
}
diff --git a/x/feegrant/types/msgs.go b/x/feegrant/types/msgs.go
index 04f09c8f3f..1f2be972d8 100644
--- a/x/feegrant/types/msgs.go
+++ b/x/feegrant/types/msgs.go
@@ -50,7 +50,12 @@ func (msg MsgGrantFeeAllowance) ValidateBasic() error {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "cannot self-grant fee authorization")
}
- return msg.GetFeeAllowanceI().ValidateBasic()
+ allowance, err := msg.GetFeeAllowanceI()
+ if err != nil {
+ return err
+ }
+
+ return allowance.ValidateBasic()
}
func (msg MsgGrantFeeAllowance) GetSigners() []sdk.AccAddress {
@@ -62,13 +67,13 @@ func (msg MsgGrantFeeAllowance) GetSigners() []sdk.AccAddress {
}
// GetFeeAllowanceI returns unpacked FeeAllowance
-func (msg MsgGrantFeeAllowance) GetFeeAllowanceI() FeeAllowanceI {
+func (msg MsgGrantFeeAllowance) GetFeeAllowanceI() (FeeAllowanceI, error) {
allowance, ok := msg.Allowance.GetCachedValue().(FeeAllowanceI)
if !ok {
- return nil
+ return nil, sdkerrors.Wrap(ErrNoAllowance, "failed to get allowance")
}
- return allowance
+ return allowance, nil
}
// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces
diff --git a/x/feegrant/types/periodic_fee.go b/x/feegrant/types/periodic_fee.go
index 61c9ba2ffe..b20eeae5f0 100644
--- a/x/feegrant/types/periodic_fee.go
+++ b/x/feegrant/types/periodic_fee.go
@@ -19,7 +19,10 @@ var _ FeeAllowanceI = (*PeriodicFeeAllowance)(nil)
//
// If remove is true (regardless of the error), the FeeAllowance will be deleted from storage
// (eg. when it is used up). (See call to RevokeFeeAllowance in Keeper.UseGrantedFees)
-func (a *PeriodicFeeAllowance) Accept(fee sdk.Coins, blockTime time.Time, blockHeight int64) (bool, error) {
+func (a *PeriodicFeeAllowance) Accept(ctx sdk.Context, fee sdk.Coins, _ []sdk.Msg) (bool, error) {
+ blockTime := ctx.BlockTime()
+ blockHeight := ctx.BlockHeight()
+
if a.Basic.Expiration.IsExpired(&blockTime, blockHeight) {
return true, sdkerrors.Wrap(ErrFeeLimitExpired, "absolute limit")
}
diff --git a/x/feegrant/types/periodic_fee_test.go b/x/feegrant/types/periodic_fee_test.go
index 229fd99cea..aa66828bab 100644
--- a/x/feegrant/types/periodic_fee_test.go
+++ b/x/feegrant/types/periodic_fee_test.go
@@ -2,16 +2,18 @@ package types_test
import (
"testing"
- "time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
+ "github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/feegrant/types"
)
func TestPeriodicFeeValidAllow(t *testing.T) {
+ app := simapp.Setup(false)
atom := sdk.NewCoins(sdk.NewInt64Coin("atom", 555))
smallAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 43))
leftAtom := sdk.NewCoins(sdk.NewInt64Coin("atom", 512))
@@ -22,7 +24,6 @@ func TestPeriodicFeeValidAllow(t *testing.T) {
allow types.PeriodicFeeAllowance
// all other checks are ignored if valid=false
fee sdk.Coins
- blockTime time.Time
blockHeight int64
valid bool
accept bool
@@ -187,8 +188,9 @@ func TestPeriodicFeeValidAllow(t *testing.T) {
}
require.NoError(t, err)
+ ctx := app.BaseApp.NewContext(false, tmproto.Header{}).WithBlockHeight(tc.blockHeight)
// now try to deduct
- remove, err := tc.allow.Accept(tc.fee, tc.blockTime, tc.blockHeight)
+ remove, err := tc.allow.Accept(ctx, tc.fee, []sdk.Msg{})
if !tc.accept {
require.Error(t, err)
return
diff --git a/x/staking/types/authz.go b/x/staking/types/authz.go
index e88288da4e..32e8b719d1 100644
--- a/x/staking/types/authz.go
+++ b/x/staking/types/authz.go
@@ -1,18 +1,21 @@
package types
import (
- tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
-
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authz "github.com/cosmos/cosmos-sdk/x/authz/exported"
)
+// TODO: Revisit this once we have propoer gas fee framework.
+// Tracking issues https://github.com/cosmos/cosmos-sdk/issues/9054, https://github.com/cosmos/cosmos-sdk/discussions/9072
+const gasCostPerIteration = uint64(10)
+
var (
- _ authz.Authorization = &StakeAuthorization{}
- TypeDelegate = "/cosmos.staking.v1beta1.Msg/Delegate"
- TypeUndelegate = "/cosmos.staking.v1beta1.Msg/Undelegate"
- TypeBeginRedelegate = "/cosmos.staking.v1beta1.Msg/BeginRedelegate"
+ _ authz.Authorization = &StakeAuthorization{}
+
+ TypeDelegate = "/cosmos.staking.v1beta1.Msg/Delegate"
+ TypeUndelegate = "/cosmos.staking.v1beta1.Msg/Undelegate"
+ TypeBeginRedelegate = "/cosmos.staking.v1beta1.Msg/BeginRedelegate"
)
// NewStakeAuthorization creates a new StakeAuthorization object.
@@ -46,8 +49,19 @@ func (authorization StakeAuthorization) MethodName() string {
return authzType
}
+func (authorization StakeAuthorization) ValidateBasic() error {
+ if authorization.MaxTokens != nil && authorization.MaxTokens.IsNegative() {
+ return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "negative coin amount: %v", authorization.MaxTokens)
+ }
+ if authorization.AuthorizationType == AuthorizationType_AUTHORIZATION_TYPE_UNSPECIFIED {
+ return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "unknown authorization type")
+ }
+
+ return nil
+}
+
// Accept implements Authorization.Accept.
-func (authorization StakeAuthorization) Accept(msg sdk.ServiceMsg, block tmproto.Header) (updated authz.Authorization, delete bool, err error) {
+func (authorization StakeAuthorization) Accept(ctx sdk.Context, msg sdk.ServiceMsg) (updated authz.Authorization, delete bool, err error) {
var validatorAddress string
var amount sdk.Coin
@@ -68,13 +82,16 @@ func (authorization StakeAuthorization) Accept(msg sdk.ServiceMsg, block tmproto
isValidatorExists := false
allowedList := authorization.GetAllowList().GetAddress()
for _, validator := range allowedList {
+ ctx.GasMeter().ConsumeGas(gasCostPerIteration, "stake authorization")
if validator == validatorAddress {
isValidatorExists = true
break
}
}
+
denyList := authorization.GetDenyList().GetAddress()
for _, validator := range denyList {
+ ctx.GasMeter().ConsumeGas(gasCostPerIteration, "stake authorization")
if validator == validatorAddress {
return nil, false, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, " cannot delegate/undelegate to %s validator", validator)
}
diff --git a/x/staking/types/authz_test.go b/x/staking/types/authz_test.go
index 18709d9ecd..c43ef9e1fb 100644
--- a/x/staking/types/authz_test.go
+++ b/x/staking/types/authz_test.go
@@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/require"
+ "github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)
@@ -21,13 +22,21 @@ var (
)
func TestAuthzAuthorizations(t *testing.T) {
+ app := simapp.Setup(false)
+ ctx := app.BaseApp.NewContext(false, tmproto.Header{})
+
+ // verify ValidateBasic returns error for the AUTHORIZATION_TYPE_UNSPECIFIED authorization type
+ delAuth, err := stakingtypes.NewStakeAuthorization([]sdk.ValAddress{val1, val2}, []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_UNSPECIFIED, &coin100)
+ require.NoError(t, err)
+ require.Error(t, delAuth.ValidateBasic())
// verify MethodName
- delAuth, _ := stakingtypes.NewStakeAuthorization([]sdk.ValAddress{val1, val2}, []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, &coin100)
+ delAuth, err = stakingtypes.NewStakeAuthorization([]sdk.ValAddress{val1, val2}, []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, &coin100)
+ require.NoError(t, err)
require.Equal(t, delAuth.MethodName(), stakingtypes.TypeDelegate)
// error both allow & deny list
- _, err := stakingtypes.NewStakeAuthorization([]sdk.ValAddress{val1, val2}, []sdk.ValAddress{val1}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, &coin100)
+ _, err = stakingtypes.NewStakeAuthorization([]sdk.ValAddress{val1, val2}, []sdk.ValAddress{val1}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, &coin100)
require.Error(t, err)
// verify MethodName
@@ -243,7 +252,7 @@ func TestAuthzAuthorizations(t *testing.T) {
t.Run(tc.msg, func(t *testing.T) {
delAuth, err := stakingtypes.NewStakeAuthorization(tc.allowed, tc.denied, tc.msgType, tc.limit)
require.NoError(t, err)
- updated, del, err := delAuth.Accept(tc.srvMsg, tmproto.Header{})
+ updated, del, err := delAuth.Accept(ctx, tc.srvMsg)
if tc.expectErr {
require.Error(t, err)
require.Equal(t, tc.isDelete, del)
diff --git a/x/upgrade/keeper/keeper.go b/x/upgrade/keeper/keeper.go
index 3342c26a8c..8f32277b80 100644
--- a/x/upgrade/keeper/keeper.go
+++ b/x/upgrade/keeper/keeper.go
@@ -8,6 +8,9 @@ import (
"path"
"path/filepath"
+ "github.com/tendermint/tendermint/libs/log"
+ tmos "github.com/tendermint/tendermint/libs/os"
+
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store/prefix"
store "github.com/cosmos/cosmos-sdk/store/types"
@@ -16,8 +19,6 @@ import (
"github.com/cosmos/cosmos-sdk/types/module"
xp "github.com/cosmos/cosmos-sdk/x/upgrade/exported"
"github.com/cosmos/cosmos-sdk/x/upgrade/types"
- "github.com/tendermint/tendermint/libs/log"
- tmos "github.com/tendermint/tendermint/libs/os"
)
// UpgradeInfoFileName file to store upgrade information
diff --git a/x/upgrade/keeper/keeper_test.go b/x/upgrade/keeper/keeper_test.go
index 50545e18e4..724848271b 100644
--- a/x/upgrade/keeper/keeper_test.go
+++ b/x/upgrade/keeper/keeper_test.go
@@ -195,7 +195,7 @@ func (s *KeeperTestSuite) TestSetUpgradedClient() {
}
// Test that the protocol version successfully increments after an
-// upgrade and is succesfully set on BaseApp's appVersion.
+// upgrade and is successfully set on BaseApp's appVersion.
func (s *KeeperTestSuite) TestIncrementProtocolVersion() {
oldProtocolVersion := s.app.BaseApp.AppVersion()
s.app.UpgradeKeeper.SetUpgradeHandler("dummy", func(_ sdk.Context, _ types.Plan, vm module.VersionMap) (module.VersionMap, error) { return vm, nil })