github.com/true-sqn/fabric@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 }