github.com/renegr87/renegr87@v2.1.1+incompatible/core/ledger/kvledger/txmgmt/privacyenabledstate/common_storage_db.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package privacyenabledstate 8 9 import ( 10 "encoding/base64" 11 "strings" 12 13 "github.com/hyperledger/fabric-lib-go/healthz" 14 "github.com/hyperledger/fabric/common/flogging" 15 "github.com/hyperledger/fabric/common/metrics" 16 "github.com/hyperledger/fabric/core/common/ccprovider" 17 "github.com/hyperledger/fabric/core/ledger" 18 "github.com/hyperledger/fabric/core/ledger/cceventmgmt" 19 "github.com/hyperledger/fabric/core/ledger/kvledger/bookkeeping" 20 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb" 21 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb/statecouchdb" 22 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb/stateleveldb" 23 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version" 24 "github.com/hyperledger/fabric/core/ledger/util" 25 "github.com/pkg/errors" 26 ) 27 28 var logger = flogging.MustGetLogger("privacyenabledstate") 29 30 const ( 31 nsJoiner = "$$" 32 pvtDataPrefix = "p" 33 hashDataPrefix = "h" 34 couchDB = "CouchDB" 35 ) 36 37 // StateDBConfig encapsulates the configuration for stateDB on the ledger. 38 type StateDBConfig struct { 39 // ledger.StateDBConfig is used to configure the stateDB for the ledger. 40 *ledger.StateDBConfig 41 // LevelDBPath is the filesystem path when statedb type is "goleveldb". 42 // It is internally computed by the ledger component, 43 // so it is not in ledger.StateDBConfig and not exposed to other components. 44 LevelDBPath string 45 } 46 47 // CommonStorageDBProvider implements interface DBProvider 48 type CommonStorageDBProvider struct { 49 statedb.VersionedDBProvider 50 HealthCheckRegistry ledger.HealthCheckRegistry 51 bookkeepingProvider bookkeeping.Provider 52 } 53 54 // NewCommonStorageDBProvider constructs an instance of DBProvider 55 func NewCommonStorageDBProvider( 56 bookkeeperProvider bookkeeping.Provider, 57 metricsProvider metrics.Provider, 58 healthCheckRegistry ledger.HealthCheckRegistry, 59 stateDBConf *StateDBConfig, 60 sysNamespaces []string, 61 ) (DBProvider, error) { 62 63 var vdbProvider statedb.VersionedDBProvider 64 var err error 65 66 if stateDBConf != nil && stateDBConf.StateDatabase == couchDB { 67 cache := statedb.NewCache(stateDBConf.CouchDB.UserCacheSizeMBs, sysNamespaces) 68 if vdbProvider, err = statecouchdb.NewVersionedDBProvider(stateDBConf.CouchDB, metricsProvider, cache); err != nil { 69 return nil, err 70 } 71 } else { 72 if vdbProvider, err = stateleveldb.NewVersionedDBProvider(stateDBConf.LevelDBPath); err != nil { 73 return nil, err 74 } 75 } 76 77 dbProvider := &CommonStorageDBProvider{vdbProvider, healthCheckRegistry, bookkeeperProvider} 78 79 err = dbProvider.RegisterHealthChecker() 80 if err != nil { 81 return nil, err 82 } 83 84 return dbProvider, nil 85 } 86 87 // RegisterHealthChecker implements function from interface DBProvider 88 func (p *CommonStorageDBProvider) RegisterHealthChecker() error { 89 if healthChecker, ok := p.VersionedDBProvider.(healthz.HealthChecker); ok { 90 return p.HealthCheckRegistry.RegisterChecker("couchdb", healthChecker) 91 } 92 return nil 93 } 94 95 // GetDBHandle implements function from interface DBProvider 96 func (p *CommonStorageDBProvider) GetDBHandle(id string) (DB, error) { 97 vdb, err := p.VersionedDBProvider.GetDBHandle(id) 98 if err != nil { 99 return nil, err 100 } 101 bookkeeper := p.bookkeepingProvider.GetDBHandle(id, bookkeeping.MetadataPresenceIndicator) 102 metadataHint := newMetadataHint(bookkeeper) 103 return NewCommonStorageDB(vdb, id, metadataHint) 104 } 105 106 // Close implements function from interface DBProvider 107 func (p *CommonStorageDBProvider) Close() { 108 p.VersionedDBProvider.Close() 109 } 110 111 // CommonStorageDB implements interface DB. This implementation uses a single database to maintain 112 // both the public and private data 113 type CommonStorageDB struct { 114 statedb.VersionedDB 115 metadataHint *metadataHint 116 } 117 118 // NewCommonStorageDB wraps a VersionedDB instance. The public data is managed directly by the wrapped versionedDB. 119 // For managing the hashed data and private data, this implementation creates separate namespaces in the wrapped db 120 func NewCommonStorageDB(vdb statedb.VersionedDB, ledgerid string, metadataHint *metadataHint) (DB, error) { 121 return &CommonStorageDB{vdb, metadataHint}, nil 122 } 123 124 // IsBulkOptimizable implements corresponding function in interface DB 125 func (s *CommonStorageDB) IsBulkOptimizable() bool { 126 _, ok := s.VersionedDB.(statedb.BulkOptimizable) 127 return ok 128 } 129 130 // LoadCommittedVersionsOfPubAndHashedKeys implements corresponding function in interface DB 131 func (s *CommonStorageDB) LoadCommittedVersionsOfPubAndHashedKeys(pubKeys []*statedb.CompositeKey, 132 hashedKeys []*HashedCompositeKey) error { 133 134 bulkOptimizable, ok := s.VersionedDB.(statedb.BulkOptimizable) 135 if !ok { 136 return nil 137 } 138 // Here, hashedKeys are merged into pubKeys to get a combined set of keys for combined loading 139 for _, key := range hashedKeys { 140 ns := deriveHashedDataNs(key.Namespace, key.CollectionName) 141 // No need to check for duplicates as hashedKeys are in separate namespace 142 var keyHashStr string 143 if !s.BytesKeySupported() { 144 keyHashStr = base64.StdEncoding.EncodeToString([]byte(key.KeyHash)) 145 } else { 146 keyHashStr = key.KeyHash 147 } 148 pubKeys = append(pubKeys, &statedb.CompositeKey{ 149 Namespace: ns, 150 Key: keyHashStr, 151 }) 152 } 153 154 err := bulkOptimizable.LoadCommittedVersions(pubKeys) 155 if err != nil { 156 return err 157 } 158 159 return nil 160 } 161 162 // ClearCachedVersions implements corresponding function in interface DB 163 func (s *CommonStorageDB) ClearCachedVersions() { 164 bulkOptimizable, ok := s.VersionedDB.(statedb.BulkOptimizable) 165 if ok { 166 bulkOptimizable.ClearCachedVersions() 167 } 168 } 169 170 // GetChaincodeEventListener implements corresponding function in interface DB 171 func (s *CommonStorageDB) GetChaincodeEventListener() cceventmgmt.ChaincodeLifecycleEventListener { 172 _, ok := s.VersionedDB.(statedb.IndexCapable) 173 if ok { 174 return s 175 } 176 return nil 177 } 178 179 // GetPrivateData implements corresponding function in interface DB 180 func (s *CommonStorageDB) GetPrivateData(namespace, collection, key string) (*statedb.VersionedValue, error) { 181 return s.GetState(derivePvtDataNs(namespace, collection), key) 182 } 183 184 // GetPrivateDataHash implements corresponding function in interface DB 185 func (s *CommonStorageDB) GetPrivateDataHash(namespace, collection, key string) (*statedb.VersionedValue, error) { 186 return s.GetValueHash(namespace, collection, util.ComputeStringHash(key)) 187 } 188 189 // GetValueHash implements corresponding function in interface DB 190 func (s *CommonStorageDB) GetValueHash(namespace, collection string, keyHash []byte) (*statedb.VersionedValue, error) { 191 keyHashStr := string(keyHash) 192 if !s.BytesKeySupported() { 193 keyHashStr = base64.StdEncoding.EncodeToString(keyHash) 194 } 195 return s.GetState(deriveHashedDataNs(namespace, collection), keyHashStr) 196 } 197 198 // GetKeyHashVersion implements corresponding function in interface DB 199 func (s *CommonStorageDB) GetKeyHashVersion(namespace, collection string, keyHash []byte) (*version.Height, error) { 200 keyHashStr := string(keyHash) 201 if !s.BytesKeySupported() { 202 keyHashStr = base64.StdEncoding.EncodeToString(keyHash) 203 } 204 return s.GetVersion(deriveHashedDataNs(namespace, collection), keyHashStr) 205 } 206 207 // GetCachedKeyHashVersion retrieves the keyhash version from cache 208 func (s *CommonStorageDB) GetCachedKeyHashVersion(namespace, collection string, keyHash []byte) (*version.Height, bool) { 209 bulkOptimizable, ok := s.VersionedDB.(statedb.BulkOptimizable) 210 if !ok { 211 return nil, false 212 } 213 214 keyHashStr := string(keyHash) 215 if !s.BytesKeySupported() { 216 keyHashStr = base64.StdEncoding.EncodeToString(keyHash) 217 } 218 return bulkOptimizable.GetCachedVersion(deriveHashedDataNs(namespace, collection), keyHashStr) 219 } 220 221 // GetPrivateDataMultipleKeys implements corresponding function in interface DB 222 func (s *CommonStorageDB) GetPrivateDataMultipleKeys(namespace, collection string, keys []string) ([]*statedb.VersionedValue, error) { 223 return s.GetStateMultipleKeys(derivePvtDataNs(namespace, collection), keys) 224 } 225 226 // GetPrivateDataRangeScanIterator implements corresponding function in interface DB 227 func (s *CommonStorageDB) GetPrivateDataRangeScanIterator(namespace, collection, startKey, endKey string) (statedb.ResultsIterator, error) { 228 return s.GetStateRangeScanIterator(derivePvtDataNs(namespace, collection), startKey, endKey) 229 } 230 231 // ExecuteQueryOnPrivateData implements corresponding function in interface DB 232 func (s CommonStorageDB) ExecuteQueryOnPrivateData(namespace, collection, query string) (statedb.ResultsIterator, error) { 233 return s.ExecuteQuery(derivePvtDataNs(namespace, collection), query) 234 } 235 236 // ApplyUpdates overrides the function in statedb.VersionedDB and throws appropriate error message 237 // Otherwise, somewhere in the code, usage of this function could lead to updating only public data. 238 func (s *CommonStorageDB) ApplyUpdates(batch *statedb.UpdateBatch, height *version.Height) error { 239 return errors.New("this function should not be invoked on this type. Please invoke function ApplyPrivacyAwareUpdates") 240 } 241 242 // ApplyPrivacyAwareUpdates implements corresponding function in interface DB 243 func (s *CommonStorageDB) ApplyPrivacyAwareUpdates(updates *UpdateBatch, height *version.Height) error { 244 // combinedUpdates includes both updates to public db and private db, which are partitioned by a separate namespace 245 combinedUpdates := updates.PubUpdates 246 addPvtUpdates(combinedUpdates, updates.PvtUpdates) 247 addHashedUpdates(combinedUpdates, updates.HashUpdates, !s.BytesKeySupported()) 248 s.metadataHint.setMetadataUsedFlag(updates) 249 return s.VersionedDB.ApplyUpdates(combinedUpdates.UpdateBatch, height) 250 } 251 252 // GetStateMetadata implements corresponding function in interface DB. This implementation provides 253 // an optimization such that it keeps track if a namespaces has never stored metadata for any of 254 // its items, the value 'nil' is returned without going to the db. This is intended to be invoked 255 // in the validation and commit path. This saves the chaincodes from paying unnecessary performance 256 // penalty if they do not use features that leverage metadata (such as key-level endorsement), 257 func (s *CommonStorageDB) GetStateMetadata(namespace, key string) ([]byte, error) { 258 if !s.metadataHint.metadataEverUsedFor(namespace) { 259 return nil, nil 260 } 261 vv, err := s.GetState(namespace, key) 262 if err != nil || vv == nil { 263 return nil, err 264 } 265 return vv.Metadata, nil 266 } 267 268 // GetPrivateDataMetadataByHash implements corresponding function in interface DB. For additional details, see 269 // description of the similar function 'GetStateMetadata' 270 func (s *CommonStorageDB) GetPrivateDataMetadataByHash(namespace, collection string, keyHash []byte) ([]byte, error) { 271 if !s.metadataHint.metadataEverUsedFor(namespace) { 272 return nil, nil 273 } 274 vv, err := s.GetValueHash(namespace, collection, keyHash) 275 if err != nil || vv == nil { 276 return nil, err 277 } 278 return vv.Metadata, nil 279 } 280 281 // HandleChaincodeDeploy initializes database artifacts for the database associated with the namespace 282 // This function deliberately suppresses the errors that occur during the creation of the indexes on couchdb. 283 // This is because, in the present code, we do not differentiate between the errors because of couchdb interaction 284 // and the errors because of bad index files - the later being unfixable by the admin. Note that the error suppression 285 // is acceptable since peer can continue in the committing role without the indexes. However, executing chaincode queries 286 // may be affected, until a new chaincode with fixed indexes is installed and instantiated 287 func (s *CommonStorageDB) HandleChaincodeDeploy(chaincodeDefinition *cceventmgmt.ChaincodeDefinition, dbArtifactsTar []byte) error { 288 //Check to see if the interface for IndexCapable is implemented 289 indexCapable, ok := s.VersionedDB.(statedb.IndexCapable) 290 if !ok { 291 return nil 292 } 293 if chaincodeDefinition == nil { 294 return errors.New("chaincode definition not found while creating couchdb index") 295 } 296 dbArtifacts, err := ccprovider.ExtractFileEntries(dbArtifactsTar, indexCapable.GetDBType()) 297 if err != nil { 298 logger.Errorf("Index creation: error extracting db artifacts from tar for chaincode [%s]: %s", chaincodeDefinition.Name, err) 299 return nil 300 } 301 302 collectionConfigMap, err := extractCollectionNames(chaincodeDefinition) 303 if err != nil { 304 logger.Errorf("Error while retrieving collection config for chaincode=[%s]: %s", 305 chaincodeDefinition.Name, err) 306 return nil 307 } 308 309 for directoryPath, archiveDirectoryEntries := range dbArtifacts { 310 // split the directory name 311 directoryPathArray := strings.Split(directoryPath, "/") 312 // process the indexes for the chain 313 if directoryPathArray[3] == "indexes" { 314 err := indexCapable.ProcessIndexesForChaincodeDeploy(chaincodeDefinition.Name, archiveDirectoryEntries) 315 if err != nil { 316 logger.Errorf("Error processing index for chaincode [%s]: %s", chaincodeDefinition.Name, err) 317 } 318 continue 319 } 320 // check for the indexes directory for the collection 321 if directoryPathArray[3] == "collections" && directoryPathArray[5] == "indexes" { 322 collectionName := directoryPathArray[4] 323 _, ok := collectionConfigMap[collectionName] 324 if !ok { 325 logger.Errorf("Error processing index for chaincode [%s]: cannot create an index for an undefined collection=[%s]", chaincodeDefinition.Name, collectionName) 326 } else { 327 err := indexCapable.ProcessIndexesForChaincodeDeploy(derivePvtDataNs(chaincodeDefinition.Name, collectionName), 328 archiveDirectoryEntries) 329 if err != nil { 330 logger.Errorf("Error processing collection index for chaincode [%s]: %s", chaincodeDefinition.Name, err) 331 } 332 } 333 } 334 } 335 return nil 336 } 337 338 // ChaincodeDeployDone is a noop for couchdb state impl 339 func (s *CommonStorageDB) ChaincodeDeployDone(succeeded bool) { 340 // NOOP 341 } 342 343 func derivePvtDataNs(namespace, collection string) string { 344 return namespace + nsJoiner + pvtDataPrefix + collection 345 } 346 347 func deriveHashedDataNs(namespace, collection string) string { 348 return namespace + nsJoiner + hashDataPrefix + collection 349 } 350 351 func addPvtUpdates(pubUpdateBatch *PubUpdateBatch, pvtUpdateBatch *PvtUpdateBatch) { 352 for ns, nsBatch := range pvtUpdateBatch.UpdateMap { 353 for _, coll := range nsBatch.GetCollectionNames() { 354 for key, vv := range nsBatch.GetUpdates(coll) { 355 pubUpdateBatch.Update(derivePvtDataNs(ns, coll), key, vv) 356 } 357 } 358 } 359 } 360 361 func addHashedUpdates(pubUpdateBatch *PubUpdateBatch, hashedUpdateBatch *HashedUpdateBatch, base64Key bool) { 362 for ns, nsBatch := range hashedUpdateBatch.UpdateMap { 363 for _, coll := range nsBatch.GetCollectionNames() { 364 for key, vv := range nsBatch.GetUpdates(coll) { 365 if base64Key { 366 key = base64.StdEncoding.EncodeToString([]byte(key)) 367 } 368 pubUpdateBatch.Update(deriveHashedDataNs(ns, coll), key, vv) 369 } 370 } 371 } 372 } 373 374 func extractCollectionNames(chaincodeDefinition *cceventmgmt.ChaincodeDefinition) (map[string]bool, error) { 375 collectionConfigs := chaincodeDefinition.CollectionConfigs 376 collectionConfigsMap := make(map[string]bool) 377 if collectionConfigs != nil { 378 for _, config := range collectionConfigs.Config { 379 sConfig := config.GetStaticCollectionConfig() 380 if sConfig == nil { 381 continue 382 } 383 collectionConfigsMap[sConfig.Name] = true 384 } 385 } 386 return collectionConfigsMap, nil 387 }