github.com/decred/politeia@v1.4.0/politeiad/plugins/ticketvote/ticketvote.go (about)

     1  // Copyright (c) 2020-2021 The Decred developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package ticketvote provides a plugin for running votes that require decred
     6  // tickets to participate.
     7  package ticketvote
     8  
     9  const (
    10  	// PluginID is the unique identifier for this plugin.
    11  	PluginID = "ticketvote"
    12  
    13  	// Plugin commands
    14  	CmdAuthorize   = "authorize"   // Authorize a vote
    15  	CmdStart       = "start"       // Start a vote
    16  	CmdCastBallot  = "castballot"  // Cast a ballot of votes
    17  	CmdDetails     = "details"     // Get vote details
    18  	CmdResults     = "results"     // Get vote results
    19  	CmdSummary     = "summary"     // Get vote summary
    20  	CmdSubmissions = "submissions" // Get runoff vote submissions
    21  	CmdInventory   = "inventory"   // Get inventory by vote status
    22  	CmdTimestamps  = "timestamps"  // Get vote timestamps
    23  )
    24  
    25  // Plugin setting keys can be used to specify custom plugin settings. Default
    26  // plugin setting values can be overridden by providing a plugin setting key
    27  // and value to the plugin on startup.
    28  const (
    29  	// SettingKeyLinkByPeriodMin is the plugin setting key for the
    30  	// SettingLinkByPeriodMin plugin setting.
    31  	SettingKeyLinkByPeriodMin = "linkbyperiodmin"
    32  
    33  	// SettingKeyLinkByPeriodMax is the plugin setting key for the
    34  	// SettingLinkByPeriodMax plugin setting.
    35  	SettingKeyLinkByPeriodMax = "linkbyperiodmax"
    36  
    37  	// SettingKeyVoteDurationMin is the plugin setting key for the
    38  	// SettingVoteDurationMin plugin setting.
    39  	SettingKeyVoteDurationMin = "votedurationmin"
    40  
    41  	// SettingKeyVoteDurationMax is the plugin setting key for the
    42  	// SettingVoteDurationMax plugin setting.
    43  	SettingKeyVoteDurationMax = "votedurationmax"
    44  
    45  	// SettingKeySummariesPageSize is the plugin setting key for the
    46  	// SettingSummariesPageSize plugin setting.
    47  	SettingKeySummariesPageSize = "summariespagesize"
    48  
    49  	// SettingKeyInventoryPageSize is the plugin setting key for the
    50  	// SettingInventoryPageSize plugin setting.
    51  	SettingKeyInventoryPageSize = "inventorypagesize"
    52  
    53  	// SettingKeyTimestampsPageSize is the plugin setting key for the
    54  	// SettingTimestampsPageSize plugin setting.
    55  	SettingKeyTimestampsPageSize = "timestampspagesize"
    56  )
    57  
    58  // Plugin setting default values. These can be overridden by providing a plugin
    59  // setting key and value to the plugin on startup.
    60  const (
    61  	// SettingMainNetLinkByPeriodMin is the default minimum amount of
    62  	// time, in seconds, that the link by period can be set to. This
    63  	// value of 2 weeks was chosen assuming a 1 week voting period on
    64  	// mainnet.
    65  	SettingMainNetLinkByPeriodMin int64 = 1209600
    66  
    67  	// SettingMainNetLinkByPeriodMax is the default maximum amount of
    68  	// time, in seconds, that the link by period can be set to. This
    69  	// value of 3 months was chosen arbitrarily.
    70  	SettingMainNetLinkByPeriodMax int64 = 7776000
    71  
    72  	// SettingTestNeLinkByPeriodMin is the default minimum amount of
    73  	// time, in seconds, that the link by period can be set to. This
    74  	// value of 1 second was chosen because this is the testnet
    75  	// default and a 1 second miniumum makes testing various scenarios
    76  	// easier.
    77  	SettingTestNetLinkByPeriodMin int64 = 1
    78  
    79  	// SettingTestNetLinkByPeriodMax is the default maximum amount of
    80  	// time, in seconds, that the link by period can be set to. This
    81  	// value of 3 months was chosen arbitrarily.
    82  	SettingTestNetLinkByPeriodMax int64 = 7776000
    83  
    84  	// SettingMainNetVoteDurationMin is the default minimum vote
    85  	// duration on mainnet in blocks.
    86  	SettingMainNetVoteDurationMin uint32 = 2016
    87  
    88  	// SettingMainNetVoteDurationMax is the default maximum vote
    89  	// duration on mainnet in blocks.
    90  	SettingMainNetVoteDurationMax uint32 = 4032
    91  
    92  	// SettingTestNetVoteDurationMin is the default minimum vote
    93  	// duration on testnet in blocks.
    94  	SettingTestNetVoteDurationMin uint32 = 1
    95  
    96  	// SettingTestNetVoteDurationMax is the default maximum vote
    97  	// duration on testnet in blocks.
    98  	SettingTestNetVoteDurationMax uint32 = 4032
    99  
   100  	// SettingSummariesPageSize is the default maximum number of
   101  	// vote summaries that can be requested at any one time.
   102  	SettingSummariesPageSize uint32 = 5
   103  
   104  	// SettingInventoryPageSize is the default maximum number of tokens
   105  	// that will be returned for any single status in an InventoryReply.
   106  	SettingInventoryPageSize uint32 = 20
   107  
   108  	// SettingTimestampsPageSize is the default maximum number of comment
   109  	// timestamps that can be requested at any one time.
   110  	SettingTimestampsPageSize uint32 = 100
   111  )
   112  
   113  // ErrorCodeT represents and error that is caused by the user.
   114  type ErrorCodeT uint32
   115  
   116  const (
   117  	// ErrorCodeInvalid is an invalid error code.
   118  	ErrorCodeInvalid ErrorCodeT = 0
   119  
   120  	// ErrorCodeTokenInvalid is returned when a record token is
   121  	// provided as part of a plugin command payload and is not a valid
   122  	// token or the payload token does not match the token that was
   123  	// used in the API request.
   124  	ErrorCodeTokenInvalid ErrorCodeT = 1
   125  
   126  	// ErrorCodePublicKeyInvalid is returned when a public key is not
   127  	// a valid hex encoded, Ed25519 public key.
   128  	ErrorCodePublicKeyInvalid ErrorCodeT = 2
   129  
   130  	// ErrorCodeSignatureInvalid is returned when a signature is not
   131  	// a valid hex encoded, Ed25519 signature or when the signature is
   132  	// wrong.
   133  	ErrorCodeSignatureInvalid ErrorCodeT = 3
   134  
   135  	// ErrorCodeRecordVersionInvalid is returned when the record
   136  	// version used in a plugin command is not the most recent record
   137  	// version.
   138  	ErrorCodeRecordVersionInvalid ErrorCodeT = 4
   139  
   140  	// ErrorCodeAuthorizationInvalid is returned when a vote
   141  	// authorization is invalid.
   142  	ErrorCodeAuthorizationInvalid ErrorCodeT = 5
   143  
   144  	// ErrorCodeStartDetailsMissing is returned when a start command
   145  	// is missing one or more of the start details that it expects to
   146  	// be present.
   147  	ErrorCodeStartDetailsMissing ErrorCodeT = 6
   148  
   149  	// ErrorCodeStartDetailsInvalid is returned when a start command
   150  	// contains a start details that is not suppose to be included.
   151  	ErrorCodeStartDetailsInvalid ErrorCodeT = 7
   152  
   153  	// ErrorCodeVoteTypeInvalid is returned when a start details vote
   154  	// type is invalid.
   155  	ErrorCodeVoteTypeInvalid ErrorCodeT = 8
   156  
   157  	// ErrorCodeVoteDurationInvalid is returned when a start details
   158  	// vote duration is invalid.
   159  	ErrorCodeVoteDurationInvalid ErrorCodeT = 9
   160  
   161  	// ErrorCodeVoteQuorumInvalid is returned when a start details
   162  	// quorum percentage is invalid.
   163  	ErrorCodeVoteQuorumInvalid ErrorCodeT = 10
   164  
   165  	// ErrorCodeVotePassRateInvalid is returned when a start details
   166  	// pass percentage is invalid.
   167  	ErrorCodeVotePassRateInvalid ErrorCodeT = 11
   168  
   169  	// ErrorCodeVoteOptionsInvalid is returned when a start details
   170  	// vote options are invalid.
   171  	ErrorCodeVoteOptionsInvalid ErrorCodeT = 12
   172  
   173  	// ErrorCodeVoteBitsInvalid is returned when a vote bit or the mask
   174  	// of a start details is invalid.
   175  	ErrorCodeVoteBitsInvalid ErrorCodeT = 13
   176  
   177  	// ErrorCodeVoteParentInvalid is returned when a parent record
   178  	// of a runoff submission's start details is invalid.
   179  	ErrorCodeVoteParentInvalid ErrorCodeT = 14
   180  
   181  	// ErrorCodeVoteStatusInvalid is returned when the record's vote
   182  	// status does not allow for the command to be executed.
   183  	ErrorCodeVoteStatusInvalid ErrorCodeT = 15
   184  
   185  	// ErrorCodeVoteMetadataInvalid is returned when vote metadata
   186  	// attached to a record is invalid.
   187  	ErrorCodeVoteMetadataInvalid ErrorCodeT = 16
   188  
   189  	// ErrorCodeLinkByInvalid is returned when a vote metadata link by
   190  	// is invalid.
   191  	ErrorCodeLinkByInvalid ErrorCodeT = 17
   192  
   193  	// ErrorCodeLinkToInvalid is returned when a vote metadata link to
   194  	// is invalid.
   195  	ErrorCodeLinkToInvalid ErrorCodeT = 18
   196  
   197  	// ErrorCodeLinkByNotExpired is returned when a runoff vote is
   198  	// attempted to be started before the link by deadline has expired.
   199  	ErrorCodeLinkByNotExpired ErrorCodeT = 19
   200  
   201  	// ErrorCodeRecordStatusInvalid is returned when a ticketvote write
   202  	// command is executed on a record that is not public.
   203  	ErrorCodeRecordStatusInvalid ErrorCodeT = 20
   204  
   205  	// ErrorCodeLast unit test only
   206  	ErrorCodeLast ErrorCodeT = 21
   207  )
   208  
   209  var (
   210  	// ErrorCodes contains the human readable error messages.
   211  	ErrorCodes = map[ErrorCodeT]string{
   212  		ErrorCodeInvalid:              "error code invalid",
   213  		ErrorCodeTokenInvalid:         "token invalid",
   214  		ErrorCodePublicKeyInvalid:     "public key invalid",
   215  		ErrorCodeSignatureInvalid:     "signature invalid",
   216  		ErrorCodeRecordVersionInvalid: "record version invalid",
   217  		ErrorCodeAuthorizationInvalid: "authorization invalid",
   218  		ErrorCodeStartDetailsMissing:  "start details missing",
   219  		ErrorCodeStartDetailsInvalid:  "start details invalid",
   220  		ErrorCodeVoteTypeInvalid:      "vote type invalid",
   221  		ErrorCodeVoteDurationInvalid:  "vote duration invalid",
   222  		ErrorCodeVoteQuorumInvalid:    "quorum percentage invalid",
   223  		ErrorCodeVotePassRateInvalid:  "pass rate invalid",
   224  		ErrorCodeVoteOptionsInvalid:   "vote options invalid",
   225  		ErrorCodeVoteBitsInvalid:      "vote bits invalid",
   226  		ErrorCodeVoteParentInvalid:    "vote parent invalid",
   227  		ErrorCodeVoteStatusInvalid:    "vote status invalid",
   228  		ErrorCodeVoteMetadataInvalid:  "vote metadata invalid",
   229  		ErrorCodeLinkByInvalid:        "linkby invalid",
   230  		ErrorCodeLinkToInvalid:        "linkto invalid",
   231  		ErrorCodeLinkByNotExpired:     "linkby not exipred",
   232  		ErrorCodeRecordStatusInvalid:  "record status invalid",
   233  	}
   234  )
   235  
   236  const (
   237  	// FileNameVoteMetadata is the filename of the VoteMetadata file
   238  	// that is saved to politeiad. VoteMetadata is saved to politeiad
   239  	// as a file, not as a metadata stream, since it contains user
   240  	// provided metadata and needs to be included in the merkle root
   241  	// that politeiad signs.
   242  	FileNameVoteMetadata = "votemetadata.json"
   243  )
   244  
   245  // VoteMetadata is metadata that is specified by the user and attached to
   246  // a record on submission. This metadata is required for certain types of
   247  // votes.
   248  type VoteMetadata struct {
   249  	// LinkBy is set when the user intends for the record to be the
   250  	// parent record in a runoff vote. It is a UNIX timestamp that
   251  	// serves as the deadline for other records to declare their intent
   252  	// to participate in the runoff vote.
   253  	LinkBy int64 `json:"linkby,omitempty"`
   254  
   255  	// LinkTo is the censorship token of a runoff vote parent record.
   256  	// It is set when a record is being submitted as a vote options in
   257  	// the runoff vote.
   258  	LinkTo string `json:"linkto,omitempty"`
   259  }
   260  
   261  // AuthDetails is the structure that is saved to disk when a vote is authorized
   262  // or a previous authorization is revoked. It contains all the fields from a
   263  // Authorize and a AuthorizeReply.
   264  type AuthDetails struct {
   265  	// Data generated by client
   266  	Token     string `json:"token"`     // Record token
   267  	Version   uint32 `json:"version"`   // Record version
   268  	Action    string `json:"action"`    // Authorize or revoke
   269  	PublicKey string `json:"publickey"` // Public key used for signature
   270  	Signature string `json:"signature"` // Signature of token+version+action
   271  
   272  	// Metadata generated by server
   273  	Timestamp int64  `json:"timestamp"` // Received UNIX timestamp
   274  	Receipt   string `json:"receipt"`   // Server signature of client signature
   275  }
   276  
   277  // VoteT represents the different types of ticket votes that are available.
   278  type VoteT uint32
   279  
   280  const (
   281  	// VoteTypeInvalid is an invalid vote type.
   282  	VoteTypeInvalid VoteT = 0
   283  
   284  	// VoteTypeStandard is used to indicate a simple approve or reject
   285  	// vote where the winner is the voting option that has met the
   286  	// specified quorum and pass requirements. Standard votes must be
   287  	// authorized before the vote can be started.
   288  	VoteTypeStandard VoteT = 1
   289  
   290  	// VoteTypeRunoff specifies a runoff vote that multiple records
   291  	// compete in. All records are voted on like normal, but there can
   292  	// only be one winner in a runoff vote. The winner is the record
   293  	// that meets the quorum requirement, meets the pass requirement,
   294  	// and that has the most net yes votes. The winning record is
   295  	// considered approved and all other records are considered to be
   296  	// rejected. If no records meet the quorum and pass requirements
   297  	// then all records are considered rejected. Note, in a runoff vote
   298  	// it's possible for a record to meet both the quorum and pass
   299  	// requirements but still be rejected if it does not have the most
   300  	// net yes votes. Runoff vote participants are not required to have
   301  	// the voting period authorized prior to the vote starting.
   302  	VoteTypeRunoff VoteT = 2
   303  )
   304  
   305  const (
   306  	// VoteOptionIDApprove is the vote option ID that indicates the vote
   307  	// should be approved. Votes that are an approve/reject vote are
   308  	// required to use this vote option ID.
   309  	VoteOptionIDApprove = "yes"
   310  
   311  	// VoteOptionIDReject is the vote option ID that indicates the vote
   312  	// should be not be approved. Votes that are an approve/reject vote
   313  	// are required to use this vote option ID.
   314  	VoteOptionIDReject = "no"
   315  )
   316  
   317  // VoteOption describes a single vote option.
   318  type VoteOption struct {
   319  	ID          string `json:"id"`          // Single, unique word (e.g. yes)
   320  	Description string `json:"description"` // Longer description of the vote
   321  	Bit         uint64 `json:"bit"`         // Bit used for this option
   322  }
   323  
   324  // VoteParams describes the options and parameters of a ticket vote.
   325  type VoteParams struct {
   326  	Token    string `json:"token"`    // Record token
   327  	Version  uint32 `json:"version"`  // Record version
   328  	Type     VoteT  `json:"type"`     // Vote type
   329  	Mask     uint64 `json:"mask"`     // Valid vote bits
   330  	Duration uint32 `json:"duration"` // Duration in blocks
   331  
   332  	// QuorumPercentage is the percent of elligible votes required for
   333  	// the vote to meet a quorum.
   334  	QuorumPercentage uint32 `json:"quorumpercentage"`
   335  
   336  	// PassPercentage is the percent of cast votes required for a vote
   337  	// option to be considered as passing.
   338  	PassPercentage uint32 `json:"passpercentage"`
   339  
   340  	Options []VoteOption `json:"options"`
   341  
   342  	// Parent is the token of the parent record. This field will only
   343  	// be populated for runoff votes.
   344  	Parent string `json:"parent,omitempty"`
   345  }
   346  
   347  // VoteDetails is the structure that is saved to disk when a vote is started.
   348  // It contains all of the fields from a Start and a StartReply. A vote details
   349  // with the eligible tickets snapshot will be ~0.35MB.
   350  //
   351  // Signature is the client signature of the SHA256 digest of the JSON encoded
   352  // Vote struct.
   353  //
   354  // Receipt is the server signature of ClientSignature+StartBlockHash.
   355  type VoteDetails struct {
   356  	// Data generated by client
   357  	Params    VoteParams `json:"params"`
   358  	PublicKey string     `json:"publickey"`
   359  	Signature string     `json:"signature"`
   360  
   361  	// Metadata generated by server
   362  	Receipt          string   `json:"receipt"`
   363  	StartBlockHeight uint32   `json:"startblockheight"`
   364  	StartBlockHash   string   `json:"startblockhash"`
   365  	EndBlockHeight   uint32   `json:"endblockheight"`
   366  	EligibleTickets  []string `json:"eligibletickets"` // Ticket hashes
   367  }
   368  
   369  // CastVoteDetails contains the details of a cast vote.
   370  //
   371  // Signature is the client signature of the Token+Ticket+VoteBit. The client
   372  // uses the ticket's largest commitment address to create the signature. The
   373  // receipt is the server signature of the client signature.
   374  type CastVoteDetails struct {
   375  	// Data generated by client
   376  	Token     string `json:"token"`     // Record token
   377  	Ticket    string `json:"ticket"`    // Ticket hash
   378  	VoteBit   string `json:"votebit"`   // Vote bit, hex encoded
   379  	Signature string `json:"signature"` // Client signature
   380  
   381  	// Metdata generated by server
   382  	Address   string `json:"address"`   // Largest commitment address
   383  	Receipt   string `json:"receipt"`   // Server signature
   384  	Timestamp int64  `json:"timestamp"` // Unix timestamp
   385  }
   386  
   387  // AuthActionT represents the ticket vote authorization actions.
   388  type AuthActionT string
   389  
   390  const (
   391  	// AuthActionAuthorize is used to authorize a ticket vote.
   392  	AuthActionAuthorize AuthActionT = "authorize"
   393  
   394  	// AuthActionRevoke is used to revoke a previous ticket vote
   395  	// authorization.
   396  	AuthActionRevoke AuthActionT = "revoke"
   397  )
   398  
   399  // Authorize authorizes a ticket vote or revokes a previous authorization.
   400  //
   401  // Signature contains the client signature of the Token+Version+Action.
   402  type Authorize struct {
   403  	Token     string      `json:"token"`     // Record token
   404  	Version   uint32      `json:"version"`   // Record version
   405  	Action    AuthActionT `json:"action"`    // Authorize or revoke
   406  	PublicKey string      `json:"publickey"` // Public key used for signature
   407  	Signature string      `json:"signature"` // Client signature
   408  }
   409  
   410  // AuthorizeReply is the reply to the Authorize command.
   411  type AuthorizeReply struct {
   412  	Timestamp int64  `json:"timestamp"` // Received UNIX timestamp
   413  	Receipt   string `json:"receipt"`   // Server signature of client signature
   414  }
   415  
   416  // StartDetails is the structure that is provided when starting a ticket vote.
   417  //
   418  // Signature is the signature of a SHA256 digest of the JSON encoded VoteParams
   419  // structure.
   420  type StartDetails struct {
   421  	Params    VoteParams `json:"params"`
   422  	PublicKey string     `json:"publickey"` // Public key used for signature
   423  	Signature string     `json:"signature"` // Client signature
   424  }
   425  
   426  // Start starts a ticket vote.
   427  type Start struct {
   428  	Starts []StartDetails `json:"starts"`
   429  }
   430  
   431  // StartReply is the reply to the Start command.
   432  //
   433  // The Receipt is the server signature of ClientSignature+StartBlockHash.
   434  type StartReply struct {
   435  	Receipt          string   `json:"receipt"`
   436  	StartBlockHeight uint32   `json:"startblockheight"`
   437  	StartBlockHash   string   `json:"startblockhash"`
   438  	EndBlockHeight   uint32   `json:"endblockheight"`
   439  	EligibleTickets  []string `json:"eligibletickets"`
   440  }
   441  
   442  // VoteErrorT represents errors that can occur while attempting to cast ticket
   443  // votes.
   444  type VoteErrorT uint32
   445  
   446  const (
   447  	// VoteErrorInvalid is an invalid vote error.
   448  	VoteErrorInvalid VoteErrorT = 0
   449  
   450  	// VoteErrorInternalError is returned when an internal server error
   451  	// occurred.
   452  	VoteErrorInternalError VoteErrorT = 1
   453  
   454  	// VoteErrorTokenInvalid is returned when the record censorship
   455  	// token is invalid.
   456  	VoteErrorTokenInvalid VoteErrorT = 2
   457  
   458  	// VoteErrorRecordNotFound is returned when the specified record
   459  	// does not exist.
   460  	VoteErrorRecordNotFound VoteErrorT = 3
   461  
   462  	// VoteErrorMultipleRecordVotes is returned when votes are casts
   463  	// for multiple records in a single ballot.
   464  	VoteErrorMultipleRecordVotes VoteErrorT = 4
   465  
   466  	// VoteErrorVoteStatusInvalid is returned when the ticket vote
   467  	// status does not allow for votes to be cast, such as when a vote
   468  	// has already finished.
   469  	VoteErrorVoteStatusInvalid VoteErrorT = 5
   470  
   471  	// VoteErrorVoteBitInvalid is returned when the vote being cast
   472  	// uses invalid vote bits.
   473  	VoteErrorVoteBitInvalid VoteErrorT = 6
   474  
   475  	// VoteErrorSignatureInvalid is returned when the vote being cast
   476  	// has an invalid signature.
   477  	VoteErrorSignatureInvalid VoteErrorT = 7
   478  
   479  	// VoteErrorTicketNotEligible is returned when a vote is being cast
   480  	// using a ticket that is not part of the vote.
   481  	VoteErrorTicketNotEligible VoteErrorT = 8
   482  
   483  	// VoteErrorTicketAlreadyVoted is returned when a vote is cast
   484  	// using a ticket that has already voted.
   485  	VoteErrorTicketAlreadyVoted VoteErrorT = 9
   486  
   487  	// VoteErrorLast unit test only.
   488  	VoteErrorLast VoteErrorT = 10
   489  )
   490  
   491  var (
   492  	// VoteErrors contains the human readable error messages for the
   493  	// vote errors.
   494  	VoteErrors = map[VoteErrorT]string{
   495  		VoteErrorInvalid:             "vote error invalid",
   496  		VoteErrorInternalError:       "internal server error",
   497  		VoteErrorTokenInvalid:        "token invalid",
   498  		VoteErrorRecordNotFound:      "record not found",
   499  		VoteErrorMultipleRecordVotes: "attempting to vote on multiple records",
   500  		VoteErrorVoteStatusInvalid:   "record vote status invalid",
   501  		VoteErrorVoteBitInvalid:      "vote bit invalid",
   502  		VoteErrorSignatureInvalid:    "signature invalid",
   503  		VoteErrorTicketNotEligible:   "ticket not eligible",
   504  		VoteErrorTicketAlreadyVoted:  "ticket already voted",
   505  	}
   506  )
   507  
   508  // CastVote is a signed ticket vote. This structure gets saved to disk when
   509  // a vote is cast.
   510  type CastVote struct {
   511  	Token     string `json:"token"`     // Record token
   512  	Ticket    string `json:"ticket"`    // Ticket ID
   513  	VoteBit   string `json:"votebit"`   // Selected vote bit, hex encoded
   514  	Signature string `json:"signature"` // Signature of Token+Ticket+VoteBit
   515  }
   516  
   517  // CastVoteReply contains the receipt for the cast vote.
   518  type CastVoteReply struct {
   519  	Ticket  string `json:"ticket"`  // Ticket ID
   520  	Receipt string `json:"receipt"` // Server signature of client signature
   521  
   522  	// The follwing fields will only be present if an error occurred
   523  	// while attempting to cast the vote.
   524  	ErrorCode    *VoteErrorT `json:"errorcode,omitempty"`
   525  	ErrorContext string      `json:"errorcontext,omitempty"`
   526  }
   527  
   528  // CastBallot casts a ballot of votes. A ballot can only contain votes for a
   529  // single record.
   530  type CastBallot struct {
   531  	Ballot []CastVote `json:"ballot"`
   532  }
   533  
   534  // CastBallotReply is a reply to a batched list of votes.
   535  type CastBallotReply struct {
   536  	Receipts []CastVoteReply `json:"receipts"`
   537  }
   538  
   539  // Details returns the vote details for a record.
   540  type Details struct{}
   541  
   542  // DetailsReply is the reply to the Details command.
   543  type DetailsReply struct {
   544  	Auths []AuthDetails `json:"auths"`
   545  	Vote  *VoteDetails  `json:"vote,omitempty"`
   546  }
   547  
   548  // Results requests the results of a vote.
   549  type Results struct{}
   550  
   551  // ResultsReply is the rely to the Results command.
   552  type ResultsReply struct {
   553  	Votes []CastVoteDetails `json:"votes"`
   554  }
   555  
   556  // VoteStatusT represents the status of a ticket vote.
   557  type VoteStatusT uint32
   558  
   559  const (
   560  	// VoteStatusInvalid is an invalid vote status.
   561  	VoteStatusInvalid VoteStatusT = 0
   562  
   563  	// VoteStatusUnauthorized indicates the ticket vote has not been
   564  	// authorized yet.
   565  	VoteStatusUnauthorized VoteStatusT = 1
   566  
   567  	// VoteStatusAuthorized indicates the ticket vote has been
   568  	// authorized.
   569  	VoteStatusAuthorized VoteStatusT = 2
   570  
   571  	// VoteStatusStarted indicates the ticket vote has been started.
   572  	VoteStatusStarted VoteStatusT = 3
   573  
   574  	// VoteStatusFinished indicates the ticket vote has finished. This
   575  	// vote status is used for vote types that do not have a clear
   576  	// approved or rejected outcome, such as multiple choice votes.
   577  	VoteStatusFinished VoteStatusT = 4
   578  
   579  	// VoteStatusApproved indicates that a vote has finished and the
   580  	// vote has met the criteria for being approved. This vote status
   581  	// is only used when the vote type allows for a clear approved or
   582  	// rejected outcome.
   583  	VoteStatusApproved VoteStatusT = 5
   584  
   585  	// VoteStatusRejected indicates that a vote has finished and the
   586  	// vote did NOT the criteria for being approved. This vote status
   587  	// is only used when the vote type allows for a clear approved or
   588  	// rejected outcome.
   589  	VoteStatusRejected VoteStatusT = 6
   590  
   591  	// VoteStatusIneligible indicates that a record is not eligible to
   592  	// be voted on. This happens when a record is censored or archived.
   593  	VoteStatusIneligible VoteStatusT = 7
   594  
   595  	// VoteStatusLast unit test only.
   596  	VoteStatusLast VoteStatusT = 8
   597  )
   598  
   599  var (
   600  	// VoteStatuses contains the human readable vote statuses.
   601  	VoteStatuses = map[VoteStatusT]string{
   602  		VoteStatusInvalid:      "invalid",
   603  		VoteStatusUnauthorized: "unauthorized",
   604  		VoteStatusAuthorized:   "authorized",
   605  		VoteStatusStarted:      "started",
   606  		VoteStatusFinished:     "finished",
   607  		VoteStatusApproved:     "approved",
   608  		VoteStatusRejected:     "rejected",
   609  		VoteStatusIneligible:   "ineligible",
   610  	}
   611  )
   612  
   613  // VoteOptionResult describes a vote option and the total number of votes that
   614  // have been cast for this option.
   615  type VoteOptionResult struct {
   616  	ID          string `json:"id"`          // Single unique word (e.g. yes)
   617  	Description string `json:"description"` // Longer description of the vote
   618  	VoteBit     uint64 `json:"votebit"`     // Bits used for this option
   619  	Votes       uint64 `json:"votes"`       // Votes cast for this option
   620  }
   621  
   622  // Summary requests the vote summary for a record.
   623  type Summary struct{}
   624  
   625  // SummaryReply is the reply to the Summary command.
   626  type SummaryReply struct {
   627  	Status VoteStatusT `json:"status"`
   628  
   629  	// Timestamp is the unix timestamp of the most recent vote status change.
   630  	//
   631  	// This field will only be populated prior to the start of the voting period.
   632  	// Once the voting period starts, the vote statuses are based on the block
   633  	// height and the vote results. These statuses do not have timestamps
   634  	// associated with them.
   635  	Timestamp int64 `json:"timestamp,omitempty"`
   636  
   637  	// The following fields will only be populated once the voting period has
   638  	// been started or has finished.
   639  	Type             VoteT              `json:"type,omitempty"`
   640  	Duration         uint32             `json:"duration,omitempty"`
   641  	StartBlockHeight uint32             `json:"startblockheight,omitempty"`
   642  	StartBlockHash   string             `json:"startblockhash,omitempty"`
   643  	EndBlockHeight   uint32             `json:"endblockheight,omitempty"`
   644  	EligibleTickets  uint32             `json:"eligibletickets,omitempty"`
   645  	QuorumPercentage uint32             `json:"quorumpercentage,omitempty"`
   646  	PassPercentage   uint32             `json:"passpercentage,omitempty"`
   647  	Results          []VoteOptionResult `json:"results,omitempty"`
   648  
   649  	// BestBlock is the best block value that was used to prepare this summary.
   650  	BestBlock uint32 `json:"bestblock"`
   651  }
   652  
   653  // Submissions requests the submissions of a runoff vote. The only records that
   654  // will have a submissions list are the parent records in a runoff vote. The
   655  // list will contain all public runoff vote submissions, i.e. records that
   656  // have linked to the parent record using the VoteMetadata.LinkTo field.
   657  type Submissions struct {
   658  	Token string `json:"token"`
   659  }
   660  
   661  // SubmissionsReply is the reply to the Submissions command.
   662  type SubmissionsReply struct {
   663  	Submissions []string `json:"submissions"`
   664  }
   665  
   666  // Inventory requests the tokens of public records in the inventory categorized
   667  // by vote status.
   668  //
   669  // The status and page arguments can be provided to request a specific page of
   670  // record tokens.
   671  //
   672  // If no status is provided then a page of tokens for all statuses will be
   673  // returned. The page argument will be ignored.
   674  type Inventory struct {
   675  	Status VoteStatusT `json:"status,omitempty"`
   676  	Page   uint32      `json:"page,omitempty"`
   677  }
   678  
   679  // InventoryReply is the reply to the Inventory command. The returned map is a
   680  // map[votestatus][]token where the votestatus key is the human readable vote
   681  // status defined by the VoteStatuses array in this package.
   682  //
   683  // Sorted by timestamp in descending order:
   684  // Unauthorized, Authorized
   685  //
   686  // Sorted by vote start block height in descending order:
   687  // Started
   688  //
   689  // Sorted by vote end block height in descending order:
   690  // Finished, Approved, Rejected
   691  type InventoryReply struct {
   692  	Tokens map[string][]string `json:"tokens"`
   693  
   694  	// BestBlock is the best block value that was used to prepare the
   695  	// inventory.
   696  	BestBlock uint32 `json:"bestblock"`
   697  }
   698  
   699  // Proof contains an inclusion proof for the digest in the merkle root. The
   700  // ExtraData field is used by certain types of proofs to include additional
   701  // data that is required to validate the proof.
   702  type Proof struct {
   703  	Type       string   `json:"type"`
   704  	Digest     string   `json:"digest"`
   705  	MerkleRoot string   `json:"merkleroot"`
   706  	MerklePath []string `json:"merklepath"`
   707  	ExtraData  string   `json:"extradata"` // JSON encoded
   708  }
   709  
   710  // Timestamp contains all of the data required to verify that a piece of data
   711  // was timestamped onto the decred blockchain.
   712  //
   713  // All digests are hex encoded SHA256 digests. The merkle root can be found in
   714  // the OP_RETURN of the specified DCR transaction.
   715  //
   716  // TxID, MerkleRoot, and Proofs will only be populated once the merkle root has
   717  // been included in a DCR tx and the tx has 6 confirmations. The Data field
   718  // will not be populated if the data has been censored.
   719  type Timestamp struct {
   720  	Data       string  `json:"data"` // JSON encoded
   721  	Digest     string  `json:"digest"`
   722  	TxID       string  `json:"txid"`
   723  	MerkleRoot string  `json:"merkleroot"`
   724  	Proofs     []Proof `json:"proofs"`
   725  }
   726  
   727  // Timestamps requests the timestamps for a ticket vote.
   728  //
   729  // If no votes page number is provided then the vote authorization and vote
   730  // details timestamps will be returned. If a votes page number is provided then
   731  // the specified page of votes will be returned.
   732  type Timestamps struct {
   733  	VotesPage uint32 `json:"votespage,omitempty"`
   734  }
   735  
   736  // TimestampsReply is the reply to the Timestamps command.
   737  type TimestampsReply struct {
   738  	Auths   []Timestamp `json:"auths"`
   739  	Details *Timestamp  `json:"details,omitempty"`
   740  	Votes   []Timestamp `json:"votes"`
   741  }