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

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package pvtstatepurgemgmt
     8  
     9  import (
    10  	proto "github.com/golang/protobuf/proto"
    11  	"github.com/hechain20/hechain/common/flogging"
    12  	"github.com/hechain20/hechain/common/ledger/util"
    13  	"github.com/hechain20/hechain/common/ledger/util/leveldbhelper"
    14  	"github.com/hechain20/hechain/core/ledger/kvledger/bookkeeping"
    15  )
    16  
    17  var logger = flogging.MustGetLogger("pvtstatepurgemgmt")
    18  
    19  const (
    20  	expiryPrefix = '1'
    21  )
    22  
    23  type expiryKeeper struct {
    24  	db *leveldbhelper.DBHandle
    25  }
    26  
    27  // expiryInfo encapsulates an 'expiryInfoKey' and corresponding private data keys.
    28  // In another words, this struct encapsulates the keys and key-hashes that are committed by
    29  // the block number 'expiryInfoKey.committingBlk' and should be expired (and hence purged)
    30  // with the commit of block number 'expiryInfoKey.expiryBlk'
    31  type expiryInfo struct {
    32  	expiryInfoKey *expiryInfoKey
    33  	pvtdataKeys   *PvtdataKeys
    34  }
    35  
    36  // expiryInfoKey is used as a key of an entry in the expiryKeeper (backed by a leveldb instance)
    37  type expiryInfoKey struct {
    38  	committingBlk uint64
    39  	expiryBlk     uint64
    40  }
    41  
    42  func newExpiryKeeper(ledgerid string, provider *bookkeeping.Provider) *expiryKeeper {
    43  	return &expiryKeeper{provider.GetDBHandle(ledgerid, bookkeeping.PvtdataExpiry)}
    44  }
    45  
    46  // update keeps track of the list of keys and their corresponding expiry block number
    47  // 'toTrack' parameter causes new entries in the expiryKeeper and  'toClear' parameter contains the entries that
    48  // are to be removed from the expiryKeeper. This function is invoked with the commit of every block. As an
    49  // example, the commit of the block with block number 50, 'toTrack' parameter may contain following two entries:
    50  // (1) &expiryInfo{&expiryInfoKey{committingBlk: 50, expiryBlk: 55}, pvtdataKeys....} and
    51  // (2) &expiryInfo{&expiryInfoKey{committingBlk: 50, expiryBlk: 60}, pvtdataKeys....}
    52  // The 'pvtdataKeys' in the first entry contains all the keys (and key-hashes) that are to be expired at block 55 (i.e., these collections have a BTL configured to 4)
    53  // and the 'pvtdataKeys' in second entry contains all the keys (and key-hashes) that are to be expired at block 60 (i.e., these collections have a BTL configured to 9).
    54  // Similarly, continuing with the above example, the parameter 'toClear' may contain following two entries
    55  // (1) &expiryInfoKey{committingBlk: 45, expiryBlk: 50} and (2) &expiryInfoKey{committingBlk: 40, expiryBlk: 50}. The first entry was created
    56  // at the time of the commit of the block number 45 and the second entry was created at the time of the commit of the block number 40, however
    57  // both are expiring with the commit of block number 50.
    58  func (ek *expiryKeeper) update(toTrack []*expiryInfo, toClear []*expiryInfoKey) error {
    59  	updateBatch := ek.db.NewUpdateBatch()
    60  	for _, expinfo := range toTrack {
    61  		k, v, err := encodeKV(expinfo)
    62  		if err != nil {
    63  			return err
    64  		}
    65  		updateBatch.Put(k, v)
    66  	}
    67  	for _, expinfokey := range toClear {
    68  		updateBatch.Delete(encodeExpiryInfoKey(expinfokey))
    69  	}
    70  	return ek.db.WriteBatch(updateBatch, true)
    71  }
    72  
    73  // retrieve returns the keys info that are supposed to be expired by the given block number
    74  func (ek *expiryKeeper) retrieve(expiringAtBlkNum uint64) ([]*expiryInfo, error) {
    75  	startKey := encodeExpiryInfoKey(&expiryInfoKey{expiryBlk: expiringAtBlkNum, committingBlk: 0})
    76  	endKey := encodeExpiryInfoKey(&expiryInfoKey{expiryBlk: expiringAtBlkNum + 1, committingBlk: 0})
    77  	itr, err := ek.db.GetIterator(startKey, endKey)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  	defer itr.Release()
    82  
    83  	var listExpinfo []*expiryInfo
    84  	for itr.Next() {
    85  		expinfo, err := decodeExpiryInfo(itr.Key(), itr.Value())
    86  		if err != nil {
    87  			return nil, err
    88  		}
    89  		listExpinfo = append(listExpinfo, expinfo)
    90  	}
    91  	return listExpinfo, nil
    92  }
    93  
    94  // retrieveByExpiryKey retrieves the expiryInfo for given expiryKey
    95  func (ek *expiryKeeper) retrieveByExpiryKey(expiryKey *expiryInfoKey) (*expiryInfo, error) {
    96  	key := encodeExpiryInfoKey(expiryKey)
    97  	value, err := ek.db.Get(key)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  	return decodeExpiryInfo(key, value)
   102  }
   103  
   104  func encodeKV(expinfo *expiryInfo) (key []byte, value []byte, err error) {
   105  	key = encodeExpiryInfoKey(expinfo.expiryInfoKey)
   106  	value, err = encodeExpiryInfoValue(expinfo.pvtdataKeys)
   107  	return
   108  }
   109  
   110  func encodeExpiryInfoKey(expinfoKey *expiryInfoKey) []byte {
   111  	key := append([]byte{expiryPrefix}, util.EncodeOrderPreservingVarUint64(expinfoKey.expiryBlk)...)
   112  	return append(key, util.EncodeOrderPreservingVarUint64(expinfoKey.committingBlk)...)
   113  }
   114  
   115  func encodeExpiryInfoValue(pvtdataKeys *PvtdataKeys) ([]byte, error) {
   116  	return proto.Marshal(pvtdataKeys)
   117  }
   118  
   119  func decodeExpiryInfo(key []byte, value []byte) (*expiryInfo, error) {
   120  	expiryBlk, n, err := util.DecodeOrderPreservingVarUint64(key[1:])
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  	committingBlk, _, err := util.DecodeOrderPreservingVarUint64(key[n+1:])
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  	pvtdataKeys := &PvtdataKeys{}
   129  	if err := proto.Unmarshal(value, pvtdataKeys); err != nil {
   130  		return nil, err
   131  	}
   132  	return &expiryInfo{
   133  			expiryInfoKey: &expiryInfoKey{committingBlk: committingBlk, expiryBlk: expiryBlk},
   134  			pvtdataKeys:   pvtdataKeys,
   135  		},
   136  		nil
   137  }