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 }