github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/pvtdatastorage/store.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package pvtdatastorage
     8  
     9  import (
    10  	"sync"
    11  	"sync/atomic"
    12  	"time"
    13  
    14  	"github.com/golang/protobuf/proto"
    15  	"github.com/hechain20/hechain/common/flogging"
    16  	"github.com/hechain20/hechain/common/ledger/util/leveldbhelper"
    17  	"github.com/hechain20/hechain/core/ledger"
    18  	"github.com/hechain20/hechain/core/ledger/confighistory"
    19  	"github.com/hechain20/hechain/core/ledger/pvtdatapolicy"
    20  	"github.com/hyperledger/fabric-protos-go/ledger/rwset"
    21  	"github.com/pkg/errors"
    22  	"github.com/willf/bitset"
    23  )
    24  
    25  var logger = flogging.MustGetLogger("pvtdatastorage")
    26  
    27  // Provider provides handle to specific 'Store' that in turn manages
    28  // private write sets for a ledger
    29  type Provider struct {
    30  	dbProvider *leveldbhelper.Provider
    31  	pvtData    *PrivateDataConfig
    32  }
    33  
    34  // PrivateDataConfig encapsulates the configuration for private data storage on the ledger
    35  type PrivateDataConfig struct {
    36  	// PrivateDataConfig is used to configure a private data storage provider
    37  	*ledger.PrivateDataConfig
    38  	// StorePath is the filesystem path for private data storage.
    39  	// It is internally computed by the ledger component,
    40  	// so it is not in ledger.PrivateDataConfig and not exposed to other components.
    41  	StorePath string
    42  }
    43  
    44  // Store manages the permanent storage of private write sets for a ledger
    45  type Store struct {
    46  	db              *leveldbhelper.DBHandle
    47  	ledgerid        string
    48  	btlPolicy       pvtdatapolicy.BTLPolicy
    49  	batchesInterval int
    50  	maxBatchSize    int
    51  	purgeInterval   uint64
    52  
    53  	isEmpty            bool
    54  	lastCommittedBlock uint64
    55  	bootsnapshotInfo   *bootsnapshotInfo
    56  
    57  	purgerLock      sync.Mutex
    58  	collElgProcSync *collElgProcSync
    59  	// After committing the pvtdata of old blocks,
    60  	// the `isLastUpdatedOldBlocksSet` is set to true.
    61  	// Once the stateDB is updated with these pvtdata,
    62  	// the `isLastUpdatedOldBlocksSet` is set to false.
    63  	// isLastUpdatedOldBlocksSet is mainly used during the
    64  	// recovery process. During the peer startup, if the
    65  	// isLastUpdatedOldBlocksSet is set to true, the pvtdata
    66  	// in the stateDB needs to be updated before finishing the
    67  	// recovery operation.
    68  	isLastUpdatedOldBlocksSet bool
    69  
    70  	deprioritizedDataReconcilerInterval time.Duration
    71  	accessDeprioMissingDataAfter        time.Time
    72  }
    73  
    74  type bootsnapshotInfo struct {
    75  	createdFromSnapshot bool
    76  	lastBlockInSnapshot uint64
    77  }
    78  
    79  type blkTranNumKey []byte
    80  
    81  type dataEntry struct {
    82  	key   *dataKey
    83  	value *rwset.CollectionPvtReadWriteSet
    84  }
    85  
    86  type expiryEntry struct {
    87  	key   *expiryKey
    88  	value *ExpiryData
    89  }
    90  
    91  type expiryKey struct {
    92  	expiringBlk   uint64
    93  	committingBlk uint64
    94  }
    95  
    96  type nsCollBlk struct {
    97  	ns, coll string
    98  	blkNum   uint64
    99  }
   100  
   101  type dataKey struct {
   102  	nsCollBlk
   103  	txNum uint64
   104  }
   105  
   106  type missingDataKey struct {
   107  	nsCollBlk
   108  }
   109  
   110  type bootKVHashesKey struct {
   111  	blkNum uint64
   112  	txNum  uint64
   113  	ns     string
   114  	coll   string
   115  }
   116  
   117  type storeEntries struct {
   118  	dataEntries             []*dataEntry
   119  	expiryEntries           []*expiryEntry
   120  	elgMissingDataEntries   map[missingDataKey]*bitset.BitSet
   121  	inelgMissingDataEntries map[missingDataKey]*bitset.BitSet
   122  }
   123  
   124  // lastUpdatedOldBlocksList keeps the list of last updated blocks
   125  // and is stored as the value of lastUpdatedOldBlocksKey (defined in kv_encoding.go)
   126  type lastUpdatedOldBlocksList []uint64
   127  
   128  //////// Provider functions  /////////////
   129  //////////////////////////////////////////
   130  
   131  // NewProvider instantiates a StoreProvider
   132  func NewProvider(conf *PrivateDataConfig) (*Provider, error) {
   133  	dbProvider, err := leveldbhelper.NewProvider(&leveldbhelper.Conf{DBPath: conf.StorePath})
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	return &Provider{
   138  		dbProvider: dbProvider,
   139  		pvtData:    conf,
   140  	}, nil
   141  }
   142  
   143  // SnapshotDataImporterFor returns an implementation of interface privacyenabledstate.SnapshotPvtdataHashesConsumer
   144  // The returned struct is expected to be registered for receiving the pvtdata hashes from snapshot and loads the data
   145  // into pvtdata store.
   146  func (p *Provider) SnapshotDataImporterFor(
   147  	ledgerID string,
   148  	lastBlockInSnapshot uint64,
   149  	membershipProvider ledger.MembershipInfoProvider,
   150  	configHistoryRetriever *confighistory.Retriever,
   151  	tempDirRoot string,
   152  ) (*SnapshotDataImporter, error) {
   153  	db := p.dbProvider.GetDBHandle(ledgerID)
   154  	batch := db.NewUpdateBatch()
   155  	batch.Put(lastBlockInBootSnapshotKey, encodeLastBlockInBootSnapshotVal(lastBlockInSnapshot))
   156  	batch.Put(lastCommittedBlkkey, encodeLastCommittedBlockVal(lastBlockInSnapshot))
   157  	if err := db.WriteBatch(batch, true); err != nil {
   158  		return nil, errors.WithMessage(err, "error while writing snapshot info to db")
   159  	}
   160  
   161  	return newSnapshotDataImporter(
   162  		ledgerID,
   163  		p.dbProvider.GetDBHandle(ledgerID),
   164  		membershipProvider,
   165  		configHistoryRetriever,
   166  		tempDirRoot,
   167  	)
   168  }
   169  
   170  // OpenStore returns a handle to a store
   171  func (p *Provider) OpenStore(ledgerid string) (*Store, error) {
   172  	dbHandle := p.dbProvider.GetDBHandle(ledgerid)
   173  	s := &Store{
   174  		db:                                  dbHandle,
   175  		ledgerid:                            ledgerid,
   176  		batchesInterval:                     p.pvtData.BatchesInterval,
   177  		maxBatchSize:                        p.pvtData.MaxBatchSize,
   178  		purgeInterval:                       uint64(p.pvtData.PurgeInterval),
   179  		deprioritizedDataReconcilerInterval: p.pvtData.DeprioritizedDataReconcilerInterval,
   180  		accessDeprioMissingDataAfter:        time.Now().Add(p.pvtData.DeprioritizedDataReconcilerInterval),
   181  		collElgProcSync: &collElgProcSync{
   182  			notification: make(chan bool, 1),
   183  			procComplete: make(chan bool, 1),
   184  		},
   185  	}
   186  	if err := s.initState(); err != nil {
   187  		return nil, err
   188  	}
   189  	s.launchCollElgProc()
   190  	logger.Debugf("Pvtdata store opened. Initial state: isEmpty [%t], lastCommittedBlock [%d]",
   191  		s.isEmpty, s.lastCommittedBlock)
   192  	return s, nil
   193  }
   194  
   195  // Close closes the store
   196  func (p *Provider) Close() {
   197  	p.dbProvider.Close()
   198  }
   199  
   200  // Drop drops channel-specific data from the pvtdata store
   201  func (p *Provider) Drop(ledgerid string) error {
   202  	return p.dbProvider.Drop(ledgerid)
   203  }
   204  
   205  //////// store functions  ////////////////
   206  //////////////////////////////////////////
   207  
   208  func (s *Store) initState() error {
   209  	var err error
   210  	if s.isEmpty, s.lastCommittedBlock, err = s.getLastCommittedBlockNum(); err != nil {
   211  		return err
   212  	}
   213  
   214  	if s.bootsnapshotInfo, err = s.fetchBootSnapshotInfo(); err != nil {
   215  		return err
   216  	}
   217  
   218  	// TODO: FAB-16298 -- the concept of pendingBatch is no longer valid
   219  	// for pvtdataStore. We can remove it v2.1. We retain the concept in
   220  	// v2.0 to allow rolling upgrade from v142 to v2.0
   221  	batchPending, err := s.hasPendingCommit()
   222  	if err != nil {
   223  		return err
   224  	}
   225  
   226  	if batchPending {
   227  		committingBlockNum := s.nextBlockNum()
   228  		batch := s.db.NewUpdateBatch()
   229  		batch.Put(lastCommittedBlkkey, encodeLastCommittedBlockVal(committingBlockNum))
   230  		batch.Delete(pendingCommitKey)
   231  		if err := s.db.WriteBatch(batch, true); err != nil {
   232  			return err
   233  		}
   234  		s.isEmpty = false
   235  		s.lastCommittedBlock = committingBlockNum
   236  	}
   237  
   238  	var blist lastUpdatedOldBlocksList
   239  	if blist, err = s.getLastUpdatedOldBlocksList(); err != nil {
   240  		return err
   241  	}
   242  	if len(blist) > 0 {
   243  		s.isLastUpdatedOldBlocksSet = true
   244  	} // false if not set
   245  
   246  	return nil
   247  }
   248  
   249  // Init initializes the store. This function is expected to be invoked before using the store
   250  func (s *Store) Init(btlPolicy pvtdatapolicy.BTLPolicy) {
   251  	s.btlPolicy = btlPolicy
   252  }
   253  
   254  // Commit commits the pvt data as well as both the eligible and ineligible
   255  // missing private data --- `eligible` denotes that the missing private data belongs to a collection
   256  // for which this peer is a member; `ineligible` denotes that the missing private data belong to a
   257  // collection for which this peer is not a member.
   258  func (s *Store) Commit(blockNum uint64, pvtData []*ledger.TxPvtData, missingPvtData ledger.TxMissingPvtData) error {
   259  	expectedBlockNum := s.nextBlockNum()
   260  	if expectedBlockNum != blockNum {
   261  		return errors.Errorf("expected block number=%d, received block number=%d", expectedBlockNum, blockNum)
   262  	}
   263  
   264  	batch := s.db.NewUpdateBatch()
   265  	var err error
   266  	var key, val []byte
   267  
   268  	storeEntries, err := prepareStoreEntries(blockNum, pvtData, s.btlPolicy, missingPvtData)
   269  	if err != nil {
   270  		return err
   271  	}
   272  
   273  	for _, dataEntry := range storeEntries.dataEntries {
   274  		key = encodeDataKey(dataEntry.key)
   275  		if val, err = encodeDataValue(dataEntry.value); err != nil {
   276  			return err
   277  		}
   278  		batch.Put(key, val)
   279  	}
   280  
   281  	for _, expiryEntry := range storeEntries.expiryEntries {
   282  		key = encodeExpiryKey(expiryEntry.key)
   283  		if val, err = encodeExpiryValue(expiryEntry.value); err != nil {
   284  			return err
   285  		}
   286  		batch.Put(key, val)
   287  	}
   288  
   289  	for missingDataKey, missingDataValue := range storeEntries.elgMissingDataEntries {
   290  		key = encodeElgPrioMissingDataKey(&missingDataKey)
   291  
   292  		if val, err = encodeMissingDataValue(missingDataValue); err != nil {
   293  			return err
   294  		}
   295  		batch.Put(key, val)
   296  	}
   297  
   298  	for missingDataKey, missingDataValue := range storeEntries.inelgMissingDataEntries {
   299  		key = encodeInelgMissingDataKey(&missingDataKey)
   300  
   301  		if val, err = encodeMissingDataValue(missingDataValue); err != nil {
   302  			return err
   303  		}
   304  		batch.Put(key, val)
   305  	}
   306  
   307  	committingBlockNum := s.nextBlockNum()
   308  	logger.Debugf("Committing private data for block [%d]", committingBlockNum)
   309  	batch.Put(lastCommittedBlkkey, encodeLastCommittedBlockVal(committingBlockNum))
   310  	if err := s.db.WriteBatch(batch, true); err != nil {
   311  		return err
   312  	}
   313  
   314  	s.isEmpty = false
   315  	atomic.StoreUint64(&s.lastCommittedBlock, committingBlockNum)
   316  	logger.Debugf("Committed private data for block [%d]", committingBlockNum)
   317  	s.performPurgeIfScheduled(committingBlockNum)
   318  	return nil
   319  }
   320  
   321  // GetLastUpdatedOldBlocksPvtData returns the pvtdata of blocks listed in `lastUpdatedOldBlocksList`
   322  // TODO FAB-16293 -- GetLastUpdatedOldBlocksPvtData() can be removed either in v2.0 or in v2.1.
   323  // If we decide to rebuild stateDB in v2.0, by default, the rebuild logic would take
   324  // care of synching stateDB with pvtdataStore without calling GetLastUpdatedOldBlocksPvtData().
   325  // Hence, it can be safely removed. Suppose if we decide not to rebuild stateDB in v2.0,
   326  // we can remove this function in v2.1.
   327  func (s *Store) GetLastUpdatedOldBlocksPvtData() (map[uint64][]*ledger.TxPvtData, error) {
   328  	if !s.isLastUpdatedOldBlocksSet {
   329  		return nil, nil
   330  	}
   331  
   332  	updatedBlksList, err := s.getLastUpdatedOldBlocksList()
   333  	if err != nil {
   334  		return nil, err
   335  	}
   336  
   337  	blksPvtData := make(map[uint64][]*ledger.TxPvtData)
   338  	for _, blkNum := range updatedBlksList {
   339  		if blksPvtData[blkNum], err = s.GetPvtDataByBlockNum(blkNum, nil); err != nil {
   340  			return nil, err
   341  		}
   342  	}
   343  	return blksPvtData, nil
   344  }
   345  
   346  func (s *Store) getLastUpdatedOldBlocksList() ([]uint64, error) {
   347  	var v []byte
   348  	var err error
   349  	if v, err = s.db.Get(lastUpdatedOldBlocksKey); err != nil {
   350  		return nil, err
   351  	}
   352  	if v == nil {
   353  		return nil, nil
   354  	}
   355  
   356  	var updatedBlksList []uint64
   357  	buf := proto.NewBuffer(v)
   358  	numBlks, err := buf.DecodeVarint()
   359  	if err != nil {
   360  		return nil, err
   361  	}
   362  	for i := 0; i < int(numBlks); i++ {
   363  		blkNum, err := buf.DecodeVarint()
   364  		if err != nil {
   365  			return nil, err
   366  		}
   367  		updatedBlksList = append(updatedBlksList, blkNum)
   368  	}
   369  	return updatedBlksList, nil
   370  }
   371  
   372  // TODO FAB-16294 -- ResetLastUpdatedOldBlocksList() can be removed in v2.1.
   373  // From v2.0 onwards, we do not store the last updatedBlksList. Only to support
   374  // the rolling upgrade from v142 to v2.0, we retain the ResetLastUpdatedOldBlocksList()
   375  // in v2.0.
   376  
   377  // ResetLastUpdatedOldBlocksList removes the `lastUpdatedOldBlocksList` entry from the store
   378  func (s *Store) ResetLastUpdatedOldBlocksList() error {
   379  	batch := s.db.NewUpdateBatch()
   380  	batch.Delete(lastUpdatedOldBlocksKey)
   381  	if err := s.db.WriteBatch(batch, true); err != nil {
   382  		return err
   383  	}
   384  	s.isLastUpdatedOldBlocksSet = false
   385  	return nil
   386  }
   387  
   388  // GetPvtDataByBlockNum returns only the pvt data  corresponding to the given block number
   389  // The pvt data is filtered by the list of 'ns/collections' supplied in the filter
   390  // A nil filter does not filter any results
   391  func (s *Store) GetPvtDataByBlockNum(blockNum uint64, filter ledger.PvtNsCollFilter) ([]*ledger.TxPvtData, error) {
   392  	logger.Debugf("Get private data for block [%d], filter=%#v", blockNum, filter)
   393  	if s.isEmpty {
   394  		return nil, errors.New("the store is empty")
   395  	}
   396  	lastCommittedBlock := atomic.LoadUint64(&s.lastCommittedBlock)
   397  	if blockNum > lastCommittedBlock {
   398  		return nil, errors.Errorf("last committed block number [%d] smaller than the requested block number [%d]", lastCommittedBlock, blockNum)
   399  	}
   400  	startKey, endKey := getDataKeysForRangeScanByBlockNum(blockNum)
   401  	logger.Debugf("Querying private data storage for write sets using startKey=%#v, endKey=%#v", startKey, endKey)
   402  	itr, err := s.db.GetIterator(startKey, endKey)
   403  	if err != nil {
   404  		return nil, err
   405  	}
   406  	defer itr.Release()
   407  
   408  	var blockPvtdata []*ledger.TxPvtData
   409  	var currentTxNum uint64
   410  	var currentTxWsetAssember *txPvtdataAssembler
   411  	firstItr := true
   412  
   413  	for itr.Next() {
   414  		dataKeyBytes := itr.Key()
   415  		v11Fmt, err := v11Format(dataKeyBytes)
   416  		if err != nil {
   417  			return nil, err
   418  		}
   419  		if v11Fmt {
   420  			return v11RetrievePvtdata(itr, filter)
   421  		}
   422  		dataValueBytes := itr.Value()
   423  		dataKey, err := decodeDatakey(dataKeyBytes)
   424  		if err != nil {
   425  			return nil, err
   426  		}
   427  		expired, err := isExpired(dataKey.nsCollBlk, s.btlPolicy, lastCommittedBlock)
   428  		if err != nil {
   429  			return nil, err
   430  		}
   431  		if expired || !passesFilter(dataKey, filter) {
   432  			continue
   433  		}
   434  		dataValue, err := decodeDataValue(dataValueBytes)
   435  		if err != nil {
   436  			return nil, err
   437  		}
   438  
   439  		if firstItr {
   440  			currentTxNum = dataKey.txNum
   441  			currentTxWsetAssember = newTxPvtdataAssembler(blockNum, currentTxNum)
   442  			firstItr = false
   443  		}
   444  
   445  		if dataKey.txNum != currentTxNum {
   446  			blockPvtdata = append(blockPvtdata, currentTxWsetAssember.getTxPvtdata())
   447  			currentTxNum = dataKey.txNum
   448  			currentTxWsetAssember = newTxPvtdataAssembler(blockNum, currentTxNum)
   449  		}
   450  		currentTxWsetAssember.add(dataKey.ns, dataValue)
   451  	}
   452  	if currentTxWsetAssember != nil {
   453  		blockPvtdata = append(blockPvtdata, currentTxWsetAssember.getTxPvtdata())
   454  	}
   455  	return blockPvtdata, nil
   456  }
   457  
   458  // GetMissingPvtDataInfoForMostRecentBlocks returns the missing private data information for the
   459  // most recent `maxBlock` blocks which miss at least a private data of a eligible collection.
   460  func (s *Store) GetMissingPvtDataInfoForMostRecentBlocks(maxBlock int) (ledger.MissingPvtDataInfo, error) {
   461  	// we assume that this function would be called by the gossip only after processing the
   462  	// last retrieved missing pvtdata info and committing the same.
   463  	if maxBlock < 1 {
   464  		return nil, nil
   465  	}
   466  
   467  	if time.Now().After(s.accessDeprioMissingDataAfter) {
   468  		s.accessDeprioMissingDataAfter = time.Now().Add(s.deprioritizedDataReconcilerInterval)
   469  		logger.Debug("fetching missing pvtdata entries from the deprioritized list")
   470  		return s.getMissingData(elgDeprioritizedMissingDataGroup, maxBlock)
   471  	}
   472  
   473  	logger.Debug("fetching missing pvtdata entries from the prioritized list")
   474  	return s.getMissingData(elgPrioritizedMissingDataGroup, maxBlock)
   475  }
   476  
   477  func (s *Store) getMissingData(group []byte, maxBlock int) (ledger.MissingPvtDataInfo, error) {
   478  	missingPvtDataInfo := make(ledger.MissingPvtDataInfo)
   479  	numberOfBlockProcessed := 0
   480  	lastProcessedBlock := uint64(0)
   481  	isMaxBlockLimitReached := false
   482  
   483  	// as we are not acquiring a read lock, new blocks can get committed while we
   484  	// construct the MissingPvtDataInfo. As a result, lastCommittedBlock can get
   485  	// changed. To ensure consistency, we atomically load the lastCommittedBlock value
   486  	lastCommittedBlock := atomic.LoadUint64(&s.lastCommittedBlock)
   487  
   488  	startKey, endKey := createRangeScanKeysForElgMissingData(lastCommittedBlock, group)
   489  	dbItr, err := s.db.GetIterator(startKey, endKey)
   490  	if err != nil {
   491  		return nil, err
   492  	}
   493  	defer dbItr.Release()
   494  
   495  	for dbItr.Next() {
   496  		missingDataKeyBytes := dbItr.Key()
   497  		missingDataKey := decodeElgMissingDataKey(missingDataKeyBytes)
   498  
   499  		if isMaxBlockLimitReached && (missingDataKey.blkNum != lastProcessedBlock) {
   500  			// ensures that exactly maxBlock number
   501  			// of blocks' entries are processed
   502  			break
   503  		}
   504  
   505  		// check whether the entry is expired. If so, move to the next item.
   506  		// As we may use the old lastCommittedBlock value, there is a possibility that
   507  		// this missing data is actually expired but we may get the stale information.
   508  		// Though it may leads to extra work of pulling the expired data, it will not
   509  		// affect the correctness. Further, as we try to fetch the most recent missing
   510  		// data (less possibility of expiring now), such scenario would be rare. In the
   511  		// best case, we can load the latest lastCommittedBlock value here atomically to
   512  		// make this scenario very rare.
   513  		lastCommittedBlock = atomic.LoadUint64(&s.lastCommittedBlock)
   514  		expired, err := isExpired(missingDataKey.nsCollBlk, s.btlPolicy, lastCommittedBlock)
   515  		if err != nil {
   516  			return nil, err
   517  		}
   518  		if expired {
   519  			continue
   520  		}
   521  
   522  		// check for an existing entry for the blkNum in the MissingPvtDataInfo.
   523  		// If no such entry exists, create one. Also, keep track of the number of
   524  		// processed block due to maxBlock limit.
   525  		if _, ok := missingPvtDataInfo[missingDataKey.blkNum]; !ok {
   526  			numberOfBlockProcessed++
   527  			if numberOfBlockProcessed == maxBlock {
   528  				isMaxBlockLimitReached = true
   529  				// as there can be more than one entry for this block,
   530  				// we cannot `break` here
   531  				lastProcessedBlock = missingDataKey.blkNum
   532  			}
   533  		}
   534  
   535  		valueBytes := dbItr.Value()
   536  		bitmap, err := decodeMissingDataValue(valueBytes)
   537  		if err != nil {
   538  			return nil, err
   539  		}
   540  
   541  		// for each transaction which misses private data, make an entry in missingBlockPvtDataInfo
   542  		for index, isSet := bitmap.NextSet(0); isSet; index, isSet = bitmap.NextSet(index + 1) {
   543  			txNum := uint64(index)
   544  			missingPvtDataInfo.Add(missingDataKey.blkNum, txNum, missingDataKey.ns, missingDataKey.coll)
   545  		}
   546  	}
   547  
   548  	return missingPvtDataInfo, nil
   549  }
   550  
   551  // FetchBootKVHashes returns the KVHashes from the data that was loaded from a snapshot at the time of
   552  // bootstrapping. This function returns an error if the supplied blkNum is greater than the last block
   553  // number in the booting snapshot
   554  func (s *Store) FetchBootKVHashes(blkNum, txNum uint64, ns, coll string) (map[string][]byte, error) {
   555  	if s.bootsnapshotInfo.createdFromSnapshot && blkNum > s.bootsnapshotInfo.lastBlockInSnapshot {
   556  		return nil, errors.New(
   557  			"unexpected call. Boot KV Hashes are persisted only for the data imported from snapshot",
   558  		)
   559  	}
   560  	encVal, err := s.db.Get(
   561  		encodeBootKVHashesKey(
   562  			&bootKVHashesKey{
   563  				blkNum: blkNum,
   564  				txNum:  txNum,
   565  				ns:     ns,
   566  				coll:   coll,
   567  			},
   568  		),
   569  	)
   570  	if err != nil || encVal == nil {
   571  		return nil, err
   572  	}
   573  	bootKVHashes, err := decodeBootKVHashesVal(encVal)
   574  	if err != nil {
   575  		return nil, err
   576  	}
   577  	return bootKVHashes.toMap(), nil
   578  }
   579  
   580  // ProcessCollsEligibilityEnabled notifies the store when the peer becomes eligible to receive data for an
   581  // existing collection. Parameter 'committingBlk' refers to the block number that contains the corresponding
   582  // collection upgrade transaction and the parameter 'nsCollMap' contains the collections for which the peer
   583  // is now eligible to receive pvt data
   584  func (s *Store) ProcessCollsEligibilityEnabled(committingBlk uint64, nsCollMap map[string][]string) error {
   585  	key := encodeCollElgKey(committingBlk)
   586  	m := newCollElgInfo(nsCollMap)
   587  	val, err := encodeCollElgVal(m)
   588  	if err != nil {
   589  		return err
   590  	}
   591  	batch := s.db.NewUpdateBatch()
   592  	batch.Put(key, val)
   593  	if err = s.db.WriteBatch(batch, true); err != nil {
   594  		return err
   595  	}
   596  	s.collElgProcSync.notify()
   597  	return nil
   598  }
   599  
   600  func (s *Store) performPurgeIfScheduled(latestCommittedBlk uint64) {
   601  	if latestCommittedBlk%s.purgeInterval != 0 {
   602  		return
   603  	}
   604  	go func() {
   605  		s.purgerLock.Lock()
   606  		logger.Debugf("Purger started: Purging expired private data till block number [%d]", latestCommittedBlk)
   607  		defer s.purgerLock.Unlock()
   608  		err := s.purgeExpiredData(0, latestCommittedBlk)
   609  		if err != nil {
   610  			logger.Warningf("Could not purge data from pvtdata store:%s", err)
   611  		}
   612  		logger.Debug("Purger finished")
   613  	}()
   614  }
   615  
   616  func (s *Store) purgeExpiredData(minBlkNum, maxBlkNum uint64) error {
   617  	expiryEntries, err := s.retrieveExpiryEntries(minBlkNum, maxBlkNum)
   618  	if err != nil || len(expiryEntries) == 0 {
   619  		return err
   620  	}
   621  
   622  	batch := s.db.NewUpdateBatch()
   623  	for _, expiryEntry := range expiryEntries {
   624  		batch.Delete(encodeExpiryKey(expiryEntry.key))
   625  		dataKeys, missingDataKeys, bootKVHashesKeys := deriveKeys(expiryEntry)
   626  
   627  		for _, dataKey := range dataKeys {
   628  			batch.Delete(encodeDataKey(dataKey))
   629  		}
   630  
   631  		for _, missingDataKey := range missingDataKeys {
   632  			batch.Delete(
   633  				encodeElgPrioMissingDataKey(missingDataKey),
   634  			)
   635  			batch.Delete(
   636  				encodeElgDeprioMissingDataKey(missingDataKey),
   637  			)
   638  			batch.Delete(
   639  				encodeInelgMissingDataKey(missingDataKey),
   640  			)
   641  		}
   642  
   643  		for _, bootKVHashesKey := range bootKVHashesKeys {
   644  			batch.Delete(encodeBootKVHashesKey(bootKVHashesKey))
   645  		}
   646  
   647  		if err := s.db.WriteBatch(batch, false); err != nil {
   648  			return err
   649  		}
   650  		batch.Reset()
   651  	}
   652  
   653  	logger.Infof("[%s] - [%d] Entries purged from private data storage till block number [%d]", s.ledgerid, len(expiryEntries), maxBlkNum)
   654  	return nil
   655  }
   656  
   657  func (s *Store) retrieveExpiryEntries(minBlkNum, maxBlkNum uint64) ([]*expiryEntry, error) {
   658  	startKey, endKey := getExpiryKeysForRangeScan(minBlkNum, maxBlkNum)
   659  	logger.Debugf("retrieveExpiryEntries(): startKey=%#v, endKey=%#v", startKey, endKey)
   660  	itr, err := s.db.GetIterator(startKey, endKey)
   661  	if err != nil {
   662  		return nil, err
   663  	}
   664  	defer itr.Release()
   665  
   666  	var expiryEntries []*expiryEntry
   667  	for itr.Next() {
   668  		expiryKeyBytes := itr.Key()
   669  		expiryValueBytes := itr.Value()
   670  		expiryKey, err := decodeExpiryKey(expiryKeyBytes)
   671  		if err != nil {
   672  			return nil, err
   673  		}
   674  		expiryValue, err := decodeExpiryValue(expiryValueBytes)
   675  		if err != nil {
   676  			return nil, err
   677  		}
   678  		expiryEntries = append(expiryEntries, &expiryEntry{key: expiryKey, value: expiryValue})
   679  	}
   680  	return expiryEntries, nil
   681  }
   682  
   683  func (s *Store) launchCollElgProc() {
   684  	go func() {
   685  		if err := s.processCollElgEvents(); err != nil {
   686  			// process collection eligibility events when store is opened -
   687  			// in case there is an unprocessed events from previous run
   688  			logger.Errorw("failed to process collection eligibility events", "err", err)
   689  		}
   690  		for {
   691  			logger.Debugf("Waiting for collection eligibility event")
   692  			s.collElgProcSync.waitForNotification()
   693  			if err := s.processCollElgEvents(); err != nil {
   694  				logger.Errorw("failed to process collection eligibility events", "err", err)
   695  			}
   696  			s.collElgProcSync.done()
   697  		}
   698  	}()
   699  }
   700  
   701  func (s *Store) processCollElgEvents() error {
   702  	logger.Debugf("Starting to process collection eligibility events")
   703  	s.purgerLock.Lock()
   704  	defer s.purgerLock.Unlock()
   705  	collElgStartKey, collElgEndKey := createRangeScanKeysForCollElg()
   706  	eventItr, err := s.db.GetIterator(collElgStartKey, collElgEndKey)
   707  	if err != nil {
   708  		return err
   709  	}
   710  	defer eventItr.Release()
   711  	batch := s.db.NewUpdateBatch()
   712  	totalEntriesConverted := 0
   713  
   714  	for eventItr.Next() {
   715  		collElgKey, collElgVal := eventItr.Key(), eventItr.Value()
   716  		blkNum := decodeCollElgKey(collElgKey)
   717  		CollElgInfo, err := decodeCollElgVal(collElgVal)
   718  		logger.Debugf("Processing collection eligibility event [blkNum=%d], CollElgInfo=%s", blkNum, CollElgInfo)
   719  		if err != nil {
   720  			logger.Errorf("This error is not expected %s", err)
   721  			continue
   722  		}
   723  		for ns, colls := range CollElgInfo.NsCollMap {
   724  			var coll string
   725  			for _, coll = range colls.Entries {
   726  				logger.Infof("Converting missing data entries from ineligible to eligible for [ns=%s, coll=%s]", ns, coll)
   727  				startKey, endKey := createRangeScanKeysForInelgMissingData(blkNum, ns, coll)
   728  				collItr, err := s.db.GetIterator(startKey, endKey)
   729  				if err != nil {
   730  					return err
   731  				}
   732  				collEntriesConverted := 0
   733  
   734  				for collItr.Next() { // each entry
   735  					originalKey, originalVal := collItr.Key(), collItr.Value()
   736  					modifiedKey := decodeInelgMissingDataKey(originalKey)
   737  					batch.Delete(originalKey)
   738  					copyVal := make([]byte, len(originalVal))
   739  					copy(copyVal, originalVal)
   740  					batch.Put(
   741  						encodeElgPrioMissingDataKey(modifiedKey),
   742  						copyVal,
   743  					)
   744  					collEntriesConverted++
   745  					if batch.Len() > s.maxBatchSize {
   746  						if err := s.db.WriteBatch(batch, true); err != nil {
   747  							return err
   748  						}
   749  						batch.Reset()
   750  						sleepTime := time.Duration(s.batchesInterval)
   751  						logger.Infof("Going to sleep for %d milliseconds between batches. Entries for [ns=%s, coll=%s] converted so far = %d",
   752  							sleepTime, ns, coll, collEntriesConverted)
   753  						s.purgerLock.Unlock()
   754  						time.Sleep(sleepTime * time.Millisecond)
   755  						s.purgerLock.Lock()
   756  					}
   757  				} // entry loop
   758  
   759  				collItr.Release()
   760  				logger.Infof("Converted all [%d] entries for [ns=%s, coll=%s]", collEntriesConverted, ns, coll)
   761  				totalEntriesConverted += collEntriesConverted
   762  			} // coll loop
   763  		} // ns loop
   764  		batch.Delete(collElgKey) // delete the collection eligibility event key as well
   765  	} // event loop
   766  
   767  	if err := s.db.WriteBatch(batch, true); err != nil {
   768  		return err
   769  	}
   770  	logger.Debugf("Converted [%d] ineligible missing data entries to eligible", totalEntriesConverted)
   771  	return nil
   772  }
   773  
   774  // LastCommittedBlockHeight returns the height of the last committed block
   775  func (s *Store) LastCommittedBlockHeight() (uint64, error) {
   776  	if s.isEmpty {
   777  		return 0, nil
   778  	}
   779  	return atomic.LoadUint64(&s.lastCommittedBlock) + 1, nil
   780  }
   781  
   782  func (s *Store) nextBlockNum() uint64 {
   783  	if s.isEmpty {
   784  		return 0
   785  	}
   786  	return atomic.LoadUint64(&s.lastCommittedBlock) + 1
   787  }
   788  
   789  // TODO: FAB-16298 -- the concept of pendingBatch is no longer valid
   790  // for pvtdataStore. We can remove it v2.1. We retain the concept in
   791  // v2.0 to allow rolling upgrade from v142 to v2.0
   792  func (s *Store) hasPendingCommit() (bool, error) {
   793  	var v []byte
   794  	var err error
   795  	if v, err = s.db.Get(pendingCommitKey); err != nil {
   796  		return false, err
   797  	}
   798  	return v != nil, nil
   799  }
   800  
   801  func (s *Store) getLastCommittedBlockNum() (bool, uint64, error) {
   802  	var v []byte
   803  	var err error
   804  	if v, err = s.db.Get(lastCommittedBlkkey); v == nil || err != nil {
   805  		return true, 0, err
   806  	}
   807  	return false, decodeLastCommittedBlockVal(v), nil
   808  }
   809  
   810  func (s *Store) fetchBootSnapshotInfo() (*bootsnapshotInfo, error) {
   811  	v, err := s.db.Get(lastBlockInBootSnapshotKey)
   812  	if err != nil {
   813  		return nil, err
   814  	}
   815  	if v == nil {
   816  		return &bootsnapshotInfo{}, nil
   817  	}
   818  
   819  	lastBlkInSnapshot, err := decodeLastBlockInBootSnapshotVal(v)
   820  	if err != nil {
   821  		return nil, err
   822  	}
   823  
   824  	return &bootsnapshotInfo{
   825  		createdFromSnapshot: true,
   826  		lastBlockInSnapshot: lastBlkInSnapshot,
   827  	}, nil
   828  }
   829  
   830  type collElgProcSync struct {
   831  	notification, procComplete chan bool
   832  }
   833  
   834  func (c *collElgProcSync) notify() {
   835  	select {
   836  	case c.notification <- true:
   837  		logger.Debugf("Signaled to collection eligibility processing routine")
   838  	default: // noop
   839  		logger.Debugf("Previous signal still pending. Skipping new signal")
   840  	}
   841  }
   842  
   843  func (c *collElgProcSync) waitForNotification() {
   844  	<-c.notification
   845  }
   846  
   847  func (c *collElgProcSync) done() {
   848  	select {
   849  	case c.procComplete <- true:
   850  	default:
   851  	}
   852  }
   853  
   854  func (c *collElgProcSync) waitForDone() {
   855  	<-c.procComplete
   856  }