github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/common/ledger/blkstorage/blockfile_mgr.go (about)

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