github.com/renegr87/renegr87@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  }