github.com/renegr87/renegr87@v2.1.1+incompatible/core/ledger/kvledger/txmgmt/statedb/statedb.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package statedb
     8  
     9  import (
    10  	"fmt"
    11  	"sort"
    12  
    13  	"github.com/hyperledger/fabric/core/common/ccprovider"
    14  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version"
    15  	"github.com/hyperledger/fabric/core/ledger/util"
    16  )
    17  
    18  //go:generate counterfeiter -o mock/results_iterator.go -fake-name ResultsIterator . ResultsIterator
    19  //go:generate counterfeiter -o mock/versioned_db.go -fake-name VersionedDB . VersionedDB
    20  
    21  // VersionedDBProvider provides an instance of an versioned DB
    22  type VersionedDBProvider interface {
    23  	// GetDBHandle returns a handle to a VersionedDB
    24  	GetDBHandle(id string) (VersionedDB, error)
    25  	// Close closes all the VersionedDB instances and releases any resources held by VersionedDBProvider
    26  	Close()
    27  }
    28  
    29  // VersionedDB lists methods that a db is supposed to implement
    30  type VersionedDB interface {
    31  	// GetState gets the value for given namespace and key. For a chaincode, the namespace corresponds to the chaincodeId
    32  	GetState(namespace string, key string) (*VersionedValue, error)
    33  	// GetVersion gets the version for given namespace and key. For a chaincode, the namespace corresponds to the chaincodeId
    34  	GetVersion(namespace string, key string) (*version.Height, error)
    35  	// GetStateMultipleKeys gets the values for multiple keys in a single call
    36  	GetStateMultipleKeys(namespace string, keys []string) ([]*VersionedValue, error)
    37  	// GetStateRangeScanIterator returns an iterator that contains all the key-values between given key ranges.
    38  	// startKey is inclusive
    39  	// endKey is exclusive
    40  	// The returned ResultsIterator contains results of type *VersionedKV
    41  	GetStateRangeScanIterator(namespace string, startKey string, endKey string) (ResultsIterator, error)
    42  	// GetStateRangeScanIteratorWithMetadata returns an iterator that contains all the key-values between given key ranges.
    43  	// startKey is inclusive
    44  	// endKey is exclusive
    45  	// metadata is a map of additional query parameters
    46  	// The returned ResultsIterator contains results of type *VersionedKV
    47  	GetStateRangeScanIteratorWithMetadata(namespace string, startKey string, endKey string, metadata map[string]interface{}) (QueryResultsIterator, error)
    48  	// ExecuteQuery executes the given query and returns an iterator that contains results of type *VersionedKV.
    49  	ExecuteQuery(namespace, query string) (ResultsIterator, error)
    50  	// ExecuteQueryWithMetadata executes the given query with associated query options and
    51  	// returns an iterator that contains results of type *VersionedKV.
    52  	// metadata is a map of additional query parameters
    53  	ExecuteQueryWithMetadata(namespace, query string, metadata map[string]interface{}) (QueryResultsIterator, error)
    54  	// ApplyUpdates applies the batch to the underlying db.
    55  	// height is the height of the highest transaction in the Batch that
    56  	// a state db implementation is expected to ues as a save point
    57  	ApplyUpdates(batch *UpdateBatch, height *version.Height) error
    58  	// GetLatestSavePoint returns the height of the highest transaction upto which
    59  	// the state db is consistent
    60  	GetLatestSavePoint() (*version.Height, error)
    61  	// ValidateKeyValue tests whether the key and value is supported by the db implementation.
    62  	// For instance, leveldb supports any bytes for the key while the couchdb supports only valid utf-8 string
    63  	// TODO make the function ValidateKeyValue return a specific error say ErrInvalidKeyValue
    64  	// However, as of now, the both implementations of this function (leveldb and couchdb) are deterministic in returing an error
    65  	// i.e., an error is returned only if the key-value are found to be invalid for the underlying db
    66  	ValidateKeyValue(key string, value []byte) error
    67  	// BytesKeySupported returns true if the implementation (underlying db) supports the any bytes to be used as key.
    68  	// For instance, leveldb supports any bytes for the key while the couchdb supports only valid utf-8 string
    69  	BytesKeySupported() bool
    70  	// Open opens the db
    71  	Open() error
    72  	// Close closes the db
    73  	Close()
    74  }
    75  
    76  //BulkOptimizable interface provides additional functions for
    77  //databases capable of batch operations
    78  type BulkOptimizable interface {
    79  	LoadCommittedVersions(keys []*CompositeKey) error
    80  	GetCachedVersion(namespace, key string) (*version.Height, bool)
    81  	ClearCachedVersions()
    82  }
    83  
    84  //IndexCapable interface provides additional functions for
    85  //databases capable of index operations
    86  type IndexCapable interface {
    87  	GetDBType() string
    88  	ProcessIndexesForChaincodeDeploy(namespace string, fileEntries []*ccprovider.TarFileEntry) error
    89  }
    90  
    91  // CompositeKey encloses Namespace and Key components
    92  type CompositeKey struct {
    93  	Namespace string
    94  	Key       string
    95  }
    96  
    97  // VersionedValue encloses value and corresponding version
    98  type VersionedValue struct {
    99  	Value    []byte
   100  	Metadata []byte
   101  	Version  *version.Height
   102  }
   103  
   104  // IsDelete returns true if this update indicates delete of a key
   105  func (vv *VersionedValue) IsDelete() bool {
   106  	return vv.Value == nil
   107  }
   108  
   109  // VersionedKV encloses key and corresponding VersionedValue
   110  type VersionedKV struct {
   111  	CompositeKey
   112  	VersionedValue
   113  }
   114  
   115  // ResultsIterator iterates over query results
   116  type ResultsIterator interface {
   117  	Next() (QueryResult, error)
   118  	Close()
   119  }
   120  
   121  // QueryResultsIterator adds GetBookmarkAndClose method
   122  type QueryResultsIterator interface {
   123  	ResultsIterator
   124  	GetBookmarkAndClose() string
   125  }
   126  
   127  // QueryResult - a general interface for supporting different types of query results. Actual types differ for different queries
   128  type QueryResult interface{}
   129  
   130  type nsUpdates struct {
   131  	M map[string]*VersionedValue
   132  }
   133  
   134  func newNsUpdates() *nsUpdates {
   135  	return &nsUpdates{make(map[string]*VersionedValue)}
   136  }
   137  
   138  // UpdateBatch encloses the details of multiple `updates`
   139  type UpdateBatch struct {
   140  	ContainsPostOrderWrites bool
   141  	Updates                 map[string]*nsUpdates
   142  }
   143  
   144  // NewUpdateBatch constructs an instance of a Batch
   145  func NewUpdateBatch() *UpdateBatch {
   146  	return &UpdateBatch{false, make(map[string]*nsUpdates)}
   147  }
   148  
   149  // Get returns the VersionedValue for the given namespace and key
   150  func (batch *UpdateBatch) Get(ns string, key string) *VersionedValue {
   151  	nsUpdates, ok := batch.Updates[ns]
   152  	if !ok {
   153  		return nil
   154  	}
   155  	vv, ok := nsUpdates.M[key]
   156  	if !ok {
   157  		return nil
   158  	}
   159  	return vv
   160  }
   161  
   162  // Put adds a key with value only. The metadata is assumed to be nil
   163  func (batch *UpdateBatch) Put(ns string, key string, value []byte, version *version.Height) {
   164  	batch.PutValAndMetadata(ns, key, value, nil, version)
   165  }
   166  
   167  // PutValAndMetadata adds a key with value and metadata
   168  // TODO introducing a new function to limit the refactoring. Later in a separate CR, the 'Put' function above should be removed
   169  func (batch *UpdateBatch) PutValAndMetadata(ns string, key string, value []byte, metadata []byte, version *version.Height) {
   170  	if value == nil {
   171  		panic("Nil value not allowed. Instead call 'Delete' function")
   172  	}
   173  	batch.Update(ns, key, &VersionedValue{value, metadata, version})
   174  }
   175  
   176  // Delete deletes a Key and associated value
   177  func (batch *UpdateBatch) Delete(ns string, key string, version *version.Height) {
   178  	batch.Update(ns, key, &VersionedValue{nil, nil, version})
   179  }
   180  
   181  // Exists checks whether the given key exists in the batch
   182  func (batch *UpdateBatch) Exists(ns string, key string) bool {
   183  	nsUpdates, ok := batch.Updates[ns]
   184  	if !ok {
   185  		return false
   186  	}
   187  	_, ok = nsUpdates.M[key]
   188  	return ok
   189  }
   190  
   191  // GetUpdatedNamespaces returns the names of the namespaces that are updated
   192  func (batch *UpdateBatch) GetUpdatedNamespaces() []string {
   193  	namespaces := make([]string, len(batch.Updates))
   194  	i := 0
   195  	for ns := range batch.Updates {
   196  		namespaces[i] = ns
   197  		i++
   198  	}
   199  	return namespaces
   200  }
   201  
   202  // Update updates the batch with a latest entry for a namespace and a key
   203  func (batch *UpdateBatch) Update(ns string, key string, vv *VersionedValue) {
   204  	batch.getOrCreateNsUpdates(ns).M[key] = vv
   205  }
   206  
   207  // GetUpdates returns all the updates for a namespace
   208  func (batch *UpdateBatch) GetUpdates(ns string) map[string]*VersionedValue {
   209  	nsUpdates, ok := batch.Updates[ns]
   210  	if !ok {
   211  		return nil
   212  	}
   213  	return nsUpdates.M
   214  }
   215  
   216  // GetRangeScanIterator returns an iterator that iterates over keys of a specific namespace in sorted order
   217  // In other word this gives the same functionality over the contents in the `UpdateBatch` as
   218  // `VersionedDB.GetStateRangeScanIterator()` method gives over the contents in the statedb
   219  // This function can be used for querying the contents in the updateBatch before they are committed to the statedb.
   220  // For instance, a validator implementation can used this to verify the validity of a range query of a transaction
   221  // where the UpdateBatch represents the union of the modifications performed by the preceding valid transactions in the same block
   222  // (Assuming Group commit approach where we commit all the updates caused by a block together).
   223  func (batch *UpdateBatch) GetRangeScanIterator(ns string, startKey string, endKey string) QueryResultsIterator {
   224  	return newNsIterator(ns, startKey, endKey, batch)
   225  }
   226  
   227  // Merge merges another updates batch with this updates batch
   228  func (batch *UpdateBatch) Merge(toMerge *UpdateBatch) {
   229  	batch.ContainsPostOrderWrites = batch.ContainsPostOrderWrites || toMerge.ContainsPostOrderWrites
   230  	for ns, nsUpdates := range toMerge.Updates {
   231  		for key, vv := range nsUpdates.M {
   232  			batch.Update(ns, key, vv)
   233  		}
   234  	}
   235  }
   236  
   237  func (batch *UpdateBatch) getOrCreateNsUpdates(ns string) *nsUpdates {
   238  	nsUpdates := batch.Updates[ns]
   239  	if nsUpdates == nil {
   240  		nsUpdates = newNsUpdates()
   241  		batch.Updates[ns] = nsUpdates
   242  	}
   243  	return nsUpdates
   244  }
   245  
   246  type nsIterator struct {
   247  	ns         string
   248  	nsUpdates  *nsUpdates
   249  	sortedKeys []string
   250  	nextIndex  int
   251  	lastIndex  int
   252  }
   253  
   254  func newNsIterator(ns string, startKey string, endKey string, batch *UpdateBatch) *nsIterator {
   255  	nsUpdates, ok := batch.Updates[ns]
   256  	if !ok {
   257  		return &nsIterator{}
   258  	}
   259  	sortedKeys := util.GetSortedKeys(nsUpdates.M)
   260  	var nextIndex int
   261  	var lastIndex int
   262  	if startKey == "" {
   263  		nextIndex = 0
   264  	} else {
   265  		nextIndex = sort.SearchStrings(sortedKeys, startKey)
   266  	}
   267  	if endKey == "" {
   268  		lastIndex = len(sortedKeys)
   269  	} else {
   270  		lastIndex = sort.SearchStrings(sortedKeys, endKey)
   271  	}
   272  	return &nsIterator{ns, nsUpdates, sortedKeys, nextIndex, lastIndex}
   273  }
   274  
   275  // Next gives next key and versioned value. It returns a nil when exhausted
   276  func (itr *nsIterator) Next() (QueryResult, error) {
   277  	if itr.nextIndex >= itr.lastIndex {
   278  		return nil, nil
   279  	}
   280  	key := itr.sortedKeys[itr.nextIndex]
   281  	vv := itr.nsUpdates.M[key]
   282  	itr.nextIndex++
   283  	return &VersionedKV{CompositeKey{itr.ns, key}, VersionedValue{vv.Value, vv.Metadata, vv.Version}}, nil
   284  }
   285  
   286  // Close implements the method from QueryResult interface
   287  func (itr *nsIterator) Close() {
   288  	// do nothing
   289  }
   290  
   291  // GetBookmarkAndClose implements the method from QueryResult interface
   292  func (itr *nsIterator) GetBookmarkAndClose() string {
   293  	// do nothing
   294  	return ""
   295  }
   296  
   297  const optionLimit = "limit"
   298  
   299  // ValidateRangeMetadata validates the JSON containing attributes for the range query
   300  func ValidateRangeMetadata(metadata map[string]interface{}) error {
   301  	for key, keyVal := range metadata {
   302  		switch key {
   303  
   304  		case optionLimit:
   305  			//Verify the pageSize is an integer
   306  			if _, ok := keyVal.(int32); ok {
   307  				continue
   308  			}
   309  			return fmt.Errorf("Invalid entry, \"limit\" must be a int32")
   310  
   311  		default:
   312  			return fmt.Errorf("Invalid entry, option %s not recognized", key)
   313  		}
   314  	}
   315  	return nil
   316  }