Adding On-Chain Module Parameters
- Step-by-Step Instructions
- 1. Define the Parameter in the Protocol Buffers File
- 2 Update the Parameter E2E Tests
- 3. Update the Default Parameter Values
- 4. Add Parameter Default to Genesis Configuration
- 5. Modify the Makefile
- 6. Create a new JSON File for the Individual Parameter Update
- 7. Update the JSON File for Updating All Parameters for the Module
- 8. Parameter Validation
- 9. Add the Parameter to
ParamSetPairs()
- 10. Update Unit Tests
- 11. Implement individual parameter updates
Adding a new on-chain module parameter involves multiple steps to ensure that the
parameter is properly integrated into the system. This guide will walk you through
the process using a generic approach, illustrated by adding a parameter to the proof
module.
See pokt-network/poktroll#595 for a real-world example.
TODO_POST_MAINNET(@bryanchriswhite): Once the next version of ignite
is out, leverage:
https://github.com/ignite/cli/issues/3684#issuecomment-2299796210
Step-by-Step Instructions
1. Define the Parameter in the Protocol Buffers File
Open the appropriate .proto
file for your module (e.g., params.proto
) and define the new parameter.
message Params {
// Other existing parameters...
// Description of the new parameter.
uint64 new_parameter_name = 3 [(gogoproto.jsontag) = "new_parameter_name"];
}
2 Update the Parameter Integration Tests
// TODO_DOCUMENT(@bryanchriswhite, #826)
3. Update the Default Parameter Values
In the corresponding Go file (e.g., x/<module>/types/params.go
), define the default value, key, and parameter name for the
new parameter and include the default in the NewParams
and DefaultParams
functions.
var (
// Other existing parameters...
KeyNewParameterName = []byte("NewParameterName")
ParamNewParameterName = "new_parameter_name"
DefaultNewParameterName uint64 = 100 // Example default value
)
func NewParams(
// Other existing parameters...
newParameterName uint64,
) Params {
return Params{
// Other existing parameters...
NewParameterName: newParameterName,
}
}
func DefaultParams() Params {
return NewParams(
// Other existing default parameters...
DefaultNewParameterName,
)
}
4. Add Parameter Default to Genesis Configuration
Add the new parameter to the genesis configuration file (e.g., config.yml
).
genesis:
proof:
params:
# Other existing parameters...
new_parameter_name: 100
5. Modify the Makefile
Add a new target in the Makefile
to update the new parameter.
.PHONY: params_update_proof_new_parameter_name
params_update_proof_new_parameter_name: ## Update the proof module new_parameter_name param
poktrolld tx authz exec ./tools/scripts/params/proof_new_parameter_name.json $(PARAM_FLAGS)
6. Create a new JSON File for the Individual Parameter Update
Create a new JSON file (e.g., proof_new_parameter_name.json
) in the tools/scripts/params
directory to specify how to update the new parameter.
{
"body": {
"messages": [
{
"@type": "/poktroll.proof.MsgUpdateParam",
"authority": "pokt10d07y265gmmuvt4z0w9aw880jnsr700j8yv32t",
"name": "new_parameter_name",
"as_int64": "100"
}
]
}
}
7. Update the JSON File for Updating All Parameters for the Module
Add a line to the existing module's MsgUpdateParam
JSON file (e.g., proof_all.json
)
with the default value for the new parameter.
{
"body": {
"messages": [
{
"@type": "/poktroll.proof.MsgUpdateParams",
"authority": "pokt10d07y265gmmuvt4z0w9aw880jnsr700j8yv32t",
"params": {
"min_relay_difficulty_bits": "0",
"proof_request_probability": "0.25",
"proof_requirement_threshold": "20",
"new_parameter_name": "100" // Add this line
}
}
]
}
}
8. Parameter Validation
8.1 New Parameter Validation
Implement a validation function for the new parameter in the corresponding params.go
file you've been working on.
func ValidateNewParameterName(v interface{}) error {
_, ok := v.(uint64)
if !ok {
return fmt.Errorf("invalid parameter type: %T", v)
}
return nil
}
8.2 Parameter Validation in Workflow
Integrate the usage of the new ValidateNewParameterName
function in the corresponding
Params#ValidateBasic()
function where this is used.
func (params *Params) ValidateBasic() error {
// ...
if err := ValidateNewParameterName(params.NewParameterName); err != nil {
return err
}
// ...
}
9. Add the Parameter to ParamSetPairs()
Include the new parameter in the ParamSetPairs
function.
func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs {
return paramtypes.ParamSetPairs{
// Other existing parameters...
paramtypes.NewParamSetPair(
KeyNewParameterName,
&p.NewParameterName,
ValidateNewParameterName,
),
}
}
10. Update Unit Tests
Add tests which exercise validation of the new parameter in your test files
(e.g., /types/params_test.go
and msg_server_update_param_test.go
).
10.1 Parameter Validation Tests
func TestParams_ValidateNewParameterName(t *testing.T) {
tests := []struct {
desc string
newParameterName interface{}
expectedErr error
}{
{
desc: "invalid type",
newParameterName: int64(-1),
expectedErr: fmt.Errorf("invalid parameter type: int64"),
},
{
desc: "valid newParameterName",
newParameterName: uint64(100),
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
err := ValidateNewParameterName(tt.newParameterName)
if tt.expectedErr != nil {
require.Error(t, err)
require.Contains(t, err.Error(), tt.expectedErr.Error())
} else {
require.NoError(t, err)
}
})
}
}
10.2 Parameter Update Tests
The example presented below corresponds to /keeper/msg_server_update_param_test.go
.
func TestMsgUpdateParam_UpdateNewParameterNameOnly(t *testing.T) {
var expectedNewParameterName uint64 = 100
// Set the parameters to their default values
k, msgSrv, ctx := setupMsgServer(t)
defaultParams := prooftypes.DefaultParams()
require.NoError(t, k.SetParams(ctx, defaultParams))
// Ensure the default values are different from the new values we want to set
require.NotEqual(t, expectedNewParameterName, defaultParams.NewParameterName)
// Update the new parameter
updateParamMsg := &prooftypes.MsgUpdateParam{
Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(),
Name: prooftypes.ParamNewParameterName,
AsType: &prooftypes.MsgUpdateParam_AsInt64{AsInt64: int64(expectedNewParameterName)},
}
res, err := msgSrv.UpdateParam(ctx, updateParamMsg)
require.NoError(t, err)
require.Equal(t, expectedNewParameterName, res.Params.NewParameterName)
// READ ME: THIS TEST SHOULD ALSO ASSERT THAT ALL OTHER PARAMS OF THE SAME MODULE REMAIN UNCHANGED
}
11. Implement individual parameter updates
11.1 Add ParamNameNewParameterName
to MsgUpdateParam#ValidateBasic()
in x/types/message_update_param.go
// Parameter name must be supported by this module.
switch msg.Name {
case ParamNumBlocksPerSession,
ParamNewParameterName:
return msg.paramTypeIsInt64()
11.2 Add ParamNameNewParameterName
to msgServer#UpdateParam()
in x/keeper/msg_server_update_param.go
case types.ParamNewParameterName:
value, ok := msg.AsType.(*types.MsgUpdateParam_AsInt64)
if !ok {
return nil, types.ErrProofParamInvalid.Wrapf("unsupported value type for %s param: %T", msg.Name, msg.AsType)
}
newParameter := uint64(value.AsInt64)
if err := types.ValidateNewParameter(newParameter); err != nil {
return nil, err
}
params.NewParameter = newParameter