github.com/sykesm/fabric@v1.1.0-preview.0.20200129034918-2aa12b1a0181/common/ledger/blkstorage/fsblkstorage/blockindex.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package fsblkstorage
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  
    13  	"github.com/golang/protobuf/proto"
    14  	"github.com/hyperledger/fabric-protos-go/common"
    15  	"github.com/hyperledger/fabric-protos-go/peer"
    16  	"github.com/hyperledger/fabric/common/ledger/blkstorage"
    17  	"github.com/hyperledger/fabric/common/ledger/blkstorage/fsblkstorage/msgs"
    18  	"github.com/hyperledger/fabric/common/ledger/util"
    19  	"github.com/hyperledger/fabric/common/ledger/util/leveldbhelper"
    20  	ledgerUtil "github.com/hyperledger/fabric/core/ledger/util"
    21  	"github.com/pkg/errors"
    22  )
    23  
    24  const (
    25  	blockNumIdxKeyPrefix        = 'n'
    26  	blockHashIdxKeyPrefix       = 'h'
    27  	txIDIdxKeyPrefix            = 't'
    28  	blockNumTranNumIdxKeyPrefix = 'a'
    29  	indexCheckpointKeyStr       = "indexCheckpointKey"
    30  )
    31  
    32  var indexCheckpointKey = []byte(indexCheckpointKeyStr)
    33  var errIndexEmpty = errors.New("NoBlockIndexed")
    34  
    35  type index interface {
    36  	getLastBlockIndexed() (uint64, error)
    37  	indexBlock(blockIdxInfo *blockIdxInfo) error
    38  	getBlockLocByHash(blockHash []byte) (*fileLocPointer, error)
    39  	getBlockLocByBlockNum(blockNum uint64) (*fileLocPointer, error)
    40  	getTxLoc(txID string) (*fileLocPointer, error)
    41  	getTXLocByBlockNumTranNum(blockNum uint64, tranNum uint64) (*fileLocPointer, error)
    42  	getBlockLocByTxID(txID string) (*fileLocPointer, error)
    43  	getTxValidationCodeByTxID(txID string) (peer.TxValidationCode, error)
    44  	isAttributeIndexed(attribute blkstorage.IndexableAttr) bool
    45  }
    46  
    47  type blockIdxInfo struct {
    48  	blockNum  uint64
    49  	blockHash []byte
    50  	flp       *fileLocPointer
    51  	txOffsets []*txindexInfo
    52  	metadata  *common.BlockMetadata
    53  }
    54  
    55  type blockIndex struct {
    56  	indexItemsMap map[blkstorage.IndexableAttr]bool
    57  	db            *leveldbhelper.DBHandle
    58  }
    59  
    60  func newBlockIndex(indexConfig *blkstorage.IndexConfig, db *leveldbhelper.DBHandle) (*blockIndex, error) {
    61  	indexItems := indexConfig.AttrsToIndex
    62  	logger.Debugf("newBlockIndex() - indexItems:[%s]", indexItems)
    63  	indexItemsMap := make(map[blkstorage.IndexableAttr]bool)
    64  	for _, indexItem := range indexItems {
    65  		indexItemsMap[indexItem] = true
    66  	}
    67  	return &blockIndex{indexItemsMap, db}, 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(indexCheckpointKey); err != nil {
    74  		return 0, err
    75  	}
    76  	if blockNumBytes == nil {
    77  		return 0, errIndexEmpty
    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 := ledgerUtil.TxValidationFlags(blockIdxInfo.metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER])
    94  	batch := leveldbhelper.NewUpdateBatch()
    95  	flpBytes, err := flp.marshal()
    96  	if err != nil {
    97  		return err
    98  	}
    99  
   100  	//Index1
   101  	if index.isAttributeIndexed(blkstorage.IndexableAttrBlockHash) {
   102  		batch.Put(constructBlockHashKey(blkHash), flpBytes)
   103  	}
   104  
   105  	//Index2
   106  	if index.isAttributeIndexed(blkstorage.IndexableAttrBlockNum) {
   107  		batch.Put(constructBlockNumKey(blkNum), flpBytes)
   108  	}
   109  
   110  	//Index3 Used to find a transaction by its transaction id
   111  	if index.isAttributeIndexed(blkstorage.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 := &msgs.TxIDIndexValProto{
   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(blkstorage.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(indexCheckpointKey, 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 blkstorage.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(blkstorage.IndexableAttrBlockHash) {
   164  		return nil, blkstorage.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, blkstorage.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(blkstorage.IndexableAttrBlockNum) {
   180  		return nil, blkstorage.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, blkstorage.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) (*msgs.TxIDIndexValProto, error) {
   227  	if !index.isAttributeIndexed(blkstorage.IndexableAttrTxID) {
   228  		return nil, blkstorage.ErrAttrNotIndexed
   229  	}
   230  	rangeScan := constructTxIDRangeScan(txID)
   231  	itr := index.db.GetIterator(rangeScan.startKey, rangeScan.stopKey)
   232  	defer itr.Release()
   233  
   234  	present := itr.Next()
   235  	if err := itr.Error(); err != nil {
   236  		return nil, errors.Wrapf(err, "error while trying to retrieve transaction info by TXID [%s]", txID)
   237  	}
   238  	if !present {
   239  		return nil, blkstorage.ErrNotFoundInIndex
   240  	}
   241  	valBytes := itr.Value()
   242  	val := &msgs.TxIDIndexValProto{}
   243  	if err := proto.Unmarshal(valBytes, val); err != nil {
   244  		return nil, errors.Wrapf(err, "unexpected error while unmarshaling bytes [%#v] into TxIDIndexValProto", valBytes)
   245  	}
   246  	return val, nil
   247  }
   248  
   249  func (index *blockIndex) getTXLocByBlockNumTranNum(blockNum uint64, tranNum uint64) (*fileLocPointer, error) {
   250  	if !index.isAttributeIndexed(blkstorage.IndexableAttrBlockNumTranNum) {
   251  		return nil, blkstorage.ErrAttrNotIndexed
   252  	}
   253  	b, err := index.db.Get(constructBlockNumTranNumKey(blockNum, tranNum))
   254  	if err != nil {
   255  		return nil, err
   256  	}
   257  	if b == nil {
   258  		return nil, blkstorage.ErrNotFoundInIndex
   259  	}
   260  	txFLP := &fileLocPointer{}
   261  	txFLP.unmarshal(b)
   262  	return txFLP, nil
   263  }
   264  
   265  func constructBlockNumKey(blockNum uint64) []byte {
   266  	blkNumBytes := util.EncodeOrderPreservingVarUint64(blockNum)
   267  	return append([]byte{blockNumIdxKeyPrefix}, blkNumBytes...)
   268  }
   269  
   270  func constructBlockHashKey(blockHash []byte) []byte {
   271  	return append([]byte{blockHashIdxKeyPrefix}, blockHash...)
   272  }
   273  
   274  func constructTxIDKey(txID string, blkNum, txNum uint64) []byte {
   275  	k := append(
   276  		[]byte{txIDIdxKeyPrefix},
   277  		util.EncodeOrderPreservingVarUint64(uint64(len(txID)))...,
   278  	)
   279  	k = append(k, txID...)
   280  	k = append(k, util.EncodeOrderPreservingVarUint64(blkNum)...)
   281  	return append(k, util.EncodeOrderPreservingVarUint64(txNum)...)
   282  }
   283  
   284  type rangeScan struct {
   285  	startKey []byte
   286  	stopKey  []byte
   287  }
   288  
   289  func constructTxIDRangeScan(txID string) *rangeScan {
   290  	sk := append(
   291  		[]byte{txIDIdxKeyPrefix},
   292  		util.EncodeOrderPreservingVarUint64(uint64(len(txID)))...,
   293  	)
   294  	sk = append(sk, txID...)
   295  	return &rangeScan{
   296  		startKey: sk,
   297  		stopKey:  append(sk, 0xff),
   298  	}
   299  }
   300  
   301  func constructBlockNumTranNumKey(blockNum uint64, txNum uint64) []byte {
   302  	blkNumBytes := util.EncodeOrderPreservingVarUint64(blockNum)
   303  	tranNumBytes := util.EncodeOrderPreservingVarUint64(txNum)
   304  	key := append(blkNumBytes, tranNumBytes...)
   305  	return append([]byte{blockNumTranNumIdxKeyPrefix}, key...)
   306  }
   307  
   308  func encodeBlockNum(blockNum uint64) []byte {
   309  	return proto.EncodeVarint(blockNum)
   310  }
   311  
   312  func decodeBlockNum(blockNumBytes []byte) uint64 {
   313  	blockNum, _ := proto.DecodeVarint(blockNumBytes)
   314  	return blockNum
   315  }
   316  
   317  type locPointer struct {
   318  	offset      int
   319  	bytesLength int
   320  }
   321  
   322  func (lp *locPointer) String() string {
   323  	return fmt.Sprintf("offset=%d, bytesLength=%d",
   324  		lp.offset, lp.bytesLength)
   325  }
   326  
   327  // fileLocPointer
   328  type fileLocPointer struct {
   329  	fileSuffixNum int
   330  	locPointer
   331  }
   332  
   333  func newFileLocationPointer(fileSuffixNum int, beginningOffset int, relativeLP *locPointer) *fileLocPointer {
   334  	flp := &fileLocPointer{fileSuffixNum: fileSuffixNum}
   335  	flp.offset = beginningOffset + relativeLP.offset
   336  	flp.bytesLength = relativeLP.bytesLength
   337  	return flp
   338  }
   339  
   340  func (flp *fileLocPointer) marshal() ([]byte, error) {
   341  	buffer := proto.NewBuffer([]byte{})
   342  	e := buffer.EncodeVarint(uint64(flp.fileSuffixNum))
   343  	if e != nil {
   344  		return nil, errors.Wrapf(e, "unexpected error while marshaling fileLocPointer [%s]", flp)
   345  	}
   346  	e = buffer.EncodeVarint(uint64(flp.offset))
   347  	if e != nil {
   348  		return nil, errors.Wrapf(e, "unexpected error while marshaling fileLocPointer [%s]", flp)
   349  	}
   350  	e = buffer.EncodeVarint(uint64(flp.bytesLength))
   351  	if e != nil {
   352  		return nil, errors.Wrapf(e, "unexpected error while marshaling fileLocPointer [%s]", flp)
   353  	}
   354  	return buffer.Bytes(), nil
   355  }
   356  
   357  func (flp *fileLocPointer) unmarshal(b []byte) error {
   358  	buffer := proto.NewBuffer(b)
   359  	i, e := buffer.DecodeVarint()
   360  	if e != nil {
   361  		return errors.Wrapf(e, "unexpected error while unmarshaling bytes [%#v] into fileLocPointer", b)
   362  	}
   363  	flp.fileSuffixNum = int(i)
   364  
   365  	i, e = buffer.DecodeVarint()
   366  	if e != nil {
   367  		return errors.Wrapf(e, "unexpected error while unmarshaling bytes [%#v] into fileLocPointer", b)
   368  	}
   369  	flp.offset = int(i)
   370  	i, e = buffer.DecodeVarint()
   371  	if e != nil {
   372  		return errors.Wrapf(e, "unexpected error while unmarshaling bytes [%#v] into fileLocPointer", b)
   373  	}
   374  	flp.bytesLength = int(i)
   375  	return nil
   376  }
   377  
   378  func (flp *fileLocPointer) String() string {
   379  	return fmt.Sprintf("fileSuffixNum=%d, %s", flp.fileSuffixNum, flp.locPointer.String())
   380  }
   381  
   382  func (blockIdxInfo *blockIdxInfo) String() string {
   383  
   384  	var buffer bytes.Buffer
   385  	for _, txOffset := range blockIdxInfo.txOffsets {
   386  		buffer.WriteString("txId=")
   387  		buffer.WriteString(txOffset.txID)
   388  		buffer.WriteString(" locPointer=")
   389  		buffer.WriteString(txOffset.loc.String())
   390  		buffer.WriteString("\n")
   391  	}
   392  	txOffsetsString := buffer.String()
   393  
   394  	return fmt.Sprintf("blockNum=%d, blockHash=%#v txOffsets=\n%s", blockIdxInfo.blockNum, blockIdxInfo.blockHash, txOffsetsString)
   395  }