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 }