github.com/Finschia/finschia-sdk@v0.48.1/x/gov/types/proposal.go (about) 1 package types 2 3 import ( 4 "fmt" 5 "strings" 6 "time" 7 8 "github.com/gogo/protobuf/proto" 9 yaml "gopkg.in/yaml.v2" 10 11 "github.com/Finschia/finschia-sdk/codec/types" 12 sdk "github.com/Finschia/finschia-sdk/types" 13 sdkerrors "github.com/Finschia/finschia-sdk/types/errors" 14 ) 15 16 // DefaultStartingProposalID is 1 17 const DefaultStartingProposalID uint64 = 1 18 19 // NewProposal creates a new Proposal instance 20 func NewProposal(content Content, id uint64, submitTime, depositEndTime time.Time) (Proposal, error) { 21 msg, ok := content.(proto.Message) 22 if !ok { 23 return Proposal{}, fmt.Errorf("%T does not implement proto.Message", content) 24 } 25 26 any, err := types.NewAnyWithValue(msg) 27 if err != nil { 28 return Proposal{}, err 29 } 30 31 p := Proposal{ 32 Content: any, 33 ProposalId: id, 34 Status: StatusDepositPeriod, 35 FinalTallyResult: EmptyTallyResult(), 36 TotalDeposit: sdk.NewCoins(), 37 SubmitTime: submitTime, 38 DepositEndTime: depositEndTime, 39 } 40 41 return p, nil 42 } 43 44 // String implements stringer interface 45 func (p Proposal) String() string { 46 out, _ := yaml.Marshal(p) 47 return string(out) 48 } 49 50 // GetContent returns the proposal Content 51 func (p Proposal) GetContent() Content { 52 content, ok := p.Content.GetCachedValue().(Content) 53 if !ok { 54 return nil 55 } 56 return content 57 } 58 59 func (p Proposal) ProposalType() string { 60 content := p.GetContent() 61 if content == nil { 62 return "" 63 } 64 return content.ProposalType() 65 } 66 67 func (p Proposal) ProposalRoute() string { 68 content := p.GetContent() 69 if content == nil { 70 return "" 71 } 72 return content.ProposalRoute() 73 } 74 75 func (p Proposal) GetTitle() string { 76 content := p.GetContent() 77 if content == nil { 78 return "" 79 } 80 return content.GetTitle() 81 } 82 83 // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces 84 func (p Proposal) UnpackInterfaces(unpacker types.AnyUnpacker) error { 85 var content Content 86 return unpacker.UnpackAny(p.Content, &content) 87 } 88 89 // Proposals is an array of proposal 90 type Proposals []Proposal 91 92 var _ types.UnpackInterfacesMessage = Proposals{} 93 94 // Equal returns true if two slices (order-dependant) of proposals are equal. 95 func (p Proposals) Equal(other Proposals) bool { 96 if len(p) != len(other) { 97 return false 98 } 99 100 for i, proposal := range p { 101 if !proposal.Equal(other[i]) { 102 return false 103 } 104 } 105 106 return true 107 } 108 109 // String implements stringer interface 110 func (p Proposals) String() string { 111 out := "ID - (Status) [Type] Title\n" 112 for _, prop := range p { 113 out += fmt.Sprintf("%d - (%s) [%s] %s\n", 114 prop.ProposalId, prop.Status, 115 prop.ProposalType(), prop.GetTitle()) 116 } 117 return strings.TrimSpace(out) 118 } 119 120 // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces 121 func (p Proposals) UnpackInterfaces(unpacker types.AnyUnpacker) error { 122 for _, x := range p { 123 err := x.UnpackInterfaces(unpacker) 124 if err != nil { 125 return err 126 } 127 } 128 return nil 129 } 130 131 type ( 132 // ProposalQueue defines a queue for proposal ids 133 ProposalQueue []uint64 134 ) 135 136 // ProposalStatusFromString turns a string into a ProposalStatus 137 func ProposalStatusFromString(str string) (ProposalStatus, error) { 138 num, ok := ProposalStatus_value[str] 139 if !ok { 140 return StatusNil, fmt.Errorf("'%s' is not a valid proposal status", str) 141 } 142 return ProposalStatus(num), nil 143 } 144 145 // ValidProposalStatus returns true if the proposal status is valid and false 146 // otherwise. 147 func ValidProposalStatus(status ProposalStatus) bool { 148 if status == StatusDepositPeriod || 149 status == StatusVotingPeriod || 150 status == StatusPassed || 151 status == StatusRejected || 152 status == StatusFailed { 153 return true 154 } 155 return false 156 } 157 158 // Marshal needed for protobuf compatibility 159 func (status ProposalStatus) Marshal() ([]byte, error) { 160 return []byte{byte(status)}, nil 161 } 162 163 // Unmarshal needed for protobuf compatibility 164 func (status *ProposalStatus) Unmarshal(data []byte) error { 165 *status = ProposalStatus(data[0]) 166 return nil 167 } 168 169 // Format implements the fmt.Formatter interface. 170 // nolint: errcheck 171 func (status ProposalStatus) Format(s fmt.State, verb rune) { 172 switch verb { 173 case 's': 174 s.Write([]byte(status.String())) 175 default: 176 // TODO: Do this conversion more directly 177 s.Write([]byte(fmt.Sprintf("%v", byte(status)))) 178 } 179 } 180 181 // Proposal types 182 const ( 183 ProposalTypeText string = "Text" 184 ) 185 186 // Implements Content Interface 187 var _ Content = &TextProposal{} 188 189 // NewTextProposal creates a text proposal Content 190 func NewTextProposal(title, description string) Content { 191 return &TextProposal{title, description} 192 } 193 194 // GetTitle returns the proposal title 195 func (tp *TextProposal) GetTitle() string { return tp.Title } 196 197 // GetDescription returns the proposal description 198 func (tp *TextProposal) GetDescription() string { return tp.Description } 199 200 // ProposalRoute returns the proposal router key 201 func (tp *TextProposal) ProposalRoute() string { return RouterKey } 202 203 // ProposalType is "Text" 204 func (tp *TextProposal) ProposalType() string { return ProposalTypeText } 205 206 // ValidateBasic validates the content's title and description of the proposal 207 func (tp *TextProposal) ValidateBasic() error { return ValidateAbstract(tp) } 208 209 // String implements Stringer interface 210 func (tp TextProposal) String() string { 211 out, _ := yaml.Marshal(tp) 212 return string(out) 213 } 214 215 var validProposalTypes = map[string]struct{}{ 216 ProposalTypeText: {}, 217 } 218 219 // RegisterProposalType registers a proposal type. It will panic if the type is 220 // already registered. 221 func RegisterProposalType(ty string) { 222 if _, ok := validProposalTypes[ty]; ok { 223 panic(fmt.Sprintf("already registered proposal type: %s", ty)) 224 } 225 226 validProposalTypes[ty] = struct{}{} 227 } 228 229 // ContentFromProposalType returns a Content object based on the proposal type. 230 func ContentFromProposalType(title, desc, ty string) Content { 231 switch ty { 232 case ProposalTypeText: 233 return NewTextProposal(title, desc) 234 235 default: 236 return nil 237 } 238 } 239 240 // IsValidProposalType returns a boolean determining if the proposal type is 241 // valid. 242 // 243 // NOTE: Modules with their own proposal types must register them. 244 func IsValidProposalType(ty string) bool { 245 _, ok := validProposalTypes[ty] 246 return ok 247 } 248 249 // ProposalHandler implements the Handler interface for governance module-based 250 // proposals (ie. TextProposal ). Since these are 251 // merely signaling mechanisms at the moment and do not affect state, it 252 // performs a no-op. 253 func ProposalHandler(_ sdk.Context, c Content) error { 254 switch c.ProposalType() { 255 case ProposalTypeText: 256 // both proposal types do not change state so this performs a no-op 257 return nil 258 259 default: 260 return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized gov proposal type: %s", c.ProposalType()) 261 } 262 }