github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/txmgmt/validation/combined_iterator.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package validation
     8  
     9  import (
    10  	"strings"
    11  
    12  	"github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/statedb"
    13  )
    14  
    15  // combinedIterator implements the interface statedb.ResultsIterator.
    16  // Internally, it maintains two iterators
    17  // - (1) dbItr - an iterator that iterates over keys present in the db
    18  // - (2) updatesItr - an iterator that iterates over keys present in the update batch
    19  //       (i.e, the keys that are inserted/updated/deleted by preceding valid transactions
    20  //        in the block and to be committed to the db as a part of block commit operation)
    21  //
    22  // This can be used where the caller wants to see what would be the final results of
    23  // iterating over a key range if the modifications of the preceding valid transactions
    24  // were to be applied to the db
    25  //
    26  // This can be used to perform validation for phantom reads in a transactions rwset
    27  type combinedIterator struct {
    28  	// input
    29  	ns            string
    30  	db            statedb.VersionedDB
    31  	updates       *statedb.UpdateBatch
    32  	endKey        string
    33  	includeEndKey bool
    34  
    35  	// internal state
    36  	dbItr        statedb.ResultsIterator
    37  	updatesItr   statedb.ResultsIterator
    38  	dbItem       *statedb.VersionedKV
    39  	updatesItem  *statedb.VersionedKV
    40  	endKeyServed bool
    41  }
    42  
    43  func newCombinedIterator(db statedb.VersionedDB, updates *statedb.UpdateBatch,
    44  	ns string, startKey string, endKey string, includeEndKey bool) (*combinedIterator, error) {
    45  	var dbItr statedb.ResultsIterator
    46  	var updatesItr statedb.ResultsIterator
    47  	var err error
    48  	if dbItr, err = db.GetStateRangeScanIterator(ns, startKey, endKey); err != nil {
    49  		return nil, err
    50  	}
    51  	updatesItr = updates.GetRangeScanIterator(ns, startKey, endKey)
    52  	var dbItem, updatesItem *statedb.VersionedKV
    53  	if dbItem, err = dbItr.Next(); err != nil {
    54  		return nil, err
    55  	}
    56  	if updatesItem, err = updatesItr.Next(); err != nil {
    57  		return nil, err
    58  	}
    59  	logger.Debugf("Combined iterator initialized. dbItem=%#v, updatesItem=%#v", dbItem, updatesItem)
    60  	return &combinedIterator{
    61  		ns, db, updates, endKey, includeEndKey,
    62  		dbItr, updatesItr, dbItem, updatesItem, false,
    63  	}, nil
    64  }
    65  
    66  // Next returns the KV from either dbItr or updatesItr that gives the next smaller key
    67  // If both gives the same keys, then it returns the KV from updatesItr.
    68  func (itr *combinedIterator) Next() (*statedb.VersionedKV, error) {
    69  	if itr.dbItem == nil && itr.updatesItem == nil {
    70  		logger.Debugf("dbItem and updatesItem both are nil.")
    71  		return itr.serveEndKeyIfNeeded()
    72  	}
    73  	var moveDBItr bool
    74  	var moveUpdatesItr bool
    75  	var selectedItem *statedb.VersionedKV
    76  	compResult := compareKeys(itr.dbItem, itr.updatesItem)
    77  	logger.Debugf("compResult=%d", compResult)
    78  	switch compResult {
    79  	case -1:
    80  		// dbItem is smaller
    81  		selectedItem = itr.dbItem
    82  		moveDBItr = true
    83  	case 0:
    84  		// both items are same so, choose the updatesItem (latest)
    85  		selectedItem = itr.updatesItem
    86  		moveUpdatesItr = true
    87  		moveDBItr = true
    88  	case 1:
    89  		// updatesItem is smaller
    90  		selectedItem = itr.updatesItem
    91  		moveUpdatesItr = true
    92  	}
    93  	var err error
    94  	if moveDBItr {
    95  		if itr.dbItem, err = itr.dbItr.Next(); err != nil {
    96  			return nil, err
    97  		}
    98  	}
    99  
   100  	if moveUpdatesItr {
   101  		if itr.updatesItem, err = itr.updatesItr.Next(); err != nil {
   102  			return nil, err
   103  		}
   104  	}
   105  	if isDelete(selectedItem) {
   106  		return itr.Next()
   107  	}
   108  	logger.Debugf("Returning item=%#v. Next dbItem=%#v, Next updatesItem=%#v", selectedItem, itr.dbItem, itr.updatesItem)
   109  	return selectedItem, nil
   110  }
   111  
   112  func (itr *combinedIterator) Close() {
   113  	itr.dbItr.Close()
   114  }
   115  
   116  func (itr *combinedIterator) GetBookmarkAndClose() string {
   117  	itr.Close()
   118  	return ""
   119  }
   120  
   121  // serveEndKeyIfNeeded returns the endKey only once and only if includeEndKey was set to true
   122  // in the constructor of combinedIterator.
   123  func (itr *combinedIterator) serveEndKeyIfNeeded() (*statedb.VersionedKV, error) {
   124  	if !itr.includeEndKey || itr.endKeyServed {
   125  		logger.Debugf("Endkey not to be served. Returning nil... [toInclude=%t, alreadyServed=%t]",
   126  			itr.includeEndKey, itr.endKeyServed)
   127  		return nil, nil
   128  	}
   129  	logger.Debug("Serving the endKey")
   130  	var vv *statedb.VersionedValue
   131  	var err error
   132  	vv = itr.updates.Get(itr.ns, itr.endKey)
   133  	logger.Debugf("endKey value from updates:%s", vv)
   134  	if vv == nil {
   135  		if vv, err = itr.db.GetState(itr.ns, itr.endKey); err != nil {
   136  			return nil, err
   137  		}
   138  		logger.Debugf("endKey value from stateDB:%s", vv)
   139  	}
   140  	itr.endKeyServed = true
   141  	if vv == nil {
   142  		return nil, nil
   143  	}
   144  	vkv := &statedb.VersionedKV{
   145  		CompositeKey: &statedb.CompositeKey{
   146  			Namespace: itr.ns,
   147  			Key:       itr.endKey,
   148  		},
   149  		VersionedValue: &statedb.VersionedValue{
   150  			Value:   vv.Value,
   151  			Version: vv.Version,
   152  		},
   153  	}
   154  
   155  	if isDelete(vkv) {
   156  		return nil, nil
   157  	}
   158  	return vkv, nil
   159  }
   160  
   161  func compareKeys(item1 *statedb.VersionedKV, item2 *statedb.VersionedKV) int {
   162  	if item1 == nil {
   163  		if item2 == nil {
   164  			return 0
   165  		}
   166  		return 1
   167  	}
   168  	if item2 == nil {
   169  		return -1
   170  	}
   171  	// at this stage both items are not nil
   172  	return strings.Compare(item1.Key, item2.Key)
   173  }
   174  
   175  func isDelete(item *statedb.VersionedKV) bool {
   176  	return item.Value == nil
   177  }