github.com/defanghe/fabric@v2.1.1+incompatible/core/ledger/pvtdatastorage/kv_encoding.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package pvtdatastorage 8 9 import ( 10 "bytes" 11 "encoding/binary" 12 "math" 13 14 "github.com/golang/protobuf/proto" 15 "github.com/hyperledger/fabric-protos-go/ledger/rwset" 16 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version" 17 "github.com/pkg/errors" 18 "github.com/willf/bitset" 19 ) 20 21 var ( 22 pendingCommitKey = []byte{0} 23 lastCommittedBlkkey = []byte{1} 24 pvtDataKeyPrefix = []byte{2} 25 expiryKeyPrefix = []byte{3} 26 eligibleMissingDataKeyPrefix = []byte{4} 27 ineligibleMissingDataKeyPrefix = []byte{5} 28 collElgKeyPrefix = []byte{6} 29 lastUpdatedOldBlocksKey = []byte{7} 30 31 nilByte = byte(0) 32 emptyValue = []byte{} 33 ) 34 35 func getDataKeysForRangeScanByBlockNum(blockNum uint64) (startKey, endKey []byte) { 36 startKey = append(pvtDataKeyPrefix, version.NewHeight(blockNum, 0).ToBytes()...) 37 endKey = append(pvtDataKeyPrefix, version.NewHeight(blockNum+1, 0).ToBytes()...) 38 return 39 } 40 41 func getExpiryKeysForRangeScan(minBlkNum, maxBlkNum uint64) (startKey, endKey []byte) { 42 startKey = append(expiryKeyPrefix, version.NewHeight(minBlkNum, 0).ToBytes()...) 43 endKey = append(expiryKeyPrefix, version.NewHeight(maxBlkNum+1, 0).ToBytes()...) 44 return 45 } 46 47 func encodeLastCommittedBlockVal(blockNum uint64) []byte { 48 return proto.EncodeVarint(blockNum) 49 } 50 51 func decodeLastCommittedBlockVal(blockNumBytes []byte) uint64 { 52 s, _ := proto.DecodeVarint(blockNumBytes) 53 return s 54 } 55 56 func encodeDataKey(key *dataKey) []byte { 57 dataKeyBytes := append(pvtDataKeyPrefix, version.NewHeight(key.blkNum, key.txNum).ToBytes()...) 58 dataKeyBytes = append(dataKeyBytes, []byte(key.ns)...) 59 dataKeyBytes = append(dataKeyBytes, nilByte) 60 return append(dataKeyBytes, []byte(key.coll)...) 61 } 62 63 func encodeDataValue(collData *rwset.CollectionPvtReadWriteSet) ([]byte, error) { 64 return proto.Marshal(collData) 65 } 66 67 func encodeExpiryKey(expiryKey *expiryKey) []byte { 68 // reusing version encoding scheme here 69 return append(expiryKeyPrefix, version.NewHeight(expiryKey.expiringBlk, expiryKey.committingBlk).ToBytes()...) 70 } 71 72 func encodeExpiryValue(expiryData *ExpiryData) ([]byte, error) { 73 return proto.Marshal(expiryData) 74 } 75 76 func decodeExpiryKey(expiryKeyBytes []byte) (*expiryKey, error) { 77 height, _, err := version.NewHeightFromBytes(expiryKeyBytes[1:]) 78 if err != nil { 79 return nil, err 80 } 81 return &expiryKey{expiringBlk: height.BlockNum, committingBlk: height.TxNum}, nil 82 } 83 84 func decodeExpiryValue(expiryValueBytes []byte) (*ExpiryData, error) { 85 expiryData := &ExpiryData{} 86 err := proto.Unmarshal(expiryValueBytes, expiryData) 87 return expiryData, err 88 } 89 90 func decodeDatakey(datakeyBytes []byte) (*dataKey, error) { 91 v, n, err := version.NewHeightFromBytes(datakeyBytes[1:]) 92 if err != nil { 93 return nil, err 94 } 95 blkNum := v.BlockNum 96 tranNum := v.TxNum 97 remainingBytes := datakeyBytes[n+1:] 98 nilByteIndex := bytes.IndexByte(remainingBytes, nilByte) 99 ns := string(remainingBytes[:nilByteIndex]) 100 coll := string(remainingBytes[nilByteIndex+1:]) 101 return &dataKey{nsCollBlk{ns, coll, blkNum}, tranNum}, nil 102 } 103 104 func decodeDataValue(datavalueBytes []byte) (*rwset.CollectionPvtReadWriteSet, error) { 105 collPvtdata := &rwset.CollectionPvtReadWriteSet{} 106 err := proto.Unmarshal(datavalueBytes, collPvtdata) 107 return collPvtdata, err 108 } 109 110 func encodeMissingDataKey(key *missingDataKey) []byte { 111 if key.isEligible { 112 // When missing pvtData reconciler asks for missing data info, 113 // it is necessary to pass the missing pvtdata info associated with 114 // the most recent block so that missing pvtdata in the state db can 115 // be fixed sooner to reduce the "private data matching public hash version 116 // is not available" error during endorserments. In order to give priority 117 // to missing pvtData in the most recent block, we use reverse order 118 // preserving encoding for the missing data key. This simplifies the 119 // implementation of GetMissingPvtDataInfoForMostRecentBlocks(). 120 keyBytes := append(eligibleMissingDataKeyPrefix, encodeReverseOrderVarUint64(key.blkNum)...) 121 keyBytes = append(keyBytes, []byte(key.ns)...) 122 keyBytes = append(keyBytes, nilByte) 123 return append(keyBytes, []byte(key.coll)...) 124 } 125 126 keyBytes := append(ineligibleMissingDataKeyPrefix, []byte(key.ns)...) 127 keyBytes = append(keyBytes, nilByte) 128 keyBytes = append(keyBytes, []byte(key.coll)...) 129 keyBytes = append(keyBytes, nilByte) 130 return append(keyBytes, []byte(encodeReverseOrderVarUint64(key.blkNum))...) 131 } 132 133 func decodeMissingDataKey(keyBytes []byte) *missingDataKey { 134 key := &missingDataKey{nsCollBlk: nsCollBlk{}} 135 if keyBytes[0] == eligibleMissingDataKeyPrefix[0] { 136 blkNum, numBytesConsumed := decodeReverseOrderVarUint64(keyBytes[1:]) 137 138 splittedKey := bytes.Split(keyBytes[numBytesConsumed+1:], []byte{nilByte}) 139 key.ns = string(splittedKey[0]) 140 key.coll = string(splittedKey[1]) 141 key.blkNum = blkNum 142 key.isEligible = true 143 return key 144 } 145 146 splittedKey := bytes.SplitN(keyBytes[1:], []byte{nilByte}, 3) //encoded bytes for blknum may contain empty bytes 147 key.ns = string(splittedKey[0]) 148 key.coll = string(splittedKey[1]) 149 key.blkNum, _ = decodeReverseOrderVarUint64(splittedKey[2]) 150 key.isEligible = false 151 return key 152 } 153 154 func encodeMissingDataValue(bitmap *bitset.BitSet) ([]byte, error) { 155 return bitmap.MarshalBinary() 156 } 157 158 func decodeMissingDataValue(bitmapBytes []byte) (*bitset.BitSet, error) { 159 bitmap := &bitset.BitSet{} 160 if err := bitmap.UnmarshalBinary(bitmapBytes); err != nil { 161 return nil, err 162 } 163 return bitmap, nil 164 } 165 166 func encodeCollElgKey(blkNum uint64) []byte { 167 return append(collElgKeyPrefix, encodeReverseOrderVarUint64(blkNum)...) 168 } 169 170 func decodeCollElgKey(b []byte) uint64 { 171 blkNum, _ := decodeReverseOrderVarUint64(b[1:]) 172 return blkNum 173 } 174 175 func encodeCollElgVal(m *CollElgInfo) ([]byte, error) { 176 return proto.Marshal(m) 177 } 178 179 func decodeCollElgVal(b []byte) (*CollElgInfo, error) { 180 m := &CollElgInfo{} 181 if err := proto.Unmarshal(b, m); err != nil { 182 return nil, errors.WithStack(err) 183 } 184 return m, nil 185 } 186 187 func createRangeScanKeysForEligibleMissingDataEntries(blkNum uint64) (startKey, endKey []byte) { 188 startKey = append(eligibleMissingDataKeyPrefix, encodeReverseOrderVarUint64(blkNum)...) 189 endKey = append(eligibleMissingDataKeyPrefix, encodeReverseOrderVarUint64(0)...) 190 191 return startKey, endKey 192 } 193 194 func createRangeScanKeysForIneligibleMissingData(maxBlkNum uint64, ns, coll string) (startKey, endKey []byte) { 195 startKey = encodeMissingDataKey( 196 &missingDataKey{ 197 nsCollBlk: nsCollBlk{ns: ns, coll: coll, blkNum: maxBlkNum}, 198 isEligible: false, 199 }, 200 ) 201 endKey = encodeMissingDataKey( 202 &missingDataKey{ 203 nsCollBlk: nsCollBlk{ns: ns, coll: coll, blkNum: 0}, 204 isEligible: false, 205 }, 206 ) 207 return 208 } 209 210 func createRangeScanKeysForCollElg() (startKey, endKey []byte) { 211 return encodeCollElgKey(math.MaxUint64), 212 encodeCollElgKey(0) 213 } 214 215 func datakeyRange(blockNum uint64) (startKey, endKey []byte) { 216 startKey = append(pvtDataKeyPrefix, version.NewHeight(blockNum, 0).ToBytes()...) 217 endKey = append(pvtDataKeyPrefix, version.NewHeight(blockNum, math.MaxUint64).ToBytes()...) 218 return 219 } 220 221 func eligibleMissingdatakeyRange(blkNum uint64) (startKey, endKey []byte) { 222 startKey = append(eligibleMissingDataKeyPrefix, encodeReverseOrderVarUint64(blkNum)...) 223 endKey = append(eligibleMissingDataKeyPrefix, encodeReverseOrderVarUint64(blkNum-1)...) 224 return 225 } 226 227 // encodeReverseOrderVarUint64 returns a byte-representation for a uint64 number such that 228 // the number is first subtracted from MaxUint64 and then all the leading 0xff bytes 229 // are trimmed and replaced by the number of such trimmed bytes. This helps in reducing the size. 230 // In the byte order comparison this encoding ensures that EncodeReverseOrderVarUint64(A) > EncodeReverseOrderVarUint64(B), 231 // If B > A 232 func encodeReverseOrderVarUint64(number uint64) []byte { 233 bytes := make([]byte, 8) 234 binary.BigEndian.PutUint64(bytes, math.MaxUint64-number) 235 numFFBytes := 0 236 for _, b := range bytes { 237 if b != 0xff { 238 break 239 } 240 numFFBytes++ 241 } 242 size := 8 - numFFBytes 243 encodedBytes := make([]byte, size+1) 244 encodedBytes[0] = proto.EncodeVarint(uint64(numFFBytes))[0] 245 copy(encodedBytes[1:], bytes[numFFBytes:]) 246 return encodedBytes 247 } 248 249 // decodeReverseOrderVarUint64 decodes the number from the bytes obtained from function 'EncodeReverseOrderVarUint64'. 250 // Also, returns the number of bytes that are consumed in the process 251 func decodeReverseOrderVarUint64(bytes []byte) (uint64, int) { 252 s, _ := proto.DecodeVarint(bytes) 253 numFFBytes := int(s) 254 decodedBytes := make([]byte, 8) 255 realBytesNum := 8 - numFFBytes 256 copy(decodedBytes[numFFBytes:], bytes[1:realBytesNum+1]) 257 numBytesConsumed := realBytesNum + 1 258 for i := 0; i < numFFBytes; i++ { 259 decodedBytes[i] = 0xff 260 } 261 return (math.MaxUint64 - binary.BigEndian.Uint64(decodedBytes)), numBytesConsumed 262 }