Skip to content

Commit

Permalink
chore: import cosmos-sdk's params
Browse files Browse the repository at this point in the history
  • Loading branch information
moul committed Oct 7, 2024
1 parent 628f965 commit 0c0f9f7
Show file tree
Hide file tree
Showing 45 changed files with 3,657 additions and 0 deletions.
26 changes: 26 additions & 0 deletions tm2/pkg/sdk/params/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!--
Guiding Principles:
Changelogs are for humans, not machines.
There should be an entry for every single version.
The same types of changes should be grouped.
Versions and sections should be linkable.
The latest version comes first.
The release date of each version is displayed.
Mention whether you follow Semantic Versioning.
Usage:
Change log entries are to be added to the Unreleased section under the
appropriate stanza (see below). Each entry should ideally include a tag and
the Github issue reference in the following format:
* (<tag>) [#<issue-number>] Changelog message.
Types of changes (Stanzas):
"Features" for new features.
"Improvements" for changes in existing functionality.
"Deprecated" for soon-to-be removed features.
"Bug Fixes" for any bug fixes.
"API Breaking" for breaking exported APIs used by developers building on SDK.
Ref: https://keepachangelog.com/en/1.0.0/
-->

# Changelog

## [Unreleased]
79 changes: 79 additions & 0 deletions tm2/pkg/sdk/params/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
sidebar_position: 1
---

# `x/params`

> Note: The Params module has been deprecated in favour of each module housing its own parameters.
## Abstract

Package params provides a globally available parameter store.

There are two main types, Keeper and Subspace. Subspace is an isolated namespace for a
paramstore, where keys are prefixed by preconfigured spacename. Keeper has a
permission to access all existing spaces.

Subspace can be used by the individual keepers, which need a private parameter store
that the other keepers cannot modify. The params Keeper can be used to add a route to `x/gov` router in order to modify any parameter in case a proposal passes.

The following contents explains how to use params module for master and user modules.

## Contents

* [Keeper](#keeper)
* [Subspace](#subspace)
* [Key](#key)
* [KeyTable](#keytable)
* [ParamSet](#paramset)

## Keeper

In the app initialization stage, [subspaces](#subspace) can be allocated for other modules' keeper using `Keeper.Subspace` and are stored in `Keeper.spaces`. Then, those modules can have a reference to their specific parameter store through `Keeper.GetSubspace`.

Example:

```go
type ExampleKeeper struct {
paramSpace paramtypes.Subspace
}

func (k ExampleKeeper) SetParams(ctx sdk.Context, params types.Params) {
k.paramSpace.SetParamSet(ctx, &params)
}
```

## Subspace

`Subspace` is a prefixed subspace of the parameter store. Each module which uses the
parameter store will take a `Subspace` to isolate permission to access.

### Key

Parameter keys are human readable alphanumeric strings. A parameter for the key
`"ExampleParameter"` is stored under `[]byte("SubspaceName" + "/" + "ExampleParameter")`,
where `"SubspaceName"` is the name of the subspace.

Subkeys are secondary parameter keys those are used along with a primary parameter key.
Subkeys can be used for grouping or dynamic parameter key generation during runtime.

### KeyTable

All of the parameter keys that will be used should be registered at the compile
time. `KeyTable` is essentially a `map[string]attribute`, where the `string` is a parameter key.

Currently, `attribute` consists of a `reflect.Type`, which indicates the parameter
type to check that provided key and value are compatible and registered, as well as a function `ValueValidatorFn` to validate values.

Only primary keys have to be registered on the `KeyTable`. Subkeys inherit the
attribute of the primary key.

### ParamSet

Modules often define parameters as a proto message. The generated struct can implement
`ParamSet` interface to be used with the following methods:

* `KeyTable.RegisterParamSet()`: registers all parameters in the struct
* `Subspace.{Get, Set}ParamSet()`: Get to & Set from the struct

The implementer should be a pointer in order to use `GetParamSet()`.
31 changes: 31 additions & 0 deletions tm2/pkg/sdk/params/autocli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package params

import (
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
paramsv1beta1 "cosmossdk.io/api/cosmos/params/v1beta1"
)

// AutoCLIOptions implements the autocli.HasAutoCLIConfig interface.
func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
return &autocliv1.ModuleOptions{
Query: &autocliv1.ServiceCommandDescriptor{
Service: paramsv1beta1.Query_ServiceDesc.ServiceName,
RpcCommandOptions: []*autocliv1.RpcCommandOptions{
{
RpcMethod: "Params",
Use: "subspace <subspace> <key>",
Short: "Query for raw parameters by subspace and key",
PositionalArgs: []*autocliv1.PositionalArgDescriptor{
{ProtoField: "subspace"},
{ProtoField: "key"},
},
},
{
RpcMethod: "Subspaces",
Use: "subspaces",
Short: "Query for all registered subspaces and all keys for a subspace",
},
},
},
}
}
92 changes: 92 additions & 0 deletions tm2/pkg/sdk/params/client/cli/tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package cli

import (
"fmt"
"strings"

"github.com/spf13/cobra"

govv1beta1 "cosmossdk.io/x/gov/types/v1beta1"
paramscutils "cosmossdk.io/x/params/client/utils"
paramproposal "cosmossdk.io/x/params/types/proposal"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/version"
)

// NewSubmitParamChangeProposalTxCmd returns a CLI command handler for creating
// a parameter change proposal governance transaction.
func NewSubmitParamChangeProposalTxCmd() *cobra.Command {
return &cobra.Command{
Use: "param-change <proposal-file>",
Args: cobra.ExactArgs(1),
Short: "Submit a parameter change proposal",
Long: strings.TrimSpace(
fmt.Sprintf(`Submit a parameter proposal along with an initial deposit.
The proposal details must be supplied via a JSON file. For values that contains
objects, only non-empty fields will be updated.
IMPORTANT: Currently parameter changes are evaluated but not validated, so it is
very important that any "value" change is valid (ie. correct type and within bounds)
for its respective parameter, eg. "MaxValidators" should be an integer and not a decimal.
Proper vetting of a parameter change proposal should prevent this from happening
(no deposits should occur during the governance process), but it should be noted
regardless.
Example:
$ %s tx gov submit-proposal param-change <path/to/proposal.json> --from=<key_or_address>
Where proposal.json contains:
{
"title": "Staking Param Change",
"description": "Update max validators",
"changes": [
{
"subspace": "staking",
"key": "MaxValidators",
"value": 105
}
],
"deposit": "1000stake"
}
`,
version.AppName,
),
),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
proposal, err := paramscutils.ParseParamChangeProposalJSON(clientCtx.LegacyAmino, args[0])
if err != nil {
return err
}

from, err := clientCtx.AddressCodec.BytesToString(clientCtx.GetFromAddress())
if err != nil {
return err
}

content := paramproposal.NewParameterChangeProposal(
proposal.Title, proposal.Description, proposal.Changes.ToParamChanges(),
)

deposit, err := sdk.ParseCoinsNormalized(proposal.Deposit)
if err != nil {
return err
}

msg, err := govv1beta1.NewMsgSubmitProposal(content, deposit, from)
if err != nil {
return err
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}
}
43 changes: 43 additions & 0 deletions tm2/pkg/sdk/params/client/cli/tx_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package cli

import (
"testing"

"github.com/stretchr/testify/require"

"cosmossdk.io/x/params/client/utils"

"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/testutil"
)

func TestParseProposal(t *testing.T) {
cdc := codec.NewLegacyAmino()
okJSON := testutil.WriteToNewTempFile(t, `
{
"title": "Staking Param Change",
"description": "Update max validators",
"changes": [
{
"subspace": "staking",
"key": "MaxValidators",
"value": 1
}
],
"deposit": "1000stake"
}
`)
proposal, err := utils.ParseParamChangeProposalJSON(cdc, okJSON.Name())
require.NoError(t, err)

require.Equal(t, "Staking Param Change", proposal.Title)
require.Equal(t, "Update max validators", proposal.Description)
require.Equal(t, "1000stake", proposal.Deposit)
require.Equal(t, utils.ParamChangesJSON{
{
Subspace: "staking",
Key: "MaxValidators",
Value: []byte{0x31},
},
}, proposal.Changes)
}
9 changes: 9 additions & 0 deletions tm2/pkg/sdk/params/client/proposal_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package client

import (
govclient "cosmossdk.io/x/gov/client"
"cosmossdk.io/x/params/client/cli"
)

// ProposalHandler is the param change proposal handler.
var ProposalHandler = govclient.NewProposalHandler(cli.NewSubmitParamChangeProposalTxCmd)
69 changes: 69 additions & 0 deletions tm2/pkg/sdk/params/client/utils/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package utils

import (
"encoding/json"
"os"

"cosmossdk.io/x/params/types/proposal"

"github.com/cosmos/cosmos-sdk/codec"
)

type (
// ParamChangesJSON defines a slice of ParamChangeJSON objects which can be
// converted to a slice of ParamChange objects.
ParamChangesJSON []ParamChangeJSON

// ParamChangeJSON defines a parameter change used in JSON input. This
// allows values to be specified in raw JSON instead of being string encoded.
ParamChangeJSON struct {
Subspace string `json:"subspace" yaml:"subspace"`
Key string `json:"key" yaml:"key"`
Value json.RawMessage `json:"value" yaml:"value"`
}

// ParamChangeProposalJSON defines a ParameterChangeProposal with a deposit used
// to parse parameter change proposals from a JSON file.
ParamChangeProposalJSON struct {
Title string `json:"title" yaml:"title"`
Description string `json:"description" yaml:"description"`
Changes ParamChangesJSON `json:"changes" yaml:"changes"`
Deposit string `json:"deposit" yaml:"deposit"`
}
)

func NewParamChangeJSON(subspace, key string, value json.RawMessage) ParamChangeJSON {
return ParamChangeJSON{subspace, key, value}
}

// ToParamChange converts a ParamChangeJSON object to ParamChange.
func (pcj ParamChangeJSON) ToParamChange() proposal.ParamChange {
return proposal.NewParamChange(pcj.Subspace, pcj.Key, string(pcj.Value))
}

// ToParamChanges converts a slice of ParamChangeJSON objects to a slice of
// ParamChange.
func (pcj ParamChangesJSON) ToParamChanges() []proposal.ParamChange {
res := make([]proposal.ParamChange, len(pcj))
for i, pc := range pcj {
res[i] = pc.ToParamChange()
}
return res
}

// ParseParamChangeProposalJSON reads and parses a ParamChangeProposalJSON from
// file.
func ParseParamChangeProposalJSON(cdc *codec.LegacyAmino, proposalFile string) (ParamChangeProposalJSON, error) {
proposal := ParamChangeProposalJSON{}

contents, err := os.ReadFile(proposalFile)
if err != nil {
return proposal, err
}

if err := cdc.UnmarshalJSON(contents, &proposal); err != nil {
return proposal, err
}

return proposal, nil
}
32 changes: 32 additions & 0 deletions tm2/pkg/sdk/params/client/utils/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package utils

import (
"encoding/json"
"testing"

"github.com/stretchr/testify/require"
)

func TestNewParamChangeJSON(t *testing.T) {
pcj := NewParamChangeJSON("subspace", "key", json.RawMessage(`{}`))
require.Equal(t, "subspace", pcj.Subspace)
require.Equal(t, "key", pcj.Key)
require.Equal(t, json.RawMessage(`{}`), pcj.Value)
}

func TestToParamChanges(t *testing.T) {
pcj1 := NewParamChangeJSON("subspace", "key1", json.RawMessage(`{}`))
pcj2 := NewParamChangeJSON("subspace", "key2", json.RawMessage(`{}`))
pcjs := ParamChangesJSON{pcj1, pcj2}

paramChanges := pcjs.ToParamChanges()
require.Len(t, paramChanges, 2)

require.Equal(t, paramChanges[0].Subspace, pcj1.Subspace)
require.Equal(t, paramChanges[0].Key, pcj1.Key)
require.Equal(t, paramChanges[0].Value, string(pcj1.Value))

require.Equal(t, paramChanges[1].Subspace, pcj2.Subspace)
require.Equal(t, paramChanges[1].Key, pcj2.Key)
require.Equal(t, paramChanges[1].Value, string(pcj2.Value))
}
Loading

0 comments on commit 0c0f9f7

Please sign in to comment.