github.com/decred/politeia@v1.4.0/politeiawww/api/comments/v1/v1.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 v1
     6  
     7  import "fmt"
     8  
     9  const (
    10  	// APIRoute is prefixed onto all routes defined in this package.
    11  	APIRoute = "/comments/v1"
    12  
    13  	// RoutePolicy returns the policy for the comments API.
    14  	RoutePolicy = "/policy"
    15  
    16  	// RouteNew adds a new comment.
    17  	RouteNew = "/new"
    18  
    19  	// RouteEdit edits a comment.
    20  	RouteEdit = "/edit"
    21  
    22  	// RouteVote votes on a comment.
    23  	RouteVote = "/vote"
    24  
    25  	// RouteDel deletes a comment.
    26  	RouteDel = "/del"
    27  
    28  	// RouteCount returns the number of comment on a record.
    29  	RouteCount = "/count"
    30  
    31  	// RouteComments returns all comments on a record.
    32  	RouteComments = "/comments"
    33  
    34  	// RouteVotes returns all comment votes of a record.
    35  	RouteVotes = "/votes"
    36  
    37  	// RouteTimestamps returns the timestamps for the comments of a record.
    38  	RouteTimestamps = "/timestamps"
    39  )
    40  
    41  // ErrorCodeT represents a user error code.
    42  type ErrorCodeT uint32
    43  
    44  const (
    45  	// ErrorCodeInvalid is an invalid error code.
    46  	ErrorCodeInvalid ErrorCodeT = 0
    47  
    48  	// ErrorCodeInputInvalid is returned when there is an error
    49  	// while prasing a command payload.
    50  	ErrorCodeInputInvalid ErrorCodeT = 1
    51  
    52  	// ErrorCodeUnauthorized is returned when the user is not authorized.
    53  	ErrorCodeUnauthorized ErrorCodeT = 2
    54  
    55  	// ErrorCodePublicKeyInvalid is returned when a public key is
    56  	// invalid.
    57  	ErrorCodePublicKeyInvalid ErrorCodeT = 3
    58  
    59  	// ErrorCodeSignatureInvalid is returned when a signature is
    60  	// invalid.
    61  	ErrorCodeSignatureInvalid ErrorCodeT = 4
    62  
    63  	// ErrorCodeRecordStateInvalid is returned when the provided state
    64  	// does not match the record state.
    65  	ErrorCodeRecordStateInvalid ErrorCodeT = 5
    66  
    67  	// ErrorCodeTokenInvalid is returned when a token is invalid.
    68  	ErrorCodeTokenInvalid ErrorCodeT = 6
    69  
    70  	// ErrorCodeRecordNotFound is returned when a record not found.
    71  	ErrorCodeRecordNotFound ErrorCodeT = 7
    72  
    73  	// ErrorCodeRecordLocked is returned when a record is locked.
    74  	ErrorCodeRecordLocked ErrorCodeT = 8
    75  
    76  	// ErrorCodePageSizeExceeded is returned when the request's page size
    77  	// exceeds the maximum page size of the request.
    78  	ErrorCodePageSizeExceeded ErrorCodeT = 9
    79  
    80  	// ErrorCodeDuplicatePayload is returned when a user tries to submit a
    81  	// duplicate payload for the comments plugin, where it tries to write
    82  	// data that already exists. Timestamp data relies on the hash of the
    83  	// payload, therefore duplicate payloads are not allowed since they will
    84  	// cause collisions.
    85  	ErrorCodeDuplicatePayload ErrorCodeT = 10
    86  
    87  	// ErrorCodeLast is used by unit tests to verify that all error codes have
    88  	// a human readable entry in the ErrorCodes map. This error will never be
    89  	// returned.
    90  	ErrorCodeLast ErrorCodeT = 11
    91  )
    92  
    93  var (
    94  	// ErrorCodes contains the human readable errors.
    95  	ErrorCodes = map[ErrorCodeT]string{
    96  		ErrorCodeInvalid:            "error invalid",
    97  		ErrorCodeInputInvalid:       "input invalid",
    98  		ErrorCodeUnauthorized:       "unauthorized",
    99  		ErrorCodePublicKeyInvalid:   "public key invalid",
   100  		ErrorCodeSignatureInvalid:   "signature invalid",
   101  		ErrorCodeRecordStateInvalid: "record state invalid",
   102  		ErrorCodeTokenInvalid:       "token invalid",
   103  		ErrorCodeRecordNotFound:     "record not found",
   104  		ErrorCodeRecordLocked:       "record is locked",
   105  		ErrorCodePageSizeExceeded:   "page size exceeded",
   106  		ErrorCodeDuplicatePayload:   "duplicate payload",
   107  	}
   108  )
   109  
   110  // UserErrorReply is the reply that the server returns when it encounters an
   111  // error that is caused by something that the user did (malformed input, bad
   112  // timing, etc). The HTTP status code will be 400.
   113  type UserErrorReply struct {
   114  	ErrorCode    ErrorCodeT `json:"errorcode"`
   115  	ErrorContext string     `json:"errorcontext,omitempty"`
   116  }
   117  
   118  // Error satisfies the error interface.
   119  func (e UserErrorReply) Error() string {
   120  	return fmt.Sprintf("user error code: %v", e.ErrorCode)
   121  }
   122  
   123  // PluginErrorReply is the reply that the server returns when it encounters
   124  // a plugin error.
   125  type PluginErrorReply struct {
   126  	PluginID     string `json:"pluginid"`
   127  	ErrorCode    uint32 `json:"errorcode"`
   128  	ErrorContext string `json:"errorcontext,omitempty"`
   129  }
   130  
   131  // Error satisfies the error interface.
   132  func (e PluginErrorReply) Error() string {
   133  	return fmt.Sprintf("plugin %v error code: %v", e.PluginID, e.ErrorCode)
   134  }
   135  
   136  // ServerErrorReply is the reply that the server returns when it encounters an
   137  // unrecoverable error while executing a command. The HTTP status code will be
   138  // 500 and the ErrorCode field will contain a UNIX timestamp that the user can
   139  // provide to the server admin to track down the error details in the logs.
   140  type ServerErrorReply struct {
   141  	ErrorCode int64 `json:"errorcode"`
   142  }
   143  
   144  // Error satisfies the error interface.
   145  func (e ServerErrorReply) Error() string {
   146  	return fmt.Sprintf("server error: %v", e.ErrorCode)
   147  }
   148  
   149  // Policy requests the comments API policy.
   150  type Policy struct{}
   151  
   152  // PolicyReply is the reply to the policy command.
   153  type PolicyReply struct {
   154  	LengthMax          uint32 `json:"lengthmax"` // In characters
   155  	VoteChangesMax     uint32 `json:"votechangesmax"`
   156  	AllowExtraData     bool   `json:"allowextradata"`
   157  	CountPageSize      uint32 `json:"countpagesize"`
   158  	TimestampsPageSize uint32 `json:"timestampspagesize"`
   159  	VotesPageSize      uint32 `json:"votespagesize"`
   160  	AllowEdits         bool   `json:"allowedits"`
   161  	EditPeriod         uint32 `json:"editperiod"`
   162  }
   163  
   164  // RecordStateT represents the state of a record.
   165  type RecordStateT uint32
   166  
   167  const (
   168  	// RecordStateInvalid is an invalid record state.
   169  	RecordStateInvalid RecordStateT = 0
   170  
   171  	// RecordStateUnvetted indicates a record has not been made public.
   172  	RecordStateUnvetted RecordStateT = 1
   173  
   174  	// RecordStateVetted indicates a record has been made public.
   175  	RecordStateVetted RecordStateT = 2
   176  )
   177  
   178  // Comment represent a record comment.
   179  //
   180  // A parent ID of 0 indicates that the comment is a base level comment and not
   181  // a reply commment.
   182  //
   183  // Comments made on a record when it is unvetted and when it is vetted are
   184  // treated as two distinct groups of comments. When a record becomes vetted the
   185  // comment ID starts back at 1.
   186  //
   187  // If a comment is deleted the PublicKey, Signature, Receipt, and Timestamp
   188  // fields will be the from the deletion action, not from the original comment.
   189  // The only fields that are retained from the original comment are the UserID
   190  // and Username so that the client can still display the correct user
   191  // information for the deleted comment. Everything else from the original
   192  // comment is permanently deleted.
   193  //
   194  // PublicKey is the user's public key that is used to verify the signature.
   195  //
   196  // Signature is the user signature of the:
   197  // State + Token + ParentID + Comment + ExtraData + ExtraDataHint
   198  //
   199  // Receipt is the server signature of the user signature.
   200  //
   201  // The PublicKey, Signature, and Receipt are all hex encoded and use the
   202  // ed25519 signature scheme.
   203  type Comment struct {
   204  	UserID    string       `json:"userid"`    // Unique user ID
   205  	Username  string       `json:"username"`  // Username
   206  	State     RecordStateT `json:"state"`     // Record state
   207  	Token     string       `json:"token"`     // Record token
   208  	ParentID  uint32       `json:"parentid"`  // Parent comment ID if reply
   209  	Comment   string       `json:"comment"`   // Comment text
   210  	PublicKey string       `json:"publickey"` // Public key used for Signature
   211  	Signature string       `json:"signature"` // Client signature
   212  	CommentID uint32       `json:"commentid"` // Comment ID
   213  	Version   uint32       `json:"version"`   // Comment version
   214  	CreatedAt int64        `json:"createdat"` // UNIX timestamp of creation time
   215  	Timestamp int64        `json:"timestamp"` // UNIX timestamp of last edit
   216  	Receipt   string       `json:"receipt"`   // Server sig of client sig
   217  	Downvotes uint64       `json:"downvotes"` // Tolal downvotes on comment
   218  	Upvotes   uint64       `json:"upvotes"`   // Total upvotes on comment
   219  
   220  	Deleted bool   `json:"deleted,omitempty"` // Comment has been deleted
   221  	Reason  string `json:"reason,omitempty"`  // Reason for deletion
   222  
   223  	// Optional fields to be used freely
   224  	ExtraData     string `json:"extradata,omitempty"`
   225  	ExtraDataHint string `json:"extradatahint,omitempty"`
   226  }
   227  
   228  // CommentVote represents a comment vote (upvote/downvote).
   229  //
   230  // PublicKey is the user's public key that is used to verify the signature.
   231  //
   232  // Signature is the user signature of the:
   233  // State + Token + CommentID + Vote
   234  //
   235  // The PublicKey and Signature are hex encoded and use the
   236  // ed25519 signature scheme.
   237  type CommentVote struct {
   238  	UserID    string       `json:"userid"`    // Unique user ID
   239  	Username  string       `json:"username"`  // Username
   240  	State     RecordStateT `json:"state"`     // Record state
   241  	Token     string       `json:"token"`     // Record token
   242  	CommentID uint32       `json:"commentid"` // Comment ID
   243  	Vote      VoteT        `json:"vote"`      // Upvote or downvote
   244  	PublicKey string       `json:"publickey"` // Public key used for signature
   245  	Signature string       `json:"signature"` // Client signature
   246  	Timestamp int64        `json:"timestamp"` // Received UNIX timestamp
   247  	Receipt   string       `json:"receipt"`   // Server sig of client sig
   248  }
   249  
   250  // New creates a new comment.
   251  //
   252  // The parent ID is used to reply to an existing comment. A parent ID of 0
   253  // indicates that the comment is a base level comment and not a reply commment.
   254  //
   255  // PublicKey is the user's public key that is used to verify the signature.
   256  //
   257  // Signature is the user signature of the:
   258  // State + Token + ParentID + Comment + ExtraData + ExtraDataHint
   259  //
   260  // The PublicKey and Signature are hex encoded and use the
   261  // ed25519 signature scheme.
   262  type New struct {
   263  	State     RecordStateT `json:"state"`
   264  	Token     string       `json:"token"`
   265  	ParentID  uint32       `json:"parentid"`
   266  	Comment   string       `json:"comment"`
   267  	PublicKey string       `json:"publickey"`
   268  	Signature string       `json:"signature"`
   269  
   270  	// Optional fields to be used freely
   271  	ExtraData     string `json:"extradata,omitempty"`
   272  	ExtraDataHint string `json:"extradatahint,omitempty"`
   273  }
   274  
   275  // NewReply is the reply to the New command.
   276  type NewReply struct {
   277  	Comment Comment `json:"comment"`
   278  }
   279  
   280  // Edit edits an existing comment.
   281  //
   282  // PublicKey is the user's public key that is used to verify the signature.
   283  //
   284  // Signature is the user signature of the:
   285  // State + Token + ParentID + CommentID + Comment + ExtraData + ExtraDataHint
   286  //
   287  // Receipt is the server signature of the user signature.
   288  //
   289  // The PublicKey, Signature, and Receipt are all hex encoded and use the
   290  // ed25519 signature scheme.
   291  type Edit struct {
   292  	UserID    string       `json:"userid"`    // Unique user ID
   293  	State     RecordStateT `json:"state"`     // Record state
   294  	Token     string       `json:"token"`     // Record token
   295  	ParentID  uint32       `json:"parentid"`  // Parent comment ID
   296  	CommentID uint32       `json:"commentid"` // Comment ID
   297  	Comment   string       `json:"comment"`   // Comment text
   298  	PublicKey string       `json:"publickey"` // Pubkey used for Signature
   299  	Signature string       `json:"signature"` // Client signature
   300  
   301  	// Optional fields to be used freely
   302  	ExtraData     string `json:"extradata,omitempty"`
   303  	ExtraDataHint string `json:"extradatahint,omitempty"`
   304  }
   305  
   306  // EditReply is the reply to the Edit command.
   307  type EditReply struct {
   308  	Comment Comment `json:"comment"`
   309  }
   310  
   311  // VoteT represents a comment upvote/downvote.
   312  type VoteT int32
   313  
   314  const (
   315  	// VoteInvalid is an invalid comment vote.
   316  	VoteInvalid VoteT = 0
   317  
   318  	// VoteDownvote represents a comment downvote.
   319  	VoteDownvote VoteT = -1
   320  
   321  	// VoteUpvote represents a comment upvote.
   322  	VoteUpvote VoteT = 1
   323  )
   324  
   325  // Vote casts a comment vote (upvote or downvote). Votes can only be cast
   326  // on vetted records.
   327  //
   328  // The effect of a new vote on a comment score depends on the previous vote
   329  // from that user ID. Example, a user upvotes a comment that they have already
   330  // upvoted, the resulting vote score is 0 due to the second upvote removing the
   331  // original upvote.
   332  //
   333  // PublicKey is the user's public key that is used to verify the signature.
   334  //
   335  // Signature is the user signature of the:
   336  // State + Token + CommentID + Vote
   337  //
   338  // The PublicKey and Signature are hex encoded and use the
   339  // ed25519 signature scheme.
   340  type Vote struct {
   341  	State     RecordStateT `json:"state"`
   342  	Token     string       `json:"token"`
   343  	CommentID uint32       `json:"commentid"`
   344  	Vote      VoteT        `json:"vote"`
   345  	PublicKey string       `json:"publickey"`
   346  	Signature string       `json:"signature"`
   347  }
   348  
   349  // VoteReply is the reply to the Vote command.
   350  type VoteReply struct {
   351  	Downvotes uint64 `json:"downvotes"` // Tolal downvotes on comment
   352  	Upvotes   uint64 `json:"upvotes"`   // Total upvotes on comment
   353  	Timestamp int64  `json:"timestamp"` // Received UNIX timestamp
   354  	Receipt   string `json:"receipt"`   // Server sig of client sig
   355  }
   356  
   357  // Del permanently deletes the provided comment. Only admins can delete
   358  // comments. A reason must be given for the deletion.
   359  //
   360  // PublicKey is the user's public key that is used to verify the signature.
   361  //
   362  // Signature is the user signature of the:
   363  // State + Token + CommentID + Reason
   364  //
   365  // The PublicKey and Signature are hex encoded and use the
   366  // ed25519 signature scheme.
   367  type Del struct {
   368  	State     RecordStateT `json:"state"`
   369  	Token     string       `json:"token"`
   370  	CommentID uint32       `json:"commentid"`
   371  	Reason    string       `json:"reason"`
   372  	PublicKey string       `json:"publickey"`
   373  	Signature string       `json:"signature"`
   374  }
   375  
   376  // DelReply is the reply to the Del command.
   377  type DelReply struct {
   378  	Comment Comment `json:"comment"`
   379  }
   380  
   381  const (
   382  	// CountPageSize is the maximum number of tokens that can be
   383  	// included in the Count command.
   384  	//
   385  	// NOTE: This is DEPRECATED and will be deleted as part of the next major
   386  	// release. Use the API's Policy route to retrieve the routes page sizes.
   387  	CountPageSize uint32 = 10
   388  )
   389  
   390  // Count requests the number of comments on that have been made on the given
   391  // records. If a record is not found for a token then it will not be included
   392  // in the returned map.
   393  type Count struct {
   394  	Tokens []string `json:"tokens"`
   395  }
   396  
   397  // CountReply is the reply to the count command.
   398  type CountReply struct {
   399  	Counts map[string]uint32 `json:"counts"`
   400  }
   401  
   402  // Comments requests a record's comments.
   403  type Comments struct {
   404  	Token string `json:"token"`
   405  }
   406  
   407  // CommentsReply is the reply to the comments command.
   408  type CommentsReply struct {
   409  	Comments []Comment `json:"comments"`
   410  }
   411  
   412  // Votes retrieves the record's comment votes that meet the provided filtering
   413  // criteria. If no filtering criteria is provided then it rerieves all comment
   414  // votes. This command is paginated, if no page is provided, then the first
   415  // page is returned. If the requested page does not exist an empty page
   416  // is returned.
   417  type Votes struct {
   418  	Token  string `json:"token"`
   419  	UserID string `json:"userid,omitempty"`
   420  	Page   uint32 `json:"page,omitempty"`
   421  }
   422  
   423  // VotesReply is the reply to the Votes command.
   424  type VotesReply struct {
   425  	Votes []CommentVote `json:"votes"`
   426  }
   427  
   428  // Proof contains an inclusion proof for the digest in the merkle root. All
   429  // digests are hex encoded SHA256 digests.
   430  //
   431  // The ExtraData field is used by certain types of proofs to include additional
   432  // data that is required to validate the proof.
   433  type Proof struct {
   434  	Type       string   `json:"type"`
   435  	Digest     string   `json:"digest"`
   436  	MerkleRoot string   `json:"merkleroot"`
   437  	MerklePath []string `json:"merklepath"`
   438  	ExtraData  string   `json:"extradata"` // JSON encoded
   439  }
   440  
   441  // Timestamp contains all of the data required to verify that a piece of data
   442  // was timestamped onto the decred blockchain.
   443  //
   444  // All digests are hex encoded SHA256 digests. The merkle root can be found in
   445  // the OP_RETURN of the specified DCR transaction.
   446  //
   447  // TxID, MerkleRoot, and Proofs will only be populated once the merkle root has
   448  // been included in a DCR tx and the tx has 6 confirmations. The Data field
   449  // will not be populated if the data has been censored.
   450  type Timestamp struct {
   451  	Data       string  `json:"data"` // JSON encoded
   452  	Digest     string  `json:"digest"`
   453  	TxID       string  `json:"txid"`
   454  	MerkleRoot string  `json:"merkleroot"`
   455  	Proofs     []Proof `json:"proofs"`
   456  }
   457  
   458  const (
   459  	// TimestampsPageSize is the maximum number of comment timestamps
   460  	// that can be requests at any one time.
   461  	//
   462  	// NOTE: This is DEPRECATED and will be deleted as part of the next major
   463  	// release. Use the API's Policy route to retrieve the routes page sizes.
   464  	TimestampsPageSize uint32 = 100
   465  )
   466  
   467  // CommentTimestamp contains the timestamps for the full history of a single
   468  // comment.
   469  //
   470  // A CommentAdd is the comments plugin structure that is saved to disk anytime
   471  // a comment is created or edited. This structure is what will be timestamped.
   472  // The data payload of a timestamp in the Adds field will contain a JSON
   473  // encoded CommentAdd. See the politeiad comments plugin API for more details
   474  // on a CommentAdd.
   475  //
   476  // A CommentDel is the comments plugin structure that is saved to disk anytime
   477  // a comment is deleted. This structure is what will be timestamped. The data
   478  // payload of a timestamp in the Del field will contain a JSON encoded
   479  // CommentDel. See the politeiad comments plugin API for more details on a
   480  // CommentDel.
   481  type CommentTimestamp struct {
   482  	Adds []Timestamp `json:"adds"`
   483  	Del  *Timestamp  `json:"del,omitempty"`
   484  }
   485  
   486  // Timestamps requests the timestamps for the comments of a record.
   487  type Timestamps struct {
   488  	Token      string   `json:"token"`
   489  	CommentIDs []uint32 `json:"commentids"`
   490  }
   491  
   492  // TimestampsReply is the reply to the Timestamps command.
   493  type TimestampsReply struct {
   494  	// map[commentID]CommentTimestamp
   495  	Comments map[uint32]CommentTimestamp `json:"comments"`
   496  }