feat(x/staking)!: Add metadata field to validator info (#21315)

This commit is contained in:
Eric Mokaya 2024-10-01 10:35:58 +03:00 committed by GitHub
parent 8763d8d99f
commit 814d4cf6b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 2769 additions and 1484 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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,
}
}
}

View File

@ -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(),
)

View File

@ -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(),
)

View File

@ -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())
)

View File

@ -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)

View File

@ -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) {

View File

@ -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

View File

@ -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))

View File

@ -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": {

View File

@ -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(),
)

View File

@ -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"

View File

@ -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{}
)

View File

@ -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)

View File

@ -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.

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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)
}

View File

@ -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")
}

View File

@ -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

View File

@ -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.

View File

@ -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

View 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
}

View 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))
})
}
}

View File

@ -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

View File

@ -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.