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(community): enable param updates via governance #1744

Closed
wants to merge 3 commits into from
Closed
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 @@ -667,6 +667,7 @@ func NewApp(
&app.mintKeeper,
&app.kavadistKeeper,
app.stakingKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName),
)

app.incentiveKeeper = incentivekeeper.NewKeeper(
Expand Down
33 changes: 33 additions & 0 deletions docs/core/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@
- [kava/community/v1beta1/tx.proto](#kava/community/v1beta1/tx.proto)
- [MsgFundCommunityPool](#kava.community.v1beta1.MsgFundCommunityPool)
- [MsgFundCommunityPoolResponse](#kava.community.v1beta1.MsgFundCommunityPoolResponse)
- [MsgUpdateParams](#kava.community.v1beta1.MsgUpdateParams)
- [MsgUpdateParamsResponse](#kava.community.v1beta1.MsgUpdateParamsResponse)

- [Msg](#kava.community.v1beta1.Msg)

Expand Down Expand Up @@ -3214,6 +3216,36 @@ MsgFundCommunityPoolResponse defines the Msg/FundCommunityPool response type.




<a name="kava.community.v1beta1.MsgUpdateParams"></a>

### MsgUpdateParams
MsgUpdateParams is the Msg/UpdateParams request type.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `authority` | [string](#string) | | authority is the address that controls the module (defaults to x/gov unless overwritten). |
| `params` | [Params](#kava.community.v1beta1.Params) | | params defines the community parameters to update.

NOTE: All parameters must be supplied.

TODO |






<a name="kava.community.v1beta1.MsgUpdateParamsResponse"></a>

### MsgUpdateParamsResponse
MsgUpdateParamsResponse defines the response structure for executing a MsgUpdateParams message.





<!-- end messages -->

<!-- end enums -->
Expand All @@ -3229,6 +3261,7 @@ Msg defines the community Msg service.
| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint |
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
| `FundCommunityPool` | [MsgFundCommunityPool](#kava.community.v1beta1.MsgFundCommunityPool) | [MsgFundCommunityPoolResponse](#kava.community.v1beta1.MsgFundCommunityPoolResponse) | FundCommunityPool defines a method to allow an account to directly fund the community module account. | |
| `UpdateParams` | [MsgUpdateParams](#kava.community.v1beta1.MsgUpdateParams) | [MsgUpdateParamsResponse](#kava.community.v1beta1.MsgUpdateParamsResponse) | UpdateParams defines a method to update the params of the community module. | |

<!-- end services -->

Expand Down
18 changes: 17 additions & 1 deletion proto/kava/community/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ package kava.community.v1beta1;
import "cosmos/base/v1beta1/coin.proto";
import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto";
import "kava/community/v1beta1/params.proto";

option go_package = "github.com/kava-labs/kava/x/community/types";
option (gogoproto.equal_all) = true;

// Msg defines the community Msg service.
service Msg {
// FundCommunityPool defines a method to allow an account to directly fund the community module account.
rpc FundCommunityPool(MsgFundCommunityPool) returns (MsgFundCommunityPoolResponse);

// UpdateParams defines a method to update the params of the community module.
rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse);
}

// MsgFundCommunityPool allows an account to directly fund the community module account.
Expand All @@ -27,3 +30,16 @@ message MsgFundCommunityPool {

// MsgFundCommunityPoolResponse defines the Msg/FundCommunityPool response type.
message MsgFundCommunityPoolResponse {}

// MsgUpdateParams is the Msg/UpdateParams request type.
message MsgUpdateParams {
// authority is the address that controls the module (defaults to x/gov unless overwritten).
string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// params defines the community parameters to update.
//
// NOTE: All parameters must be supplied.
Params params = 2 [(gogoproto.nullable) = false]; // TODO
}

// MsgUpdateParamsResponse defines the response structure for executing a MsgUpdateParams message.
message MsgUpdateParamsResponse {}
3 changes: 3 additions & 0 deletions x/community/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type Keeper struct {
stakingKeeper types.StakingKeeper

legacyCommunityPoolAddress sdk.AccAddress
authority sdk.AccAddress
}

// NewKeeper creates a new community Keeper instance
Expand All @@ -40,6 +41,7 @@ func NewKeeper(
mk types.MintKeeper,
kk types.KavadistKeeper,
sk types.StakingKeeper,
authority sdk.AccAddress,
) Keeper {
// ensure community module account is set
addr := ak.GetModuleAddress(types.ModuleAccountName)
Expand All @@ -65,6 +67,7 @@ func NewKeeper(
moduleAddress: addr,

legacyCommunityPoolAddress: legacyAddr,
authority: authority,
}
}

Expand Down
17 changes: 17 additions & 0 deletions x/community/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package keeper
import (
"context"

errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

"github.com/kava-labs/kava/x/community/types"
)
Expand Down Expand Up @@ -47,3 +49,18 @@ func (s msgServer) FundCommunityPool(goCtx context.Context, msg *types.MsgFundCo

return &types.MsgFundCommunityPoolResponse{}, nil
}

// UpdateParams overwrites the module params provided the msg is authorized.
func (s msgServer) UpdateParams(goCtx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) {
if s.keeper.authority.String() != msg.Authority {
return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "invalid authority; expected %s, got %s", s.keeper.authority, msg.Authority)
}
if err := msg.Params.Validate(); err != nil {
return nil, errorsmod.Wrap(types.ErrInvalidParams, err.Error())
}

ctx := sdk.UnwrapSDKContext(goCtx)
s.keeper.SetParams(ctx, msg.Params)

return &types.MsgUpdateParamsResponse{}, nil
}
72 changes: 72 additions & 0 deletions x/community/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ package keeper_test

import (
"testing"
"time"

sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/stretchr/testify/suite"

"github.com/kava-labs/kava/app"
Expand Down Expand Up @@ -110,3 +114,71 @@ func (suite *msgServerTestSuite) TestMsgFundCommunityPool() {
})
}
}

func (suite *msgServerTestSuite) TestMsgUpdateParams() {
testCases := []struct {
name string
setup func() *types.MsgUpdateParams
expectedErr error
}{
{
name: "new params overwrite existing",
setup: func() *types.MsgUpdateParams {
newParams := types.DefaultParams()
newParams.UpgradeTimeDisableInflation = time.Date(2050, 1, 1, 0, 0, 0, 0, time.UTC)
return &types.MsgUpdateParams{
Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(),
Params: newParams,
}
},
},
{
name: "msg with invalid authority is rejected",
setup: func() *types.MsgUpdateParams {
return &types.MsgUpdateParams{
Authority: authtypes.NewModuleAddress("not gov").String(),
Params: types.Params{
UpgradeTimeDisableInflation: time.Date(2050, 1, 1, 0, 0, 0, 0, time.UTC),
},
}
},
expectedErr: sdkerrors.ErrUnauthorized,
},
{
name: "msg with invalid params is rejected",
setup: func() *types.MsgUpdateParams {
return &types.MsgUpdateParams{
Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(),
Params: types.Params{
UpgradeTimeDisableInflation: time.Time{},
StakingRewardsPerSecond: sdkmath.LegacyNewDec(-5), // invalid
UpgradeTimeSetStakingRewardsPerSecond: sdkmath.LegacyNewDec(1000),
},
}
},
expectedErr: types.ErrInvalidParams,
},
}

for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.SetupTest()

msg := tc.setup()

oldParams, found := suite.Keeper.GetParams(suite.Ctx)
suite.Require().True(found)
_, err := suite.msgServer.UpdateParams(sdk.WrapSDKContext(suite.Ctx), msg)
newParams, found := suite.Keeper.GetParams(suite.Ctx)
suite.Require().True(found)

if tc.expectedErr == nil {
suite.NoError(err)
suite.Equal(msg.Params, newParams)
} else {
suite.ErrorIs(err, tc.expectedErr)
suite.Equal(oldParams, newParams)
}
})
}
}
6 changes: 5 additions & 1 deletion x/community/types/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package types

import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/legacy"
"github.com/cosmos/cosmos-sdk/codec/types"
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -12,7 +13,9 @@ import (

// RegisterLegacyAminoCodec registers all the necessary types and interfaces for the module.
func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
cdc.RegisterConcrete(&MsgFundCommunityPool{}, "community/MsgFundCommunityPool", nil)
legacy.RegisterAminoMsg(cdc, &MsgFundCommunityPool{}, "community/MsgFundCommunityPool")
legacy.RegisterAminoMsg(cdc, &MsgUpdateParams{}, "community/MsgUpdateParams")

cdc.RegisterConcrete(&CommunityPoolLendDepositProposal{}, "kava/CommunityPoolLendDepositProposal", nil)
cdc.RegisterConcrete(&CommunityPoolLendWithdrawProposal{}, "kava/CommunityPoolLendWithdrawProposal", nil)
cdc.RegisterConcrete(&CommunityCDPRepayDebtProposal{}, "kava/CommunityCDPRepayDebtProposal", nil)
Expand All @@ -24,6 +27,7 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
func RegisterInterfaces(registry types.InterfaceRegistry) {
registry.RegisterImplementations((*sdk.Msg)(nil),
&MsgFundCommunityPool{},
&MsgUpdateParams{},
)
registry.RegisterImplementations((*govv1beta1.Content)(nil),
&CommunityPoolLendDepositProposal{},
Expand Down
7 changes: 7 additions & 0 deletions x/community/types/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package types

import errorsmod "cosmossdk.io/errors"

var (
ErrInvalidParams = errorsmod.Register(ModuleName, 1, "invalid params")
)
45 changes: 45 additions & 0 deletions x/community/types/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
var (
_ sdk.Msg = &MsgFundCommunityPool{}
_ legacytx.LegacyMsg = &MsgFundCommunityPool{}
_ sdk.Msg = &MsgUpdateParams{}
_ legacytx.LegacyMsg = &MsgUpdateParams{}
)

// NewMsgFundCommunityPool returns a new MsgFundCommunityPool
Expand Down Expand Up @@ -55,3 +57,46 @@ func (msg MsgFundCommunityPool) GetSigners() []sdk.AccAddress {
}
return []sdk.AccAddress{depositor}
}

// NewMsgUpdateParams returns a new MsgUpdateParams
func NewMsgUpdateParams(authority sdk.AccAddress, params Params) MsgUpdateParams {
return MsgUpdateParams{
Authority: authority.String(),
Params: params,
}
}

// Route return the message type used for routing the message.
func (msg MsgUpdateParams) Route() string { return ModuleName }

// Type returns a human-readable string for the message, intended for utilization within tags.
func (msg MsgUpdateParams) Type() string { return sdk.MsgTypeURL(&msg) }

// ValidateBasic does a simple validation check that doesn't require access to any other information.
func (msg MsgUpdateParams) ValidateBasic() error {
_, err := sdk.AccAddressFromBech32(msg.Authority)
if err != nil {
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, err.Error())
}

if err := msg.Params.Validate(); err != nil {
return errorsmod.Wrap(ErrInvalidParams, err.Error())
}

return nil
}

// GetSignBytes gets the canonical byte representation of the Msg.
func (msg MsgUpdateParams) GetSignBytes() []byte {
bz := ModuleCdc.MustMarshalJSON(&msg)
return sdk.MustSortJSON(bz)
}

// GetSigners returns the addresses of signers that must sign.
func (msg MsgUpdateParams) GetSigners() []sdk.AccAddress {
authority, err := sdk.AccAddressFromBech32(msg.Authority)
if err != nil {
panic(err)
}
return []sdk.AccAddress{authority}
}
Loading