github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/txmgmt/statedb/statedb.go (about)

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