github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/common/ledger/blkstorage/blockfile_mgr.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  	"math"
    13  	"sync"
    14  	"sync/atomic"
    15  
    16  	"github.com/davecgh/go-spew/spew"
    17  	"github.com/golang/protobuf/proto"
    18  	"github.com/hyperledger/fabric-protos-go/common"
    19  	"github.com/hyperledger/fabric-protos-go/peer"
    20  	"github.com/osdi23p228/fabric/common/ledger/util"
    21  	"github.com/osdi23p228/fabric/common/ledger/util/leveldbhelper"
    22  	"github.com/osdi23p228/fabric/protoutil"
    23  	"github.com/pkg/errors"
    24  )
    25  
    26  const (
    27  	blockfilePrefix                   = "blockfile_"
    28  	bootstrappingSnapshotInfoFile     = "bootstrappingSnapshot.info"
    29  	bootstrappingSnapshotInfoTempFile = "bootstrappingSnapshotTemp.info"
    30  )
    31  
    32  var (
    33  	blkMgrInfoKey = []byte("blkMgrInfo")
    34  )
    35  
    36  type blockfileMgr struct {
    37  	rootDir                   string
    38  	conf                      *Conf
    39  	db                        *leveldbhelper.DBHandle
    40  	index                     *blockIndex
    41  	blockfilesInfo            *blockfilesInfo
    42  	bootstrappingSnapshotInfo *BootstrappingSnapshotInfo
    43  	blkfilesInfoCond          *sync.Cond
    44  	currentFileWriter         *blockfileWriter
    45  	bcInfo                    atomic.Value
    46  }
    47  
    48  /*
    49  Creates a new manager that will manage the files used for block persistence.
    50  This manager manages the file system FS including
    51    -- the directory where the files are stored
    52    -- the individual files where the blocks are stored
    53    -- the blockfilesInfo which tracks the latest file being persisted to
    54    -- the index which tracks what block and transaction is in what file
    55  When a new blockfile manager is started (i.e. only on start-up), it checks
    56  if this start-up is the first time the system is coming up or is this a restart
    57  of the system.
    58  
    59  The blockfile manager stores blocks of data into a file system.  That file
    60  storage is done by creating sequentially numbered files of a configured size
    61  i.e blockfile_000000, blockfile_000001, etc..
    62  
    63  Each transaction in a block is stored with information about the number of
    64  bytes in that transaction
    65   Adding txLoc [fileSuffixNum=0, offset=3, bytesLength=104] for tx [1:0] to index
    66   Adding txLoc [fileSuffixNum=0, offset=107, bytesLength=104] for tx [1:1] to index
    67  Each block is stored with the total encoded length of that block as well as the
    68  tx location offsets.
    69  
    70  Remember that these steps are only done once at start-up of the system.
    71  At start up a new manager:
    72    *) Checks if the directory for storing files exists, if not creates the dir
    73    *) Checks if the key value database exists, if not creates one
    74         (will create a db dir)
    75    *) Determines the blockfilesInfo used for storage
    76  		-- Loads from db if exist, if not instantiate a new blockfilesInfo
    77  		-- If blockfilesInfo was loaded from db, compares to FS
    78  		-- If blockfilesInfo and file system are not in sync, syncs blockfilesInfo from FS
    79    *) Starts a new file writer
    80  		-- truncates file per blockfilesInfo to remove any excess past last block
    81    *) Determines the index information used to find tx and blocks in
    82    the file blkstorage
    83  		-- Instantiates a new blockIdxInfo
    84  		-- Loads the index from the db if exists
    85  		-- syncIndex comparing the last block indexed to what is in the FS
    86  		-- If index and file system are not in sync, syncs index from the FS
    87    *)  Updates blockchain info used by the APIs
    88  */
    89  func newBlockfileMgr(id string, conf *Conf, indexConfig *IndexConfig, indexStore *leveldbhelper.DBHandle) (*blockfileMgr, error) {
    90  	logger.Debugf("newBlockfileMgr() initializing file-based block storage for ledger: %s ", id)
    91  	rootDir := conf.getLedgerBlockDir(id)
    92  	_, err := util.CreateDirIfMissing(rootDir)
    93  	if err != nil {
    94  		panic(fmt.Sprintf("Error creating block storage root dir [%s]: %s", rootDir, err))
    95  	}
    96  	mgr := &blockfileMgr{rootDir: rootDir, conf: conf, db: indexStore}
    97  
    98  	blockfilesInfo, err := mgr.loadBlkfilesInfo()
    99  	if err != nil {
   100  		panic(fmt.Sprintf("Could not get block file info for current block file from db: %s", err))
   101  	}
   102  	if blockfilesInfo == nil {
   103  		logger.Info(`Getting block information from block storage`)
   104  		if blockfilesInfo, err = constructBlockfilesInfo(rootDir); err != nil {
   105  			panic(fmt.Sprintf("Could not build blockfilesInfo info from block files: %s", err))
   106  		}
   107  		logger.Debugf("Info constructed by scanning the blocks dir = %s", spew.Sdump(blockfilesInfo))
   108  	} else {
   109  		logger.Debug(`Synching block information from block storage (if needed)`)
   110  		syncBlockfilesInfoFromFS(rootDir, blockfilesInfo)
   111  	}
   112  	err = mgr.saveBlkfilesInfo(blockfilesInfo, true)
   113  	if err != nil {
   114  		panic(fmt.Sprintf("Could not save next block file info to db: %s", err))
   115  	}
   116  
   117  	currentFileWriter, err := newBlockfileWriter(deriveBlockfilePath(rootDir, blockfilesInfo.latestFileNumber))
   118  	if err != nil {
   119  		panic(fmt.Sprintf("Could not open writer to current file: %s", err))
   120  	}
   121  	err = currentFileWriter.truncateFile(blockfilesInfo.latestFileSize)
   122  	if err != nil {
   123  		panic(fmt.Sprintf("Could not truncate current file to known size in db: %s", err))
   124  	}
   125  	if mgr.index, err = newBlockIndex(indexConfig, indexStore); err != nil {
   126  		panic(fmt.Sprintf("error in block index: %s", err))
   127  	}
   128  
   129  	mgr.blockfilesInfo = blockfilesInfo
   130  	bsi, err := loadBootstrappingSnapshotInfo(rootDir)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  	mgr.bootstrappingSnapshotInfo = bsi
   135  	mgr.currentFileWriter = currentFileWriter
   136  	mgr.blkfilesInfoCond = sync.NewCond(&sync.Mutex{})
   137  
   138  	if err := mgr.syncIndex(); err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	bcInfo := &common.BlockchainInfo{}
   143  
   144  	if mgr.bootstrappingSnapshotInfo != nil {
   145  		bcInfo.Height = mgr.bootstrappingSnapshotInfo.LastBlockNum + 1
   146  		bcInfo.CurrentBlockHash = mgr.bootstrappingSnapshotInfo.LastBlockHash
   147  		bcInfo.PreviousBlockHash = mgr.bootstrappingSnapshotInfo.PreviousBlockHash
   148  	}
   149  
   150  	if !blockfilesInfo.noBlockFiles {
   151  		lastBlockHeader, err := mgr.retrieveBlockHeaderByNumber(blockfilesInfo.lastPersistedBlock)
   152  		if err != nil {
   153  			panic(fmt.Sprintf("Could not retrieve header of the last block form file: %s", err))
   154  		}
   155  		lastBlockHash := protoutil.BlockHeaderHash(lastBlockHeader)
   156  		previousBlockHash := lastBlockHeader.PreviousHash
   157  		bcInfo = &common.BlockchainInfo{
   158  			Height:            blockfilesInfo.lastPersistedBlock + 1,
   159  			CurrentBlockHash:  lastBlockHash,
   160  			PreviousBlockHash: previousBlockHash}
   161  	}
   162  	mgr.bcInfo.Store(bcInfo)
   163  	return mgr, nil
   164  }
   165  
   166  func bootstrapFromSnapshottedTxIDs(
   167  	snapshotDir string,
   168  	snapshotInfo *SnapshotInfo,
   169  	conf *Conf,
   170  	indexStore *leveldbhelper.DBHandle,
   171  ) error {
   172  	rootDir := conf.getLedgerBlockDir(snapshotInfo.LedgerID)
   173  	isEmpty, err := util.CreateDirIfMissing(rootDir)
   174  	if err != nil {
   175  		return err
   176  	}
   177  	if !isEmpty {
   178  		return errors.Errorf("dir %s not empty", rootDir)
   179  	}
   180  
   181  	bsi := &BootstrappingSnapshotInfo{
   182  		LastBlockNum:      snapshotInfo.LastBlockNum,
   183  		LastBlockHash:     snapshotInfo.LastBlockHash,
   184  		PreviousBlockHash: snapshotInfo.PreviousBlockHash,
   185  	}
   186  
   187  	bsiBytes, err := proto.Marshal(bsi)
   188  	if err != nil {
   189  		return err
   190  	}
   191  
   192  	if err := createAndSyncFileAtomically(
   193  		rootDir,
   194  		bootstrappingSnapshotInfoTempFile,
   195  		bootstrappingSnapshotInfoFile,
   196  		bsiBytes,
   197  	); err != nil {
   198  		return err
   199  	}
   200  	if err := importTxIDsFromSnapshot(snapshotDir, snapshotInfo.LastBlockNum, indexStore); err != nil {
   201  		return err
   202  	}
   203  	return nil
   204  }
   205  
   206  func syncBlockfilesInfoFromFS(rootDir string, blkfilesInfo *blockfilesInfo) {
   207  	logger.Debugf("Starting blockfilesInfo=%s", blkfilesInfo)
   208  	//Checks if the file suffix of where the last block was written exists
   209  	filePath := deriveBlockfilePath(rootDir, blkfilesInfo.latestFileNumber)
   210  	exists, size, err := util.FileExists(filePath)
   211  	if err != nil {
   212  		panic(fmt.Sprintf("Error in checking whether file [%s] exists: %s", filePath, err))
   213  	}
   214  	logger.Debugf("status of file [%s]: exists=[%t], size=[%d]", filePath, exists, size)
   215  	//Test is !exists because when file number is first used the file does not exist yet
   216  	//checks that the file exists and that the size of the file is what is stored in blockfilesInfo
   217  	//status of file [<blkstorage_location>/blocks/blockfile_000000]: exists=[false], size=[0]
   218  	if !exists || int(size) == blkfilesInfo.latestFileSize {
   219  		// blockfilesInfo is in sync with the file on disk
   220  		return
   221  	}
   222  	//Scan the file system to verify that the blockfilesInfo stored in db is correct
   223  	_, endOffsetLastBlock, numBlocks, err := scanForLastCompleteBlock(
   224  		rootDir, blkfilesInfo.latestFileNumber, int64(blkfilesInfo.latestFileSize))
   225  	if err != nil {
   226  		panic(fmt.Sprintf("Could not open current file for detecting last block in the file: %s", err))
   227  	}
   228  	blkfilesInfo.latestFileSize = int(endOffsetLastBlock)
   229  	if numBlocks == 0 {
   230  		return
   231  	}
   232  	//Updates the blockfilesInfo for the actual last block number stored and it's end location
   233  	if blkfilesInfo.noBlockFiles {
   234  		blkfilesInfo.lastPersistedBlock = uint64(numBlocks - 1)
   235  	} else {
   236  		blkfilesInfo.lastPersistedBlock += uint64(numBlocks)
   237  	}
   238  	blkfilesInfo.noBlockFiles = false
   239  	logger.Debugf("blockfilesInfo after updates by scanning the last file segment:%s", blkfilesInfo)
   240  }
   241  
   242  func deriveBlockfilePath(rootDir string, suffixNum int) string {
   243  	return rootDir + "/" + blockfilePrefix + fmt.Sprintf("%06d", suffixNum)
   244  }
   245  
   246  func (mgr *blockfileMgr) close() {
   247  	mgr.currentFileWriter.close()
   248  }
   249  
   250  func (mgr *blockfileMgr) moveToNextFile() {
   251  	blkfilesInfo := &blockfilesInfo{
   252  		latestFileNumber:   mgr.blockfilesInfo.latestFileNumber + 1,
   253  		latestFileSize:     0,
   254  		lastPersistedBlock: mgr.blockfilesInfo.lastPersistedBlock}
   255  
   256  	nextFileWriter, err := newBlockfileWriter(
   257  		deriveBlockfilePath(mgr.rootDir, blkfilesInfo.latestFileNumber))
   258  
   259  	if err != nil {
   260  		panic(fmt.Sprintf("Could not open writer to next file: %s", err))
   261  	}
   262  	mgr.currentFileWriter.close()
   263  	err = mgr.saveBlkfilesInfo(blkfilesInfo, true)
   264  	if err != nil {
   265  		panic(fmt.Sprintf("Could not save next block file info to db: %s", err))
   266  	}
   267  	mgr.currentFileWriter = nextFileWriter
   268  	mgr.updateBlockfilesInfo(blkfilesInfo)
   269  }
   270  
   271  func (mgr *blockfileMgr) addBlock(block *common.Block) error {
   272  	bcInfo := mgr.getBlockchainInfo()
   273  	if block.Header.Number != bcInfo.Height {
   274  		return errors.Errorf(
   275  			"block number should have been %d but was %d",
   276  			mgr.getBlockchainInfo().Height, block.Header.Number,
   277  		)
   278  	}
   279  
   280  	// Add the previous hash check - Though, not essential but may not be a bad idea to
   281  	// verify the field `block.Header.PreviousHash` present in the block.
   282  	// This check is a simple bytes comparison and hence does not cause any observable performance penalty
   283  	// and may help in detecting a rare scenario if there is any bug in the ordering service.
   284  	if !bytes.Equal(block.Header.PreviousHash, bcInfo.CurrentBlockHash) {
   285  		return errors.Errorf(
   286  			"unexpected Previous block hash. Expected PreviousHash = [%x], PreviousHash referred in the latest block= [%x]",
   287  			bcInfo.CurrentBlockHash, block.Header.PreviousHash,
   288  		)
   289  	}
   290  	blockBytes, info, err := serializeBlock(block)
   291  	if err != nil {
   292  		return errors.WithMessage(err, "error serializing block")
   293  	}
   294  	blockHash := protoutil.BlockHeaderHash(block.Header)
   295  	//Get the location / offset where each transaction starts in the block and where the block ends
   296  	txOffsets := info.txOffsets
   297  	currentOffset := mgr.blockfilesInfo.latestFileSize
   298  
   299  	blockBytesLen := len(blockBytes)
   300  	blockBytesEncodedLen := proto.EncodeVarint(uint64(blockBytesLen))
   301  	totalBytesToAppend := blockBytesLen + len(blockBytesEncodedLen)
   302  
   303  	//Determine if we need to start a new file since the size of this block
   304  	//exceeds the amount of space left in the current file
   305  	if currentOffset+totalBytesToAppend > mgr.conf.maxBlockfileSize {
   306  		mgr.moveToNextFile()
   307  		currentOffset = 0
   308  	}
   309  	//append blockBytesEncodedLen to the file
   310  	err = mgr.currentFileWriter.append(blockBytesEncodedLen, false)
   311  	if err == nil {
   312  		//append the actual block bytes to the file
   313  		err = mgr.currentFileWriter.append(blockBytes, true)
   314  	}
   315  	if err != nil {
   316  		truncateErr := mgr.currentFileWriter.truncateFile(mgr.blockfilesInfo.latestFileSize)
   317  		if truncateErr != nil {
   318  			panic(fmt.Sprintf("Could not truncate current file to known size after an error during block append: %s", err))
   319  		}
   320  		return errors.WithMessage(err, "error appending block to file")
   321  	}
   322  
   323  	//Update the blockfilesInfo with the results of adding the new block
   324  	currentBlkfilesInfo := mgr.blockfilesInfo
   325  	newBlkfilesInfo := &blockfilesInfo{
   326  		latestFileNumber:   currentBlkfilesInfo.latestFileNumber,
   327  		latestFileSize:     currentBlkfilesInfo.latestFileSize + totalBytesToAppend,
   328  		noBlockFiles:       false,
   329  		lastPersistedBlock: block.Header.Number}
   330  	//save the blockfilesInfo in the database
   331  	if err = mgr.saveBlkfilesInfo(newBlkfilesInfo, false); err != nil {
   332  		truncateErr := mgr.currentFileWriter.truncateFile(currentBlkfilesInfo.latestFileSize)
   333  		if truncateErr != nil {
   334  			panic(fmt.Sprintf("Error in truncating current file to known size after an error in saving blockfiles info: %s", err))
   335  		}
   336  		return errors.WithMessage(err, "error saving blockfiles file info to db")
   337  	}
   338  
   339  	//Index block file location pointer updated with file suffex and offset for the new block
   340  	blockFLP := &fileLocPointer{fileSuffixNum: newBlkfilesInfo.latestFileNumber}
   341  	blockFLP.offset = currentOffset
   342  	// shift the txoffset because we prepend length of bytes before block bytes
   343  	for _, txOffset := range txOffsets {
   344  		txOffset.loc.offset += len(blockBytesEncodedLen)
   345  	}
   346  	//save the index in the database
   347  	if err = mgr.index.indexBlock(&blockIdxInfo{
   348  		blockNum: block.Header.Number, blockHash: blockHash,
   349  		flp: blockFLP, txOffsets: txOffsets, metadata: block.Metadata}); err != nil {
   350  		return err
   351  	}
   352  
   353  	//update the blockfilesInfo (for storage) and the blockchain info (for APIs) in the manager
   354  	mgr.updateBlockfilesInfo(newBlkfilesInfo)
   355  	mgr.updateBlockchainInfo(blockHash, block)
   356  	return nil
   357  }
   358  
   359  func (mgr *blockfileMgr) syncIndex() error {
   360  	nextIndexableBlock := uint64(0)
   361  	lastBlockIndexed, err := mgr.index.getLastBlockIndexed()
   362  	if err != nil {
   363  		if err != errIndexSavePointKeyNotPresent {
   364  			return err
   365  		}
   366  	} else {
   367  		nextIndexableBlock = lastBlockIndexed + 1
   368  	}
   369  
   370  	if nextIndexableBlock == 0 && mgr.bootstrappedFromSnapshot() {
   371  		// This condition can happen only if there was a peer crash or failure during
   372  		// bootstrapping the ledger from a snapshot or the index is dropped/corrupted afterward
   373  		return errors.Errorf(
   374  			"cannot sync index with block files. blockstore is bootstrapped from a snapshot and first available block=[%d]",
   375  			mgr.firstPossibleBlockNumberInBlockFiles(),
   376  		)
   377  	}
   378  
   379  	if mgr.blockfilesInfo.noBlockFiles {
   380  		logger.Debug("No block files present. This happens when there has not been any blocks added to the ledger yet")
   381  		return nil
   382  	}
   383  
   384  	if nextIndexableBlock == mgr.blockfilesInfo.lastPersistedBlock+1 {
   385  		logger.Debug("Both the block files and indices are in sync.")
   386  		return nil
   387  	}
   388  
   389  	startFileNum := 0
   390  	startOffset := 0
   391  	skipFirstBlock := false
   392  	endFileNum := mgr.blockfilesInfo.latestFileNumber
   393  
   394  	firstAvailableBlkNum, err := retrieveFirstBlockNumFromFile(mgr.rootDir, 0)
   395  	if err != nil {
   396  		return err
   397  	}
   398  
   399  	if nextIndexableBlock > firstAvailableBlkNum {
   400  		logger.Debugf("Last block indexed [%d], Last block present in block files [%d]", lastBlockIndexed, mgr.blockfilesInfo.lastPersistedBlock)
   401  		var flp *fileLocPointer
   402  		if flp, err = mgr.index.getBlockLocByBlockNum(lastBlockIndexed); err != nil {
   403  			return err
   404  		}
   405  		startFileNum = flp.fileSuffixNum
   406  		startOffset = flp.locPointer.offset
   407  		skipFirstBlock = true
   408  	}
   409  
   410  	logger.Infof("Start building index from block [%d] to last block [%d]", nextIndexableBlock, mgr.blockfilesInfo.lastPersistedBlock)
   411  
   412  	//open a blockstream to the file location that was stored in the index
   413  	var stream *blockStream
   414  	if stream, err = newBlockStream(mgr.rootDir, startFileNum, int64(startOffset), endFileNum); err != nil {
   415  		return err
   416  	}
   417  	var blockBytes []byte
   418  	var blockPlacementInfo *blockPlacementInfo
   419  
   420  	if skipFirstBlock {
   421  		if blockBytes, _, err = stream.nextBlockBytesAndPlacementInfo(); err != nil {
   422  			return err
   423  		}
   424  		if blockBytes == nil {
   425  			return errors.Errorf("block bytes for block num = [%d] should not be nil here. The indexes for the block are already present",
   426  				lastBlockIndexed)
   427  		}
   428  	}
   429  
   430  	//Should be at the last block already, but go ahead and loop looking for next blockBytes.
   431  	//If there is another block, add it to the index.
   432  	//This will ensure block indexes are correct, for example if peer had crashed before indexes got updated.
   433  	blockIdxInfo := &blockIdxInfo{}
   434  	for {
   435  		if blockBytes, blockPlacementInfo, err = stream.nextBlockBytesAndPlacementInfo(); err != nil {
   436  			return err
   437  		}
   438  		if blockBytes == nil {
   439  			break
   440  		}
   441  		info, err := extractSerializedBlockInfo(blockBytes)
   442  		if err != nil {
   443  			return err
   444  		}
   445  
   446  		//The blockStartOffset will get applied to the txOffsets prior to indexing within indexBlock(),
   447  		//therefore just shift by the difference between blockBytesOffset and blockStartOffset
   448  		numBytesToShift := int(blockPlacementInfo.blockBytesOffset - blockPlacementInfo.blockStartOffset)
   449  		for _, offset := range info.txOffsets {
   450  			offset.loc.offset += numBytesToShift
   451  		}
   452  
   453  		//Update the blockIndexInfo with what was actually stored in file system
   454  		blockIdxInfo.blockHash = protoutil.BlockHeaderHash(info.blockHeader)
   455  		blockIdxInfo.blockNum = info.blockHeader.Number
   456  		blockIdxInfo.flp = &fileLocPointer{fileSuffixNum: blockPlacementInfo.fileNum,
   457  			locPointer: locPointer{offset: int(blockPlacementInfo.blockStartOffset)}}
   458  		blockIdxInfo.txOffsets = info.txOffsets
   459  		blockIdxInfo.metadata = info.metadata
   460  
   461  		logger.Debugf("syncIndex() indexing block [%d]", blockIdxInfo.blockNum)
   462  		if err = mgr.index.indexBlock(blockIdxInfo); err != nil {
   463  			return err
   464  		}
   465  		if blockIdxInfo.blockNum%10000 == 0 {
   466  			logger.Infof("Indexed block number [%d]", blockIdxInfo.blockNum)
   467  		}
   468  	}
   469  	logger.Infof("Finished building index. Last block indexed [%d]", blockIdxInfo.blockNum)
   470  	return nil
   471  }
   472  
   473  func (mgr *blockfileMgr) getBlockchainInfo() *common.BlockchainInfo {
   474  	return mgr.bcInfo.Load().(*common.BlockchainInfo)
   475  }
   476  
   477  func (mgr *blockfileMgr) updateBlockfilesInfo(blkfilesInfo *blockfilesInfo) {
   478  	mgr.blkfilesInfoCond.L.Lock()
   479  	defer mgr.blkfilesInfoCond.L.Unlock()
   480  	mgr.blockfilesInfo = blkfilesInfo
   481  	logger.Debugf("Broadcasting about update blockfilesInfo: %s", blkfilesInfo)
   482  	mgr.blkfilesInfoCond.Broadcast()
   483  }
   484  
   485  func (mgr *blockfileMgr) updateBlockchainInfo(latestBlockHash []byte, latestBlock *common.Block) {
   486  	currentBCInfo := mgr.getBlockchainInfo()
   487  	newBCInfo := &common.BlockchainInfo{
   488  		Height:            currentBCInfo.Height + 1,
   489  		CurrentBlockHash:  latestBlockHash,
   490  		PreviousBlockHash: latestBlock.Header.PreviousHash}
   491  
   492  	mgr.bcInfo.Store(newBCInfo)
   493  }
   494  
   495  func (mgr *blockfileMgr) retrieveBlockByHash(blockHash []byte) (*common.Block, error) {
   496  	logger.Debugf("retrieveBlockByHash() - blockHash = [%#v]", blockHash)
   497  	loc, err := mgr.index.getBlockLocByHash(blockHash)
   498  	if err != nil {
   499  		return nil, err
   500  	}
   501  	return mgr.fetchBlock(loc)
   502  }
   503  
   504  func (mgr *blockfileMgr) retrieveBlockByNumber(blockNum uint64) (*common.Block, error) {
   505  	logger.Debugf("retrieveBlockByNumber() - blockNum = [%d]", blockNum)
   506  
   507  	// interpret math.MaxUint64 as a request for last block
   508  	if blockNum == math.MaxUint64 {
   509  		blockNum = mgr.getBlockchainInfo().Height - 1
   510  	}
   511  	if blockNum < mgr.firstPossibleBlockNumberInBlockFiles() {
   512  		return nil, errors.Errorf(
   513  			"cannot serve block [%d]. The ledger is bootstrapped from a snapshot. First available block = [%d]",
   514  			blockNum, mgr.firstPossibleBlockNumberInBlockFiles(),
   515  		)
   516  	}
   517  	loc, err := mgr.index.getBlockLocByBlockNum(blockNum)
   518  	if err != nil {
   519  		return nil, err
   520  	}
   521  	return mgr.fetchBlock(loc)
   522  }
   523  
   524  func (mgr *blockfileMgr) retrieveBlockByTxID(txID string) (*common.Block, error) {
   525  	logger.Debugf("retrieveBlockByTxID() - txID = [%s]", txID)
   526  	loc, err := mgr.index.getBlockLocByTxID(txID)
   527  	if err == errNilValue {
   528  		return nil, errors.Errorf(
   529  			"details for the TXID [%s] not available. Ledger bootstrapped from a snapshot. First available block = [%d]",
   530  			txID, mgr.firstPossibleBlockNumberInBlockFiles())
   531  	}
   532  	if err != nil {
   533  		return nil, err
   534  	}
   535  	return mgr.fetchBlock(loc)
   536  }
   537  
   538  func (mgr *blockfileMgr) retrieveTxValidationCodeByTxID(txID string) (peer.TxValidationCode, error) {
   539  	logger.Debugf("retrieveTxValidationCodeByTxID() - txID = [%s]", txID)
   540  	validationCode, err := mgr.index.getTxValidationCodeByTxID(txID)
   541  	if err == errNilValue {
   542  		return peer.TxValidationCode(-1), errors.Errorf(
   543  			"details for the TXID [%s] not available. Ledger bootstrapped from a snapshot. First available block = [%d]",
   544  			txID, mgr.firstPossibleBlockNumberInBlockFiles())
   545  	}
   546  	return validationCode, err
   547  }
   548  
   549  func (mgr *blockfileMgr) retrieveBlockHeaderByNumber(blockNum uint64) (*common.BlockHeader, error) {
   550  	logger.Debugf("retrieveBlockHeaderByNumber() - blockNum = [%d]", blockNum)
   551  	if blockNum < mgr.firstPossibleBlockNumberInBlockFiles() {
   552  		return nil, errors.Errorf(
   553  			"cannot serve block [%d]. The ledger is bootstrapped from a snapshot. First available block = [%d]",
   554  			blockNum, mgr.firstPossibleBlockNumberInBlockFiles(),
   555  		)
   556  	}
   557  	loc, err := mgr.index.getBlockLocByBlockNum(blockNum)
   558  	if err != nil {
   559  		return nil, err
   560  	}
   561  	blockBytes, err := mgr.fetchBlockBytes(loc)
   562  	if err != nil {
   563  		return nil, err
   564  	}
   565  	info, err := extractSerializedBlockInfo(blockBytes)
   566  	if err != nil {
   567  		return nil, err
   568  	}
   569  	return info.blockHeader, nil
   570  }
   571  
   572  func (mgr *blockfileMgr) retrieveBlocks(startNum uint64) (*blocksItr, error) {
   573  	if startNum < mgr.firstPossibleBlockNumberInBlockFiles() {
   574  		return nil, errors.Errorf(
   575  			"cannot serve block [%d]. The ledger is bootstrapped from a snapshot. First available block = [%d]",
   576  			startNum, mgr.firstPossibleBlockNumberInBlockFiles(),
   577  		)
   578  	}
   579  	return newBlockItr(mgr, startNum), nil
   580  }
   581  
   582  func (mgr *blockfileMgr) retrieveTransactionByID(txID string) (*common.Envelope, error) {
   583  	logger.Debugf("retrieveTransactionByID() - txId = [%s]", txID)
   584  	loc, err := mgr.index.getTxLoc(txID)
   585  	if err == errNilValue {
   586  		return nil, errors.Errorf(
   587  			"details for the TXID [%s] not available. Ledger bootstrapped from a snapshot. First available block = [%d]",
   588  			txID, mgr.firstPossibleBlockNumberInBlockFiles())
   589  	}
   590  	if err != nil {
   591  		return nil, err
   592  	}
   593  	return mgr.fetchTransactionEnvelope(loc)
   594  }
   595  
   596  func (mgr *blockfileMgr) retrieveTransactionByBlockNumTranNum(blockNum uint64, tranNum uint64) (*common.Envelope, error) {
   597  	logger.Debugf("retrieveTransactionByBlockNumTranNum() - blockNum = [%d], tranNum = [%d]", blockNum, tranNum)
   598  	if blockNum < mgr.firstPossibleBlockNumberInBlockFiles() {
   599  		return nil, errors.Errorf(
   600  			"cannot serve block [%d]. The ledger is bootstrapped from a snapshot. First available block = [%d]",
   601  			blockNum, mgr.firstPossibleBlockNumberInBlockFiles(),
   602  		)
   603  	}
   604  	loc, err := mgr.index.getTXLocByBlockNumTranNum(blockNum, tranNum)
   605  	if err != nil {
   606  		return nil, err
   607  	}
   608  	return mgr.fetchTransactionEnvelope(loc)
   609  }
   610  
   611  func (mgr *blockfileMgr) fetchBlock(lp *fileLocPointer) (*common.Block, error) {
   612  	blockBytes, err := mgr.fetchBlockBytes(lp)
   613  	if err != nil {
   614  		return nil, err
   615  	}
   616  	block, err := deserializeBlock(blockBytes)
   617  	if err != nil {
   618  		return nil, err
   619  	}
   620  	return block, nil
   621  }
   622  
   623  func (mgr *blockfileMgr) fetchTransactionEnvelope(lp *fileLocPointer) (*common.Envelope, error) {
   624  	logger.Debugf("Entering fetchTransactionEnvelope() %v\n", lp)
   625  	var err error
   626  	var txEnvelopeBytes []byte
   627  	if txEnvelopeBytes, err = mgr.fetchRawBytes(lp); err != nil {
   628  		return nil, err
   629  	}
   630  	_, n := proto.DecodeVarint(txEnvelopeBytes)
   631  	return protoutil.GetEnvelopeFromBlock(txEnvelopeBytes[n:])
   632  }
   633  
   634  func (mgr *blockfileMgr) fetchBlockBytes(lp *fileLocPointer) ([]byte, error) {
   635  	stream, err := newBlockfileStream(mgr.rootDir, lp.fileSuffixNum, int64(lp.offset))
   636  	if err != nil {
   637  		return nil, err
   638  	}
   639  	defer stream.close()
   640  	b, err := stream.nextBlockBytes()
   641  	if err != nil {
   642  		return nil, err
   643  	}
   644  	return b, nil
   645  }
   646  
   647  func (mgr *blockfileMgr) fetchRawBytes(lp *fileLocPointer) ([]byte, error) {
   648  	filePath := deriveBlockfilePath(mgr.rootDir, lp.fileSuffixNum)
   649  	reader, err := newBlockfileReader(filePath)
   650  	if err != nil {
   651  		return nil, err
   652  	}
   653  	defer reader.close()
   654  	b, err := reader.read(lp.offset, lp.bytesLength)
   655  	if err != nil {
   656  		return nil, err
   657  	}
   658  	return b, nil
   659  }
   660  
   661  //Get the current blockfilesInfo information that is stored in the database
   662  func (mgr *blockfileMgr) loadBlkfilesInfo() (*blockfilesInfo, error) {
   663  	var b []byte
   664  	var err error
   665  	if b, err = mgr.db.Get(blkMgrInfoKey); b == nil || err != nil {
   666  		return nil, err
   667  	}
   668  	i := &blockfilesInfo{}
   669  	if err = i.unmarshal(b); err != nil {
   670  		return nil, err
   671  	}
   672  	logger.Debugf("loaded blockfilesInfo:%s", i)
   673  	return i, nil
   674  }
   675  
   676  func (mgr *blockfileMgr) saveBlkfilesInfo(i *blockfilesInfo, sync bool) error {
   677  	b, err := i.marshal()
   678  	if err != nil {
   679  		return err
   680  	}
   681  	if err = mgr.db.Put(blkMgrInfoKey, b, sync); err != nil {
   682  		return err
   683  	}
   684  	return nil
   685  }
   686  
   687  func (mgr *blockfileMgr) firstPossibleBlockNumberInBlockFiles() uint64 {
   688  	if mgr.bootstrappingSnapshotInfo == nil {
   689  		return 0
   690  	}
   691  	return mgr.bootstrappingSnapshotInfo.LastBlockNum + 1
   692  }
   693  
   694  func (mgr *blockfileMgr) bootstrappedFromSnapshot() bool {
   695  	return mgr.firstPossibleBlockNumberInBlockFiles() > 0
   696  }
   697  
   698  // scanForLastCompleteBlock scan a given block file and detects the last offset in the file
   699  // after which there may lie a block partially written (towards the end of the file in a crash scenario).
   700  func scanForLastCompleteBlock(rootDir string, fileNum int, startingOffset int64) ([]byte, int64, int, error) {
   701  	//scan the passed file number suffix starting from the passed offset to find the last completed block
   702  	numBlocks := 0
   703  	var lastBlockBytes []byte
   704  	blockStream, errOpen := newBlockfileStream(rootDir, fileNum, startingOffset)
   705  	if errOpen != nil {
   706  		return nil, 0, 0, errOpen
   707  	}
   708  	defer blockStream.close()
   709  	var errRead error
   710  	var blockBytes []byte
   711  	for {
   712  		blockBytes, errRead = blockStream.nextBlockBytes()
   713  		if blockBytes == nil || errRead != nil {
   714  			break
   715  		}
   716  		lastBlockBytes = blockBytes
   717  		numBlocks++
   718  	}
   719  	if errRead == ErrUnexpectedEndOfBlockfile {
   720  		logger.Debugf(`Error:%s
   721  		The error may happen if a crash has happened during block appending.
   722  		Resetting error to nil and returning current offset as a last complete block's end offset`, errRead)
   723  		errRead = nil
   724  	}
   725  	logger.Debugf("scanForLastCompleteBlock(): last complete block ends at offset=[%d]", blockStream.currentOffset)
   726  	return lastBlockBytes, blockStream.currentOffset, numBlocks, errRead
   727  }
   728  
   729  // blockfilesInfo maintains the summary about the blockfiles
   730  type blockfilesInfo struct {
   731  	latestFileNumber   int
   732  	latestFileSize     int
   733  	noBlockFiles       bool
   734  	lastPersistedBlock uint64
   735  }
   736  
   737  func (i *blockfilesInfo) marshal() ([]byte, error) {
   738  	buffer := proto.NewBuffer([]byte{})
   739  	var err error
   740  	if err = buffer.EncodeVarint(uint64(i.latestFileNumber)); err != nil {
   741  		return nil, errors.Wrapf(err, "error encoding the latestFileNumber [%d]", i.latestFileNumber)
   742  	}
   743  	if err = buffer.EncodeVarint(uint64(i.latestFileSize)); err != nil {
   744  		return nil, errors.Wrapf(err, "error encoding the latestFileSize [%d]", i.latestFileSize)
   745  	}
   746  	if err = buffer.EncodeVarint(i.lastPersistedBlock); err != nil {
   747  		return nil, errors.Wrapf(err, "error encoding the lastPersistedBlock [%d]", i.lastPersistedBlock)
   748  	}
   749  	var noBlockFilesMarker uint64
   750  	if i.noBlockFiles {
   751  		noBlockFilesMarker = 1
   752  	}
   753  	if err = buffer.EncodeVarint(noBlockFilesMarker); err != nil {
   754  		return nil, errors.Wrapf(err, "error encoding noBlockFiles [%d]", noBlockFilesMarker)
   755  	}
   756  	return buffer.Bytes(), nil
   757  }
   758  
   759  func (i *blockfilesInfo) unmarshal(b []byte) error {
   760  	buffer := proto.NewBuffer(b)
   761  	var val uint64
   762  	var noBlockFilesMarker uint64
   763  	var err error
   764  
   765  	if val, err = buffer.DecodeVarint(); err != nil {
   766  		return err
   767  	}
   768  	i.latestFileNumber = int(val)
   769  
   770  	if val, err = buffer.DecodeVarint(); err != nil {
   771  		return err
   772  	}
   773  	i.latestFileSize = int(val)
   774  
   775  	if val, err = buffer.DecodeVarint(); err != nil {
   776  		return err
   777  	}
   778  	i.lastPersistedBlock = val
   779  	if noBlockFilesMarker, err = buffer.DecodeVarint(); err != nil {
   780  		return err
   781  	}
   782  	i.noBlockFiles = noBlockFilesMarker == 1
   783  	return nil
   784  }
   785  
   786  func (i *blockfilesInfo) String() string {
   787  	return fmt.Sprintf("latestFileNumber=[%d], latestFileSize=[%d], noBlockFiles=[%t], lastPersistedBlock=[%d]",
   788  		i.latestFileNumber, i.latestFileSize, i.noBlockFiles, i.lastPersistedBlock)
   789  }