github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/core/ledger/kvledger/txmgmt/statedb/statedb.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package statedb
    18  
    19  import (
    20  	"sort"
    21  
    22  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version"
    23  	"github.com/hyperledger/fabric/core/ledger/util"
    24  )
    25  
    26  // VersionedDBProvider provides an instance of an versioned DB
    27  type VersionedDBProvider interface {
    28  	// GetDBHandle returns a handle to a VersionedDB
    29  	GetDBHandle(id string) (VersionedDB, error)
    30  	// Close closes all the VersionedDB instances and releases any resources held by VersionedDBProvider
    31  	Close()
    32  }
    33  
    34  // VersionedDB lists methods that a db is supposed to implement
    35  type VersionedDB interface {
    36  	// GetState gets the value for given namespace and key. For a chaincode, the namespace corresponds to the chaincodeId
    37  	GetState(namespace string, key string) (*VersionedValue, error)
    38  	// GetStateMultipleKeys gets the values for multiple keys in a single call
    39  	GetStateMultipleKeys(namespace string, keys []string) ([]*VersionedValue, error)
    40  	// GetStateRangeScanIterator returns an iterator that contains all the key-values between given key ranges.
    41  	// startKey is inclusive
    42  	// endKey is exclusive
    43  	// The returned ResultsIterator contains results of type *VersionedKV
    44  	GetStateRangeScanIterator(namespace string, startKey string, endKey string) (ResultsIterator, error)
    45  	// ExecuteQuery executes the given query and returns an iterator that contains results of type *VersionedKV.
    46  	ExecuteQuery(namespace, query string) (ResultsIterator, error)
    47  	// ApplyUpdates applies the batch to the underlying db.
    48  	// height is the height of the highest transaction in the Batch that
    49  	// a state db implementation is expected to ues as a save point
    50  	ApplyUpdates(batch *UpdateBatch, height *version.Height) error
    51  	// GetLatestSavePoint returns the height of the highest transaction upto which
    52  	// the state db is consistent
    53  	GetLatestSavePoint() (*version.Height, error)
    54  	// ValidateKey tests whether the key is supported by the db implementation.
    55  	// For instance, leveldb supports any bytes for the key while the couchdb supports only valid utf-8 string
    56  	ValidateKey(key string) error
    57  	// Open opens the db
    58  	Open() error
    59  	// Close closes the db
    60  	Close()
    61  }
    62  
    63  // CompositeKey encloses Namespace and Key components
    64  type CompositeKey struct {
    65  	Namespace string
    66  	Key       string
    67  }
    68  
    69  // VersionedValue encloses value and corresponding version
    70  type VersionedValue struct {
    71  	Value   []byte
    72  	Version *version.Height
    73  }
    74  
    75  // VersionedKV encloses key and corresponding VersionedValue
    76  type VersionedKV struct {
    77  	CompositeKey
    78  	VersionedValue
    79  }
    80  
    81  // ResultsIterator hepls in iterates over query results
    82  type ResultsIterator interface {
    83  	Next() (QueryResult, error)
    84  	Close()
    85  }
    86  
    87  // QueryResult - a general interface for supporting different types of query results. Actual types differ for different queries
    88  type QueryResult interface{}
    89  
    90  type nsUpdates struct {
    91  	m map[string]*VersionedValue
    92  }
    93  
    94  func newNsUpdates() *nsUpdates {
    95  	return &nsUpdates{make(map[string]*VersionedValue)}
    96  }
    97  
    98  // UpdateBatch encloses the details of multiple `updates`
    99  type UpdateBatch struct {
   100  	updates map[string]*nsUpdates
   101  }
   102  
   103  // NewUpdateBatch constructs an instance of a Batch
   104  func NewUpdateBatch() *UpdateBatch {
   105  	return &UpdateBatch{make(map[string]*nsUpdates)}
   106  }
   107  
   108  // Get returns the VersionedValue for the given namespace and key
   109  func (batch *UpdateBatch) Get(ns string, key string) *VersionedValue {
   110  	nsUpdates, ok := batch.updates[ns]
   111  	if !ok {
   112  		return nil
   113  	}
   114  	vv, ok := nsUpdates.m[key]
   115  	if !ok {
   116  		return nil
   117  	}
   118  	return vv
   119  }
   120  
   121  // Put adds a VersionedKV
   122  func (batch *UpdateBatch) Put(ns string, key string, value []byte, version *version.Height) {
   123  	if value == nil {
   124  		panic("Nil value not allowed")
   125  	}
   126  	nsUpdates := batch.getOrCreateNsUpdates(ns)
   127  	nsUpdates.m[key] = &VersionedValue{value, version}
   128  }
   129  
   130  // Delete deletes a Key and associated value
   131  func (batch *UpdateBatch) Delete(ns string, key string, version *version.Height) {
   132  	nsUpdates := batch.getOrCreateNsUpdates(ns)
   133  	nsUpdates.m[key] = &VersionedValue{nil, version}
   134  }
   135  
   136  // Exists checks whether the given key exists in the batch
   137  func (batch *UpdateBatch) Exists(ns string, key string) bool {
   138  	nsUpdates, ok := batch.updates[ns]
   139  	if !ok {
   140  		return false
   141  	}
   142  	_, ok = nsUpdates.m[key]
   143  	return ok
   144  }
   145  
   146  // GetUpdatedNamespaces returns the names of the namespaces that are updated
   147  func (batch *UpdateBatch) GetUpdatedNamespaces() []string {
   148  	namespaces := make([]string, len(batch.updates))
   149  	i := 0
   150  	for ns := range batch.updates {
   151  		namespaces[i] = ns
   152  		i++
   153  	}
   154  	return namespaces
   155  }
   156  
   157  // GetUpdates returns all the updates for a namespace
   158  func (batch *UpdateBatch) GetUpdates(ns string) map[string]*VersionedValue {
   159  	nsUpdates, ok := batch.updates[ns]
   160  	if !ok {
   161  		return nil
   162  	}
   163  	return nsUpdates.m
   164  }
   165  
   166  // GetRangeScanIterator returns an iterator that iterates over keys of a specific namespace in sorted order
   167  // In other word this gives the same functionality over the contents in the `UpdateBatch` as
   168  // `VersionedDB.GetStateRangeScanIterator()` method gives over the contents in the statedb
   169  // This function can be used for querying the contents in the updateBatch before they are committed to the statedb.
   170  // For instance, a validator implementation can used this to verify the validity of a range query of a transaction
   171  // where the UpdateBatch represents the union of the modifications performed by the preceding valid transactions in the same block
   172  // (Assuming Group commit approach where we commit all the updates caused by a block together).
   173  func (batch *UpdateBatch) GetRangeScanIterator(ns string, startKey string, endKey string) ResultsIterator {
   174  	return newNsIterator(ns, startKey, endKey, batch)
   175  }
   176  
   177  func (batch *UpdateBatch) getOrCreateNsUpdates(ns string) *nsUpdates {
   178  	nsUpdates := batch.updates[ns]
   179  	if nsUpdates == nil {
   180  		nsUpdates = newNsUpdates()
   181  		batch.updates[ns] = nsUpdates
   182  	}
   183  	return nsUpdates
   184  }
   185  
   186  type nsIterator struct {
   187  	ns         string
   188  	nsUpdates  *nsUpdates
   189  	sortedKeys []string
   190  	nextIndex  int
   191  	lastIndex  int
   192  }
   193  
   194  func newNsIterator(ns string, startKey string, endKey string, batch *UpdateBatch) *nsIterator {
   195  	nsUpdates, ok := batch.updates[ns]
   196  	if !ok {
   197  		return &nsIterator{}
   198  	}
   199  	sortedKeys := util.GetSortedKeys(nsUpdates.m)
   200  	var nextIndex int
   201  	var lastIndex int
   202  	if startKey == "" {
   203  		nextIndex = 0
   204  	} else {
   205  		nextIndex = sort.SearchStrings(sortedKeys, startKey)
   206  	}
   207  	if endKey == "" {
   208  		lastIndex = len(sortedKeys)
   209  	} else {
   210  		lastIndex = sort.SearchStrings(sortedKeys, endKey)
   211  	}
   212  	return &nsIterator{ns, nsUpdates, sortedKeys, nextIndex, lastIndex}
   213  }
   214  
   215  // Next gives next key and versioned value. It returns a nil when exhausted
   216  func (itr *nsIterator) Next() (QueryResult, error) {
   217  	if itr.nextIndex >= itr.lastIndex {
   218  		return nil, nil
   219  	}
   220  	key := itr.sortedKeys[itr.nextIndex]
   221  	vv := itr.nsUpdates.m[key]
   222  	itr.nextIndex++
   223  	return &VersionedKV{CompositeKey{itr.ns, key}, VersionedValue{vv.Value, vv.Version}}, nil
   224  }
   225  
   226  // Close implements the method from QueryResult interface
   227  func (itr *nsIterator) Close() {
   228  	// do nothing
   229  }