github.com/decred/politeia@v1.4.0/politeiad/backendv2/backendv2.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 backendv2 6 7 import ( 8 "errors" 9 "fmt" 10 11 "github.com/decred/politeia/politeiad/api/v1/identity" 12 ) 13 14 var ( 15 // ErrShutdown is returned when the backend is shutdown. 16 ErrShutdown = errors.New("backend is shutdown") 17 18 // ErrTokenInvalid is returned when a token is invalid. 19 ErrTokenInvalid = errors.New("token is invalid") 20 21 // ErrRecordNotFound is returned when a record is not found. 22 ErrRecordNotFound = errors.New("record not found") 23 24 // ErrRecordLocked is returned when a record is attempted to be 25 // updated but the record status does not allow further updates. 26 ErrRecordLocked = errors.New("record is locked") 27 28 // ErrNoRecordChanges is returned when a record update does not 29 // contain any changes. 30 ErrNoRecordChanges = errors.New("no record changes") 31 32 // ErrPluginIDInvalid is returned when a invalid plugin ID is used. 33 ErrPluginIDInvalid = errors.New("plugin id invalid") 34 35 // ErrPluginCmdInvalid is returned when a invalid plugin command is 36 // used. 37 ErrPluginCmdInvalid = errors.New("plugin command invalid") 38 39 // ErrDuplicatePayload is returned when a duplicate payload is sent to 40 // a plugin, where it tries to write data that already exists. Timestamp 41 // data relies on the hash of the payload, therefore duplicate payloads 42 // are not allowed since they will cause collisions. 43 ErrDuplicatePayload = errors.New("duplicate payload") 44 ) 45 46 // StateT represents the state of a record. 47 type StateT uint32 48 49 const ( 50 // StateInvalid is an invalid record state. 51 StateInvalid StateT = 0 52 53 // StateUnvetted indicates a record has not been made public. 54 StateUnvetted StateT = 1 55 56 // StateVetted indicates a record has been made public. 57 StateVetted StateT = 2 58 59 // StateLast used for unit test only. 60 StateLast StateT = 3 61 ) 62 63 var ( 64 // States contains the human readable record states. 65 States = map[StateT]string{ 66 StateInvalid: "invalid", 67 StateUnvetted: "unvetted", 68 StateVetted: "vetted", 69 } 70 ) 71 72 // StatusT represents the status of a record. 73 type StatusT uint32 74 75 const ( 76 // StatusInvalid is an invalid status code. 77 StatusInvalid StatusT = 0 78 79 // StatusUnreviewed indicates a record has not been made public 80 // yet. The state of an unreviewed record will always be unvetted. 81 StatusUnreviewed StatusT = 1 82 83 // StatusPublic indicates a record has been made public. The state 84 // of a public record will always be vetted. 85 StatusPublic StatusT = 2 86 87 // StatusCensored indicates a record has been censored. A censored 88 // record is locked from any further updates and all record content 89 // is permanently deleted. A censored record can have a state of 90 // either unvetted or vetted. 91 StatusCensored StatusT = 3 92 93 // StatusArchived indicates a record has been archived. An archived 94 // record is locked from any further updates. An archived record 95 // have a state of either unvetted or vetted. 96 StatusArchived StatusT = 4 97 98 // StatusLast is used for unit test validation of human readable 99 // errors. 100 StatusLast StatusT = 5 101 ) 102 103 var ( 104 // Statuses contains the human readable record statuses. 105 Statuses = map[StatusT]string{ 106 StatusInvalid: "invalid", 107 StatusUnreviewed: "unreviewed", 108 StatusPublic: "public", 109 StatusCensored: "censored", 110 StatusArchived: "archived", 111 } 112 ) 113 114 // StatusTransitionError indicates an invalid record status transition. 115 type StatusTransitionError struct { 116 From StatusT 117 To StatusT 118 } 119 120 // Error satisfies the error interface. 121 func (s StatusTransitionError) Error() string { 122 return fmt.Sprintf("invalid record status transition %v (%v) -> %v (%v)", 123 Statuses[s.From], s.From, Statuses[s.To], s.To) 124 } 125 126 // RecordMetadata represents metadata that is created by the backend on record 127 // submission and updates. 128 // 129 // The record version is incremented anytime the record files are updated. The 130 // record iteration is incremented anytime record files, metadata, or the 131 // record status are updated. 132 type RecordMetadata struct { 133 Token string `json:"token"` // Record identifier, hex encoded 134 Version uint32 `json:"version"` // Record version 135 Iteration uint32 `json:"iteration"` // Record iteration 136 State StateT `json:"state"` // Unvetted or vetted 137 Status StatusT `json:"status"` // Record status 138 Timestamp int64 `json:"timestamp"` // Last updated 139 Merkle string `json:"merkle"` // Merkle root of record files 140 } 141 142 // MetadataStream describes a single metada stream. 143 type MetadataStream struct { 144 PluginID string `json:"pluginid"` // Plugin identity 145 StreamID uint32 `json:"streamid"` // Stream identity 146 Payload string `json:"payload"` // JSON encoded metadata 147 } 148 149 // File represents a record file. 150 type File struct { 151 Name string `json:"name"` // Basename of the file 152 MIME string `json:"mime"` // MIME type 153 Digest string `json:"digest"` // SHA256 of decoded Payload 154 Payload string `json:"payload"` // Base64 encoded file payload 155 } 156 157 // Record is a permanent record that includes the submitted files, metadata and 158 // internal metadata. 159 type Record struct { 160 RecordMetadata RecordMetadata `json:"recordmetadata"` 161 Metadata []MetadataStream `json:"metadata"` 162 Files []File `json:"files"` 163 } 164 165 // ContentErrorCodeT represents a record content error. 166 type ContentErrorCodeT uint32 167 168 const ( 169 ContentErrorInvalid ContentErrorCodeT = 0 170 ContentErrorMetadataStreamInvalid ContentErrorCodeT = 1 171 ContentErrorMetadataStreamDuplicate ContentErrorCodeT = 2 172 ContentErrorFilesEmpty ContentErrorCodeT = 3 173 ContentErrorFileNameInvalid ContentErrorCodeT = 4 174 ContentErrorFileNameDuplicate ContentErrorCodeT = 5 175 ContentErrorFileDigestInvalid ContentErrorCodeT = 6 176 ContentErrorFilePayloadInvalid ContentErrorCodeT = 7 177 ContentErrorFileMIMETypeInvalid ContentErrorCodeT = 8 178 ContentErrorFileMIMETypeUnsupported ContentErrorCodeT = 9 179 ) 180 181 // ContentError is returned when the content of a record does not pass 182 // validation. 183 type ContentError struct { 184 ErrorCode ContentErrorCodeT `json:"errorcode"` 185 ErrorContext string `json:"errorcontext"` 186 } 187 188 // Error satisfies the error interface. 189 func (e ContentError) Error() string { 190 return fmt.Sprintf("content error code: %v", e.ErrorCode) 191 } 192 193 // RecordRequest is used to request a record. It gives the caller granular 194 // control over what is returned. The only required field is the token. All 195 // other fields are optional. All record files are returned by default unless 196 // one of the file arguments is provided. 197 // 198 // Version is used to request a specific version of a record. If no version is 199 // provided then the most recent version of the record will be returned. 200 // 201 // Filenames can be used to request specific files. If filenames is not empty 202 // then the specified files will be the only files that are returned. 203 // 204 // OmitAllFiles can be used to retrieve a record without any of the record 205 // files. This supersedes the filenames argument. 206 type RecordRequest struct { 207 Token []byte 208 Version uint32 209 Filenames []string 210 OmitAllFiles bool 211 } 212 213 // Proof contains an inclusion proof for the digest in the merkle root. All 214 // digests are hex encoded SHA256 digests. 215 // 216 // The ExtraData field is used by certain types of proofs to include additional 217 // data that is required to validate the proof. 218 type Proof struct { 219 Type string 220 Digest string 221 MerkleRoot string 222 MerklePath []string 223 ExtraData string // JSON encoded 224 } 225 226 // Timestamp contains all of the data required to verify that a piece of record 227 // content was timestamped onto the decred blockchain. 228 // 229 // All digests are hex encoded SHA256 digests. The merkle root can be found in 230 // the OP_RETURN of the specified DCR transaction. 231 // 232 // TxID, MerkleRoot, and Proofs will only be populated once the merkle root has 233 // been included in a DCR tx and the tx has 6 confirmations. The Data field 234 // will not be populated if the data has been censored. 235 type Timestamp struct { 236 Data string // JSON encoded 237 Digest string 238 TxID string 239 MerkleRoot string 240 Proofs []Proof 241 } 242 243 // RecordTimestamps contains a Timestamp for all record data. 244 type RecordTimestamps struct { 245 RecordMetadata Timestamp 246 247 Metadata map[string]map[uint32]Timestamp // [pluginID][streamID]Timestamp 248 Files map[string]Timestamp // map[filename]Timestamp 249 } 250 251 // Inventory contains the tokens of records in the inventory categorized by 252 // record state and record status. Tokens are sorted by the timestamp of the 253 // status change from newest to oldest. 254 type Inventory struct { 255 Unvetted map[StatusT][]string 256 Vetted map[StatusT][]string 257 } 258 259 // PluginSetting represents a configurable plugin setting. 260 // 261 // The value can either contain a single value or multiple values. Multiple 262 // values will be formatted as a JSON encoded []string. 263 type PluginSetting struct { 264 Key string // Name of setting 265 Value string // Value of setting 266 } 267 268 // Plugin describes a plugin and its settings. 269 type Plugin struct { 270 ID string 271 Settings []PluginSetting 272 273 // Identity contains the full identity that the plugin uses to 274 // create receipts, i.e. signatures of user provided data that 275 // prove the backend received and processed a plugin command. 276 Identity *identity.FullIdentity 277 } 278 279 // PluginError represents an error that occurred during plugin execution that 280 // was caused by the user. 281 type PluginError struct { 282 PluginID string 283 ErrorCode uint32 284 ErrorContext string 285 } 286 287 // Error satisfies the error interface. 288 func (e PluginError) Error() string { 289 return fmt.Sprintf("%v plugin error code %v", 290 e.PluginID, e.ErrorCode) 291 } 292 293 // Backend provides an API for interacting with records in the backend. 294 type Backend interface { 295 // RecordNew creates a new record. 296 RecordNew([]MetadataStream, []File) (*Record, error) 297 298 // RecordEdit edits an existing record. 299 RecordEdit(token []byte, mdAppend, mdOverwrite []MetadataStream, 300 filesAdd []File, filesDel []string) (*Record, error) 301 302 // RecordEditMetadata edits the metadata of a record without 303 // editing any record files. 304 RecordEditMetadata(token []byte, mdAppend, 305 mdOverwrite []MetadataStream) (*Record, error) 306 307 // RecordSetStatus sets the status of a record. 308 RecordSetStatus(token []byte, s StatusT, mdAppend, 309 mdOverwrite []MetadataStream) (*Record, error) 310 311 // RecordExists returns whether a record exists. 312 RecordExists(token []byte) bool 313 314 // RecordTimestamps returns the timestamps for a record. If no 315 // version is provided then timestamps for the most recent version 316 // will be returned. 317 RecordTimestamps(token []byte, version uint32) (*RecordTimestamps, error) 318 319 // Records retreives a batch of records. If a record is not found 320 // then it is simply not included in the returned map. An error is 321 // not returned. 322 Records(reqs []RecordRequest) (map[string]Record, error) 323 324 // Inventory returns the tokens of records in the inventory 325 // categorized by record state and record status. The tokens are 326 // ordered by the timestamp of their most recent status change, 327 // sorted from newest to oldest. 328 // 329 // The state, status, and page arguments can be provided to request 330 // a specific page of record tokens. 331 // 332 // If no status is provided then the most recent page of tokens for 333 // all statuses will be returned. All other arguments are ignored. 334 Inventory(state StateT, status StatusT, pageSize, 335 pageNumber uint32) (*Inventory, error) 336 337 // InventoryOrdered returns a page of record tokens ordered by the 338 // timestamp of their most recent status change from newest to 339 // oldest. The returned tokens will include all record statuses. 340 InventoryOrdered(s StateT, pageSize, pageNumber uint32) ([]string, error) 341 342 // PluginRegister registers a plugin. 343 PluginRegister(Plugin) error 344 345 // PluginSetup performs any required plugin setup. 346 PluginSetup(pluginID string) error 347 348 // PluginRead executes a read-only plugin command. 349 PluginRead(token []byte, pluginID, pluginCmd, 350 payload string) (string, error) 351 352 // PluginWrite executes a plugin command that writes data. 353 PluginWrite(token []byte, pluginID, pluginCmd, 354 payload string) (string, error) 355 356 // PluginInventory returns all registered plugins. 357 PluginInventory() []Plugin 358 359 // Fsck performs a synchronous filesystem check that verifies 360 // the coherency of record and plugin data and caches. 361 Fsck() error 362 363 // Close performs cleanup of the backend. 364 Close() 365 }