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  }