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  }