github.com/Finschia/finschia-sdk@v0.49.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 "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 func (status ProposalStatus) Format(s fmt.State, verb rune) { 171 var err error 172 173 switch verb { 174 case 's': 175 _, err = s.Write([]byte(status.String())) 176 default: 177 // TODO: Do this conversion more directly 178 _, err = s.Write([]byte(fmt.Sprintf("%v", byte(status)))) 179 } 180 181 if err != nil { 182 panic(err) 183 } 184 } 185 186 // Proposal types 187 const ( 188 ProposalTypeText string = "Text" 189 ) 190 191 // Implements Content Interface 192 var _ Content = &TextProposal{} 193 194 // NewTextProposal creates a text proposal Content 195 func NewTextProposal(title, description string) Content { 196 return &TextProposal{title, description} 197 } 198 199 // GetTitle returns the proposal title 200 func (tp *TextProposal) GetTitle() string { return tp.Title } 201 202 // GetDescription returns the proposal description 203 func (tp *TextProposal) GetDescription() string { return tp.Description } 204 205 // ProposalRoute returns the proposal router key 206 func (tp *TextProposal) ProposalRoute() string { return RouterKey } 207 208 // ProposalType is "Text" 209 func (tp *TextProposal) ProposalType() string { return ProposalTypeText } 210 211 // ValidateBasic validates the content's title and description of the proposal 212 func (tp *TextProposal) ValidateBasic() error { return ValidateAbstract(tp) } 213 214 // String implements Stringer interface 215 func (tp TextProposal) String() string { 216 out, _ := yaml.Marshal(tp) 217 return string(out) 218 } 219 220 var validProposalTypes = map[string]struct{}{ 221 ProposalTypeText: {}, 222 } 223 224 // RegisterProposalType registers a proposal type. It will panic if the type is 225 // already registered. 226 func RegisterProposalType(ty string) { 227 if _, ok := validProposalTypes[ty]; ok { 228 panic(fmt.Sprintf("already registered proposal type: %s", ty)) 229 } 230 231 validProposalTypes[ty] = struct{}{} 232 } 233 234 // ContentFromProposalType returns a Content object based on the proposal type. 235 func ContentFromProposalType(title, desc, ty string) Content { 236 switch ty { 237 case ProposalTypeText: 238 return NewTextProposal(title, desc) 239 240 default: 241 return nil 242 } 243 } 244 245 // IsValidProposalType returns a boolean determining if the proposal type is 246 // valid. 247 // 248 // NOTE: Modules with their own proposal types must register them. 249 func IsValidProposalType(ty string) bool { 250 _, ok := validProposalTypes[ty] 251 return ok 252 } 253 254 // ProposalHandler implements the Handler interface for governance module-based 255 // proposals (ie. TextProposal ). Since these are 256 // merely signaling mechanisms at the moment and do not affect state, it 257 // performs a no-op. 258 func ProposalHandler(_ sdk.Context, c Content) error { 259 switch c.ProposalType() { 260 case ProposalTypeText: 261 // both proposal types do not change state so this performs a no-op 262 return nil 263 264 default: 265 return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized gov proposal type: %s", c.ProposalType()) 266 } 267 }