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  }