github.com/decred/politeia@v1.4.0/politeiad/backendv2/tstorebe/tstore/recordindex.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 tstore 6 7 import ( 8 "bytes" 9 "encoding/base64" 10 "encoding/hex" 11 "encoding/json" 12 "fmt" 13 "sort" 14 15 backend "github.com/decred/politeia/politeiad/backendv2" 16 "github.com/decred/politeia/politeiad/backendv2/tstorebe/store" 17 "github.com/decred/politeia/politeiad/backendv2/tstorebe/tlog" 18 "github.com/decred/politeia/util" 19 "github.com/google/trillian" 20 "google.golang.org/grpc/codes" 21 ) 22 23 // recordIndex contains the merkle leaf hashes of all the record content leaves 24 // for a specific record version and iteration. The record index can be used to 25 // lookup the trillian log leaves for the record content and the log leaves can 26 // be used to lookup the kv store blobs. 27 // 28 // A record is updated in three steps: 29 // 30 // 1. Record content is saved to the kv store. 31 // 32 // 2. A trillian leaf is created for each piece of record content. The kv store 33 // key for each piece of content is stuffed into the LogLeaf.ExtraData 34 // field. The leaves are appended onto the trillian tree. 35 // 36 // 3. If there are failures in steps 1 or 2 for any of the blobs then the 37 // update will exit without completing. No unwinding is performed. Blobs 38 // will be left in the kv store as orphaned blobs. The trillian tree is 39 // append-only so once a leaf is appended, it's there permanently. If steps 40 // 1 and 2 are successful then a recordIndex is created, saved to the kv 41 // store, and appended onto the trillian tree. 42 // 43 // Appending a recordIndex onto the trillian tree is the last operation that 44 // occurs during a record update. If a recordIndex exists in the tree then the 45 // update is considered successful. Any record content leaves that are not part 46 // of a recordIndex are considered to be orphaned and can be disregarded. 47 type recordIndex struct { 48 State backend.StateT `json:"state"` 49 50 // Version represents the version of the record. The version is 51 // only incremented when the record files are updated. Metadata 52 // only updates do no increment the version. 53 Version uint32 `json:"version"` 54 55 // Iteration represents the iteration of the record. The iteration is 56 // incremented anytime any record content changes. This includes file 57 // changes that bump the version, metadata stream only updates that 58 // don't bump the version, and status changes. 59 Iteration uint32 `json:"iteration"` 60 61 // The following fields contain the merkle leaf hashes of the trillian 62 // log leaves for the record content. The merkle leaf hash can be used 63 // to lookup the log leaf. The log leaf ExtraData field contains the 64 // key for the record content in the key-value store. 65 RecordMetadata []byte `json:"recordmetadata"` 66 Files map[string][]byte `json:"files"` // [filename]merkle 67 68 // [pluginID][streamID]merkle 69 Metadata map[string]map[uint32][]byte `json:"metadata"` 70 71 // Frozen is used to indicate that the tree for this record has been 72 // frozen. This happens as a result of certain record status changes. 73 // The only thing that can be appended onto a frozen tree is one 74 // additional anchor record. Once a frozen tree has been anchored, the 75 // tstore fsck function will update the status of the tree to frozen in 76 // trillian, at which point trillian will not allow any additional 77 // leaves to be appended onto the tree. 78 Frozen bool `json:"frozen,omitempty"` 79 } 80 81 // recordIndexSave saves a record index to tstore. 82 func (t *Tstore) recordIndexSave(treeID int64, idx recordIndex) error { 83 // Only vetted data should be saved plain text 84 var encrypt bool 85 switch idx.State { 86 case backend.StateUnvetted: 87 encrypt = true 88 case backend.StateVetted: 89 // Save plain text 90 encrypt = false 91 default: 92 // Something is wrong 93 e := fmt.Sprintf("invalid record state %v %v", 94 treeID, idx.State) 95 panic(e) 96 } 97 98 log.Debugf("Saving record index") 99 100 // Save record index to the store 101 be, err := convertBlobEntryFromRecordIndex(idx) 102 if err != nil { 103 return err 104 } 105 b, err := store.Blobify(*be) 106 if err != nil { 107 return err 108 } 109 key := storeKeyNew(encrypt) 110 kv := map[string][]byte{key: b} 111 err = t.store.Put(kv, encrypt) 112 if err != nil { 113 return fmt.Errorf("store Put: %v", err) 114 } 115 116 // Append record index leaf to trillian tree 117 d, err := hex.DecodeString(be.Digest) 118 if err != nil { 119 return err 120 } 121 extraData, err := extraDataEncode(key, 122 dataDescriptorRecordIndex, idx.State) 123 if err != nil { 124 return err 125 } 126 leaves := []*trillian.LogLeaf{ 127 tlog.NewLogLeaf(d, extraData), 128 } 129 queued, _, err := t.tlog.LeavesAppend(treeID, leaves) 130 if err != nil { 131 return fmt.Errorf("LeavesAppend: %v", err) 132 } 133 if len(queued) != 1 { 134 return fmt.Errorf("wrong number of queud leaves: got %v, "+ 135 "want 1", len(queued)) 136 } 137 failed := make([]string, 0, len(queued)) 138 for _, v := range queued { 139 c := codes.Code(v.QueuedLeaf.GetStatus().GetCode()) 140 if c != codes.OK { 141 failed = append(failed, fmt.Sprintf("%v", c)) 142 } 143 } 144 if len(failed) > 0 { 145 return fmt.Errorf("append leaves failed: %v", failed) 146 } 147 148 return nil 149 } 150 151 // recordIndexes returns all record indexes found in the provided trillian 152 // leaves. 153 func (t *Tstore) recordIndexes(leaves []*trillian.LogLeaf) ([]recordIndex, error) { 154 // Walk the leaves and compile the keys for all record indexes. Once a 155 // record is made vetted the record history is considered to restart. 156 // If any vetted indexes exist, ignore all unvetted indexes. 157 var ( 158 keysUnvetted = make([]string, 0, 256) 159 keysVetted = make([]string, 0, 256) 160 ) 161 for _, v := range leaves { 162 ed, err := extraDataDecode(v.ExtraData) 163 if err != nil { 164 return nil, err 165 } 166 if ed.Desc != dataDescriptorRecordIndex { 167 continue 168 } 169 // This is a record index leaf 170 switch ed.State { 171 case backend.StateUnvetted: 172 keysUnvetted = append(keysUnvetted, ed.storeKey()) 173 case backend.StateVetted: 174 keysVetted = append(keysVetted, ed.storeKey()) 175 default: 176 // Should not happen 177 return nil, fmt.Errorf("invalid extra data state: "+ 178 "%v %v", v.LeafIndex, ed.State) 179 } 180 } 181 keys := keysUnvetted 182 if len(keysVetted) > 0 { 183 keys = keysVetted 184 } 185 if len(keys) == 0 { 186 // No records have been added to this tree yet 187 return nil, backend.ErrRecordNotFound 188 } 189 190 // Get record indexes from store 191 blobs, err := t.store.Get(keys) 192 if err != nil { 193 return nil, fmt.Errorf("store Get: %v", err) 194 } 195 missing := make([]string, 0, len(keys)) 196 for _, v := range keys { 197 if _, ok := blobs[v]; !ok { 198 missing = append(missing, v) 199 } 200 } 201 if len(missing) > 0 { 202 return nil, fmt.Errorf("record index not found: %v", missing) 203 } 204 205 var ( 206 unvetted = make([]recordIndex, 0, len(blobs)) 207 vetted = make([]recordIndex, 0, len(blobs)) 208 ) 209 for _, v := range blobs { 210 be, err := store.Deblob(v) 211 if err != nil { 212 return nil, err 213 } 214 ri, err := convertRecordIndexFromBlobEntry(*be) 215 if err != nil { 216 return nil, err 217 } 218 switch ri.State { 219 case backend.StateUnvetted: 220 unvetted = append(unvetted, *ri) 221 case backend.StateVetted: 222 vetted = append(vetted, *ri) 223 default: 224 return nil, fmt.Errorf("invalid record index state: %v", 225 ri.State) 226 } 227 } 228 229 indexes := unvetted 230 if len(vetted) > 0 { 231 indexes = vetted 232 } 233 234 // Sort indexes by iteration, smallest to largets. The leaves ordering 235 // was not preserved in the returned blobs map. 236 sort.SliceStable(indexes, func(i, j int) bool { 237 return indexes[i].Iteration < indexes[j].Iteration 238 }) 239 240 // Sanity check. Index iterations should start with 1 and be 241 // sequential. Index versions should start with 1 and also be 242 // sequential, but duplicate versions can exist as long as the 243 // iteration has been incremented. 244 var versionPrev uint32 245 var i uint32 = 1 246 for _, v := range indexes { 247 if v.Iteration != i { 248 return nil, fmt.Errorf("invalid record index "+ 249 "iteration: got %v, want %v", v.Iteration, i) 250 } 251 diff := v.Version - versionPrev 252 if diff != 0 && diff != 1 { 253 return nil, fmt.Errorf("invalid record index version: "+ 254 "curr version %v, prev version %v", 255 v.Version, versionPrev) 256 } 257 258 i++ 259 versionPrev = v.Version 260 } 261 262 return indexes, nil 263 } 264 265 // recordIndex returns the specified version of a record index for a slice of 266 // trillian leaves. 267 func (t *Tstore) recordIndex(leaves []*trillian.LogLeaf, version uint32) (*recordIndex, error) { 268 indexes, err := t.recordIndexes(leaves) 269 if err != nil { 270 return nil, err 271 } 272 return parseRecordIndex(indexes, version) 273 } 274 275 // recordIndexLatest returns the most recent record index for a slice of 276 // trillian leaves. 277 func (t *Tstore) recordIndexLatest(leaves []*trillian.LogLeaf) (*recordIndex, error) { 278 return t.recordIndex(leaves, 0) 279 } 280 281 // parseRecordIndex takes a list of record indexes and returns the most recent 282 // iteration of the specified version. A version of 0 indicates that the latest 283 // version should be returned. A backend.ErrRecordNotFound is returned if the 284 // provided version does not exist. 285 func parseRecordIndex(indexes []recordIndex, version uint32) (*recordIndex, error) { 286 if len(indexes) == 0 { 287 return nil, backend.ErrRecordNotFound 288 } 289 290 // This function should only be used on record indexes that share the 291 // same record state. We would not want to accidentally return an 292 // unvetted index if the record is vetted. It is the responsibility of 293 // the caller to only provide a single state. 294 state := indexes[0].State 295 if state == backend.StateInvalid { 296 return nil, fmt.Errorf("invalid record index state: %v", state) 297 } 298 for _, v := range indexes { 299 if v.State != state { 300 return nil, fmt.Errorf("multiple record index states "+ 301 "found: %v %v", v.State, state) 302 } 303 } 304 305 // Return the record index for the specified version 306 var ri *recordIndex 307 if version == 0 { 308 // A version of 0 indicates that the most recent version should 309 // be returned. 310 ri = &indexes[len(indexes)-1] 311 } else { 312 // Walk the indexes backwards so the most recent iteration of 313 // the specified version is selected. 314 for i := len(indexes) - 1; i >= 0; i-- { 315 r := indexes[i] 316 if r.Version == version { 317 ri = &r 318 break 319 } 320 } 321 } 322 if ri == nil { 323 // The specified version does not exist 324 return nil, backend.ErrRecordNotFound 325 } 326 327 return ri, nil 328 } 329 330 func convertBlobEntryFromRecordIndex(ri recordIndex) (*store.BlobEntry, error) { 331 data, err := json.Marshal(ri) 332 if err != nil { 333 return nil, err 334 } 335 hint, err := json.Marshal( 336 store.DataDescriptor{ 337 Type: store.DataTypeStructure, 338 Descriptor: dataDescriptorRecordIndex, 339 }) 340 if err != nil { 341 return nil, err 342 } 343 be := store.NewBlobEntry(hint, data) 344 return &be, nil 345 } 346 347 func convertRecordIndexFromBlobEntry(be store.BlobEntry) (*recordIndex, error) { 348 // Decode and validate data hint 349 b, err := base64.StdEncoding.DecodeString(be.DataHint) 350 if err != nil { 351 return nil, fmt.Errorf("decode DataHint: %v", err) 352 } 353 var dd store.DataDescriptor 354 err = json.Unmarshal(b, &dd) 355 if err != nil { 356 return nil, fmt.Errorf("unmarshal DataHint: %v", err) 357 } 358 if dd.Descriptor != dataDescriptorRecordIndex { 359 return nil, fmt.Errorf("unexpected data descriptor: got %v, "+ 360 "want %v", dd.Descriptor, dataDescriptorRecordIndex) 361 } 362 363 // Decode data 364 b, err = base64.StdEncoding.DecodeString(be.Data) 365 if err != nil { 366 return nil, fmt.Errorf("decode Data: %v", err) 367 } 368 digest, err := hex.DecodeString(be.Digest) 369 if err != nil { 370 return nil, fmt.Errorf("decode digest: %v", err) 371 } 372 if !bytes.Equal(util.Digest(b), digest) { 373 return nil, fmt.Errorf("data is not coherent; got %x, want %x", 374 util.Digest(b), digest) 375 } 376 var ri recordIndex 377 err = json.Unmarshal(b, &ri) 378 if err != nil { 379 return nil, fmt.Errorf("unmarshal recordIndex: %v", err) 380 } 381 382 return &ri, nil 383 }