github.com/renegr87/renegr87@v2.1.1+incompatible/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/helper.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package lockbasedtxmgr 8 9 import ( 10 "fmt" 11 12 "github.com/hyperledger/fabric-protos-go/ledger/queryresult" 13 "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 14 commonledger "github.com/hyperledger/fabric/common/ledger" 15 ledger "github.com/hyperledger/fabric/core/ledger" 16 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil" 17 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb" 18 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/storageutil" 19 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/txmgr" 20 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version" 21 "github.com/hyperledger/fabric/core/ledger/util" 22 "github.com/pkg/errors" 23 ) 24 25 const ( 26 queryReadsHashingEnabled = true 27 maxDegreeQueryReadsHashing = uint32(50) 28 ) 29 30 type queryHelper struct { 31 txmgr *LockBasedTxMgr 32 collNameValidator *collNameValidator 33 rwsetBuilder *rwsetutil.RWSetBuilder 34 itrs []*resultsItr 35 err error 36 doneInvoked bool 37 hasher ledger.Hasher 38 } 39 40 func newQueryHelper(txmgr *LockBasedTxMgr, rwsetBuilder *rwsetutil.RWSetBuilder, performCollCheck bool, hasher ledger.Hasher) *queryHelper { 41 helper := &queryHelper{ 42 txmgr: txmgr, 43 rwsetBuilder: rwsetBuilder, 44 hasher: hasher, 45 } 46 validator := newCollNameValidator(txmgr.ledgerid, txmgr.ccInfoProvider, &lockBasedQueryExecutor{helper: helper}, !performCollCheck) 47 helper.collNameValidator = validator 48 return helper 49 } 50 51 func (h *queryHelper) getState(ns string, key string) ([]byte, []byte, error) { 52 if err := h.checkDone(); err != nil { 53 return nil, nil, err 54 } 55 versionedValue, err := h.txmgr.db.GetState(ns, key) 56 if err != nil { 57 return nil, nil, err 58 } 59 val, metadata, ver := decomposeVersionedValue(versionedValue) 60 if h.rwsetBuilder != nil { 61 h.rwsetBuilder.AddToReadSet(ns, key, ver) 62 } 63 return val, metadata, nil 64 } 65 66 func (h *queryHelper) getStateMultipleKeys(namespace string, keys []string) ([][]byte, error) { 67 if err := h.checkDone(); err != nil { 68 return nil, err 69 } 70 versionedValues, err := h.txmgr.db.GetStateMultipleKeys(namespace, keys) 71 if err != nil { 72 return nil, nil 73 } 74 values := make([][]byte, len(versionedValues)) 75 for i, versionedValue := range versionedValues { 76 val, _, ver := decomposeVersionedValue(versionedValue) 77 if h.rwsetBuilder != nil { 78 h.rwsetBuilder.AddToReadSet(namespace, keys[i], ver) 79 } 80 values[i] = val 81 } 82 return values, nil 83 } 84 85 func (h *queryHelper) getStateRangeScanIterator(namespace string, startKey string, endKey string) (ledger.QueryResultsIterator, error) { 86 if err := h.checkDone(); err != nil { 87 return nil, err 88 } 89 itr, err := newResultsItr( 90 namespace, 91 startKey, 92 endKey, 93 nil, 94 h.txmgr.db, 95 h.rwsetBuilder, 96 queryReadsHashingEnabled, 97 maxDegreeQueryReadsHashing, 98 h.hasher, 99 ) 100 if err != nil { 101 return nil, err 102 } 103 h.itrs = append(h.itrs, itr) 104 return itr, nil 105 } 106 107 func (h *queryHelper) getStateRangeScanIteratorWithMetadata(namespace string, startKey string, endKey string, metadata map[string]interface{}) (ledger.QueryResultsIterator, error) { 108 if err := h.checkDone(); err != nil { 109 return nil, err 110 } 111 itr, err := newResultsItr( 112 namespace, 113 startKey, 114 endKey, 115 metadata, 116 h.txmgr.db, 117 h.rwsetBuilder, 118 queryReadsHashingEnabled, 119 maxDegreeQueryReadsHashing, 120 h.hasher, 121 ) 122 if err != nil { 123 return nil, err 124 } 125 h.itrs = append(h.itrs, itr) 126 return itr, nil 127 } 128 129 func (h *queryHelper) executeQuery(namespace, query string) (commonledger.ResultsIterator, error) { 130 if err := h.checkDone(); err != nil { 131 return nil, err 132 } 133 dbItr, err := h.txmgr.db.ExecuteQuery(namespace, query) 134 if err != nil { 135 return nil, err 136 } 137 return &queryResultsItr{DBItr: dbItr, RWSetBuilder: h.rwsetBuilder}, nil 138 } 139 140 func (h *queryHelper) executeQueryWithMetadata(namespace, query string, metadata map[string]interface{}) (ledger.QueryResultsIterator, error) { 141 if err := h.checkDone(); err != nil { 142 return nil, err 143 } 144 dbItr, err := h.txmgr.db.ExecuteQueryWithMetadata(namespace, query, metadata) 145 if err != nil { 146 return nil, err 147 } 148 return &queryResultsItr{DBItr: dbItr, RWSetBuilder: h.rwsetBuilder}, nil 149 } 150 151 func (h *queryHelper) getPrivateData(ns, coll, key string) ([]byte, error) { 152 if err := h.validateCollName(ns, coll); err != nil { 153 return nil, err 154 } 155 if err := h.checkDone(); err != nil { 156 return nil, err 157 } 158 159 var err error 160 var hashVersion *version.Height 161 var versionedValue *statedb.VersionedValue 162 163 if versionedValue, err = h.txmgr.db.GetPrivateData(ns, coll, key); err != nil { 164 return nil, err 165 } 166 167 // metadata is always nil for private data - because, the metadata is part of the hashed key (instead of raw key) 168 val, _, ver := decomposeVersionedValue(versionedValue) 169 170 keyHash := util.ComputeStringHash(key) 171 if hashVersion, err = h.txmgr.db.GetKeyHashVersion(ns, coll, keyHash); err != nil { 172 return nil, err 173 } 174 if !version.AreSame(hashVersion, ver) { 175 return nil, &txmgr.ErrPvtdataNotAvailable{Msg: fmt.Sprintf( 176 "private data matching public hash version is not available. Public hash version = %s, Private data version = %s", 177 hashVersion, ver)} 178 } 179 if h.rwsetBuilder != nil { 180 h.rwsetBuilder.AddToHashedReadSet(ns, coll, key, ver) 181 } 182 return val, nil 183 } 184 185 func (h *queryHelper) getPrivateDataValueHash(ns, coll, key string) (valueHash, metadataBytes []byte, err error) { 186 if err := h.validateCollName(ns, coll); err != nil { 187 return nil, nil, err 188 } 189 if err := h.checkDone(); err != nil { 190 return nil, nil, err 191 } 192 var versionedValue *statedb.VersionedValue 193 if versionedValue, err = h.txmgr.db.GetPrivateDataHash(ns, coll, key); err != nil { 194 return nil, nil, err 195 } 196 valHash, metadata, ver := decomposeVersionedValue(versionedValue) 197 if h.rwsetBuilder != nil { 198 h.rwsetBuilder.AddToHashedReadSet(ns, coll, key, ver) 199 } 200 return valHash, metadata, nil 201 } 202 203 func (h *queryHelper) getPrivateDataMultipleKeys(ns, coll string, keys []string) ([][]byte, error) { 204 if err := h.validateCollName(ns, coll); err != nil { 205 return nil, err 206 } 207 if err := h.checkDone(); err != nil { 208 return nil, err 209 } 210 versionedValues, err := h.txmgr.db.GetPrivateDataMultipleKeys(ns, coll, keys) 211 if err != nil { 212 return nil, nil 213 } 214 values := make([][]byte, len(versionedValues)) 215 for i, versionedValue := range versionedValues { 216 val, _, ver := decomposeVersionedValue(versionedValue) 217 if h.rwsetBuilder != nil { 218 h.rwsetBuilder.AddToHashedReadSet(ns, coll, keys[i], ver) 219 } 220 values[i] = val 221 } 222 return values, nil 223 } 224 225 func (h *queryHelper) getPrivateDataRangeScanIterator(namespace, collection, startKey, endKey string) (commonledger.ResultsIterator, error) { 226 if err := h.validateCollName(namespace, collection); err != nil { 227 return nil, err 228 } 229 if err := h.checkDone(); err != nil { 230 return nil, err 231 } 232 dbItr, err := h.txmgr.db.GetPrivateDataRangeScanIterator(namespace, collection, startKey, endKey) 233 if err != nil { 234 return nil, err 235 } 236 return &pvtdataResultsItr{namespace, collection, dbItr}, nil 237 } 238 239 func (h *queryHelper) executeQueryOnPrivateData(namespace, collection, query string) (commonledger.ResultsIterator, error) { 240 if err := h.validateCollName(namespace, collection); err != nil { 241 return nil, err 242 } 243 if err := h.checkDone(); err != nil { 244 return nil, err 245 } 246 dbItr, err := h.txmgr.db.ExecuteQueryOnPrivateData(namespace, collection, query) 247 if err != nil { 248 return nil, err 249 } 250 return &pvtdataResultsItr{namespace, collection, dbItr}, nil 251 } 252 253 func (h *queryHelper) getStateMetadata(ns string, key string) (map[string][]byte, error) { 254 if err := h.checkDone(); err != nil { 255 return nil, err 256 } 257 var metadataBytes []byte 258 var err error 259 if h.rwsetBuilder == nil { 260 // reads versions are not getting recorded, retrieve metadata value via optimized path 261 if metadataBytes, err = h.txmgr.db.GetStateMetadata(ns, key); err != nil { 262 return nil, err 263 } 264 } else { 265 if _, metadataBytes, err = h.getState(ns, key); err != nil { 266 return nil, err 267 } 268 } 269 return storageutil.DeserializeMetadata(metadataBytes) 270 } 271 272 func (h *queryHelper) getPrivateDataMetadata(ns, coll, key string) (map[string][]byte, error) { 273 if h.rwsetBuilder == nil { 274 // reads versions are not getting recorded, retrieve metadata value via optimized path 275 return h.getPrivateDataMetadataByHash(ns, coll, util.ComputeStringHash(key)) 276 } 277 if err := h.validateCollName(ns, coll); err != nil { 278 return nil, err 279 } 280 if err := h.checkDone(); err != nil { 281 return nil, err 282 } 283 _, metadataBytes, err := h.getPrivateDataValueHash(ns, coll, key) 284 if err != nil { 285 return nil, err 286 } 287 return storageutil.DeserializeMetadata(metadataBytes) 288 } 289 290 func (h *queryHelper) getPrivateDataMetadataByHash(ns, coll string, keyhash []byte) (map[string][]byte, error) { 291 if err := h.validateCollName(ns, coll); err != nil { 292 return nil, err 293 } 294 if err := h.checkDone(); err != nil { 295 return nil, err 296 } 297 if h.rwsetBuilder != nil { 298 // this requires to improve rwset builder to accept a keyhash 299 return nil, errors.New("retrieving private data metadata by keyhash is not supported in simulation. This function is only available for query as yet") 300 } 301 metadataBytes, err := h.txmgr.db.GetPrivateDataMetadataByHash(ns, coll, keyhash) 302 if err != nil { 303 return nil, err 304 } 305 return storageutil.DeserializeMetadata(metadataBytes) 306 } 307 308 func (h *queryHelper) done() { 309 if h.doneInvoked { 310 return 311 } 312 313 defer func() { 314 h.txmgr.commitRWLock.RUnlock() 315 h.doneInvoked = true 316 for _, itr := range h.itrs { 317 itr.Close() 318 } 319 }() 320 } 321 322 func (h *queryHelper) addRangeQueryInfo() { 323 for _, itr := range h.itrs { 324 if h.rwsetBuilder != nil { 325 results, hash, err := itr.rangeQueryResultsHelper.Done() 326 if err != nil { 327 h.err = err 328 return 329 } 330 if results != nil { 331 rwsetutil.SetRawReads(itr.rangeQueryInfo, results) 332 } 333 if hash != nil { 334 rwsetutil.SetMerkelSummary(itr.rangeQueryInfo, hash) 335 } 336 h.rwsetBuilder.AddToRangeQuerySet(itr.ns, itr.rangeQueryInfo) 337 } 338 } 339 } 340 341 func (h *queryHelper) checkDone() error { 342 if h.doneInvoked { 343 return errors.New("this instance should not be used after calling Done()") 344 } 345 return nil 346 } 347 348 func (h *queryHelper) validateCollName(ns, coll string) error { 349 return h.collNameValidator.validateCollName(ns, coll) 350 } 351 352 // resultsItr implements interface ledger.ResultsIterator 353 // this wraps the actual db iterator and intercept the calls 354 // to build rangeQueryInfo in the ReadWriteSet that is used 355 // for performing phantom read validation during commit 356 type resultsItr struct { 357 ns string 358 endKey string 359 dbItr statedb.ResultsIterator 360 rwSetBuilder *rwsetutil.RWSetBuilder 361 rangeQueryInfo *kvrwset.RangeQueryInfo 362 rangeQueryResultsHelper *rwsetutil.RangeQueryResultsHelper 363 } 364 365 func newResultsItr(ns string, startKey string, endKey string, metadata map[string]interface{}, 366 db statedb.VersionedDB, rwsetBuilder *rwsetutil.RWSetBuilder, enableHashing bool, maxDegree uint32, hasher ledger.Hasher) (*resultsItr, error) { 367 var err error 368 var dbItr statedb.ResultsIterator 369 if metadata == nil { 370 dbItr, err = db.GetStateRangeScanIterator(ns, startKey, endKey) 371 } else { 372 dbItr, err = db.GetStateRangeScanIteratorWithMetadata(ns, startKey, endKey, metadata) 373 } 374 if err != nil { 375 return nil, err 376 } 377 itr := &resultsItr{ns: ns, dbItr: dbItr} 378 // it's a simulation request so, enable capture of range query info 379 if rwsetBuilder != nil { 380 itr.rwSetBuilder = rwsetBuilder 381 itr.endKey = endKey 382 // just set the StartKey... set the EndKey later below in the Next() method. 383 itr.rangeQueryInfo = &kvrwset.RangeQueryInfo{StartKey: startKey} 384 resultsHelper, err := rwsetutil.NewRangeQueryResultsHelper(enableHashing, maxDegree, hasher) 385 if err != nil { 386 return nil, err 387 } 388 itr.rangeQueryResultsHelper = resultsHelper 389 } 390 return itr, nil 391 } 392 393 // Next implements method in interface ledger.ResultsIterator 394 // Before returning the next result, update the EndKey and ItrExhausted in rangeQueryInfo 395 // If we set the EndKey in the constructor (as we do for the StartKey) to what is 396 // supplied in the original query, we may be capturing the unnecessary longer range if the 397 // caller decides to stop iterating at some intermediate point. Alternatively, we could have 398 // set the EndKey and ItrExhausted in the Close() function but it may not be desirable to change 399 // transactional behaviour based on whether the Close() was invoked or not 400 func (itr *resultsItr) Next() (commonledger.QueryResult, error) { 401 queryResult, err := itr.dbItr.Next() 402 if err != nil { 403 return nil, err 404 } 405 itr.updateRangeQueryInfo(queryResult) 406 if queryResult == nil { 407 return nil, nil 408 } 409 versionedKV := queryResult.(*statedb.VersionedKV) 410 return &queryresult.KV{Namespace: versionedKV.Namespace, Key: versionedKV.Key, Value: versionedKV.Value}, nil 411 } 412 413 // GetBookmarkAndClose implements method in interface ledger.ResultsIterator 414 func (itr *resultsItr) GetBookmarkAndClose() string { 415 returnBookmark := "" 416 if queryResultIterator, ok := itr.dbItr.(statedb.QueryResultsIterator); ok { 417 returnBookmark = queryResultIterator.GetBookmarkAndClose() 418 } 419 return returnBookmark 420 } 421 422 // updateRangeQueryInfo updates two attributes of the rangeQueryInfo 423 // 1) The EndKey - set to either a) latest key that is to be returned to the caller (if the iterator is not exhausted) 424 // because, we do not know if the caller is again going to invoke Next() or not. 425 // or b) the last key that was supplied in the original query (if the iterator is exhausted) 426 // 2) The ItrExhausted - set to true if the iterator is going to return nil as a result of the Next() call 427 func (itr *resultsItr) updateRangeQueryInfo(queryResult statedb.QueryResult) { 428 if itr.rwSetBuilder == nil { 429 return 430 } 431 432 if queryResult == nil { 433 // caller scanned till the iterator got exhausted. 434 // So, set the endKey to the actual endKey supplied in the query 435 itr.rangeQueryInfo.ItrExhausted = true 436 itr.rangeQueryInfo.EndKey = itr.endKey 437 return 438 } 439 versionedKV := queryResult.(*statedb.VersionedKV) 440 itr.rangeQueryResultsHelper.AddResult(rwsetutil.NewKVRead(versionedKV.Key, versionedKV.Version)) 441 // Set the end key to the latest key retrieved by the caller. 442 // Because, the caller may actually not invoke the Next() function again 443 itr.rangeQueryInfo.EndKey = versionedKV.Key 444 } 445 446 // Close implements method in interface ledger.ResultsIterator 447 func (itr *resultsItr) Close() { 448 itr.dbItr.Close() 449 } 450 451 type queryResultsItr struct { 452 DBItr statedb.ResultsIterator 453 RWSetBuilder *rwsetutil.RWSetBuilder 454 } 455 456 // Next implements method in interface ledger.ResultsIterator 457 func (itr *queryResultsItr) Next() (commonledger.QueryResult, error) { 458 459 queryResult, err := itr.DBItr.Next() 460 if err != nil { 461 return nil, err 462 } 463 if queryResult == nil { 464 return nil, nil 465 } 466 versionedQueryRecord := queryResult.(*statedb.VersionedKV) 467 logger.Debugf("queryResultsItr.Next() returned a record:%s", string(versionedQueryRecord.Value)) 468 469 if itr.RWSetBuilder != nil { 470 itr.RWSetBuilder.AddToReadSet(versionedQueryRecord.Namespace, versionedQueryRecord.Key, versionedQueryRecord.Version) 471 } 472 return &queryresult.KV{Namespace: versionedQueryRecord.Namespace, Key: versionedQueryRecord.Key, Value: versionedQueryRecord.Value}, nil 473 } 474 475 // Close implements method in interface ledger.ResultsIterator 476 func (itr *queryResultsItr) Close() { 477 itr.DBItr.Close() 478 } 479 480 func (itr *queryResultsItr) GetBookmarkAndClose() string { 481 returnBookmark := "" 482 if queryResultIterator, ok := itr.DBItr.(statedb.QueryResultsIterator); ok { 483 returnBookmark = queryResultIterator.GetBookmarkAndClose() 484 } 485 return returnBookmark 486 } 487 488 func decomposeVersionedValue(versionedValue *statedb.VersionedValue) ([]byte, []byte, *version.Height) { 489 var value []byte 490 var metadata []byte 491 var ver *version.Height 492 if versionedValue != nil { 493 value = versionedValue.Value 494 ver = versionedValue.Version 495 metadata = versionedValue.Metadata 496 } 497 return value, metadata, ver 498 } 499 500 // pvtdataResultsItr iterates over results of a query on pvt data 501 type pvtdataResultsItr struct { 502 ns string 503 coll string 504 dbItr statedb.ResultsIterator 505 } 506 507 // Next implements method in interface ledger.ResultsIterator 508 func (itr *pvtdataResultsItr) Next() (commonledger.QueryResult, error) { 509 queryResult, err := itr.dbItr.Next() 510 if err != nil { 511 return nil, err 512 } 513 if queryResult == nil { 514 return nil, nil 515 } 516 versionedQueryRecord := queryResult.(*statedb.VersionedKV) 517 return &queryresult.KV{ 518 Namespace: itr.ns, 519 Key: versionedQueryRecord.Key, 520 Value: versionedQueryRecord.Value, 521 }, nil 522 } 523 524 // Close implements method in interface ledger.ResultsIterator 525 func (itr *pvtdataResultsItr) Close() { 526 itr.dbItr.Close() 527 }