github.com/decred/politeia@v1.4.0/politeiad/backendv2/tstorebe/tstore/tstoreclient.go (about) 1 // Copyright (c) 2020-2022 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 "strings" 14 15 backend "github.com/decred/politeia/politeiad/backendv2" 16 "github.com/decred/politeia/politeiad/backendv2/tstorebe/plugins" 17 "github.com/decred/politeia/politeiad/backendv2/tstorebe/store" 18 "github.com/decred/politeia/politeiad/backendv2/tstorebe/tlog" 19 "github.com/google/trillian" 20 "google.golang.org/grpc/codes" 21 ) 22 23 var ( 24 _ plugins.TstoreClient = (*tstoreClient)(nil) 25 ) 26 27 // tstoreClient satisfies the plugin TstoreClient interface. 28 type tstoreClient struct { 29 pluginID string 30 tstore *Tstore 31 } 32 33 // NewTstoreClient returns a new TstoreClient interface that is backed by a 34 // tstoreClient structure. 35 func NewTstoreClient(tstore *Tstore, pluginID string) plugins.TstoreClient { 36 return &tstoreClient{ 37 pluginID: pluginID, 38 tstore: tstore, 39 } 40 } 41 42 // BlobSave saves a BlobEntry to the tstore instance. The BlobEntry will be 43 // encrypted prior to being written to disk if the record is unvetted. The 44 // digest of the data, i.e. BlobEntry.Digest, can be thought of as the blob ID 45 // and can be used to get/del the blob from tstore. 46 // 47 // This function satisfies the plugins TstoreClient interface. 48 func (t *tstoreClient) BlobSave(token []byte, be store.BlobEntry) error { 49 log.Tracef("BlobSave: %x", token) 50 51 // Verify tree is not frozen 52 treeID := treeIDFromToken(token) 53 leaves, err := t.tstore.leavesAll(treeID) 54 if err != nil { 55 return err 56 } 57 idx, err := t.tstore.recordIndexLatest(leaves) 58 if err != nil { 59 return err 60 } 61 if idx.Frozen { 62 // The tree is frozen. The record is locked. 63 return backend.ErrRecordLocked 64 } 65 66 // Parse the data descriptor 67 b, err := base64.StdEncoding.DecodeString(be.DataHint) 68 if err != nil { 69 return err 70 } 71 var dd store.DataDescriptor 72 err = json.Unmarshal(b, &dd) 73 if err != nil { 74 return err 75 } 76 77 // Only vetted data should be saved plain text 78 var encrypt bool 79 switch idx.State { 80 case backend.StateUnvetted: 81 encrypt = true 82 case backend.StateVetted: 83 // Save plain text 84 encrypt = false 85 default: 86 // Something is wrong 87 panic(fmt.Sprintf("invalid record state %v %v", treeID, idx.State)) 88 } 89 90 // Prepare blob and digest 91 digest, err := hex.DecodeString(be.Digest) 92 if err != nil { 93 return err 94 } 95 blob, err := store.Blobify(be) 96 if err != nil { 97 return err 98 } 99 key := storeKeyNew(encrypt) 100 kv := map[string][]byte{key: blob} 101 102 log.Debugf("Saving plugin data blob %v", dd.Descriptor) 103 104 // Save blob to store 105 err = t.tstore.store.Put(kv, encrypt) 106 if err != nil { 107 return fmt.Errorf("store Put: %v", err) 108 } 109 110 // Prepare log leaf 111 extraData, err := extraDataEncode(key, dd.Descriptor, idx.State) 112 if err != nil { 113 return err 114 } 115 leaves = []*trillian.LogLeaf{ 116 tlog.NewLogLeaf(digest, extraData), 117 } 118 119 // Append log leaf to trillian tree 120 queued, _, err := t.tstore.tlog.LeavesAppend(treeID, leaves) 121 if err != nil { 122 return fmt.Errorf("LeavesAppend: %v", err) 123 } 124 if len(queued) != 1 { 125 return fmt.Errorf("wrong queued leaves count: got %v, want 1", 126 len(queued)) 127 } 128 c := codes.Code(queued[0].QueuedLeaf.GetStatus().GetCode()) 129 switch c { 130 case codes.OK: 131 // This is ok; continue 132 case codes.AlreadyExists: 133 return backend.ErrDuplicatePayload 134 default: 135 return fmt.Errorf("queued leaf error: %v", c) 136 } 137 138 return nil 139 } 140 141 // BlobsDel deletes the blobs that correspond to the provided digests. Blobs 142 // can be deleted from both frozen and non-frozen records. 143 // 144 // This function satisfies the plugins TstoreClient interface. 145 func (t *tstoreClient) BlobsDel(token []byte, digests [][]byte) error { 146 log.Tracef("BlobsDel: %x %x", token, digests) 147 148 // Get all tree leaves 149 treeID := treeIDFromToken(token) 150 leaves, err := t.tstore.leavesAll(treeID) 151 if err != nil { 152 return err 153 } 154 155 // Put merkle leaf hashes into a map so that we can tell if a leaf 156 // corresponds to one of the target merkle leaf hashes in O(n) 157 // time. 158 merkleHashes := make(map[string]struct{}, len(digests)) 159 for _, v := range digests { 160 m := hex.EncodeToString(tlog.MerkleLeafHash(v)) 161 merkleHashes[m] = struct{}{} 162 } 163 164 // Aggregate the key-value store keys for the provided merkle leaf 165 // hashes. 166 keys := make([]string, 0, len(digests)) 167 for _, v := range leaves { 168 _, ok := merkleHashes[hex.EncodeToString(v.MerkleLeafHash)] 169 if ok { 170 ed, err := extraDataDecode(v.ExtraData) 171 if err != nil { 172 return err 173 } 174 keys = append(keys, ed.storeKey()) 175 } 176 } 177 178 // Delete file blobs from the store 179 err = t.tstore.store.Del(keys) 180 if err != nil { 181 return fmt.Errorf("store Del: %v", err) 182 } 183 184 return nil 185 } 186 187 // Blobs returns the blobs that correspond to the provided digests. If a blob 188 // does not exist it will not be included in the returned map. If a record 189 // is vetted, only vetted blobs will be returned. 190 // 191 // This function satisfies the plugins TstoreClient interface. 192 func (t *tstoreClient) Blobs(token []byte, digests [][]byte) (map[string]store.BlobEntry, error) { 193 log.Tracef("Blobs: %x %x", token, digests) 194 195 if len(digests) == 0 { 196 return map[string]store.BlobEntry{}, nil 197 } 198 199 // Get leaves 200 treeID := treeIDFromToken(token) 201 leaves, err := t.tstore.leavesAll(treeID) 202 if err != nil { 203 return nil, err 204 } 205 206 // Determine if the record is vetted. If the record is vetted, only 207 // vetted blobs will be returned. 208 isVetted := recordIsVetted(leaves) 209 210 // Put digests into a map 211 ds := make(map[string]struct{}, len(digests)) 212 for _, v := range digests { 213 ds[hex.EncodeToString(v)] = struct{}{} 214 } 215 216 // Find the log leaves for the provided digests. matchedLeaves and 217 // matchedKeys MUST share the same ordering. 218 var ( 219 matchedLeaves = make([]*trillian.LogLeaf, 0, len(digests)) 220 matchedKeys = make([]string, 0, len(digests)) 221 ) 222 for _, v := range leaves { 223 ed, err := extraDataDecode(v.ExtraData) 224 if err != nil { 225 return nil, err 226 } 227 if isVetted && ed.State == backend.StateUnvetted { 228 // We don't return unvetted blobs if the record is vetted 229 continue 230 } 231 232 // Check if this is one of the target digests 233 if _, ok := ds[hex.EncodeToString(v.LeafValue)]; ok { 234 // Its a match! 235 matchedLeaves = append(matchedLeaves, v) 236 matchedKeys = append(matchedKeys, ed.storeKey()) 237 } 238 } 239 if len(matchedKeys) == 0 { 240 return map[string]store.BlobEntry{}, nil 241 } 242 243 // Pull the blobs from the store 244 blobs, err := t.tstore.store.Get(matchedKeys) 245 if err != nil { 246 return nil, fmt.Errorf("store Get: %v", err) 247 } 248 249 // Prepare reply 250 entries := make(map[string]store.BlobEntry, len(matchedKeys)) 251 for i, v := range matchedKeys { 252 b, ok := blobs[v] 253 if !ok { 254 // Blob wasn't found in the store. Skip it. 255 continue 256 } 257 be, err := store.Deblob(b) 258 if err != nil { 259 return nil, err 260 } 261 262 // Get the corresponding digest 263 l := matchedLeaves[i] 264 digest := hex.EncodeToString(l.LeafValue) 265 entries[digest] = *be 266 } 267 268 return entries, nil 269 } 270 271 // BlobsByDataDesc returns all blobs that match the provided data descriptors. 272 // The blobs will be ordered from oldest to newest. If a record is vetted then 273 // only vetted blobs will be returned. 274 // 275 // This function satisfies the plugins TstoreClient interface. 276 func (t *tstoreClient) BlobsByDataDesc(token []byte, dataDesc []string) ([]store.BlobEntry, error) { 277 log.Tracef("BlobsByDataDesc: %x %v", token, dataDesc) 278 279 // Get leaves 280 treeID := treeIDFromToken(token) 281 leaves, err := t.tstore.leavesAll(treeID) 282 if err != nil { 283 return nil, err 284 } 285 286 // Find all matching leaves 287 matches := leavesForDescriptor(leaves, dataDesc) 288 if len(matches) == 0 { 289 return []store.BlobEntry{}, nil 290 } 291 292 // Aggregate the keys of all the matches 293 keys := make([]string, 0, len(matches)) 294 for _, v := range matches { 295 ed, err := extraDataDecode(v.ExtraData) 296 if err != nil { 297 return nil, err 298 } 299 keys = append(keys, ed.storeKey()) 300 } 301 302 // Pull the blobs from the store 303 blobs, err := t.tstore.store.Get(keys) 304 if err != nil { 305 return nil, fmt.Errorf("store Get: %v", err) 306 } 307 if len(blobs) != len(keys) { 308 // One or more blobs were not found 309 return nil, fmt.Errorf("%v/%v blobs not found", 310 len(keys)-len(blobs), len(keys)) 311 } 312 313 // Prepare reply. The blob entries should be in the same order as 314 // the keys, i.e. ordered from oldest to newest. 315 entries := make([]store.BlobEntry, 0, len(keys)) 316 for _, v := range keys { 317 b, ok := blobs[v] 318 if !ok { 319 return nil, fmt.Errorf("blob not found: %v", v) 320 } 321 be, err := store.Deblob(b) 322 if err != nil { 323 return nil, err 324 } 325 entries = append(entries, *be) 326 } 327 328 return entries, nil 329 } 330 331 // DigestsByDataDesc returns the digests of all blobs that match the provided 332 // data descriptor. If a record is vetted then only vetted digests will be 333 // returned. 334 // 335 // This function satisfies the plugins TstoreClient interface. 336 func (t *tstoreClient) DigestsByDataDesc(token []byte, dataDesc []string) ([][]byte, error) { 337 log.Tracef("DigestsByDataDesc: %x %v", token, dataDesc) 338 339 // Get leaves 340 treeID := treeIDFromToken(token) 341 leaves, err := t.tstore.leavesAll(treeID) 342 if err != nil { 343 return nil, err 344 } 345 346 // Find all matching leaves 347 matches := leavesForDescriptor(leaves, dataDesc) 348 349 // Aggregate the digests, i.e. the leaf value, for all the matches 350 digests := make([][]byte, 0, len(matches)) 351 for _, v := range matches { 352 digests = append(digests, v.LeafValue) 353 } 354 355 return digests, nil 356 } 357 358 // Timestamp returns the timestamp for the data blob that corresponds to the 359 // provided digest. If a record is vetted, only vetted timestamps will be 360 // returned. 361 // 362 // This function satisfies the plugins TstoreClient interface. 363 func (t *tstoreClient) Timestamp(token []byte, digest []byte) (*backend.Timestamp, error) { 364 log.Tracef("Timestamp: %x %x", token, digest) 365 366 // Get tree leaves 367 treeID := treeIDFromToken(token) 368 leaves, err := t.tstore.leavesAll(treeID) 369 if err != nil { 370 return nil, err 371 } 372 373 // Determine if the record is vetted 374 isVetted := recordIsVetted(leaves) 375 376 // If the record is vetted we cannot return an unvetted timestamp. 377 // Find the leaf for the digest and verify that its not unvetted. 378 if isVetted { 379 for _, v := range leaves { 380 if !bytes.Equal(v.LeafValue, digest) { 381 // Not the target leaf 382 continue 383 } 384 385 // This is the target leaf. Verify that its vetted. 386 ed, err := extraDataDecode(v.ExtraData) 387 if err != nil { 388 return nil, err 389 } 390 if ed.State != backend.StateVetted { 391 log.Debugf("Caller is requesting an unvetted timestamp " + 392 "for a vetted record; not allowed") 393 return &backend.Timestamp{ 394 Proofs: []backend.Proof{}, 395 }, nil 396 } 397 } 398 } 399 400 // Get merkle leaf hash 401 m := tlog.MerkleLeafHash(digest) 402 403 // Get timestamp 404 return t.tstore.timestamp(treeID, m, leaves) 405 } 406 407 // CachePut saves the provided key-value pairs to the key-value store. It 408 // prefixes the keys with the plugin ID in order to limit the access of the 409 // plugins only to the data they own. 410 // 411 // This function satisfies the plugins TstoreClient interface. 412 func (t *tstoreClient) CachePut(blobs map[string][]byte, encrypt bool) error { 413 log.Tracef("CachePut: %v %v", t.pluginID, encrypt) 414 415 // Prefix keys with pluginID, in order to strict plugins access only to 416 // the data they own. 417 prefixedBlobs := prefixMapKeys(t.pluginID, blobs) 418 419 return t.tstore.store.Put(prefixedBlobs, encrypt) 420 } 421 422 // CacheDel deletes the provided blobs from the key-value store. This 423 // operation is performed atomically. It prefixes the keys with the plugin 424 // ID in order to limit the access of the plugins only to the data they own. 425 // 426 // This function satisfies the plugins TstoreClient interface. 427 func (t *tstoreClient) CacheDel(keys []string) error { 428 log.Tracef("CacheDel: %v %v", t.pluginID, keys) 429 430 // Prefix keys with pluginID, in order to strict plugins access only to 431 // the data they own. 432 pkeys := prefixKeys(t.pluginID, keys) 433 434 return t.tstore.store.Del(pkeys) 435 } 436 437 // CacheGet returns blobs from the key-value store for the provided keys. An 438 // entry will not exist in the returned map if for any blobs that are not 439 // found. It is the responsibility of the caller to ensure a blob 440 // was returned for all provided keys. It prefixes the keys with the plugin 441 // ID in order to limit the access of the plugins only to the data they own. 442 func (t *tstoreClient) CacheGet(keys []string) (map[string][]byte, error) { 443 log.Tracef("CacheGet: %v %v", t.pluginID, keys) 444 445 // Prefix keys with pluginID, in order to strict plugins access only to 446 // the data they own. 447 pkeys := prefixKeys(t.pluginID, keys) 448 449 prefixedBlobs, err := t.tstore.store.Get(pkeys) 450 if err != nil { 451 return nil, err 452 } 453 454 // Delete plugin specific prefix from returned keys. 455 blobs := unprefixMapKeys(t.pluginID, prefixedBlobs) 456 457 return blobs, nil 458 } 459 460 // Record is a wrapper of the tstore Record func. 461 func (t *tstoreClient) Record(token []byte, version uint32) (*backend.Record, error) { 462 return t.tstore.Record(token, version) 463 } 464 465 // RecordLatest is a wrapper of the tstore RecordLatest func. 466 func (t *tstoreClient) RecordLatest(token []byte) (*backend.Record, error) { 467 return t.tstore.RecordLatest(token) 468 } 469 470 // RecordPartial is a wrapper of the tstore RecordPartial func. 471 func (t *tstoreClient) RecordPartial(token []byte, version uint32, filenames []string, omitAllFiles bool) (*backend.Record, error) { 472 return t.tstore.RecordPartial(token, version, filenames, omitAllFiles) 473 } 474 475 // RecordState is a wraper of the tstore RecordState func. 476 func (t *tstoreClient) RecordState(token []byte) (backend.StateT, error) { 477 return t.tstore.RecordState(token) 478 } 479 480 // leavesForDescriptor returns all leaves that have and extra data descriptor 481 // that matches the provided descriptor. If a record is vetted, only vetted 482 // leaves will be returned. 483 func leavesForDescriptor(leaves []*trillian.LogLeaf, descriptors []string) []*trillian.LogLeaf { 484 // Put descriptors into a map for 0(n) lookups 485 desc := make(map[string]struct{}, len(descriptors)) 486 for _, v := range descriptors { 487 desc[v] = struct{}{} 488 } 489 490 // Determine if the record is vetted. If the record is vetted then 491 // only vetted leaves will be returned. 492 isVetted := recordIsVetted(leaves) 493 494 // Walk leaves and aggregate all leaves that match the provided 495 // data descriptor. 496 matches := make([]*trillian.LogLeaf, 0, len(leaves)) 497 for _, v := range leaves { 498 ed, err := extraDataDecode(v.ExtraData) 499 if err != nil { 500 panic(err) 501 } 502 if _, ok := desc[ed.Desc]; !ok { 503 // Not one of the data descriptor we're looking for 504 continue 505 } 506 if isVetted && ed.State != backend.StateVetted { 507 // Unvetted leaf on a vetted record. Don't use it. 508 continue 509 } 510 511 // We have a match! 512 matches = append(matches, v) 513 } 514 515 return matches 516 } 517 518 // prefixMapKeys accepts a map of []byte indexed by string keys, and it 519 // prefixes all the map keys with the given string prefix. 520 func prefixMapKeys(prefix string, m map[string][]byte) map[string][]byte { 521 pm := make(map[string][]byte, len(m)) 522 523 for k, v := range m { 524 pm[prefixKey(prefix, k)] = v 525 } 526 527 return pm 528 } 529 530 // prefixKeys accepts a list of string keys, and it returns the keys prefixed 531 // with the given prefix. 532 func prefixKeys(prefix string, keys []string) []string { 533 pkeys := make([]string, 0, len(keys)) 534 for _, key := range keys { 535 pkeys = append(pkeys, prefixKey(prefix, key)) 536 } 537 538 return pkeys 539 } 540 541 // prefixKey prefixes the given key with given prefix. 542 func prefixKey(prefix, key string) string { 543 return prefix + "-" + key 544 } 545 546 // unprefixMapKeys accepts a map of []byte indexed by string keys which 547 // are prefixed with a plugin specific prefix, it returns a new map without 548 // the plugin specific prefixes. 549 func unprefixMapKeys(prefix string, m map[string][]byte) map[string][]byte { 550 nm := make(map[string][]byte, len(m)) 551 552 for k, v := range m { 553 nm[unprefixKey(prefix, k)] = v 554 } 555 556 return nm 557 } 558 559 // unprefixKey removes a given prefix from a given key. 560 func unprefixKey(prefix, key string) string { 561 return strings.Replace(key, prefix+"-", "", 1) 562 }