github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/common/ledger/blkstorage/blockindex.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package blkstorage
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"path/filepath"
    13  	"unicode/utf8"
    14  
    15  	"github.com/golang/protobuf/proto"
    16  	"github.com/hyperledger/fabric-protos-go/common"
    17  	"github.com/hyperledger/fabric-protos-go/peer"
    18  	"github.com/osdi23p228/fabric/common/ledger/snapshot"
    19  	"github.com/osdi23p228/fabric/common/ledger/util"
    20  	"github.com/osdi23p228/fabric/common/ledger/util/leveldbhelper"
    21  	"github.com/osdi23p228/fabric/internal/pkg/txflags"
    22  	"github.com/pkg/errors"
    23  )
    24  
    25  const (
    26  	blockNumIdxKeyPrefix        = 'n'
    27  	blockHashIdxKeyPrefix       = 'h'
    28  	txIDIdxKeyPrefix            = 't'
    29  	blockNumTranNumIdxKeyPrefix = 'a'
    30  	indexSavePointKeyStr        = "indexCheckpointKey"
    31  
    32  	snapshotFileFormat       = byte(1)
    33  	snapshotDataFileName     = "txids.data"
    34  	snapshotMetadataFileName = "txids.metadata"
    35  )
    36  
    37  var (
    38  	indexSavePointKey              = []byte(indexSavePointKeyStr)
    39  	errIndexSavePointKeyNotPresent = errors.New("NoBlockIndexed")
    40  	errNilValue                    = errors.New("")
    41  	importTxIDsBatchSize           = uint64(1000) // txID is 64 bytes, so batch size roughly translates to 64KB
    42  )
    43  
    44  type blockIdxInfo struct {
    45  	blockNum  uint64
    46  	blockHash []byte
    47  	flp       *fileLocPointer
    48  	txOffsets []*txindexInfo
    49  	metadata  *common.BlockMetadata
    50  }
    51  
    52  type blockIndex struct {
    53  	indexItemsMap map[IndexableAttr]bool
    54  	db            *leveldbhelper.DBHandle
    55  }
    56  
    57  func newBlockIndex(indexConfig *IndexConfig, db *leveldbhelper.DBHandle) (*blockIndex, error) {
    58  	indexItems := indexConfig.AttrsToIndex
    59  	logger.Debugf("newBlockIndex() - indexItems:[%s]", indexItems)
    60  	indexItemsMap := make(map[IndexableAttr]bool)
    61  	for _, indexItem := range indexItems {
    62  		indexItemsMap[indexItem] = true
    63  	}
    64  	return &blockIndex{
    65  		indexItemsMap: indexItemsMap,
    66  		db:            db,
    67  	}, nil
    68  }
    69  
    70  func (index *blockIndex) getLastBlockIndexed() (uint64, error) {
    71  	var blockNumBytes []byte
    72  	var err error
    73  	if blockNumBytes, err = index.db.Get(indexSavePointKey); err != nil {
    74  		return 0, err
    75  	}
    76  	if blockNumBytes == nil {
    77  		return 0, errIndexSavePointKeyNotPresent
    78  	}
    79  	return decodeBlockNum(blockNumBytes), nil
    80  }
    81  
    82  func (index *blockIndex) indexBlock(blockIdxInfo *blockIdxInfo) error {
    83  	// do not index anything
    84  	if len(index.indexItemsMap) == 0 {
    85  		logger.Debug("Not indexing block... as nothing to index")
    86  		return nil
    87  	}
    88  	logger.Debugf("Indexing block [%s]", blockIdxInfo)
    89  	flp := blockIdxInfo.flp
    90  	txOffsets := blockIdxInfo.txOffsets
    91  	blkNum := blockIdxInfo.blockNum
    92  	blkHash := blockIdxInfo.blockHash
    93  	txsfltr := txflags.ValidationFlags(blockIdxInfo.metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER])
    94  	batch := index.db.NewUpdateBatch()
    95  	flpBytes, err := flp.marshal()
    96  	if err != nil {
    97  		return err
    98  	}
    99  
   100  	//Index1
   101  	if index.isAttributeIndexed(IndexableAttrBlockHash) {
   102  		batch.Put(constructBlockHashKey(blkHash), flpBytes)
   103  	}
   104  
   105  	//Index2
   106  	if index.isAttributeIndexed(IndexableAttrBlockNum) {
   107  		batch.Put(constructBlockNumKey(blkNum), flpBytes)
   108  	}
   109  
   110  	//Index3 Used to find a transaction by its transaction id
   111  	if index.isAttributeIndexed(IndexableAttrTxID) {
   112  		for i, txoffset := range txOffsets {
   113  			txFlp := newFileLocationPointer(flp.fileSuffixNum, flp.offset, txoffset.loc)
   114  			logger.Debugf("Adding txLoc [%s] for tx ID: [%s] to txid-index", txFlp, txoffset.txID)
   115  			txFlpBytes, marshalErr := txFlp.marshal()
   116  			if marshalErr != nil {
   117  				return marshalErr
   118  			}
   119  
   120  			indexVal := &TxIDIndexValue{
   121  				BlkLocation:      flpBytes,
   122  				TxLocation:       txFlpBytes,
   123  				TxValidationCode: int32(txsfltr.Flag(i)),
   124  			}
   125  			indexValBytes, err := proto.Marshal(indexVal)
   126  			if err != nil {
   127  				return errors.Wrap(err, "unexpected error while marshaling TxIDIndexValProto message")
   128  			}
   129  			batch.Put(
   130  				constructTxIDKey(txoffset.txID, blkNum, uint64(i)),
   131  				indexValBytes,
   132  			)
   133  		}
   134  	}
   135  
   136  	//Index4 - Store BlockNumTranNum will be used to query history data
   137  	if index.isAttributeIndexed(IndexableAttrBlockNumTranNum) {
   138  		for i, txoffset := range txOffsets {
   139  			txFlp := newFileLocationPointer(flp.fileSuffixNum, flp.offset, txoffset.loc)
   140  			logger.Debugf("Adding txLoc [%s] for tx number:[%d] ID: [%s] to blockNumTranNum index", txFlp, i, txoffset.txID)
   141  			txFlpBytes, marshalErr := txFlp.marshal()
   142  			if marshalErr != nil {
   143  				return marshalErr
   144  			}
   145  			batch.Put(constructBlockNumTranNumKey(blkNum, uint64(i)), txFlpBytes)
   146  		}
   147  	}
   148  
   149  	batch.Put(indexSavePointKey, encodeBlockNum(blockIdxInfo.blockNum))
   150  	// Setting snyc to true as a precaution, false may be an ok optimization after further testing.
   151  	if err := index.db.WriteBatch(batch, true); err != nil {
   152  		return err
   153  	}
   154  	return nil
   155  }
   156  
   157  func (index *blockIndex) isAttributeIndexed(attribute IndexableAttr) bool {
   158  	_, ok := index.indexItemsMap[attribute]
   159  	return ok
   160  }
   161  
   162  func (index *blockIndex) getBlockLocByHash(blockHash []byte) (*fileLocPointer, error) {
   163  	if !index.isAttributeIndexed(IndexableAttrBlockHash) {
   164  		return nil, ErrAttrNotIndexed
   165  	}
   166  	b, err := index.db.Get(constructBlockHashKey(blockHash))
   167  	if err != nil {
   168  		return nil, err
   169  	}
   170  	if b == nil {
   171  		return nil, ErrNotFoundInIndex
   172  	}
   173  	blkLoc := &fileLocPointer{}
   174  	blkLoc.unmarshal(b)
   175  	return blkLoc, nil
   176  }
   177  
   178  func (index *blockIndex) getBlockLocByBlockNum(blockNum uint64) (*fileLocPointer, error) {
   179  	if !index.isAttributeIndexed(IndexableAttrBlockNum) {
   180  		return nil, ErrAttrNotIndexed
   181  	}
   182  	b, err := index.db.Get(constructBlockNumKey(blockNum))
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  	if b == nil {
   187  		return nil, ErrNotFoundInIndex
   188  	}
   189  	blkLoc := &fileLocPointer{}
   190  	blkLoc.unmarshal(b)
   191  	return blkLoc, nil
   192  }
   193  
   194  func (index *blockIndex) getTxLoc(txID string) (*fileLocPointer, error) {
   195  	v, err := index.getTxIDVal(txID)
   196  	if err != nil {
   197  		return nil, err
   198  	}
   199  	txFLP := &fileLocPointer{}
   200  	if err = txFLP.unmarshal(v.TxLocation); err != nil {
   201  		return nil, err
   202  	}
   203  	return txFLP, nil
   204  }
   205  
   206  func (index *blockIndex) getBlockLocByTxID(txID string) (*fileLocPointer, error) {
   207  	v, err := index.getTxIDVal(txID)
   208  	if err != nil {
   209  		return nil, err
   210  	}
   211  	blkFLP := &fileLocPointer{}
   212  	if err = blkFLP.unmarshal(v.BlkLocation); err != nil {
   213  		return nil, err
   214  	}
   215  	return blkFLP, nil
   216  }
   217  
   218  func (index *blockIndex) getTxValidationCodeByTxID(txID string) (peer.TxValidationCode, error) {
   219  	v, err := index.getTxIDVal(txID)
   220  	if err != nil {
   221  		return peer.TxValidationCode(-1), err
   222  	}
   223  	return peer.TxValidationCode(v.TxValidationCode), nil
   224  }
   225  
   226  func (index *blockIndex) getTxIDVal(txID string) (*TxIDIndexValue, error) {
   227  	if !index.isAttributeIndexed(IndexableAttrTxID) {
   228  		return nil, ErrAttrNotIndexed
   229  	}
   230  	rangeScan := constructTxIDRangeScan(txID)
   231  	itr, err := index.db.GetIterator(rangeScan.startKey, rangeScan.stopKey)
   232  	if err != nil {
   233  		return nil, errors.WithMessagef(err, "error while trying to retrieve transaction info by TXID [%s]", txID)
   234  	}
   235  	defer itr.Release()
   236  
   237  	present := itr.Next()
   238  	if err := itr.Error(); err != nil {
   239  		return nil, errors.Wrapf(err, "error while trying to retrieve transaction info by TXID [%s]", txID)
   240  	}
   241  	if !present {
   242  		return nil, ErrNotFoundInIndex
   243  	}
   244  	valBytes := itr.Value()
   245  	if len(valBytes) == 0 {
   246  		return nil, errNilValue
   247  	}
   248  	val := &TxIDIndexValue{}
   249  	if err := proto.Unmarshal(valBytes, val); err != nil {
   250  		return nil, errors.Wrapf(err, "unexpected error while unmarshaling bytes [%#v] into TxIDIndexValProto", valBytes)
   251  	}
   252  	return val, nil
   253  }
   254  
   255  func (index *blockIndex) getTXLocByBlockNumTranNum(blockNum uint64, tranNum uint64) (*fileLocPointer, error) {
   256  	if !index.isAttributeIndexed(IndexableAttrBlockNumTranNum) {
   257  		return nil, ErrAttrNotIndexed
   258  	}
   259  	b, err := index.db.Get(constructBlockNumTranNumKey(blockNum, tranNum))
   260  	if err != nil {
   261  		return nil, err
   262  	}
   263  	if b == nil {
   264  		return nil, ErrNotFoundInIndex
   265  	}
   266  	txFLP := &fileLocPointer{}
   267  	txFLP.unmarshal(b)
   268  	return txFLP, nil
   269  }
   270  
   271  func (index *blockIndex) exportUniqueTxIDs(dir string, newHashFunc snapshot.NewHashFunc) (map[string][]byte, error) {
   272  	if !index.isAttributeIndexed(IndexableAttrTxID) {
   273  		return nil, ErrAttrNotIndexed
   274  	}
   275  
   276  	dbItr, err := index.db.GetIterator([]byte{txIDIdxKeyPrefix}, []byte{txIDIdxKeyPrefix + 1})
   277  	if err != nil {
   278  		return nil, err
   279  	}
   280  	defer dbItr.Release()
   281  
   282  	var previousTxID string
   283  	var numTxIDs uint64 = 0
   284  	var dataFile *snapshot.FileWriter
   285  	for dbItr.Next() {
   286  		if err := dbItr.Error(); err != nil {
   287  			return nil, errors.Wrap(err, "internal leveldb error while iterating for txids")
   288  		}
   289  		txID, err := retrieveTxID(dbItr.Key())
   290  		if err != nil {
   291  			return nil, err
   292  		}
   293  		// duplicate TxID may be present in the index
   294  		if previousTxID == txID {
   295  			continue
   296  		}
   297  		previousTxID = txID
   298  		if numTxIDs == 0 { // first iteration, create the data file
   299  			dataFile, err = snapshot.CreateFile(filepath.Join(dir, snapshotDataFileName), snapshotFileFormat, newHashFunc)
   300  			if err != nil {
   301  				return nil, err
   302  			}
   303  			defer dataFile.Close()
   304  		}
   305  		if err := dataFile.EncodeString(txID); err != nil {
   306  			return nil, err
   307  		}
   308  		numTxIDs++
   309  	}
   310  
   311  	if dataFile == nil {
   312  		return nil, nil
   313  	}
   314  
   315  	dataHash, err := dataFile.Done()
   316  	if err != nil {
   317  		return nil, err
   318  	}
   319  
   320  	// create the metadata file
   321  	metadataFile, err := snapshot.CreateFile(filepath.Join(dir, snapshotMetadataFileName), snapshotFileFormat, newHashFunc)
   322  	if err != nil {
   323  		return nil, err
   324  	}
   325  	defer metadataFile.Close()
   326  
   327  	if err = metadataFile.EncodeUVarint(numTxIDs); err != nil {
   328  		return nil, err
   329  	}
   330  	metadataHash, err := metadataFile.Done()
   331  	if err != nil {
   332  		return nil, err
   333  	}
   334  
   335  	return map[string][]byte{
   336  		snapshotDataFileName:     dataHash,
   337  		snapshotMetadataFileName: metadataHash,
   338  	}, nil
   339  }
   340  
   341  func importTxIDsFromSnapshot(
   342  	snapshotDir string,
   343  	lastBlockNumInSnapshot uint64,
   344  	db *leveldbhelper.DBHandle) error {
   345  
   346  	batch := db.NewUpdateBatch()
   347  	txIDsMetadata, err := snapshot.OpenFile(filepath.Join(snapshotDir, snapshotMetadataFileName), snapshotFileFormat)
   348  	if err != nil {
   349  		return err
   350  	}
   351  	numTxIDs, err := txIDsMetadata.DecodeUVarInt()
   352  	if err != nil {
   353  		return err
   354  	}
   355  	txIDsData, err := snapshot.OpenFile(filepath.Join(snapshotDir, snapshotDataFileName), snapshotFileFormat)
   356  	if err != nil {
   357  		return err
   358  	}
   359  	for i := uint64(0); i < numTxIDs; i++ {
   360  		txID, err := txIDsData.DecodeString()
   361  		if err != nil {
   362  			return err
   363  		}
   364  		batch.Put(
   365  			constructTxIDKey(txID, lastBlockNumInSnapshot, uint64(i)),
   366  			[]byte{},
   367  		)
   368  		if (i+1)%importTxIDsBatchSize == 0 {
   369  			if err := db.WriteBatch(batch, true); err != nil {
   370  				return err
   371  			}
   372  			batch = db.NewUpdateBatch()
   373  		}
   374  	}
   375  	batch.Put(indexSavePointKey, encodeBlockNum(lastBlockNumInSnapshot))
   376  	if err := db.WriteBatch(batch, true); err != nil {
   377  		return err
   378  	}
   379  	return nil
   380  }
   381  
   382  func constructBlockNumKey(blockNum uint64) []byte {
   383  	blkNumBytes := util.EncodeOrderPreservingVarUint64(blockNum)
   384  	return append([]byte{blockNumIdxKeyPrefix}, blkNumBytes...)
   385  }
   386  
   387  func constructBlockHashKey(blockHash []byte) []byte {
   388  	return append([]byte{blockHashIdxKeyPrefix}, blockHash...)
   389  }
   390  
   391  func constructTxIDKey(txID string, blkNum, txNum uint64) []byte {
   392  	k := append(
   393  		[]byte{txIDIdxKeyPrefix},
   394  		util.EncodeOrderPreservingVarUint64(uint64(len(txID)))...,
   395  	)
   396  	k = append(k, txID...)
   397  	k = append(k, util.EncodeOrderPreservingVarUint64(blkNum)...)
   398  	return append(k, util.EncodeOrderPreservingVarUint64(txNum)...)
   399  }
   400  
   401  // retrieveTxID takes input an encoded txid key of the format `prefix:len(TxID):TxID:BlkNum:TxNum`
   402  // and returns the TxID from this
   403  func retrieveTxID(encodedTxIDKey []byte) (string, error) {
   404  	if len(encodedTxIDKey) == 0 {
   405  		return "", errors.New("invalid txIDKey - zero-length slice")
   406  	}
   407  	if encodedTxIDKey[0] != txIDIdxKeyPrefix {
   408  		return "", errors.Errorf("invalid txIDKey {%x} - unexpected prefix", encodedTxIDKey)
   409  	}
   410  	remainingBytes := encodedTxIDKey[utf8.RuneLen(txIDIdxKeyPrefix):]
   411  
   412  	txIDLen, n, err := util.DecodeOrderPreservingVarUint64(remainingBytes)
   413  	if err != nil {
   414  		return "", errors.WithMessagef(err, "invalid txIDKey {%x}", encodedTxIDKey)
   415  	}
   416  	remainingBytes = remainingBytes[n:]
   417  	if len(remainingBytes) <= int(txIDLen) {
   418  		return "", errors.Errorf("invalid txIDKey {%x}, fewer bytes present", encodedTxIDKey)
   419  	}
   420  	return string(remainingBytes[:int(txIDLen)]), nil
   421  }
   422  
   423  type rangeScan struct {
   424  	startKey []byte
   425  	stopKey  []byte
   426  }
   427  
   428  func constructTxIDRangeScan(txID string) *rangeScan {
   429  	sk := append(
   430  		[]byte{txIDIdxKeyPrefix},
   431  		util.EncodeOrderPreservingVarUint64(uint64(len(txID)))...,
   432  	)
   433  	sk = append(sk, txID...)
   434  	return &rangeScan{
   435  		startKey: sk,
   436  		stopKey:  append(sk, 0xff),
   437  	}
   438  }
   439  
   440  func constructBlockNumTranNumKey(blockNum uint64, txNum uint64) []byte {
   441  	blkNumBytes := util.EncodeOrderPreservingVarUint64(blockNum)
   442  	tranNumBytes := util.EncodeOrderPreservingVarUint64(txNum)
   443  	key := append(blkNumBytes, tranNumBytes...)
   444  	return append([]byte{blockNumTranNumIdxKeyPrefix}, key...)
   445  }
   446  
   447  func encodeBlockNum(blockNum uint64) []byte {
   448  	return proto.EncodeVarint(blockNum)
   449  }
   450  
   451  func decodeBlockNum(blockNumBytes []byte) uint64 {
   452  	blockNum, _ := proto.DecodeVarint(blockNumBytes)
   453  	return blockNum
   454  }
   455  
   456  type locPointer struct {
   457  	offset      int
   458  	bytesLength int
   459  }
   460  
   461  func (lp *locPointer) String() string {
   462  	return fmt.Sprintf("offset=%d, bytesLength=%d",
   463  		lp.offset, lp.bytesLength)
   464  }
   465  
   466  // fileLocPointer
   467  type fileLocPointer struct {
   468  	fileSuffixNum int
   469  	locPointer
   470  }
   471  
   472  func newFileLocationPointer(fileSuffixNum int, beginningOffset int, relativeLP *locPointer) *fileLocPointer {
   473  	flp := &fileLocPointer{fileSuffixNum: fileSuffixNum}
   474  	flp.offset = beginningOffset + relativeLP.offset
   475  	flp.bytesLength = relativeLP.bytesLength
   476  	return flp
   477  }
   478  
   479  func (flp *fileLocPointer) marshal() ([]byte, error) {
   480  	buffer := proto.NewBuffer([]byte{})
   481  	e := buffer.EncodeVarint(uint64(flp.fileSuffixNum))
   482  	if e != nil {
   483  		return nil, errors.Wrapf(e, "unexpected error while marshaling fileLocPointer [%s]", flp)
   484  	}
   485  	e = buffer.EncodeVarint(uint64(flp.offset))
   486  	if e != nil {
   487  		return nil, errors.Wrapf(e, "unexpected error while marshaling fileLocPointer [%s]", flp)
   488  	}
   489  	e = buffer.EncodeVarint(uint64(flp.bytesLength))
   490  	if e != nil {
   491  		return nil, errors.Wrapf(e, "unexpected error while marshaling fileLocPointer [%s]", flp)
   492  	}
   493  	return buffer.Bytes(), nil
   494  }
   495  
   496  func (flp *fileLocPointer) unmarshal(b []byte) error {
   497  	buffer := proto.NewBuffer(b)
   498  	i, e := buffer.DecodeVarint()
   499  	if e != nil {
   500  		return errors.Wrapf(e, "unexpected error while unmarshaling bytes [%#v] into fileLocPointer", b)
   501  	}
   502  	flp.fileSuffixNum = int(i)
   503  
   504  	i, e = buffer.DecodeVarint()
   505  	if e != nil {
   506  		return errors.Wrapf(e, "unexpected error while unmarshaling bytes [%#v] into fileLocPointer", b)
   507  	}
   508  	flp.offset = int(i)
   509  	i, e = buffer.DecodeVarint()
   510  	if e != nil {
   511  		return errors.Wrapf(e, "unexpected error while unmarshaling bytes [%#v] into fileLocPointer", b)
   512  	}
   513  	flp.bytesLength = int(i)
   514  	return nil
   515  }
   516  
   517  func (flp *fileLocPointer) String() string {
   518  	return fmt.Sprintf("fileSuffixNum=%d, %s", flp.fileSuffixNum, flp.locPointer.String())
   519  }
   520  
   521  func (blockIdxInfo *blockIdxInfo) String() string {
   522  	var buffer bytes.Buffer
   523  	for _, txOffset := range blockIdxInfo.txOffsets {
   524  		buffer.WriteString("txId=")
   525  		buffer.WriteString(txOffset.txID)
   526  		buffer.WriteString(" locPointer=")
   527  		buffer.WriteString(txOffset.loc.String())
   528  		buffer.WriteString("\n")
   529  	}
   530  	txOffsetsString := buffer.String()
   531  
   532  	return fmt.Sprintf("blockNum=%d, blockHash=%#v txOffsets=\n%s", blockIdxInfo.blockNum, blockIdxInfo.blockHash, txOffsetsString)
   533  }