diff --git a/x/distribution/types/errors.go b/x/distribution/types/errors.go index 808d3f86b7..0ce6a76d88 100644 --- a/x/distribution/types/errors.go +++ b/x/distribution/types/errors.go @@ -16,4 +16,5 @@ var ( ErrEmptyProposalRecipient = errors.Register(ModuleName, 11, "invalid community pool spend proposal recipient") ErrNoValidatorExists = errors.Register(ModuleName, 12, "validator does not exist") ErrNoDelegationExists = errors.Register(ModuleName, 13, "delegation does not exist") + ErrInvalidProposalContent = errors.Register(ModuleName, 14, "invalid proposal content") ) diff --git a/x/distribution/types/proposal.go b/x/distribution/types/proposal.go index 847bdd382f..257e546dcc 100644 --- a/x/distribution/types/proposal.go +++ b/x/distribution/types/proposal.go @@ -1,7 +1,9 @@ package types import ( - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "strings" + + errorsmod "cosmossdk.io/errors" ) // GetTitle returns the title of a community pool spend proposal. @@ -18,7 +20,7 @@ func (csp *CommunityPoolSpendProposal) ProposalType() string { return "Community // ValidateBasic runs basic stateless validity checks func (csp *CommunityPoolSpendProposal) ValidateBasic() error { - err := govtypes.ValidateAbstract(csp) + err := validateAbstract(csp) if err != nil { return err } @@ -31,3 +33,30 @@ func (csp *CommunityPoolSpendProposal) ValidateBasic() error { return nil } + +// validateAbstract is semantically duplicated from x/gov/types/v1beta1/proposal.go to avoid a cyclic dependency +// between these two modules, x/distribution and x/gov. +// See: https://github.com/cosmos/cosmos-sdk/blob/4a6a1e3cb8de459891cb0495052589673d14ef51/x/gov/types/v1beta1/proposal.go#L196-L214 +func validateAbstract(c *CommunityPoolSpendProposal) error { + const ( + MaxTitleLength = 140 + MaxDescriptionLength = 10000 + ) + title := c.GetTitle() + if len(strings.TrimSpace(title)) == 0 { + return errorsmod.Wrap(ErrInvalidProposalContent, "proposal title cannot be blank") + } + if len(title) > MaxTitleLength { + return errorsmod.Wrapf(ErrInvalidProposalContent, "proposal title is longer than max length of %d", MaxTitleLength) + } + + description := c.GetDescription() + if len(description) == 0 { + return errorsmod.Wrap(ErrInvalidProposalContent, "proposal description cannot be blank") + } + if len(description) > MaxDescriptionLength { + return errorsmod.Wrapf(ErrInvalidProposalContent, "proposal description is longer than max length of %d", MaxDescriptionLength) + } + + return nil +}