github.com/kchristidis/fabric@v1.0.4-0.20171028114726-837acd08cde1/common/ledger/blkstorage/fsblkstorage/blockfile_mgr.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package fsblkstorage
    18  
    19  import (
    20  	"fmt"
    21  	"math"
    22  	"sync"
    23  	"sync/atomic"
    24  
    25  	"github.com/golang/protobuf/proto"
    26  	"github.com/hyperledger/fabric/common/flogging"
    27  	"github.com/hyperledger/fabric/common/ledger/blkstorage"
    28  	"github.com/hyperledger/fabric/common/ledger/util"
    29  	"github.com/hyperledger/fabric/common/ledger/util/leveldbhelper"
    30  	"github.com/hyperledger/fabric/protos/common"
    31  	"github.com/hyperledger/fabric/protos/peer"
    32  	putil "github.com/hyperledger/fabric/protos/utils"
    33  )
    34  
    35  var logger = flogging.MustGetLogger("fsblkstorage")
    36  
    37  const (
    38  	blockfilePrefix = "blockfile_"
    39  )
    40  
    41  var (
    42  	blkMgrInfoKey = []byte("blkMgrInfo")
    43  )
    44  
    45  type blockfileMgr struct {
    46  	rootDir           string
    47  	conf              *Conf
    48  	db                *leveldbhelper.DBHandle
    49  	index             index
    50  	cpInfo            *checkpointInfo
    51  	cpInfoCond        *sync.Cond
    52  	currentFileWriter *blockfileWriter
    53  	bcInfo            atomic.Value
    54  }
    55  
    56  /*
    57  Creates a new manager that will manage the files used for block persistence.
    58  This manager manages the file system FS including
    59    -- the directory where the files are stored
    60    -- the individual files where the blocks are stored
    61    -- the checkpoint which tracks the latest file being persisted to
    62    -- the index which tracks what block and transaction is in what file
    63  When a new blockfile manager is started (i.e. only on start-up), it checks
    64  if this start-up is the first time the system is coming up or is this a restart
    65  of the system.
    66  
    67  The blockfile manager stores blocks of data into a file system.  That file
    68  storage is done by creating sequentially numbered files of a configured size
    69  i.e blockfile_000000, blockfile_000001, etc..
    70  
    71  Each transcation in a block is stored with information about the number of
    72  bytes in that transaction
    73   Adding txLoc [fileSuffixNum=0, offset=3, bytesLength=104] for tx [1:0] to index
    74   Adding txLoc [fileSuffixNum=0, offset=107, bytesLength=104] for tx [1:1] to index
    75  Each block is stored with the total encoded length of that block as well as the
    76  tx location offsets.
    77  
    78  Remember that these steps are only done once at start-up of the system.
    79  At start up a new manager:
    80    *) Checks if the directory for storing files exists, if not creates the dir
    81    *) Checks if the key value database exists, if not creates one
    82         (will create a db dir)
    83    *) Determines the checkpoint information (cpinfo) used for storage
    84  		-- Loads from db if exist, if not instantiate a new cpinfo
    85  		-- If cpinfo was loaded from db, compares to FS
    86  		-- If cpinfo and file system are not in sync, syncs cpInfo from FS
    87    *) Starts a new file writer
    88  		-- truncates file per cpinfo to remove any excess past last block
    89    *) Determines the index information used to find tx and blocks in
    90    the file blkstorage
    91  		-- Instantiates a new blockIdxInfo
    92  		-- Loads the index from the db if exists
    93  		-- syncIndex comparing the last block indexed to what is in the FS
    94  		-- If index and file system are not in sync, syncs index from the FS
    95    *)  Updates blockchain info used by the APIs
    96  */
    97  func newBlockfileMgr(id string, conf *Conf, indexConfig *blkstorage.IndexConfig, indexStore *leveldbhelper.DBHandle) *blockfileMgr {
    98  	logger.Debugf("newBlockfileMgr() initializing file-based block storage for ledger: %s ", id)
    99  	//Determine the root directory for the blockfile storage, if it does not exist create it
   100  	rootDir := conf.getLedgerBlockDir(id)
   101  	_, err := util.CreateDirIfMissing(rootDir)
   102  	if err != nil {
   103  		panic(fmt.Sprintf("Error: %s", err))
   104  	}
   105  	// Instantiate the manager, i.e. blockFileMgr structure
   106  	mgr := &blockfileMgr{rootDir: rootDir, conf: conf, db: indexStore}
   107  
   108  	// cp = checkpointInfo, retrieve from the database the file suffix or number of where blocks were stored.
   109  	// It also retrieves the current size of that file and the last block number that was written to that file.
   110  	// At init checkpointInfo:latestFileChunkSuffixNum=[0], latestFileChunksize=[0], lastBlockNumber=[0]
   111  	cpInfo, err := mgr.loadCurrentInfo()
   112  	if err != nil {
   113  		panic(fmt.Sprintf("Could not get block file info for current block file from db: %s", err))
   114  	}
   115  	if cpInfo == nil { //if no cpInfo stored in db initiate to zero
   116  		cpInfo = &checkpointInfo{0, 0, true, 0}
   117  		err = mgr.saveCurrentInfo(cpInfo, true)
   118  		if err != nil {
   119  			panic(fmt.Sprintf("Could not save next block file info to db: %s", err))
   120  		}
   121  	}
   122  	//Verify that the checkpoint stored in db is accurate with what is actually stored in block file system
   123  	// If not the same, sync the cpInfo and the file system
   124  	syncCPInfoFromFS(rootDir, cpInfo)
   125  	//Open a writer to the file identified by the number and truncate it to only contain the latest block
   126  	// that was completely saved (file system, index, cpinfo, etc)
   127  	currentFileWriter, err := newBlockfileWriter(deriveBlockfilePath(rootDir, cpInfo.latestFileChunkSuffixNum))
   128  	if err != nil {
   129  		panic(fmt.Sprintf("Could not open writer to current file: %s", err))
   130  	}
   131  	//Truncate the file to remove excess past last block
   132  	err = currentFileWriter.truncateFile(cpInfo.latestFileChunksize)
   133  	if err != nil {
   134  		panic(fmt.Sprintf("Could not truncate current file to known size in db: %s", err))
   135  	}
   136  
   137  	// Create a new KeyValue store database handler for the blocks index in the keyvalue database
   138  	mgr.index = newBlockIndex(indexConfig, indexStore)
   139  
   140  	// Update the manager with the checkpoint info and the file writer
   141  	mgr.cpInfo = cpInfo
   142  	mgr.currentFileWriter = currentFileWriter
   143  	// Create a checkpoint condition (event) variable, for the  goroutine waiting for
   144  	// or announcing the occurrence of an event.
   145  	mgr.cpInfoCond = sync.NewCond(&sync.Mutex{})
   146  
   147  	// Verify that the index stored in db is accurate with what is actually stored in block file system
   148  	// If not the same, sync the index and the file system
   149  	mgr.syncIndex()
   150  
   151  	// init BlockchainInfo for external API's
   152  	bcInfo := &common.BlockchainInfo{
   153  		Height:            0,
   154  		CurrentBlockHash:  nil,
   155  		PreviousBlockHash: nil}
   156  
   157  	//If start up is a restart of an existing storage, update BlockchainInfo for external API's
   158  	if !cpInfo.isChainEmpty {
   159  		lastBlockHeader, err := mgr.retrieveBlockHeaderByNumber(cpInfo.lastBlockNumber)
   160  		if err != nil {
   161  			panic(fmt.Sprintf("Could not retrieve header of the last block form file: %s", err))
   162  		}
   163  		lastBlockHash := lastBlockHeader.Hash()
   164  		previousBlockHash := lastBlockHeader.PreviousHash
   165  		bcInfo = &common.BlockchainInfo{
   166  			Height:            cpInfo.lastBlockNumber + 1,
   167  			CurrentBlockHash:  lastBlockHash,
   168  			PreviousBlockHash: previousBlockHash}
   169  	}
   170  	mgr.bcInfo.Store(bcInfo)
   171  	//return the new manager (blockfileMgr)
   172  	return mgr
   173  }
   174  
   175  //cp = checkpointInfo, from the database gets the file suffix and the size of
   176  // the file of where the last block was written.  Also retrieves contains the
   177  // last block number that was written.  At init
   178  //checkpointInfo:latestFileChunkSuffixNum=[0], latestFileChunksize=[0], lastBlockNumber=[0]
   179  func syncCPInfoFromFS(rootDir string, cpInfo *checkpointInfo) {
   180  	logger.Debugf("Starting checkpoint=%s", cpInfo)
   181  	//Checks if the file suffix of where the last block was written exists
   182  	filePath := deriveBlockfilePath(rootDir, cpInfo.latestFileChunkSuffixNum)
   183  	exists, size, err := util.FileExists(filePath)
   184  	if err != nil {
   185  		panic(fmt.Sprintf("Error in checking whether file [%s] exists: %s", filePath, err))
   186  	}
   187  	logger.Debugf("status of file [%s]: exists=[%t], size=[%d]", filePath, exists, size)
   188  	//Test is !exists because when file number is first used the file does not exist yet
   189  	//checks that the file exists and that the size of the file is what is stored in cpinfo
   190  	//status of file [/tmp/tests/ledger/blkstorage/fsblkstorage/blocks/blockfile_000000]: exists=[false], size=[0]
   191  	if !exists || int(size) == cpInfo.latestFileChunksize {
   192  		// check point info is in sync with the file on disk
   193  		return
   194  	}
   195  	//Scan the file system to verify that the checkpoint info stored in db is correct
   196  	endOffsetLastBlock, numBlocks, err := scanForLastCompleteBlock(
   197  		rootDir, cpInfo.latestFileChunkSuffixNum, int64(cpInfo.latestFileChunksize))
   198  	if err != nil {
   199  		panic(fmt.Sprintf("Could not open current file for detecting last block in the file: %s", err))
   200  	}
   201  	cpInfo.latestFileChunksize = int(endOffsetLastBlock)
   202  	if numBlocks == 0 {
   203  		return
   204  	}
   205  	//Updates the checkpoint info for the actual last block number stored and it's end location
   206  	if cpInfo.isChainEmpty {
   207  		cpInfo.lastBlockNumber = uint64(numBlocks - 1)
   208  	} else {
   209  		cpInfo.lastBlockNumber += uint64(numBlocks)
   210  	}
   211  	cpInfo.isChainEmpty = false
   212  	logger.Debugf("Checkpoint after updates by scanning the last file segment:%s", cpInfo)
   213  }
   214  
   215  func deriveBlockfilePath(rootDir string, suffixNum int) string {
   216  	return rootDir + "/" + blockfilePrefix + fmt.Sprintf("%06d", suffixNum)
   217  }
   218  
   219  func (mgr *blockfileMgr) close() {
   220  	mgr.currentFileWriter.close()
   221  }
   222  
   223  func (mgr *blockfileMgr) moveToNextFile() {
   224  	cpInfo := &checkpointInfo{
   225  		latestFileChunkSuffixNum: mgr.cpInfo.latestFileChunkSuffixNum + 1,
   226  		latestFileChunksize:      0,
   227  		lastBlockNumber:          mgr.cpInfo.lastBlockNumber}
   228  
   229  	nextFileWriter, err := newBlockfileWriter(
   230  		deriveBlockfilePath(mgr.rootDir, cpInfo.latestFileChunkSuffixNum))
   231  
   232  	if err != nil {
   233  		panic(fmt.Sprintf("Could not open writer to next file: %s", err))
   234  	}
   235  	mgr.currentFileWriter.close()
   236  	err = mgr.saveCurrentInfo(cpInfo, true)
   237  	if err != nil {
   238  		panic(fmt.Sprintf("Could not save next block file info to db: %s", err))
   239  	}
   240  	mgr.currentFileWriter = nextFileWriter
   241  	mgr.updateCheckpoint(cpInfo)
   242  }
   243  
   244  func (mgr *blockfileMgr) addBlock(block *common.Block) error {
   245  	if block.Header.Number != mgr.getBlockchainInfo().Height {
   246  		return fmt.Errorf("Block number should have been %d but was %d", mgr.getBlockchainInfo().Height, block.Header.Number)
   247  	}
   248  	blockBytes, info, err := serializeBlock(block)
   249  	if err != nil {
   250  		return fmt.Errorf("Error while serializing block: %s", err)
   251  	}
   252  	blockHash := block.Header.Hash()
   253  	//Get the location / offset where each transaction starts in the block and where the block ends
   254  	txOffsets := info.txOffsets
   255  	currentOffset := mgr.cpInfo.latestFileChunksize
   256  	if err != nil {
   257  		return fmt.Errorf("Error while serializing block: %s", err)
   258  	}
   259  	blockBytesLen := len(blockBytes)
   260  	blockBytesEncodedLen := proto.EncodeVarint(uint64(blockBytesLen))
   261  	totalBytesToAppend := blockBytesLen + len(blockBytesEncodedLen)
   262  
   263  	//Determine if we need to start a new file since the size of this block
   264  	//exceeds the amount of space left in the current file
   265  	if currentOffset+totalBytesToAppend > mgr.conf.maxBlockfileSize {
   266  		mgr.moveToNextFile()
   267  		currentOffset = 0
   268  	}
   269  	//append blockBytesEncodedLen to the file
   270  	err = mgr.currentFileWriter.append(blockBytesEncodedLen, false)
   271  	if err == nil {
   272  		//append the actual block bytes to the file
   273  		err = mgr.currentFileWriter.append(blockBytes, true)
   274  	}
   275  	if err != nil {
   276  		truncateErr := mgr.currentFileWriter.truncateFile(mgr.cpInfo.latestFileChunksize)
   277  		if truncateErr != nil {
   278  			panic(fmt.Sprintf("Could not truncate current file to known size after an error during block append: %s", err))
   279  		}
   280  		return fmt.Errorf("Error while appending block to file: %s", err)
   281  	}
   282  
   283  	//Update the checkpoint info with the results of adding the new block
   284  	currentCPInfo := mgr.cpInfo
   285  	newCPInfo := &checkpointInfo{
   286  		latestFileChunkSuffixNum: currentCPInfo.latestFileChunkSuffixNum,
   287  		latestFileChunksize:      currentCPInfo.latestFileChunksize + totalBytesToAppend,
   288  		isChainEmpty:             false,
   289  		lastBlockNumber:          block.Header.Number}
   290  	//save the checkpoint information in the database
   291  	if err = mgr.saveCurrentInfo(newCPInfo, false); err != nil {
   292  		truncateErr := mgr.currentFileWriter.truncateFile(currentCPInfo.latestFileChunksize)
   293  		if truncateErr != nil {
   294  			panic(fmt.Sprintf("Error in truncating current file to known size after an error in saving checkpoint info: %s", err))
   295  		}
   296  		return fmt.Errorf("Error while saving current file info to db: %s", err)
   297  	}
   298  
   299  	//Index block file location pointer updated with file suffex and offset for the new block
   300  	blockFLP := &fileLocPointer{fileSuffixNum: newCPInfo.latestFileChunkSuffixNum}
   301  	blockFLP.offset = currentOffset
   302  	// shift the txoffset because we prepend length of bytes before block bytes
   303  	for _, txOffset := range txOffsets {
   304  		txOffset.loc.offset += len(blockBytesEncodedLen)
   305  	}
   306  	//save the index in the database
   307  	mgr.index.indexBlock(&blockIdxInfo{
   308  		blockNum: block.Header.Number, blockHash: blockHash,
   309  		flp: blockFLP, txOffsets: txOffsets, metadata: block.Metadata})
   310  
   311  	//update the checkpoint info (for storage) and the blockchain info (for APIs) in the manager
   312  	mgr.updateCheckpoint(newCPInfo)
   313  	mgr.updateBlockchainInfo(blockHash, block)
   314  	return nil
   315  }
   316  
   317  func (mgr *blockfileMgr) syncIndex() error {
   318  	var lastBlockIndexed uint64
   319  	var indexEmpty bool
   320  	var err error
   321  	//from the database, get the last block that was indexed
   322  	if lastBlockIndexed, err = mgr.index.getLastBlockIndexed(); err != nil {
   323  		if err != errIndexEmpty {
   324  			return err
   325  		}
   326  		indexEmpty = true
   327  	}
   328  	//initialize index to file number:zero, offset:zero and blockNum:0
   329  	startFileNum := 0
   330  	startOffset := 0
   331  	blockNum := uint64(0)
   332  	skipFirstBlock := false
   333  	//get the last file that blocks were added to using the checkpoint info
   334  	endFileNum := mgr.cpInfo.latestFileChunkSuffixNum
   335  	//if the index stored in the db has value, update the index information with those values
   336  	if !indexEmpty {
   337  		var flp *fileLocPointer
   338  		if flp, err = mgr.index.getBlockLocByBlockNum(lastBlockIndexed); err != nil {
   339  			return err
   340  		}
   341  		startFileNum = flp.fileSuffixNum
   342  		startOffset = flp.locPointer.offset
   343  		blockNum = lastBlockIndexed
   344  		skipFirstBlock = true
   345  	}
   346  
   347  	//open a blockstream to the file location that was stored in the index
   348  	var stream *blockStream
   349  	if stream, err = newBlockStream(mgr.rootDir, startFileNum, int64(startOffset), endFileNum); err != nil {
   350  		return err
   351  	}
   352  	var blockBytes []byte
   353  	var blockPlacementInfo *blockPlacementInfo
   354  
   355  	if skipFirstBlock {
   356  		if blockBytes, _, err = stream.nextBlockBytesAndPlacementInfo(); err != nil {
   357  			return err
   358  		}
   359  		if blockBytes == nil {
   360  			return fmt.Errorf("block bytes for block num = [%d] should not be nil here. The indexes for the block are already present",
   361  				lastBlockIndexed)
   362  		}
   363  	}
   364  
   365  	//Should be at the last block already, but go ahead and loop looking for next blockBytes.
   366  	//If there is another block, add it to the index.
   367  	//This will ensure block indexes are correct, for example if peer had crashed before indexes got updated.
   368  	for {
   369  		if blockBytes, blockPlacementInfo, err = stream.nextBlockBytesAndPlacementInfo(); err != nil {
   370  			return err
   371  		}
   372  		if blockBytes == nil {
   373  			break
   374  		}
   375  		info, err := extractSerializedBlockInfo(blockBytes)
   376  		if err != nil {
   377  			return err
   378  		}
   379  
   380  		//The blockStartOffset will get applied to the txOffsets prior to indexing within indexBlock(),
   381  		//therefore just shift by the difference between blockBytesOffset and blockStartOffset
   382  		numBytesToShift := int(blockPlacementInfo.blockBytesOffset - blockPlacementInfo.blockStartOffset)
   383  		for _, offset := range info.txOffsets {
   384  			offset.loc.offset += numBytesToShift
   385  		}
   386  
   387  		//Update the blockIndexInfo with what was actually stored in file system
   388  		blockIdxInfo := &blockIdxInfo{}
   389  		blockIdxInfo.blockHash = info.blockHeader.Hash()
   390  		blockIdxInfo.blockNum = info.blockHeader.Number
   391  		blockIdxInfo.flp = &fileLocPointer{fileSuffixNum: blockPlacementInfo.fileNum,
   392  			locPointer: locPointer{offset: int(blockPlacementInfo.blockStartOffset)}}
   393  		blockIdxInfo.txOffsets = info.txOffsets
   394  		blockIdxInfo.metadata = info.metadata
   395  
   396  		logger.Debugf("syncIndex() indexing block [%d]", blockIdxInfo.blockNum)
   397  		if err = mgr.index.indexBlock(blockIdxInfo); err != nil {
   398  			return err
   399  		}
   400  		blockNum++
   401  	}
   402  	return nil
   403  }
   404  
   405  func (mgr *blockfileMgr) getBlockchainInfo() *common.BlockchainInfo {
   406  	return mgr.bcInfo.Load().(*common.BlockchainInfo)
   407  }
   408  
   409  func (mgr *blockfileMgr) updateCheckpoint(cpInfo *checkpointInfo) {
   410  	mgr.cpInfoCond.L.Lock()
   411  	defer mgr.cpInfoCond.L.Unlock()
   412  	mgr.cpInfo = cpInfo
   413  	logger.Debugf("Broadcasting about update checkpointInfo: %s", cpInfo)
   414  	mgr.cpInfoCond.Broadcast()
   415  }
   416  
   417  func (mgr *blockfileMgr) updateBlockchainInfo(latestBlockHash []byte, latestBlock *common.Block) {
   418  	currentBCInfo := mgr.getBlockchainInfo()
   419  	newBCInfo := &common.BlockchainInfo{
   420  		Height:            currentBCInfo.Height + 1,
   421  		CurrentBlockHash:  latestBlockHash,
   422  		PreviousBlockHash: latestBlock.Header.PreviousHash}
   423  
   424  	mgr.bcInfo.Store(newBCInfo)
   425  }
   426  
   427  func (mgr *blockfileMgr) retrieveBlockByHash(blockHash []byte) (*common.Block, error) {
   428  	logger.Debugf("retrieveBlockByHash() - blockHash = [%#v]", blockHash)
   429  	loc, err := mgr.index.getBlockLocByHash(blockHash)
   430  	if err != nil {
   431  		return nil, err
   432  	}
   433  	return mgr.fetchBlock(loc)
   434  }
   435  
   436  func (mgr *blockfileMgr) retrieveBlockByNumber(blockNum uint64) (*common.Block, error) {
   437  	logger.Debugf("retrieveBlockByNumber() - blockNum = [%d]", blockNum)
   438  
   439  	// interpret math.MaxUint64 as a request for last block
   440  	if blockNum == math.MaxUint64 {
   441  		blockNum = mgr.getBlockchainInfo().Height - 1
   442  	}
   443  
   444  	loc, err := mgr.index.getBlockLocByBlockNum(blockNum)
   445  	if err != nil {
   446  		return nil, err
   447  	}
   448  	return mgr.fetchBlock(loc)
   449  }
   450  
   451  func (mgr *blockfileMgr) retrieveBlockByTxID(txID string) (*common.Block, error) {
   452  	logger.Debugf("retrieveBlockByTxID() - txID = [%s]", txID)
   453  
   454  	loc, err := mgr.index.getBlockLocByTxID(txID)
   455  
   456  	if err != nil {
   457  		return nil, err
   458  	}
   459  	return mgr.fetchBlock(loc)
   460  }
   461  
   462  func (mgr *blockfileMgr) retrieveTxValidationCodeByTxID(txID string) (peer.TxValidationCode, error) {
   463  	logger.Debugf("retrieveTxValidationCodeByTxID() - txID = [%s]", txID)
   464  	return mgr.index.getTxValidationCodeByTxID(txID)
   465  }
   466  
   467  func (mgr *blockfileMgr) retrieveBlockHeaderByNumber(blockNum uint64) (*common.BlockHeader, error) {
   468  	logger.Debugf("retrieveBlockHeaderByNumber() - blockNum = [%d]", blockNum)
   469  	loc, err := mgr.index.getBlockLocByBlockNum(blockNum)
   470  	if err != nil {
   471  		return nil, err
   472  	}
   473  	blockBytes, err := mgr.fetchBlockBytes(loc)
   474  	if err != nil {
   475  		return nil, err
   476  	}
   477  	info, err := extractSerializedBlockInfo(blockBytes)
   478  	if err != nil {
   479  		return nil, err
   480  	}
   481  	return info.blockHeader, nil
   482  }
   483  
   484  func (mgr *blockfileMgr) retrieveBlocks(startNum uint64) (*blocksItr, error) {
   485  	return newBlockItr(mgr, startNum), nil
   486  }
   487  
   488  func (mgr *blockfileMgr) retrieveTransactionByID(txID string) (*common.Envelope, error) {
   489  	logger.Debugf("retrieveTransactionByID() - txId = [%s]", txID)
   490  	loc, err := mgr.index.getTxLoc(txID)
   491  	if err != nil {
   492  		return nil, err
   493  	}
   494  	return mgr.fetchTransactionEnvelope(loc)
   495  }
   496  
   497  func (mgr *blockfileMgr) retrieveTransactionByBlockNumTranNum(blockNum uint64, tranNum uint64) (*common.Envelope, error) {
   498  	logger.Debugf("retrieveTransactionByBlockNumTranNum() - blockNum = [%d], tranNum = [%d]", blockNum, tranNum)
   499  	loc, err := mgr.index.getTXLocByBlockNumTranNum(blockNum, tranNum)
   500  	if err != nil {
   501  		return nil, err
   502  	}
   503  	return mgr.fetchTransactionEnvelope(loc)
   504  }
   505  
   506  func (mgr *blockfileMgr) fetchBlock(lp *fileLocPointer) (*common.Block, error) {
   507  	blockBytes, err := mgr.fetchBlockBytes(lp)
   508  	if err != nil {
   509  		return nil, err
   510  	}
   511  	block, err := deserializeBlock(blockBytes)
   512  	if err != nil {
   513  		return nil, err
   514  	}
   515  	return block, nil
   516  }
   517  
   518  func (mgr *blockfileMgr) fetchTransactionEnvelope(lp *fileLocPointer) (*common.Envelope, error) {
   519  	logger.Debugf("Entering fetchTransactionEnvelope() %v\n", lp)
   520  	var err error
   521  	var txEnvelopeBytes []byte
   522  	if txEnvelopeBytes, err = mgr.fetchRawBytes(lp); err != nil {
   523  		return nil, err
   524  	}
   525  	_, n := proto.DecodeVarint(txEnvelopeBytes)
   526  	return putil.GetEnvelopeFromBlock(txEnvelopeBytes[n:])
   527  }
   528  
   529  func (mgr *blockfileMgr) fetchBlockBytes(lp *fileLocPointer) ([]byte, error) {
   530  	stream, err := newBlockfileStream(mgr.rootDir, lp.fileSuffixNum, int64(lp.offset))
   531  	if err != nil {
   532  		return nil, err
   533  	}
   534  	defer stream.close()
   535  	b, err := stream.nextBlockBytes()
   536  	if err != nil {
   537  		return nil, err
   538  	}
   539  	return b, nil
   540  }
   541  
   542  func (mgr *blockfileMgr) fetchRawBytes(lp *fileLocPointer) ([]byte, error) {
   543  	filePath := deriveBlockfilePath(mgr.rootDir, lp.fileSuffixNum)
   544  	reader, err := newBlockfileReader(filePath)
   545  	if err != nil {
   546  		return nil, err
   547  	}
   548  	defer reader.close()
   549  	b, err := reader.read(lp.offset, lp.bytesLength)
   550  	if err != nil {
   551  		return nil, err
   552  	}
   553  	return b, nil
   554  }
   555  
   556  //Get the current checkpoint information that is stored in the database
   557  func (mgr *blockfileMgr) loadCurrentInfo() (*checkpointInfo, error) {
   558  	var b []byte
   559  	var err error
   560  	if b, err = mgr.db.Get(blkMgrInfoKey); b == nil || err != nil {
   561  		return nil, err
   562  	}
   563  	i := &checkpointInfo{}
   564  	if err = i.unmarshal(b); err != nil {
   565  		return nil, err
   566  	}
   567  	logger.Debugf("loaded checkpointInfo:%s", i)
   568  	return i, nil
   569  }
   570  
   571  func (mgr *blockfileMgr) saveCurrentInfo(i *checkpointInfo, sync bool) error {
   572  	b, err := i.marshal()
   573  	if err != nil {
   574  		return err
   575  	}
   576  	if err = mgr.db.Put(blkMgrInfoKey, b, sync); err != nil {
   577  		return err
   578  	}
   579  	return nil
   580  }
   581  
   582  // scanForLastCompleteBlock scan a given block file and detects the last offset in the file
   583  // after which there may lie a block partially written (towards the end of the file in a crash scenario).
   584  func scanForLastCompleteBlock(rootDir string, fileNum int, startingOffset int64) (int64, int, error) {
   585  	//scan the passed file number suffix starting from the passed offset to find the last completed block
   586  	numBlocks := 0
   587  	blockStream, errOpen := newBlockfileStream(rootDir, fileNum, startingOffset)
   588  	if errOpen != nil {
   589  		return 0, 0, errOpen
   590  	}
   591  	defer blockStream.close()
   592  	var errRead error
   593  	var blockBytes []byte
   594  	for {
   595  		blockBytes, errRead = blockStream.nextBlockBytes()
   596  		if blockBytes == nil || errRead != nil {
   597  			break
   598  		}
   599  		numBlocks++
   600  	}
   601  	if errRead == ErrUnexpectedEndOfBlockfile {
   602  		logger.Debugf(`Error:%s
   603  		The error may happen if a crash has happened during block appending.
   604  		Resetting error to nil and returning current offset as a last complete block's end offset`, errRead)
   605  		errRead = nil
   606  	}
   607  	logger.Debugf("scanForLastCompleteBlock(): last complete block ends at offset=[%d]", blockStream.currentOffset)
   608  	return blockStream.currentOffset, numBlocks, errRead
   609  }
   610  
   611  // checkpointInfo
   612  type checkpointInfo struct {
   613  	latestFileChunkSuffixNum int
   614  	latestFileChunksize      int
   615  	isChainEmpty             bool
   616  	lastBlockNumber          uint64
   617  }
   618  
   619  func (i *checkpointInfo) marshal() ([]byte, error) {
   620  	buffer := proto.NewBuffer([]byte{})
   621  	var err error
   622  	if err = buffer.EncodeVarint(uint64(i.latestFileChunkSuffixNum)); err != nil {
   623  		return nil, err
   624  	}
   625  	if err = buffer.EncodeVarint(uint64(i.latestFileChunksize)); err != nil {
   626  		return nil, err
   627  	}
   628  	if err = buffer.EncodeVarint(i.lastBlockNumber); err != nil {
   629  		return nil, err
   630  	}
   631  	var chainEmptyMarker uint64
   632  	if i.isChainEmpty {
   633  		chainEmptyMarker = 1
   634  	}
   635  	if err = buffer.EncodeVarint(chainEmptyMarker); err != nil {
   636  		return nil, err
   637  	}
   638  	return buffer.Bytes(), nil
   639  }
   640  
   641  func (i *checkpointInfo) unmarshal(b []byte) error {
   642  	buffer := proto.NewBuffer(b)
   643  	var val uint64
   644  	var chainEmptyMarker uint64
   645  	var err error
   646  
   647  	if val, err = buffer.DecodeVarint(); err != nil {
   648  		return err
   649  	}
   650  	i.latestFileChunkSuffixNum = int(val)
   651  
   652  	if val, err = buffer.DecodeVarint(); err != nil {
   653  		return err
   654  	}
   655  	i.latestFileChunksize = int(val)
   656  
   657  	if val, err = buffer.DecodeVarint(); err != nil {
   658  		return err
   659  	}
   660  	i.lastBlockNumber = val
   661  	if chainEmptyMarker, err = buffer.DecodeVarint(); err != nil {
   662  		return err
   663  	}
   664  	i.isChainEmpty = chainEmptyMarker == 1
   665  	return nil
   666  }
   667  
   668  func (i *checkpointInfo) String() string {
   669  	return fmt.Sprintf("latestFileChunkSuffixNum=[%d], latestFileChunksize=[%d], isChainEmpty=[%t], lastBlockNumber=[%d]",
   670  		i.latestFileChunkSuffixNum, i.latestFileChunksize, i.isChainEmpty, i.lastBlockNumber)
   671  }