Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: x/rewards param store migration #468

Merged
merged 9 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@ func NewArchwayApp(
app.Keepers.AccountKeeper,
app.Keepers.BankKeeper,
app.getSubspace(rewardsTypes.ModuleName),
govModuleAddr,
)

// Note we set up mint keeper after the x/rewards keeper
Expand Down
6 changes: 6 additions & 0 deletions app/upgrades/latest/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (

"github.com/archway-network/archway/app/keepers"
"github.com/archway-network/archway/app/upgrades"
rewardstypes "github.com/archway-network/archway/x/rewards/types"
)

// This upgrade handler is used for all the current changes to the protocol
Expand Down Expand Up @@ -78,6 +79,11 @@ var Upgrade = upgrades.Upgrade{
// wasm
case wasmtypes.ModuleName:
keyTable = wasmtypes.ParamKeyTable() //nolint:staticcheck

// archway modules
case rewardstypes.ModuleName:
keyTable = rewardstypes.ParamKeyTable()

default:
continue
}
Expand Down
30 changes: 29 additions & 1 deletion proto/archway/rewards/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ service Msg {
// SetFlatFee sets or updates or removes the flat fee to interact with the
// contract Method is authorized to the contract owner.
rpc SetFlatFee(MsgSetFlatFee) returns (MsgSetFlatFeeResponse);

// UpdateParams defines a governance operation for updating the x/rewards
// module parameters. The authority is defined in the keeper.
//
// Since: archway v5 && cosmos-sdk 0.47
rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse);
}

// MsgSetContractMetadata is the request for Msg.SetContractMetadata.
Expand Down Expand Up @@ -80,4 +86,26 @@ message MsgSetFlatFee {
}

// MsgSetFlatFeeResponse is the response for Msg.SetFlatFee.
message MsgSetFlatFeeResponse {}
message MsgSetFlatFeeResponse {}


// MsgUpdateParams is the Msg/UpdateParams request type.
//
// Since: archway v5 && cosmos-sdk 0.47
message MsgUpdateParams {
option (cosmos.msg.v1.signer) = "authority";

// authority is the address that controls the module (defaults to x/gov unless overwritten).
string authority = 1;

// params defines the x/rewards parameters to update.
//
// NOTE: All parameters must be supplied.
Params params = 2 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "params,omitempty"];
}

// MsgUpdateParamsResponse defines the response structure for executing a
// MsgUpdateParams message.
//
// Since: archway v5 && cosmos-sdk 0.47
message MsgUpdateParamsResponse {}
18 changes: 18 additions & 0 deletions x/rewards/exported/exported.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package exported

import (
sdk "github.com/cosmos/cosmos-sdk/types"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
)

type (
ParamSet = paramtypes.ParamSet

// Subspace defines an interface that implements the legacy x/params Subspace
// type.
//
// NOTE: This is used solely for migration of x/params managed parameters.
Subspace interface {
GetParamSet(ctx sdk.Context, ps ParamSet)
}
)
4 changes: 3 additions & 1 deletion x/rewards/keeper/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState {

// InitGenesis initializes the module genesis state.
func (k Keeper) InitGenesis(ctx sdk.Context, state *types.GenesisState) {
k.SetParams(ctx, state.Params)
if err := k.SetParams(ctx, state.Params); err != nil {
panic(err)
}
k.state.ContractMetadataState(ctx).Import(state.ContractsMetadata)
k.state.BlockRewardsState(ctx).Import(state.BlockRewards)
k.state.TxRewardsState(ctx).Import(state.TxRewards)
Expand Down
9 changes: 8 additions & 1 deletion x/rewards/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@ type Keeper struct {
trackingKeeper TrackingKeeperExpected
authKeeper AuthKeeperExpected
bankKeeper BankKeeperExpected
authority string // this should be the x/gov module account
}

// NewKeeper creates a new Keeper instance.
func NewKeeper(cdc codec.Codec, key storetypes.StoreKey, contractInfoReader ContractInfoReaderExpected, trackingKeeper TrackingKeeperExpected, ak AuthKeeperExpected, bk BankKeeperExpected, ps paramTypes.Subspace) Keeper {
func NewKeeper(cdc codec.Codec, key storetypes.StoreKey, contractInfoReader ContractInfoReaderExpected, trackingKeeper TrackingKeeperExpected, ak AuthKeeperExpected, bk BankKeeperExpected, ps paramTypes.Subspace, authority string) Keeper {
if !ps.HasKeyTable() {
ps = ps.WithKeyTable(types.ParamKeyTable())
}
Expand All @@ -65,6 +66,7 @@ func NewKeeper(cdc codec.Codec, key storetypes.StoreKey, contractInfoReader Cont
trackingKeeper: trackingKeeper,
authKeeper: ak,
bankKeeper: bk,
authority: authority,
}
}

Expand Down Expand Up @@ -105,3 +107,8 @@ func (k Keeper) GetRewardsRecords(ctx sdk.Context, rewardsAddr sdk.AccAddress, p

return k.state.RewardsRecord(ctx).GetRewardsRecordByRewardsAddressPaginated(rewardsAddr, pageReq)
}

// GetAuthority returns the x/rewards module's authority.
func (k Keeper) GetAuthority() string {
return k.authority
}
27 changes: 27 additions & 0 deletions x/rewards/keeper/migrations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package keeper

import (
sdk "github.com/cosmos/cosmos-sdk/types"

v2 "github.com/archway-network/archway/x/rewards/migrations/v2"
)

// Migrator is a struct for handling in-place store migrations.
type Migrator struct {
keeper Keeper
}

// NewMigrator returns a new Migrator.
func NewMigrator(keeper Keeper) Migrator {
return Migrator{
keeper: keeper,
}
}

// Migrate1to2 migrates the x/rewards module state from the consensus
// version 1 to version 2. Specifically, it takes the parameters that are currently stored
// and managed by the x/params module and stores them directly into the x/rewards
// module state.
func (m Migrator) Migrate1to2(ctx sdk.Context) error {
return v2.MigrateStore(ctx, m.keeper.state.key, m.keeper.paramStore, m.keeper.cdc)
}
31 changes: 31 additions & 0 deletions x/rewards/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keeper
import (
"context"

errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -112,3 +113,33 @@ func (s MsgServer) SetFlatFee(c context.Context, request *types.MsgSetFlatFee) (

return &types.MsgSetFlatFeeResponse{}, nil
}

// UpdateParams implements types.MsgServer.
func (s MsgServer) UpdateParams(c context.Context, request *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) {
if request == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

ctx := sdk.UnwrapSDKContext(c)

_, err := sdk.AccAddressFromBech32(request.Authority)
if err != nil {
return nil, err // returning error "as is" since this should not happen due to the earlier ValidateBasic call
}

if request.GetAuthority() != s.keeper.GetAuthority() {
return nil, errorsmod.Wrap(types.ErrUnauthorized, "sender address is not authorized address to update module params")
}

err = request.GetParams().Validate() // need to explicitly validate as x/gov invokes this msg and it does not validate
if err != nil {
return nil, err
}

err = s.keeper.SetParams(ctx, request.GetParams())
if err != nil {
return nil, err
}

return &types.MsgUpdateParamsResponse{}, nil
}
72 changes: 72 additions & 0 deletions x/rewards/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"

e2eTesting "github.com/archway-network/archway/e2e/testing"
"github.com/archway-network/archway/pkg/testutils"
Expand Down Expand Up @@ -341,3 +342,74 @@ func (s *KeeperTestSuite) TestMsgServer_SetFlatFee() {
})
}
}

func (s *KeeperTestSuite) TestMsgServer_UpdateParams() {
ctx, k := s.chain.GetContext(), s.chain.GetApp().Keepers.RewardsKeeper
account := s.chain.GetAccount(0)

server := keeper.NewMsgServer(k)

govAddress := s.chain.GetApp().Keepers.AccountKeeper.GetModuleAddress(govtypes.ModuleName)

testCases := []struct {
testCase string
prepare func() *rewardstypes.MsgUpdateParams
expectError bool
}{
{
testCase: "fail: invalid params",
prepare: func() *rewardstypes.MsgUpdateParams {
params := rewardstypes.DefaultParams()
params.InflationRewardsRatio = sdk.NewDecWithPrec(-2, 2)
return &rewardstypes.MsgUpdateParams{
Authority: govAddress.String(),
Params: params,
}
},
expectError: true,
},
{
testCase: "fail: invalid authority address",
prepare: func() *rewardstypes.MsgUpdateParams {
return &rewardstypes.MsgUpdateParams{
Authority: "👻",
Params: rewardstypes.DefaultParams(),
}
},
expectError: true,
},
{
testCase: "fail: authority address is not gov address",
prepare: func() *rewardstypes.MsgUpdateParams {
return &rewardstypes.MsgUpdateParams{
Authority: account.Address.String(),
Params: rewardstypes.DefaultParams(),
}
},
expectError: true,
},
{
testCase: "ok: valid params with x/gov address",
prepare: func() *rewardstypes.MsgUpdateParams {
return &rewardstypes.MsgUpdateParams{
Authority: govAddress.String(),
Params: rewardstypes.DefaultParams(),
}
},
expectError: false,
},
}

for _, tc := range testCases {
s.Run(fmt.Sprintf("Case: %s", tc.testCase), func() {
req := tc.prepare()
res, err := server.UpdateParams(sdk.WrapSDKContext(ctx), req)
if tc.expectError {
s.Require().Error(err)
} else {
s.Require().NoError(err)
s.Require().Equal(&rewardstypes.MsgUpdateParamsResponse{}, res)
}
})
}
}
39 changes: 21 additions & 18 deletions x/rewards/keeper/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,42 @@ import (

// InflationRewardsRatio return inflation rewards params ratio.
func (k Keeper) InflationRewardsRatio(ctx sdk.Context) (res sdk.Dec) {
k.paramStore.Get(ctx, types.InflationRewardsRatioParamKey, &res)
return
return k.GetParams(ctx).InflationRewardsRatio
}

// TxFeeRebateRatio return tx fee rebate rewards params ratio.
func (k Keeper) TxFeeRebateRatio(ctx sdk.Context) (res sdk.Dec) {
k.paramStore.Get(ctx, types.TxFeeRebateRatioParamKey, &res)
return
return k.GetParams(ctx).TxFeeRebateRatio
}

// MaxWithdrawRecords return the maximum number of types.RewardsRecord objects used for the withdrawal operation.
func (k Keeper) MaxWithdrawRecords(ctx sdk.Context) (res uint64) {
k.paramStore.Get(ctx, types.MaxWithdrawRecordsParamKey, &res)
return
return k.GetParams(ctx).MaxWithdrawRecords
}

func (k Keeper) MinimumPriceOfGas(ctx sdk.Context) sdk.DecCoin {
var res sdk.DecCoin
k.paramStore.Get(ctx, types.MinPriceOfGasParamKey, &res)
return res
return k.GetParams(ctx).MinPriceOfGas
}

// GetParams return all module parameters.
func (k Keeper) GetParams(ctx sdk.Context) types.Params {
return types.NewParams(
k.InflationRewardsRatio(ctx),
k.TxFeeRebateRatio(ctx),
k.MaxWithdrawRecords(ctx),
k.MinimumPriceOfGas(ctx),
)
func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
store := ctx.KVStore(k.state.key)
bz := store.Get(types.ParamsKey)
if bz == nil {
return params
}

k.cdc.MustUnmarshal(bz, &params)
return params
}

// SetParams sets all module parameters.
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
k.paramStore.SetParamSet(ctx, &params)
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) error {
store := ctx.KVStore(k.state.key)
bz, err := k.cdc.Marshal(&params)
if err != nil {
return err
}
store.Set(types.ParamsKey, bz)
return nil
}
33 changes: 33 additions & 0 deletions x/rewards/migrations/v2/migrate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package v2

import (
"github.com/cosmos/cosmos-sdk/codec"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/archway-network/archway/x/rewards/exported"
"github.com/archway-network/archway/x/rewards/types"
)

// MigrateStore migrates the x/rewards module state from the consensus version 1 to
// version 2. Specifically, it takes the parameters that are currently stored
// and managed by the x/params module and stores them directly into the x/rewards
// module state.
func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, legacySubspace exported.Subspace, cdc codec.BinaryCodec) error {
store := ctx.KVStore(storeKey)
var currParams types.Params
legacySubspace.GetParamSet(ctx, &currParams)

if err := currParams.Validate(); err != nil {
return err
}

bz, err := cdc.Marshal(&currParams)
if err != nil {
return err
}

store.Set(types.ParamsKey, bz)

return nil
}
Loading
Loading