feat(x/staking)!: Add metadata field to validator info (#21315)
This commit is contained in:
parent
8763d8d99f
commit
814d4cf6b7
File diff suppressed because it is too large
Load Diff
@ -41,12 +41,12 @@ func getExtension(extID int32, m proto.Message) *gogoproto.ExtensionDesc {
|
||||
for id, desc := range proto.RegisteredExtensions(m) { //nolint:staticcheck // keep for backward compatibility
|
||||
if id == extID {
|
||||
return &gogoproto.ExtensionDesc{
|
||||
ExtendedType: desc.ExtendedType,
|
||||
ExtensionType: desc.ExtensionType,
|
||||
Field: desc.Field,
|
||||
Name: desc.Name,
|
||||
Tag: desc.Tag,
|
||||
Filename: desc.Filename,
|
||||
ExtendedType: desc.ExtendedType,
|
||||
ExtensionType: desc.ExtensionType,
|
||||
Field: desc.Field,
|
||||
Name: desc.Name,
|
||||
Tag: desc.Tag,
|
||||
Filename: desc.Filename,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -353,7 +353,7 @@ func initTestnetFiles(
|
||||
valStr,
|
||||
valPubKeys[i],
|
||||
sdk.NewCoin(args.bondTokenDenom, valTokens),
|
||||
stakingtypes.NewDescription(nodeDirName, "", "", "", ""),
|
||||
stakingtypes.NewDescription(nodeDirName, "", "", "", "", stakingtypes.Metadata{}),
|
||||
stakingtypes.NewCommissionRates(math.LegacyOneDec(), math.LegacyOneDec(), math.LegacyOneDec()),
|
||||
math.OneInt(),
|
||||
)
|
||||
|
||||
@ -296,7 +296,7 @@ func initTestnetFiles[T transaction.Tx](
|
||||
valStr,
|
||||
valPubKeys[i],
|
||||
sdk.NewCoin(args.bondTokenDenom, valTokens),
|
||||
stakingtypes.NewDescription(nodeDirName, "", "", "", ""),
|
||||
stakingtypes.NewDescription(nodeDirName, "", "", "", "", stakingtypes.Metadata{}),
|
||||
stakingtypes.NewCommissionRates(math.LegacyOneDec(), math.LegacyOneDec(), math.LegacyOneDec()),
|
||||
math.OneInt(),
|
||||
)
|
||||
|
||||
@ -34,7 +34,7 @@ import (
|
||||
var (
|
||||
valTokens = sdk.TokensFromConsensusPower(42, sdk.DefaultPowerReduction)
|
||||
TestProposal = v1beta1.NewTextProposal("Test", "description")
|
||||
TestDescription = stakingtypes.NewDescription("T", "E", "S", "T", "Z")
|
||||
TestDescription = stakingtypes.NewDescription("T", "E", "S", "T", "Z", stakingtypes.Metadata{})
|
||||
TestCommissionRates = stakingtypes.NewCommissionRates(math.LegacyZeroDec(), math.LegacyZeroDec(), math.LegacyZeroDec())
|
||||
)
|
||||
|
||||
|
||||
@ -83,7 +83,7 @@ func TestSlashingMsgs(t *testing.T) {
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
description := stakingtypes.NewDescription("foo_moniker", "", "", "", "")
|
||||
description := stakingtypes.NewDescription("foo_moniker", "", "", "", "", stakingtypes.Metadata{})
|
||||
commission := stakingtypes.NewCommissionRates(math.LegacyZeroDec(), math.LegacyZeroDec(), math.LegacyZeroDec())
|
||||
|
||||
addrStrVal, err := valaddrCodec.BytesToString(addr1)
|
||||
|
||||
@ -2,6 +2,8 @@ package keeper_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -214,6 +216,26 @@ func bondTypeGenerator() *rapid.Generator[stakingtypes.BondStatus] {
|
||||
})
|
||||
}
|
||||
|
||||
func metadataGenerator() *rapid.Generator[stakingtypes.Metadata] {
|
||||
return rapid.Custom(func(t *rapid.T) stakingtypes.Metadata {
|
||||
return stakingtypes.Metadata{
|
||||
ProfilePicUri: generateUri(t),
|
||||
SocialHandleUris: []string{generateUri(t), generateUri(t)},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func generateUri(t *rapid.T) string {
|
||||
host := fmt.Sprintf("%s.com", rapid.StringN(5, 250, 255).Draw(t, "host"))
|
||||
path := rapid.StringN(5, 250, 255).Draw(t, "path")
|
||||
uri := url.URL{
|
||||
Scheme: "https",
|
||||
Host: host,
|
||||
Path: path,
|
||||
}
|
||||
return uri.String()
|
||||
}
|
||||
|
||||
// createValidator creates a validator with random values.
|
||||
func createValidator(t *testing.T, rt *rapid.T, _ *deterministicFixture) stakingtypes.Validator {
|
||||
t.Helper()
|
||||
@ -233,6 +255,7 @@ func createValidator(t *testing.T, rt *rapid.T, _ *deterministicFixture) staking
|
||||
rapid.StringN(5, 250, 255).Draw(rt, "website"),
|
||||
rapid.StringN(5, 250, 255).Draw(rt, "securityContact"),
|
||||
rapid.StringN(5, 250, 255).Draw(rt, "details"),
|
||||
metadataGenerator().Draw(rt, "metadata"),
|
||||
),
|
||||
UnbondingHeight: rapid.Int64Min(1).Draw(rt, "unbonding-height"),
|
||||
UnbondingTime: time.Now().Add(durationGenerator().Draw(rt, "duration")),
|
||||
@ -308,6 +331,7 @@ func getStaticValidator(t *testing.T, f *deterministicFixture) stakingtypes.Vali
|
||||
"website",
|
||||
"securityContact",
|
||||
"details",
|
||||
stakingtypes.Metadata{},
|
||||
),
|
||||
UnbondingHeight: 10,
|
||||
UnbondingTime: time.Date(2022, 10, 1, 0, 0, 0, 0, time.UTC),
|
||||
@ -343,6 +367,7 @@ func getStaticValidator2(t *testing.T, f *deterministicFixture) stakingtypes.Val
|
||||
"website",
|
||||
"securityContact",
|
||||
"details",
|
||||
stakingtypes.Metadata{},
|
||||
),
|
||||
UnbondingHeight: 100132,
|
||||
UnbondingTime: time.Date(2025, 10, 1, 0, 0, 0, 0, time.UTC),
|
||||
@ -408,7 +433,7 @@ func TestGRPCValidator(t *testing.T) {
|
||||
ValidatorAddr: val.OperatorAddress,
|
||||
}
|
||||
|
||||
testdata.DeterministicIterations(t, f.ctx, req, f.queryClient.Validator, 1915, false)
|
||||
testdata.DeterministicIterations(t, f.ctx, req, f.queryClient.Validator, 1921, false)
|
||||
}
|
||||
|
||||
func TestGRPCValidators(t *testing.T) {
|
||||
@ -434,7 +459,7 @@ func TestGRPCValidators(t *testing.T) {
|
||||
getStaticValidator(t, f)
|
||||
getStaticValidator2(t, f)
|
||||
|
||||
testdata.DeterministicIterations(t, f.ctx, &stakingtypes.QueryValidatorsRequest{}, f.queryClient.Validators, 2862, false)
|
||||
testdata.DeterministicIterations(t, f.ctx, &stakingtypes.QueryValidatorsRequest{}, f.queryClient.Validators, 2880, false)
|
||||
}
|
||||
|
||||
func TestGRPCValidatorDelegations(t *testing.T) {
|
||||
@ -479,7 +504,7 @@ func TestGRPCValidatorDelegations(t *testing.T) {
|
||||
ValidatorAddr: validator.OperatorAddress,
|
||||
}
|
||||
|
||||
testdata.DeterministicIterations(t, f.ctx, req, f.queryClient.ValidatorDelegations, 14610, false)
|
||||
testdata.DeterministicIterations(t, f.ctx, req, f.queryClient.ValidatorDelegations, 14628, false)
|
||||
}
|
||||
|
||||
func TestGRPCValidatorUnbondingDelegations(t *testing.T) {
|
||||
@ -569,7 +594,7 @@ func TestGRPCDelegation(t *testing.T) {
|
||||
DelegatorAddr: delegator1,
|
||||
}
|
||||
|
||||
testdata.DeterministicIterations(t, f.ctx, req, f.queryClient.Delegation, 4680, false)
|
||||
testdata.DeterministicIterations(t, f.ctx, req, f.queryClient.Delegation, 4686, false)
|
||||
}
|
||||
|
||||
func TestGRPCUnbondingDelegation(t *testing.T) {
|
||||
@ -652,7 +677,7 @@ func TestGRPCDelegatorDelegations(t *testing.T) {
|
||||
DelegatorAddr: delegator1,
|
||||
}
|
||||
|
||||
testdata.DeterministicIterations(t, f.ctx, req, f.queryClient.DelegatorDelegations, 4283, false)
|
||||
testdata.DeterministicIterations(t, f.ctx, req, f.queryClient.DelegatorDelegations, 4289, false)
|
||||
}
|
||||
|
||||
func TestGRPCDelegatorValidator(t *testing.T) {
|
||||
@ -690,7 +715,7 @@ func TestGRPCDelegatorValidator(t *testing.T) {
|
||||
ValidatorAddr: validator.OperatorAddress,
|
||||
}
|
||||
|
||||
testdata.DeterministicIterations(t, f.ctx, req, f.queryClient.DelegatorValidator, 3563, false)
|
||||
testdata.DeterministicIterations(t, f.ctx, req, f.queryClient.DelegatorValidator, 3569, false)
|
||||
}
|
||||
|
||||
func TestGRPCDelegatorUnbondingDelegations(t *testing.T) {
|
||||
@ -772,7 +797,7 @@ func TestGRPCDelegatorValidators(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
|
||||
req := &stakingtypes.QueryDelegatorValidatorsRequest{DelegatorAddr: delegator1}
|
||||
testdata.DeterministicIterations(t, f.ctx, req, f.queryClient.DelegatorValidators, 3166, false)
|
||||
testdata.DeterministicIterations(t, f.ctx, req, f.queryClient.DelegatorValidators, 3172, false)
|
||||
}
|
||||
|
||||
func TestGRPCPool(t *testing.T) {
|
||||
@ -856,7 +881,7 @@ func TestGRPCRedelegations(t *testing.T) {
|
||||
DstValidatorAddr: validator2,
|
||||
}
|
||||
|
||||
testdata.DeterministicIterations(t, f.ctx, req, f.queryClient.Redelegations, 3920, false)
|
||||
testdata.DeterministicIterations(t, f.ctx, req, f.queryClient.Redelegations, 3926, false)
|
||||
}
|
||||
|
||||
func TestGRPCParams(t *testing.T) {
|
||||
|
||||
@ -41,7 +41,7 @@ func TestInitGenesis(t *testing.T) {
|
||||
Status: types.Bonded,
|
||||
Tokens: valTokens,
|
||||
DelegatorShares: math.LegacyNewDecFromInt(valTokens),
|
||||
Description: types.NewDescription("hoop", "", "", "", ""),
|
||||
Description: types.NewDescription("hoop", "", "", "", "", types.Metadata{}),
|
||||
}
|
||||
assert.NilError(t, f.stakingKeeper.SetValidator(f.sdkCtx, bondedVal))
|
||||
|
||||
@ -67,7 +67,7 @@ func TestInitGenesis(t *testing.T) {
|
||||
Status: types.Bonded,
|
||||
Tokens: valTokens,
|
||||
DelegatorShares: math.LegacyNewDecFromInt(valTokens),
|
||||
Description: types.NewDescription("hoop", "", "", "", ""),
|
||||
Description: types.NewDescription("hoop", "", "", "", "", types.Metadata{}),
|
||||
}
|
||||
bondedVal2 := types.Validator{
|
||||
OperatorAddress: sdk.ValAddress(addrs[2]).String(),
|
||||
@ -75,7 +75,7 @@ func TestInitGenesis(t *testing.T) {
|
||||
Status: types.Bonded,
|
||||
Tokens: valTokens,
|
||||
DelegatorShares: math.LegacyNewDecFromInt(valTokens),
|
||||
Description: types.NewDescription("bloop", "", "", "", ""),
|
||||
Description: types.NewDescription("bloop", "", "", "", "", types.Metadata{}),
|
||||
}
|
||||
|
||||
// append new bonded validators to the list
|
||||
@ -148,7 +148,7 @@ func TestInitGenesis_PoolsBalanceMismatch(t *testing.T) {
|
||||
Jailed: false,
|
||||
Tokens: math.NewInt(10),
|
||||
DelegatorShares: math.LegacyNewDecFromInt(math.NewInt(10)),
|
||||
Description: types.NewDescription("bloop", "", "", "", ""),
|
||||
Description: types.NewDescription("bloop", "", "", "", "", types.Metadata{}),
|
||||
}
|
||||
|
||||
params := types.Params{
|
||||
@ -195,7 +195,7 @@ func TestInitGenesisLargeValidatorSet(t *testing.T) {
|
||||
validators[i], err = types.NewValidator(
|
||||
sdk.ValAddress(addrs[i]).String(),
|
||||
PKs[i],
|
||||
types.NewDescription(fmt.Sprintf("#%d", i), "", "", "", ""),
|
||||
types.NewDescription(fmt.Sprintf("#%d", i), "", "", "", "", types.Metadata{}),
|
||||
)
|
||||
assert.NilError(t, err)
|
||||
validators[i].Status = types.Bonded
|
||||
|
||||
@ -44,7 +44,7 @@ func TestCancelUnbondingDelegation(t *testing.T) {
|
||||
delegatorAddr := addrs[1]
|
||||
|
||||
// setup a new validator with bonded status
|
||||
validator, err := types.NewValidator(valAddr.String(), PKs[0], types.NewDescription("Validator", "", "", "", ""))
|
||||
validator, err := types.NewValidator(valAddr.String(), PKs[0], types.NewDescription("Validator", "", "", "", "", types.Metadata{}))
|
||||
validator.Status = types.Bonded
|
||||
assert.NilError(t, err)
|
||||
assert.NilError(t, f.stakingKeeper.SetValidator(ctx, validator))
|
||||
|
||||
@ -377,10 +377,12 @@ func TestAminoJSON_LegacyParity(t *testing.T) {
|
||||
"staking/create_validator": {
|
||||
gogo: &stakingtypes.MsgCreateValidator{Pubkey: pubkeyAny},
|
||||
pulsar: &stakingapi.MsgCreateValidator{
|
||||
Pubkey: pubkeyAnyPulsar,
|
||||
Description: &stakingapi.Description{},
|
||||
Commission: &stakingapi.CommissionRates{},
|
||||
Value: &v1beta1.Coin{},
|
||||
Pubkey: pubkeyAnyPulsar,
|
||||
Description: &stakingapi.Description{
|
||||
Metadata: &stakingapi.Metadata{},
|
||||
},
|
||||
Commission: &stakingapi.CommissionRates{},
|
||||
Value: &v1beta1.Coin{},
|
||||
},
|
||||
},
|
||||
"staking/msg_cancel_unbonding_delegation_response": {
|
||||
|
||||
@ -493,7 +493,7 @@ func New(l Logger, baseDir string, cfg Config) (NetworkI, error) {
|
||||
sdk.ValAddress(addr).String(),
|
||||
valPubKeys[i],
|
||||
sdk.NewCoin(cfg.BondDenom, cfg.BondedTokens),
|
||||
stakingtypes.NewDescription(nodeDirName, "", "", "", ""),
|
||||
stakingtypes.NewDescription(nodeDirName, "", "", "", "", stakingtypes.Metadata{}),
|
||||
stakingtypes.NewCommissionRates(commission, sdkmath.LegacyOneDec(), sdkmath.LegacyOneDec()),
|
||||
sdkmath.OneInt(),
|
||||
)
|
||||
|
||||
@ -4,11 +4,11 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
types "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
fmt "fmt"
|
||||
github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types"
|
||||
types1 "github.com/cosmos/cosmos-sdk/types"
|
||||
_ "github.com/cosmos/cosmos-sdk/types/tx/amino"
|
||||
types "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
_ "github.com/cosmos/gogoproto/gogoproto"
|
||||
proto "github.com/cosmos/gogoproto/proto"
|
||||
io "io"
|
||||
|
||||
@ -36,7 +36,7 @@ var (
|
||||
pk2 = priv2.PubKey()
|
||||
addr1 = sdk.AccAddress(pk1.Address())
|
||||
addr2 = sdk.AccAddress(pk2.Address())
|
||||
desc = stakingtypes.NewDescription("testname", "", "", "", "")
|
||||
desc = stakingtypes.NewDescription("testname", "", "", "", "", stakingtypes.Metadata{})
|
||||
comm = stakingtypes.CommissionRates{}
|
||||
)
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ func TestNetGenesisState(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateGenesisMultipleMessages(t *testing.T) {
|
||||
desc := stakingtypes.NewDescription("testname", "", "", "", "")
|
||||
desc := stakingtypes.NewDescription("testname", "", "", "", "", stakingtypes.Metadata{})
|
||||
comm := stakingtypes.CommissionRates{}
|
||||
valAc := codectestutil.CodecOptions{}.GetValidatorCodec()
|
||||
|
||||
@ -66,7 +66,7 @@ func TestValidateGenesisMultipleMessages(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateGenesisBadMessage(t *testing.T) {
|
||||
desc := stakingtypes.NewDescription("testname", "", "", "", "")
|
||||
desc := stakingtypes.NewDescription("testname", "", "", "", "", stakingtypes.Metadata{})
|
||||
pk1Addr, err := codectestutil.CodecOptions{}.GetValidatorCodec().BytesToString(pk1.Address())
|
||||
require.NoError(t, err)
|
||||
msg1 := stakingtypes.NewMsgEditValidator(pk1Addr, desc, nil, nil)
|
||||
|
||||
@ -34,6 +34,9 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
* [#19537](https://github.com/cosmos/cosmos-sdk/pull/19537) Changing `MinCommissionRate` in `MsgUpdateParams` now updates the minimum commission rate for all validators.
|
||||
* [#20434](https://github.com/cosmos/cosmos-sdk/pull/20434) Add consensus address to validator query response
|
||||
* [#21315](https://github.com/cosmos/cosmos-sdk/pull/21315) Create metadata type and add metadata field in validator details proto
|
||||
* Add parsing of `metadata-profile-pic-uri` in `create-validator` JSON.
|
||||
* Add cli flag: `metadata-profile-pic-uri` to `edit-validator` cmd.
|
||||
|
||||
### Improvements
|
||||
|
||||
@ -42,6 +45,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
* [#19277](https://github.com/cosmos/cosmos-sdk/pull/19277) Hooks calls on `SetUnbondingDelegationEntry`, `SetRedelegationEntry`, `Slash` and `RemoveValidator` returns errors instead of logging just like other hooks calls.
|
||||
* [#18636](https://github.com/cosmos/cosmos-sdk/pull/18636) `IterateBondedValidatorsByPower`, `GetDelegatorBonded`, `Delegate`, `Unbond`, `Slash`, `Jail`, `SlashRedelegation`, `ApplyAndReturnValidatorSetUpdates` methods no longer panics on any kind of errors but instead returns appropriate errors.
|
||||
* [#18506](https://github.com/cosmos/cosmos-sdk/pull/18506) Detect the length of the ed25519 pubkey in CreateValidator to prevent panic.
|
||||
* [#21315](https://github.com/cosmos/cosmos-sdk/pull/21315) Add a `Validate` method to the `Description` type that validates the metadata as well as other description details.
|
||||
|
||||
|
||||
### API Breaking Changes
|
||||
@ -96,8 +100,11 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
* [#17335](https://github.com/cosmos/cosmos-sdk/pull/17335) Remove usage of `"cosmossdk.io/x/staking/types".Infraction_*` in favour of `"cosmossdk.io/api/cosmos/staking/v1beta1".Infraction_` in order to remove dependency between modules on staking
|
||||
* [#20295](https://github.com/cosmos/cosmos-sdk/pull/20295) `GetValidatorByConsAddr` now returns the Cosmos SDK `cryptotypes.Pubkey` instead of `cometcrypto.Publickey`. The caller is responsible to translate the returned value to the expected type.
|
||||
* Remove `CmtConsPublicKey()` and `TmConsPublicKey()` from `Validator` interface and as methods on the `Validator` struct.
|
||||
* [#21480](https://github.com/cosmos/cosmos-sdk/pull/21480) ConsensusKeeper is required to be passed to the keeper.
|
||||
|
||||
* [#21480](https://github.com/cosmos/cosmos-sdk/pull/21480) ConsensusKeeper is required to be passed to the keeper.
|
||||
* [#21315](https://github.com/cosmos/cosmos-sdk/pull/21315) New struct `Metadata` to store extra validator information.
|
||||
* New field `Metadata` introduced in `types`: `Description`.
|
||||
* The signature of `NewDescription` has changed to accept an extra argument of type `Metadata`.
|
||||
|
||||
### State Breaking changes
|
||||
|
||||
* [#18841](https://github.com/cosmos/cosmos-sdk/pull/18841) In a undelegation or redelegation if the shares being left delegated correspond to less than 1 token (in base denom) the entire delegation gets removed.
|
||||
|
||||
@ -15,12 +15,14 @@ const (
|
||||
FlagSharesAmount = "shares-amount"
|
||||
FlagSharesFraction = "shares-fraction"
|
||||
|
||||
FlagMoniker = "moniker"
|
||||
FlagEditMoniker = "new-moniker"
|
||||
FlagIdentity = "identity"
|
||||
FlagWebsite = "website"
|
||||
FlagSecurityContact = "security-contact"
|
||||
FlagDetails = "details"
|
||||
FlagMoniker = "moniker"
|
||||
FlagEditMoniker = "new-moniker"
|
||||
FlagIdentity = "identity"
|
||||
FlagWebsite = "website"
|
||||
FlagSecurityContact = "security-contact"
|
||||
FlagDetails = "details"
|
||||
FlagMetadataProfilePicUri = "metadata-profile-pic-uri"
|
||||
FlagMetadataSocialHandleUris = "metadata-social-handle-uris"
|
||||
|
||||
FlagCommissionRate = "commission-rate"
|
||||
FlagCommissionMaxRate = "commission-max-rate"
|
||||
@ -89,6 +91,8 @@ func flagSetDescriptionEdit() *flag.FlagSet {
|
||||
fs.String(FlagWebsite, types.DoNotModifyDesc, "The validator's (optional) website")
|
||||
fs.String(FlagSecurityContact, types.DoNotModifyDesc, "The validator's (optional) security contact email")
|
||||
fs.String(FlagDetails, types.DoNotModifyDesc, "The validator's (optional) details")
|
||||
fs.String(FlagMetadataProfilePicUri, "", "The validator's profile pic uri")
|
||||
fs.StringArray(FlagMetadataSocialHandleUris, []string{}, "The validator's social handles uris")
|
||||
|
||||
return fs
|
||||
}
|
||||
|
||||
@ -72,6 +72,7 @@ Where validator.json contains:
|
||||
"website": "validator's (optional) website",
|
||||
"security": "validator's (optional) security contact email",
|
||||
"details": "validator's (optional) details",
|
||||
"metadata-profile-pic-uri": "link to validator's (optional) profile picture",
|
||||
"commission-rate": "0.1",
|
||||
"commission-max-rate": "0.2",
|
||||
"commission-max-change-rate": "0.01",
|
||||
@ -129,7 +130,15 @@ func NewEditValidatorCmd() *cobra.Command {
|
||||
website, _ := cmd.Flags().GetString(FlagWebsite)
|
||||
security, _ := cmd.Flags().GetString(FlagSecurityContact)
|
||||
details, _ := cmd.Flags().GetString(FlagDetails)
|
||||
description := types.NewDescription(moniker, identity, website, security, details)
|
||||
profilePicUri, _ := cmd.Flags().GetString(FlagMetadataProfilePicUri)
|
||||
socialHandlesUris, _ := cmd.Flags().GetStringArray(FlagMetadataSocialHandleUris)
|
||||
|
||||
metadata := types.Metadata{
|
||||
ProfilePicUri: profilePicUri,
|
||||
SocialHandleUris: socialHandlesUris,
|
||||
}
|
||||
|
||||
description := types.NewDescription(moniker, identity, website, security, details, metadata)
|
||||
|
||||
var newRate *math.LegacyDec
|
||||
|
||||
@ -183,6 +192,7 @@ func newBuildCreateValidatorMsg(clientCtx client.Context, txf tx.Factory, fs *fl
|
||||
val.Website,
|
||||
val.Security,
|
||||
val.Details,
|
||||
val.Metadata,
|
||||
)
|
||||
|
||||
valStr, err := valAc.BytesToString(sdk.ValAddress(valAddr))
|
||||
@ -225,6 +235,8 @@ func CreateValidatorMsgFlagSet(ipDefault string) (fs *flag.FlagSet, defaultsDesc
|
||||
fsCreateValidator.String(FlagSecurityContact, "", "The validator's (optional) security contact email")
|
||||
fsCreateValidator.String(FlagDetails, "", "The validator's (optional) details")
|
||||
fsCreateValidator.String(FlagIdentity, "", "The (optional) identity signature (ex. UPort or Keybase)")
|
||||
fsCreateValidator.String(FlagMetadataProfilePicUri, "", "The validator's profile pic uri")
|
||||
fsCreateValidator.StringArray(FlagMetadataSocialHandleUris, []string{}, "The validator's social handles uris")
|
||||
fsCreateValidator.AddFlagSet(FlagSetCommissionCreate())
|
||||
fsCreateValidator.AddFlagSet(FlagSetMinSelfDelegation())
|
||||
fsCreateValidator.AddFlagSet(FlagSetAmount())
|
||||
@ -263,6 +275,7 @@ type TxCreateValidatorConfig struct {
|
||||
SecurityContact string
|
||||
Details string
|
||||
Identity string
|
||||
Metadata types.Metadata
|
||||
}
|
||||
|
||||
func PrepareConfigForTxCreateValidator(flagSet *flag.FlagSet, moniker, nodeID, chainID string, valPubKey cryptotypes.PubKey) (TxCreateValidatorConfig, error) {
|
||||
@ -302,6 +315,14 @@ func PrepareConfigForTxCreateValidator(flagSet *flag.FlagSet, moniker, nodeID, c
|
||||
return c, err
|
||||
}
|
||||
|
||||
profilePicUri, err := flagSet.GetString(FlagMetadataProfilePicUri)
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
metadata := types.Metadata{
|
||||
ProfilePicUri: profilePicUri,
|
||||
}
|
||||
|
||||
c.Amount, err = flagSet.GetString(FlagAmount)
|
||||
if err != nil {
|
||||
return c, err
|
||||
@ -338,6 +359,7 @@ func PrepareConfigForTxCreateValidator(flagSet *flag.FlagSet, moniker, nodeID, c
|
||||
c.SecurityContact = securityContact
|
||||
c.Details = details
|
||||
c.Identity = identity
|
||||
c.Metadata = metadata
|
||||
c.ChainID = chainID
|
||||
c.Moniker = moniker
|
||||
|
||||
@ -379,6 +401,7 @@ func BuildCreateValidatorMsg(clientCtx client.Context, config TxCreateValidatorC
|
||||
config.Website,
|
||||
config.SecurityContact,
|
||||
config.Details,
|
||||
config.Metadata,
|
||||
)
|
||||
|
||||
// get the initial validator commission parameters
|
||||
|
||||
@ -24,23 +24,26 @@ type validator struct {
|
||||
Website string
|
||||
Security string
|
||||
Details string
|
||||
Metadata types.Metadata
|
||||
CommissionRates types.CommissionRates
|
||||
MinSelfDelegation math.Int
|
||||
}
|
||||
|
||||
func parseAndValidateValidatorJSON(cdc codec.Codec, path string) (validator, error) {
|
||||
type internalVal struct {
|
||||
Amount string `json:"amount"`
|
||||
PubKey json.RawMessage `json:"pubkey"`
|
||||
Moniker string `json:"moniker"`
|
||||
Identity string `json:"identity,omitempty"`
|
||||
Website string `json:"website,omitempty"`
|
||||
Security string `json:"security,omitempty"`
|
||||
Details string `json:"details,omitempty"`
|
||||
CommissionRate string `json:"commission-rate"`
|
||||
CommissionMaxRate string `json:"commission-max-rate"`
|
||||
CommissionMaxChange string `json:"commission-max-change-rate"`
|
||||
MinSelfDelegation string `json:"min-self-delegation"`
|
||||
Amount string `json:"amount"`
|
||||
PubKey json.RawMessage `json:"pubkey"`
|
||||
Moniker string `json:"moniker"`
|
||||
Identity string `json:"identity,omitempty"`
|
||||
Website string `json:"website,omitempty"`
|
||||
Security string `json:"security,omitempty"`
|
||||
Details string `json:"details,omitempty"`
|
||||
MetadataProfilePicUri string `json:"metadata-profile-pic-uri,omitempty"`
|
||||
MetadataSocialHandlesUris []string `json:"metadata-social-handles-uris,omitempty"`
|
||||
CommissionRate string `json:"commission-rate"`
|
||||
CommissionMaxRate string `json:"commission-max-rate"`
|
||||
CommissionMaxChange string `json:"commission-max-change-rate"`
|
||||
MinSelfDelegation string `json:"min-self-delegation"`
|
||||
}
|
||||
|
||||
contents, err := os.ReadFile(path)
|
||||
@ -87,6 +90,11 @@ func parseAndValidateValidatorJSON(cdc codec.Codec, path string) (validator, err
|
||||
return validator{}, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "minimum self delegation must be a positive integer")
|
||||
}
|
||||
|
||||
metadata, err := buildMetadata(v.MetadataProfilePicUri, v.MetadataSocialHandlesUris)
|
||||
if err != nil {
|
||||
return validator{}, err
|
||||
}
|
||||
|
||||
return validator{
|
||||
Amount: amount,
|
||||
PubKey: pk,
|
||||
@ -95,6 +103,7 @@ func parseAndValidateValidatorJSON(cdc codec.Codec, path string) (validator, err
|
||||
Website: v.Website,
|
||||
Security: v.Security,
|
||||
Details: v.Details,
|
||||
Metadata: metadata,
|
||||
CommissionRates: commissionRates,
|
||||
MinSelfDelegation: minSelfDelegation,
|
||||
}, nil
|
||||
@ -124,3 +133,12 @@ func buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr string) (commiss
|
||||
|
||||
return commission, nil
|
||||
}
|
||||
|
||||
func buildMetadata(profilePicUri string, socialHandlesUris []string) (metadata types.Metadata, err error) {
|
||||
metadata.ProfilePicUri = profilePicUri
|
||||
metadata.SocialHandleUris = socialHandlesUris
|
||||
if err := metadata.Validate(); err != nil {
|
||||
return metadata, err
|
||||
}
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
@ -481,7 +481,7 @@ func (s *KeeperTestSuite) TestValidatorsMigrationToColls() {
|
||||
// legacy Set method
|
||||
s.ctx.KVStore(s.key).Set(getValidatorKey(valAddrs[i]), valBz)
|
||||
},
|
||||
"d8acdcf8b7c8e17f3e83f0a4c293f89ad619a5dcb14d232911ccc5da15653177",
|
||||
"55565aebbb67e1de08d0f17634ad168c68eae74f5cc9074e3a1ec4d1fbff16e5",
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
@ -507,7 +507,7 @@ func (s *KeeperTestSuite) TestValidatorsMigrationToColls() {
|
||||
err := s.stakingKeeper.SetValidator(s.ctx, val)
|
||||
s.Require().NoError(err)
|
||||
},
|
||||
"d8acdcf8b7c8e17f3e83f0a4c293f89ad619a5dcb14d232911ccc5da15653177",
|
||||
"55565aebbb67e1de08d0f17634ad168c68eae74f5cc9074e3a1ec4d1fbff16e5",
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
@ -109,7 +109,7 @@ func (k msgServer) CreateValidator(ctx context.Context, msg *types.MsgCreateVali
|
||||
)
|
||||
}
|
||||
|
||||
if _, err := msg.Description.EnsureLength(); err != nil {
|
||||
if _, err := msg.Description.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -176,7 +176,7 @@ func (k msgServer) EditValidator(ctx context.Context, msg *types.MsgEditValidato
|
||||
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid validator address: %s", err)
|
||||
}
|
||||
|
||||
if msg.Description == (types.Description{}) {
|
||||
if msg.Description.IsEmpty() {
|
||||
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "empty description")
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,11 @@ deps:
|
||||
repository: cosmos-proto
|
||||
commit: 04467658e59e44bbb22fe568206e1f70
|
||||
digest: shake256:73a640bd60e0c523b0f8237ff34eab67c45a38b64bbbde1d80224819d272dbf316ac183526bd245f994af6608b025f5130483d0133c5edd385531326b5990466
|
||||
- remote: buf.build
|
||||
owner: cosmos
|
||||
repository: cosmos-sdk
|
||||
commit: 05419252bcc241ea8023acf1ed4cadc5
|
||||
digest: shake256:1e54a48c19a8b59d35e0a7efa76402939f515f2d8005df099856f24c37c20a52800308f025abb8cffcd014d437b49707388aaca4865d9d063d8f25d5d4eb77d5
|
||||
- remote: buf.build
|
||||
owner: cosmos
|
||||
repository: gogo-proto
|
||||
@ -19,5 +24,15 @@ deps:
|
||||
- remote: buf.build
|
||||
owner: googleapis
|
||||
repository: googleapis
|
||||
commit: 7e6f6e774e29406da95bd61cdcdbc8bc
|
||||
digest: shake256:fe43dd2265ea0c07d76bd925eeba612667cf4c948d2ce53d6e367e1b4b3cb5fa69a51e6acb1a6a50d32f894f054a35e6c0406f6808a483f2752e10c866ffbf73
|
||||
commit: 8bc2c51e08c447cd8886cdea48a73e14
|
||||
digest: shake256:a969155953a5cedc5b2df5b42c368f2bc66ff8ce1804bc96e0f14ff2ee8a893687963058909df844d1643cdbc98ff099d2daa6bc9f9f5b8886c49afdc60e19af
|
||||
- remote: buf.build
|
||||
owner: protocolbuffers
|
||||
repository: wellknowntypes
|
||||
commit: 657250e6a39648cbb169d079a60bd9ba
|
||||
digest: shake256:00de25001b8dd2e29d85fc4bcc3ede7aed886d76d67f5e0f7a9b320b90f871d3eb73507d50818d823a0512f3f8db77a11c043685528403e31ff3fef18323a9fb
|
||||
- remote: buf.build
|
||||
owner: tendermint
|
||||
repository: tendermint
|
||||
commit: 33ed361a90514289beabf3189e1d7665
|
||||
digest: shake256:038267e06294714fd883610626554b04a127b576b4e253befb4206cb72d5d3c1eeccacd4b9ec8e3fb891f7c14e1cb0f770c077d2989638995b0a61c85afedb1d
|
||||
|
||||
@ -1,17 +1,16 @@
|
||||
syntax = "proto3";
|
||||
package cosmos.staking.v1beta1;
|
||||
|
||||
import "amino/amino.proto";
|
||||
import "cometbft/abci/v1/types.proto";
|
||||
import "cometbft/types/v1/types.proto";
|
||||
import "cosmos/base/v1beta1/coin.proto";
|
||||
import "cosmos_proto/cosmos.proto";
|
||||
import "gogoproto/gogo.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/duration.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
import "cosmos_proto/cosmos.proto";
|
||||
import "cosmos/base/v1beta1/coin.proto";
|
||||
import "amino/amino.proto";
|
||||
import "cometbft/types/v1/types.proto";
|
||||
import "cometbft/abci/v1/types.proto";
|
||||
|
||||
option go_package = "cosmossdk.io/x/staking/types";
|
||||
|
||||
// HistoricalInfo contains header and validator information for a given block.
|
||||
@ -79,6 +78,19 @@ message Description {
|
||||
string security_contact = 4;
|
||||
// details define other optional details.
|
||||
string details = 5;
|
||||
// metadata defines extra information about the validator.
|
||||
Metadata metadata = 6 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
|
||||
}
|
||||
|
||||
// Metadata defines extra information about the validator.
|
||||
message Metadata {
|
||||
option (gogoproto.equal) = true;
|
||||
|
||||
// profile_pic_uri defines a link to the validator profile picture.
|
||||
string profile_pic_uri = 1;
|
||||
|
||||
// social_handle_uris defines a string array of uris to the validator's social handles.
|
||||
repeated string social_handle_uris = 2;
|
||||
}
|
||||
|
||||
// Validator defines a validator, together with the total amount of the
|
||||
@ -221,7 +233,8 @@ message UnbondingDelegation {
|
||||
string validator_address = 2 [(cosmos_proto.scalar) = "cosmos.ValidatorAddressString"];
|
||||
// entries are the unbonding delegation entries.
|
||||
repeated UnbondingDelegationEntry entries = 3
|
||||
[(gogoproto.nullable) = false, (amino.dont_omitempty) = true]; // unbonding delegation entries
|
||||
[(gogoproto.nullable) = false,
|
||||
(amino.dont_omitempty) = true]; // unbonding delegation entries
|
||||
}
|
||||
|
||||
// UnbondingDelegationEntry defines an unbonding object with relevant metadata.
|
||||
@ -294,7 +307,8 @@ message Redelegation {
|
||||
string validator_dst_address = 3 [(cosmos_proto.scalar) = "cosmos.ValidatorAddressString"];
|
||||
// entries are the redelegation entries.
|
||||
repeated RedelegationEntry entries = 4
|
||||
[(gogoproto.nullable) = false, (amino.dont_omitempty) = true]; // redelegation entries
|
||||
[(gogoproto.nullable) = false,
|
||||
(amino.dont_omitempty) = true]; // redelegation entries
|
||||
}
|
||||
|
||||
// Params defines the parameters for the x/staking module.
|
||||
|
||||
@ -39,12 +39,17 @@ func MsgCreateValidatorFactory(k *keeper.Keeper) simsx.SimMsgFactoryFn[*types.Ms
|
||||
}
|
||||
|
||||
selfDelegation := valOper.LiquidBalance().RandSubsetCoin(reporter, bondDenom)
|
||||
|
||||
description := types.NewDescription(
|
||||
r.StringN(10),
|
||||
r.StringN(10),
|
||||
r.StringN(10),
|
||||
r.StringN(10),
|
||||
r.StringN(10),
|
||||
types.Metadata{
|
||||
ProfilePicUri: RandURIOfHostLength(r.Rand, 10),
|
||||
SocialHandleUris: RandSocialHandleURIs(r.Rand, 2, 10),
|
||||
},
|
||||
)
|
||||
|
||||
maxCommission := math.LegacyNewDecWithPrec(int64(r.IntInRange(0, 100)), 2)
|
||||
@ -138,7 +143,10 @@ func MsgEditValidatorFactory(k *keeper.Keeper) simsx.SimMsgFactoryFn[*types.MsgE
|
||||
}
|
||||
valOpAddrBz := must(k.ValidatorAddressCodec().StringToBytes(val.GetOperator()))
|
||||
valOper := testData.GetAccountbyAccAddr(reporter, valOpAddrBz)
|
||||
d := types.NewDescription(r.StringN(10), r.StringN(10), r.StringN(10), r.StringN(10), r.StringN(10))
|
||||
d := types.NewDescription(r.StringN(10), r.StringN(10), r.StringN(10), r.StringN(10), r.StringN(10), types.Metadata{
|
||||
ProfilePicUri: RandURIOfHostLength(r.Rand, 10),
|
||||
SocialHandleUris: RandSocialHandleURIs(r.Rand, 2, 10),
|
||||
})
|
||||
|
||||
msg := types.NewMsgEditValidator(val.GetOperator(), d, &newCommissionRate, nil)
|
||||
return []simsx.SimAccount{valOper}, msg
|
||||
|
||||
36
x/staking/simulation/rand_util.go
Normal file
36
x/staking/simulation/rand_util.go
Normal file
@ -0,0 +1,36 @@
|
||||
package simulation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/url"
|
||||
|
||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
)
|
||||
|
||||
// RandURIOfHostLength returns a random valid uri with hostname length n. If n = 0, returns an empty string.
|
||||
func RandURIOfHostLength(r *rand.Rand, n int) string {
|
||||
if n == 0 {
|
||||
return ""
|
||||
}
|
||||
tld := ".com"
|
||||
hostLength := n - len(tld)
|
||||
uri := &url.URL{
|
||||
Scheme: "https",
|
||||
Host: fmt.Sprintf("%s%s", simtypes.RandStringOfLength(r, hostLength), tld),
|
||||
}
|
||||
|
||||
return uri.String()
|
||||
}
|
||||
|
||||
// RandSocialHandleURIs returns a string array of length num with uris.
|
||||
func RandSocialHandleURIs(r *rand.Rand, num, uriHostLength int) []string {
|
||||
if num == 0 {
|
||||
return []string{}
|
||||
}
|
||||
var socialHandles []string
|
||||
for i := 0; i < num; i++ {
|
||||
socialHandles = append(socialHandles, RandURIOfHostLength(r, uriHostLength))
|
||||
}
|
||||
return socialHandles
|
||||
}
|
||||
60
x/staking/simulation/rand_util_test.go
Normal file
60
x/staking/simulation/rand_util_test.go
Normal file
@ -0,0 +1,60 @@
|
||||
package simulation_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/x/staking/simulation"
|
||||
)
|
||||
|
||||
func TestRandURIOfHostLength(t *testing.T) {
|
||||
t.Parallel()
|
||||
r := rand.New(rand.NewSource(time.Now().Unix()))
|
||||
tests := []struct {
|
||||
name string
|
||||
n int
|
||||
want int
|
||||
}{
|
||||
{"0-size", 0, 0},
|
||||
{"10-size", 10, 10},
|
||||
{"1_000_000-size", 1_000_000, 1_000_000},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tc := tt
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := 0
|
||||
uri := simulation.RandURIOfHostLength(r, tc.n)
|
||||
if uri != "" {
|
||||
parsedUri, err := url.Parse(uri)
|
||||
require.NoError(t, err)
|
||||
got = len(parsedUri.Host)
|
||||
}
|
||||
require.Equal(t, tc.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandSocialHandleURIs(t *testing.T) {
|
||||
t.Parallel()
|
||||
r := rand.New(rand.NewSource(time.Now().Unix()))
|
||||
tests := []struct {
|
||||
name string
|
||||
n int
|
||||
want int
|
||||
}{
|
||||
{"0-handles", 0, 0},
|
||||
{"10-handles", 10, 10},
|
||||
{"100-handles", 100, 100},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tc := tt
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
uris := simulation.RandSocialHandleURIs(r, tc.n, 10)
|
||||
require.Equal(t, tc.want, len(uris))
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -64,7 +64,7 @@ func (msg MsgCreateValidator) Validate(ac address.Codec) error {
|
||||
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid delegation amount")
|
||||
}
|
||||
|
||||
if msg.Description == (Description{}) {
|
||||
if msg.Description.IsEmpty() {
|
||||
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "empty description")
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,7 @@ package types
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@ -188,13 +189,14 @@ func (v Validator) IsUnbonding() bool {
|
||||
// constant used in flags to indicate that description field should not be updated
|
||||
const DoNotModifyDesc = "[do-not-modify]"
|
||||
|
||||
func NewDescription(moniker, identity, website, securityContact, details string) Description {
|
||||
func NewDescription(moniker, identity, website, securityContact, details string, metadata Metadata) Description {
|
||||
return Description{
|
||||
Moniker: moniker,
|
||||
Identity: identity,
|
||||
Website: website,
|
||||
SecurityContact: securityContact,
|
||||
Details: details,
|
||||
Metadata: metadata,
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,13 +223,18 @@ func (d Description) UpdateDescription(d2 Description) (Description, error) {
|
||||
d2.Details = d.Details
|
||||
}
|
||||
|
||||
if d2.Metadata.ProfilePicUri == DoNotModifyDesc {
|
||||
d2.Metadata.ProfilePicUri = d.Metadata.ProfilePicUri
|
||||
}
|
||||
|
||||
return NewDescription(
|
||||
d2.Moniker,
|
||||
d2.Identity,
|
||||
d2.Website,
|
||||
d2.SecurityContact,
|
||||
d2.Details,
|
||||
).EnsureLength()
|
||||
d.Metadata,
|
||||
).Validate()
|
||||
}
|
||||
|
||||
// EnsureLength ensures the length of a validator's description.
|
||||
@ -255,6 +262,40 @@ func (d Description) EnsureLength() (Description, error) {
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (d Description) IsEmpty() bool {
|
||||
return d.Moniker == "" && d.Details == "" && d.Identity == "" && d.Website == "" && d.SecurityContact == "" &&
|
||||
d.Metadata.ProfilePicUri == "" && len(d.Metadata.SocialHandleUris) == 0
|
||||
}
|
||||
|
||||
// Validate calls metadata.Validate() description.EnsureLength()
|
||||
func (d Description) Validate() (Description, error) {
|
||||
if err := d.Metadata.Validate(); err != nil {
|
||||
return d, err
|
||||
}
|
||||
|
||||
return d.EnsureLength()
|
||||
}
|
||||
|
||||
// Validate checks that the metadata fields are valid. For the ProfilePicUri, checks if a valid URI.
|
||||
func (m Metadata) Validate() error {
|
||||
if m.ProfilePicUri != "" {
|
||||
_, err := url.ParseRequestURI(m.ProfilePicUri)
|
||||
if err != nil {
|
||||
return errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid profile_pic_uri format: %s, err: %s", m.ProfilePicUri, err)
|
||||
}
|
||||
}
|
||||
|
||||
if m.SocialHandleUris != nil {
|
||||
for _, socialHandleUri := range m.SocialHandleUris {
|
||||
_, err := url.ParseRequestURI(socialHandleUri)
|
||||
if err != nil {
|
||||
return errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid social_handle_uri: %s, err: %s", socialHandleUri, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ModuleValidatorUpdate returns a appmodule.ValidatorUpdate from a staking validator type
|
||||
// with the full validator power.
|
||||
// It replaces the previous ABCIValidatorUpdate function.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user