Skip to content

Commit

Permalink
Merge PR #2605: Paramstore Subkey
Browse files Browse the repository at this point in the history
  • Loading branch information
mossid authored and jackzampolin committed Feb 5, 2019
1 parent a2b73c8 commit f15ad04
Show file tree
Hide file tree
Showing 24 changed files with 181 additions and 142 deletions.
1 change: 1 addition & 0 deletions PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ IMPROVEMENTS
* [\#3424](https://github.com/cosmos/cosmos-sdk/issues/3424) Allow generation of gentxs with empty memo field.

* SDK
* [\#2605] x/params add subkey accessing
* [\#2986](https://github.com/cosmos/cosmos-sdk/pull/2986) Store Refactor
* \#3435 Test that store implementations do not allow nil values

Expand Down
2 changes: 2 additions & 0 deletions docs/spec/params/keeper.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

In the app initialization stage, `Keeper.Subspace(Paramspace)` is passed to the user modules, and the subspaces are stored in `Keeper.spaces`. Later it can be retrieved with `Keeper.GetSubspace`, so the keepers holding `Keeper` can access to any subspace. For example, Gov module can take `Keeper` as its argument and modify parameter of any subspace when a `ParameterChangeProposal` is accepted.

Example:

```go
type MasterKeeper struct {
pk params.Keeper
Expand Down
76 changes: 13 additions & 63 deletions docs/spec/params/subspace.md
Original file line number Diff line number Diff line change
@@ -1,76 +1,26 @@
# Subspace

## Basic Usage
`Subspace` is a prefixed subspace of the parameter store. Each module who use the parameter store will take a `Subspace`, not the `Keeper`, to isolate permission to access.

First, declare parameter space and parameter keys for the module. Then include params.Subspace in the keeper. Since we prefix the keys with the spacename, it is recommended to use the same name with the module's.
## Key

```go
const (
DefaultParamspace = "mymodule"
)
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.

const (
KeyParameter1 = "myparameter1"
KeyParameter2 = "myparameter2"
)
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.

type Keeper struct {
cdc *wire.Codec
key sdk.StoreKey
## KeyTable

ps params.Subspace
}
```
All of the paramter 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.

Pass a params.Subspace to NewKeeper with DefaultParamSubspace (or another)
Currently, `attribute` only consists of `reflect.Type`, which indicates the parameter type. It is needed even if the state machine has no error, because the paraeter can be modified externally, for example via the governance.

```go
app.myKeeper = mymodule.NewKeeper(cdc, key, app.paramStore.SubStore(mymodule.DefaultParamspace))
```
Only primary keys have to be registered on the `KeyTable`. Subkeys inherit the attribute of the primary key.

`NewKeeper` should register a `TypeTable`, which defines a map from parameter keys from types.
## ParamSet

```go
func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, space params.Subspace) Keeper {
return Keeper {
cdc: cdc,
key: key,
ps: space.WithTypeTable(ParamTypeTable()),
}
}
```
Modules often define a struct of parameters. Instead of calling methods with each of those parameters, when the struct implements `ParamSet`, it can be used with the following methods:

Now we can access to the paramstore using Paramstore Keys

```go
var param MyStruct
k.ps.Get(KeyParameter1, &param)
k.ps.Set(KeyParameter2, param)
```

# Genesis Usage

Declare a struct for parameters and make it implement params.ParamSet. It will then be able to be passed to SetParamSet.

```go
type MyParams struct {
Parameter1 uint64
Parameter2 string
}

// Implements params.ParamSet
// KeyValuePairs must return the list of (ParamKey, PointerToTheField)
func (p *MyParams) KeyValuePairs() params.KeyValuePairs {
return params.KeyFieldPairs {
{KeyParameter1, &p.Parameter1},
{KeyParameter2, &p.Parameter2},
}
}

func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) {
k.ps.SetParamSet(ctx, &data.params)
}
```

The method is pointer receiver because there could be a case that we read from the store and set the result to the struct.
* `KeyTable.RegisterParamSet()`: registers all parameters in the struct
* `Subspace.{Get, Set}ParamSet()`: Get to & Set from the struct

The implementor should be a pointer in order to use `GetParamSet()`
2 changes: 1 addition & 1 deletion x/auth/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func NewAccountKeeper(
key: key,
proto: proto,
cdc: cdc,
paramSubspace: paramstore.WithTypeTable(ParamTypeTable()),
paramSubspace: paramstore.WithKeyTable(ParamKeyTable()),
}
}

Expand Down
10 changes: 5 additions & 5 deletions x/auth/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ type Params struct {
}

// ParamTable for staking module
func ParamTypeTable() params.TypeTable {
return params.NewTypeTable().RegisterParamSet(&Params{})
func ParamKeyTable() params.KeyTable {
return params.NewKeyTable().RegisterParamSet(&Params{})
}

// KeyValuePairs implements the ParamSet interface and returns all the key/value
// ParamSetPairs implements the ParamSet interface and returns all the key/value pairs
// pairs of auth module's parameters.
// nolint
func (p *Params) KeyValuePairs() params.KeyValuePairs {
return params.KeyValuePairs{
func (p *Params) ParamSetPairs() params.ParamSetPairs {
return params.ParamSetPairs{
{KeyMaxMemoCharacters, &p.MaxMemoCharacters},
{KeyTxSigLimit, &p.TxSigLimit},
{KeyTxSizeCostPerByte, &p.TxSizeCostPerByte},
Expand Down
2 changes: 1 addition & 1 deletion x/bank/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func NewBaseKeeper(ak auth.AccountKeeper,
paramSpace params.Subspace,
codespace sdk.CodespaceType) BaseKeeper {

ps := paramSpace.WithTypeTable(ParamTypeTable())
ps := paramSpace.WithKeyTable(ParamKeyTable())
return BaseKeeper{
BaseSendKeeper: NewBaseSendKeeper(ak, ps, codespace),
ak: ak,
Expand Down
4 changes: 2 additions & 2 deletions x/bank/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ const (
var ParamStoreKeySendEnabled = []byte("sendenabled")

// type declaration for parameters
func ParamTypeTable() params.TypeTable {
return params.NewTypeTable(
func ParamKeyTable() params.KeyTable {
return params.NewKeyTable(
ParamStoreKeySendEnabled, false,
)
}
2 changes: 1 addition & 1 deletion x/distribution/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramSpace params.Subspace, c
keeper := Keeper{
storeKey: key,
cdc: cdc,
paramSpace: paramSpace.WithTypeTable(ParamTypeTable()),
paramSpace: paramSpace.WithKeyTable(ParamKeyTable()),
bankKeeper: ck,
stakingKeeper: sk,
feeCollectionKeeper: fck,
Expand Down
4 changes: 2 additions & 2 deletions x/distribution/keeper/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
)

// type declaration for parameters
func ParamTypeTable() params.TypeTable {
return params.NewTypeTable(
func ParamKeyTable() params.KeyTable {
return params.NewKeyTable(
ParamStoreKeyCommunityTax, sdk.Dec{},
ParamStoreKeyBaseProposerReward, sdk.Dec{},
ParamStoreKeyBonusProposerReward, sdk.Dec{},
Expand Down
8 changes: 4 additions & 4 deletions x/gov/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ var (
BurnedDepositCoinsAccAddr = sdk.AccAddress(crypto.AddressHash([]byte("govBurnedDepositCoins")))
)

// Type declaration for parameters
func ParamTypeTable() params.TypeTable {
return params.NewTypeTable(
// Key declaration for parameters
func ParamKeyTable() params.KeyTable {
return params.NewKeyTable(
ParamStoreKeyDepositParams, DepositParams{},
ParamStoreKeyVotingParams, VotingParams{},
ParamStoreKeyTallyParams, TallyParams{},
Expand Down Expand Up @@ -80,7 +80,7 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramsKeeper params.Keeper, p
return Keeper{
storeKey: key,
paramsKeeper: paramsKeeper,
paramSpace: paramSpace.WithTypeTable(ParamTypeTable()),
paramSpace: paramSpace.WithKeyTable(ParamKeyTable()),
ck: ck,
ds: ds,
vs: ds.GetValidatorSet(),
Expand Down
6 changes: 3 additions & 3 deletions x/mint/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey,
keeper := Keeper{
storeKey: key,
cdc: cdc,
paramSpace: paramSpace.WithTypeTable(ParamTypeTable()),
paramSpace: paramSpace.WithKeyTable(ParamKeyTable()),
sk: sk,
fck: fck,
}
Expand All @@ -39,8 +39,8 @@ var (
)

// ParamTable for staking module
func ParamTypeTable() params.TypeTable {
return params.NewTypeTable(
func ParamKeyTable() params.KeyTable {
return params.NewKeyTable(
ParamStoreKeyParams, Params{},
)
}
Expand Down
36 changes: 33 additions & 3 deletions x/params/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,51 @@ recommended to use the same name with the module's.
)
type Keeper struct {
cdc *wire.Codec
cdc *codec.Codec
key sdk.StoreKey
ps params.Subspace
}
func ParamKeyTable() params.KeyTable {
return params.NewKeyTable(
KeyParameter1, MyStruct{},
KeyParameter2, MyStruct{},
)
}
func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, ps params.Subspace) Keeper {
return Keeper {
cdc: cdc,
key: key,
ps: ps.WithKeyTable(ParamKeyTable()),
}
}
Pass a params.Subspace to NewKeeper with DefaultParamspace (or another)
app.myKeeper = mymodule.NewKeeper(app.paramStore.SubStore(mymodule.DefaultParamspace))
Now we can access to the paramstore using Paramstore Keys
var param MyStruct
k.ps.Get(KeyParameter1, &param)
k.ps.Set(KeyParameter2, param)
k.ps.Get(ctx, KeyParameter1, &param)
k.ps.Set(ctx, KeyParameter2, param)
If you want to store an unknown number of parameters, or want to store a mapping,
you can use subkeys. Subkeys can be used with a main key, where the subkeys are
inheriting the key properties.
func ParamKeyTable() params.KeyTable {
return params.NewKeyTable(
KeyParamMain, MyStruct{},
)
}
func (k Keeper) GetDynamicParameter(ctx sdk.Context, subkey []byte) (res MyStruct) {
k.ps.GetWithSubkey(ctx, KeyParamMain, subkey, &res)
}
Genesis Usage:
Expand Down
8 changes: 4 additions & 4 deletions x/params/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func TestKeeper(t *testing.T) {
{"key7", 9058701},
}

table := NewTypeTable(
table := NewKeyTable(
[]byte("key1"), int64(0),
[]byte("key2"), int64(0),
[]byte("key3"), int64(0),
Expand All @@ -71,8 +71,8 @@ func TestKeeper(t *testing.T) {
tkey := sdk.NewTransientStoreKey("transient_test")
ctx := defaultContext(skey, tkey)
keeper := NewKeeper(cdc, skey, tkey)
space := keeper.Subspace("test").WithTypeTable(table)
store := prefix.NewStore(ctx.KVStore(skey), []byte("test/"))
space := keeper.Subspace("test").WithKeyTable(table)

// Set params
for i, kv := range kvs {
Expand Down Expand Up @@ -163,7 +163,7 @@ func TestSubspace(t *testing.T) {
{"struct", s{1}, s{0}, new(s)},
}

table := NewTypeTable(
table := NewKeyTable(
[]byte("string"), string(""),
[]byte("bool"), bool(false),
[]byte("int16"), int16(0),
Expand All @@ -179,7 +179,7 @@ func TestSubspace(t *testing.T) {
)

store := prefix.NewStore(ctx.KVStore(key), []byte("test/"))
space := keeper.Subspace("test").WithTypeTable(table)
space := keeper.Subspace("test").WithKeyTable(table)

// Test space.Set, space.Modified
for i, kv := range kvs {
Expand Down
17 changes: 12 additions & 5 deletions x/params/subspace.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package params

import (
"testing"

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

"github.com/cosmos/cosmos-sdk/x/params/subspace"
)

Expand All @@ -9,11 +13,14 @@ type (
Subspace = subspace.Subspace
ReadOnlySubspace = subspace.ReadOnlySubspace
ParamSet = subspace.ParamSet
KeyValuePairs = subspace.KeyValuePairs
TypeTable = subspace.TypeTable
ParamSetPairs = subspace.ParamSetPairs
KeyTable = subspace.KeyTable
)

// re-export functions from subspace
func NewTypeTable(keytypes ...interface{}) TypeTable {
return subspace.NewTypeTable(keytypes...)
// nolint - re-export functions from subspace
func NewKeyTable(keytypes ...interface{}) KeyTable {
return subspace.NewKeyTable(keytypes...)
}
func DefaultTestComponents(t *testing.T) (sdk.Context, Subspace, func() sdk.CommitID) {
return subspace.DefaultTestComponents(t)
}
6 changes: 3 additions & 3 deletions x/params/subspace/pair.go → x/params/subspace/paramset.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package subspace

// Used for associating paramsubspace key and field of param structs
type KeyValuePair struct {
type ParamSetPair struct {
Key []byte
Value interface{}
}

// Slice of KeyFieldPair
type KeyValuePairs []KeyValuePair
type ParamSetPairs []ParamSetPair

// Interface for structs containing parameters for a module
type ParamSet interface {
KeyValuePairs() KeyValuePairs
ParamSetPairs() ParamSetPairs
}
Loading

0 comments on commit f15ad04

Please sign in to comment.