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 }