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

     1  /*
     2  Copyright hechain. 2022 All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package blkstorage
     8  
     9  import (
    10  	"fmt"
    11  	"io/ioutil"
    12  	"os"
    13  	"testing"
    14  
    15  	"github.com/golang/protobuf/proto"
    16  	"github.com/hechain20/hechain/common/ledger/testutil"
    17  	"github.com/hechain20/hechain/internal/pkg/txflags"
    18  	"github.com/hechain20/hechain/protoutil"
    19  	"github.com/hyperledger/fabric-protos-go/common"
    20  	"github.com/hyperledger/fabric-protos-go/peer"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  func TestBlockfileMgrBlockReadWrite(t *testing.T) {
    25  	env := newTestEnv(t, NewConf(testPath(), 0))
    26  	defer env.Cleanup()
    27  	blkfileMgrWrapper := newTestBlockfileWrapper(env, "testLedger")
    28  	defer blkfileMgrWrapper.close()
    29  	blocks := testutil.ConstructTestBlocks(t, 10)
    30  	blkfileMgrWrapper.addBlocks(blocks)
    31  	blkfileMgrWrapper.testGetBlockByHash(blocks)
    32  	blkfileMgrWrapper.testGetBlockByNumber(blocks)
    33  }
    34  
    35  func TestAddBlockWithWrongHash(t *testing.T) {
    36  	env := newTestEnv(t, NewConf(testPath(), 0))
    37  	defer env.Cleanup()
    38  	blkfileMgrWrapper := newTestBlockfileWrapper(env, "testLedger")
    39  	defer blkfileMgrWrapper.close()
    40  	blocks := testutil.ConstructTestBlocks(t, 10)
    41  	blkfileMgrWrapper.addBlocks(blocks[0:9])
    42  	lastBlock := blocks[9]
    43  	lastBlock.Header.PreviousHash = []byte("someJunkHash") // set the hash to something unexpected
    44  	err := blkfileMgrWrapper.blockfileMgr.addBlock(lastBlock)
    45  	require.Error(t, err, "An error is expected when adding a block with some unexpected hash")
    46  	require.Contains(t, err.Error(), "unexpected Previous block hash. Expected PreviousHash")
    47  	t.Logf("err = %s", err)
    48  }
    49  
    50  func TestBlockfileMgrCrashDuringWriting(t *testing.T) {
    51  	testBlockfileMgrCrashDuringWriting(t, 10, 2, 1000, 10, false)
    52  	testBlockfileMgrCrashDuringWriting(t, 10, 2, 1000, 1, false)
    53  	testBlockfileMgrCrashDuringWriting(t, 10, 2, 1000, 0, false)
    54  	testBlockfileMgrCrashDuringWriting(t, 0, 0, 1000, 10, false)
    55  	testBlockfileMgrCrashDuringWriting(t, 0, 5, 1000, 10, false)
    56  
    57  	testBlockfileMgrCrashDuringWriting(t, 10, 2, 1000, 10, true)
    58  	testBlockfileMgrCrashDuringWriting(t, 10, 2, 1000, 1, true)
    59  	testBlockfileMgrCrashDuringWriting(t, 10, 2, 1000, 0, true)
    60  	testBlockfileMgrCrashDuringWriting(t, 0, 0, 1000, 10, true)
    61  	testBlockfileMgrCrashDuringWriting(t, 0, 5, 1000, 10, true)
    62  }
    63  
    64  func testBlockfileMgrCrashDuringWriting(t *testing.T, numBlksBeforeSavingBlkfilesInfo int,
    65  	numBlksAfterSavingBlkfilesInfo int, numLastBlockBytes int, numPartialBytesToWrite int,
    66  	deleteBFInfo bool) {
    67  	env := newTestEnv(t, NewConf(testPath(), 0))
    68  	defer env.Cleanup()
    69  	ledgerid := "testLedger"
    70  	blkfileMgrWrapper := newTestBlockfileWrapper(env, ledgerid)
    71  	bg, gb := testutil.NewBlockGenerator(t, ledgerid, false)
    72  
    73  	// create all necessary blocks
    74  	totalBlocks := numBlksBeforeSavingBlkfilesInfo + numBlksAfterSavingBlkfilesInfo
    75  	allBlocks := []*common.Block{gb}
    76  	allBlocks = append(allBlocks, bg.NextTestBlocks(totalBlocks+1)...)
    77  
    78  	// identify the blocks that are to be added beforeCP, afterCP, and after restart
    79  	blocksBeforeSavingBlkfilesInfo := []*common.Block{}
    80  	blocksAfterSavingBlkfilesInfo := []*common.Block{}
    81  	if numBlksBeforeSavingBlkfilesInfo != 0 {
    82  		blocksBeforeSavingBlkfilesInfo = allBlocks[0:numBlksBeforeSavingBlkfilesInfo]
    83  	}
    84  	if numBlksAfterSavingBlkfilesInfo != 0 {
    85  		blocksAfterSavingBlkfilesInfo = allBlocks[numBlksBeforeSavingBlkfilesInfo : numBlksBeforeSavingBlkfilesInfo+numBlksAfterSavingBlkfilesInfo]
    86  	}
    87  	blocksAfterRestart := allBlocks[numBlksBeforeSavingBlkfilesInfo+numBlksAfterSavingBlkfilesInfo:]
    88  
    89  	// add blocks before cp
    90  	blkfileMgrWrapper.addBlocks(blocksBeforeSavingBlkfilesInfo)
    91  	currentBlkfilesInfo := blkfileMgrWrapper.blockfileMgr.blockfilesInfo
    92  	blkfilesInfo1 := &blockfilesInfo{
    93  		latestFileNumber:   currentBlkfilesInfo.latestFileNumber,
    94  		latestFileSize:     currentBlkfilesInfo.latestFileSize,
    95  		noBlockFiles:       currentBlkfilesInfo.noBlockFiles,
    96  		lastPersistedBlock: currentBlkfilesInfo.lastPersistedBlock,
    97  	}
    98  
    99  	// add blocks after cp
   100  	blkfileMgrWrapper.addBlocks(blocksAfterSavingBlkfilesInfo)
   101  	blkfilesInfo2 := blkfileMgrWrapper.blockfileMgr.blockfilesInfo
   102  
   103  	// simulate a crash scenario
   104  	lastBlockBytes := []byte{}
   105  	encodedLen := proto.EncodeVarint(uint64(numLastBlockBytes))
   106  	randomBytes := testutil.ConstructRandomBytes(t, numLastBlockBytes)
   107  	lastBlockBytes = append(lastBlockBytes, encodedLen...)
   108  	lastBlockBytes = append(lastBlockBytes, randomBytes...)
   109  	partialBytes := lastBlockBytes[:numPartialBytesToWrite]
   110  	blkfileMgrWrapper.blockfileMgr.currentFileWriter.append(partialBytes, true)
   111  	if deleteBFInfo {
   112  		err := blkfileMgrWrapper.blockfileMgr.db.Delete(blkMgrInfoKey, true)
   113  		require.NoError(t, err)
   114  	} else {
   115  		blkfileMgrWrapper.blockfileMgr.saveBlkfilesInfo(blkfilesInfo1, true)
   116  	}
   117  	blkfileMgrWrapper.close()
   118  
   119  	// simulate a start after a crash
   120  	blkfileMgrWrapper = newTestBlockfileWrapper(env, ledgerid)
   121  	defer blkfileMgrWrapper.close()
   122  	blkfilesInfo3 := blkfileMgrWrapper.blockfileMgr.blockfilesInfo
   123  	require.Equal(t, blkfilesInfo2, blkfilesInfo3)
   124  
   125  	// add fresh blocks after restart
   126  	blkfileMgrWrapper.addBlocks(blocksAfterRestart)
   127  	testBlockfileMgrBlockIterator(t, blkfileMgrWrapper.blockfileMgr, 0, len(allBlocks)-1, allBlocks)
   128  }
   129  
   130  func TestBlockfileMgrBlockIterator(t *testing.T) {
   131  	env := newTestEnv(t, NewConf(testPath(), 0))
   132  	defer env.Cleanup()
   133  	blkfileMgrWrapper := newTestBlockfileWrapper(env, "testLedger")
   134  	defer blkfileMgrWrapper.close()
   135  	blocks := testutil.ConstructTestBlocks(t, 10)
   136  	blkfileMgrWrapper.addBlocks(blocks)
   137  	testBlockfileMgrBlockIterator(t, blkfileMgrWrapper.blockfileMgr, 0, 7, blocks[0:8])
   138  }
   139  
   140  func testBlockfileMgrBlockIterator(t *testing.T, blockfileMgr *blockfileMgr,
   141  	firstBlockNum int, lastBlockNum int, expectedBlocks []*common.Block) {
   142  	itr, err := blockfileMgr.retrieveBlocks(uint64(firstBlockNum))
   143  	require.NoError(t, err, "Error while getting blocks iterator")
   144  	defer itr.Close()
   145  	numBlocksItrated := 0
   146  	for {
   147  		block, err := itr.Next()
   148  		require.NoError(t, err, "Error while getting block number [%d] from iterator", numBlocksItrated)
   149  		require.Equal(t, expectedBlocks[numBlocksItrated], block)
   150  		numBlocksItrated++
   151  		if numBlocksItrated == lastBlockNum-firstBlockNum+1 {
   152  			break
   153  		}
   154  	}
   155  	require.Equal(t, lastBlockNum-firstBlockNum+1, numBlocksItrated)
   156  }
   157  
   158  func TestBlockfileMgrBlockchainInfo(t *testing.T) {
   159  	env := newTestEnv(t, NewConf(testPath(), 0))
   160  	defer env.Cleanup()
   161  	blkfileMgrWrapper := newTestBlockfileWrapper(env, "testLedger")
   162  	defer blkfileMgrWrapper.close()
   163  
   164  	bcInfo := blkfileMgrWrapper.blockfileMgr.getBlockchainInfo()
   165  	require.Equal(t, &common.BlockchainInfo{Height: 0, CurrentBlockHash: nil, PreviousBlockHash: nil}, bcInfo)
   166  
   167  	blocks := testutil.ConstructTestBlocks(t, 10)
   168  	blkfileMgrWrapper.addBlocks(blocks)
   169  	bcInfo = blkfileMgrWrapper.blockfileMgr.getBlockchainInfo()
   170  	require.Equal(t, uint64(10), bcInfo.Height)
   171  }
   172  
   173  func TestTxIDExists(t *testing.T) {
   174  	t.Run("green-path", func(t *testing.T) {
   175  		env := newTestEnv(t, NewConf(testPath(), 0))
   176  		defer env.Cleanup()
   177  
   178  		blkStore, err := env.provider.Open("testLedger")
   179  		require.NoError(t, err)
   180  		defer blkStore.Shutdown()
   181  
   182  		blocks := testutil.ConstructTestBlocks(t, 2)
   183  		for _, blk := range blocks {
   184  			require.NoError(t, blkStore.AddBlock(blk))
   185  		}
   186  
   187  		for _, blk := range blocks {
   188  			for i := range blk.Data.Data {
   189  				txID, err := protoutil.GetOrComputeTxIDFromEnvelope(blk.Data.Data[i])
   190  				require.NoError(t, err)
   191  				exists, err := blkStore.TxIDExists(txID)
   192  				require.NoError(t, err)
   193  				require.True(t, exists)
   194  			}
   195  		}
   196  		exists, err := blkStore.TxIDExists("non-existent-txid")
   197  		require.NoError(t, err)
   198  		require.False(t, exists)
   199  	})
   200  
   201  	t.Run("error-path", func(t *testing.T) {
   202  		env := newTestEnv(t, NewConf(testPath(), 0))
   203  		defer env.Cleanup()
   204  
   205  		blkStore, err := env.provider.Open("testLedger")
   206  		require.NoError(t, err)
   207  		defer blkStore.Shutdown()
   208  
   209  		env.provider.Close()
   210  		exists, err := blkStore.TxIDExists("random")
   211  		require.EqualError(t, err, "error while trying to check the presence of TXID [random]: internal leveldb error while obtaining db iterator: leveldb: closed")
   212  		require.False(t, exists)
   213  	})
   214  }
   215  
   216  func TestBlockfileMgrGetTxById(t *testing.T) {
   217  	env := newTestEnv(t, NewConf(testPath(), 0))
   218  	defer env.Cleanup()
   219  	blkfileMgrWrapper := newTestBlockfileWrapper(env, "testLedger")
   220  	defer blkfileMgrWrapper.close()
   221  	blocks := testutil.ConstructTestBlocks(t, 2)
   222  	blkfileMgrWrapper.addBlocks(blocks)
   223  	for _, blk := range blocks {
   224  		for j, txEnvelopeBytes := range blk.Data.Data {
   225  			// blockNum starts with 0
   226  			txID, err := protoutil.GetOrComputeTxIDFromEnvelope(blk.Data.Data[j])
   227  			require.NoError(t, err)
   228  			txEnvelopeFromFileMgr, err := blkfileMgrWrapper.blockfileMgr.retrieveTransactionByID(txID)
   229  			require.NoError(t, err, "Error while retrieving tx from blkfileMgr")
   230  			txEnvelope, err := protoutil.GetEnvelopeFromBlock(txEnvelopeBytes)
   231  			require.NoError(t, err, "Error while unmarshalling tx")
   232  			require.Equal(t, txEnvelope, txEnvelopeFromFileMgr)
   233  		}
   234  	}
   235  }
   236  
   237  // TestBlockfileMgrGetTxByIdDuplicateTxid tests that a transaction with an existing txid
   238  // (within same block or a different block) should not over-write the index by-txid (FAB-8557)
   239  func TestBlockfileMgrGetTxByIdDuplicateTxid(t *testing.T) {
   240  	env := newTestEnv(t, NewConf(testPath(), 0))
   241  	defer env.Cleanup()
   242  	blkStore, err := env.provider.Open("testLedger")
   243  	require.NoError(env.t, err)
   244  	blkFileMgr := blkStore.fileMgr
   245  	bg, gb := testutil.NewBlockGenerator(t, "testLedger", false)
   246  	require.NoError(t, blkFileMgr.addBlock(gb))
   247  
   248  	block1 := bg.NextBlockWithTxid(
   249  		[][]byte{
   250  			[]byte("tx with id=txid-1"),
   251  			[]byte("tx with id=txid-2"),
   252  			[]byte("another tx with existing id=txid-1"),
   253  		},
   254  		[]string{"txid-1", "txid-2", "txid-1"},
   255  	)
   256  	txValidationFlags := txflags.New(3)
   257  	txValidationFlags.SetFlag(0, peer.TxValidationCode_VALID)
   258  	txValidationFlags.SetFlag(1, peer.TxValidationCode_INVALID_OTHER_REASON)
   259  	txValidationFlags.SetFlag(2, peer.TxValidationCode_DUPLICATE_TXID)
   260  	block1.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txValidationFlags
   261  	require.NoError(t, blkFileMgr.addBlock(block1))
   262  
   263  	block2 := bg.NextBlockWithTxid(
   264  		[][]byte{
   265  			[]byte("tx with id=txid-3"),
   266  			[]byte("yet another tx with existing id=txid-1"),
   267  		},
   268  		[]string{"txid-3", "txid-1"},
   269  	)
   270  	txValidationFlags = txflags.New(2)
   271  	txValidationFlags.SetFlag(0, peer.TxValidationCode_VALID)
   272  	txValidationFlags.SetFlag(1, peer.TxValidationCode_DUPLICATE_TXID)
   273  	block2.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txValidationFlags
   274  	require.NoError(t, blkFileMgr.addBlock(block2))
   275  
   276  	txenvp1, err := protoutil.GetEnvelopeFromBlock(block1.Data.Data[0])
   277  	require.NoError(t, err)
   278  	txenvp2, err := protoutil.GetEnvelopeFromBlock(block1.Data.Data[1])
   279  	require.NoError(t, err)
   280  	txenvp3, err := protoutil.GetEnvelopeFromBlock(block2.Data.Data[0])
   281  	require.NoError(t, err)
   282  
   283  	indexedTxenvp, _ := blkFileMgr.retrieveTransactionByID("txid-1")
   284  	require.Equal(t, txenvp1, indexedTxenvp)
   285  	indexedTxenvp, _ = blkFileMgr.retrieveTransactionByID("txid-2")
   286  	require.Equal(t, txenvp2, indexedTxenvp)
   287  	indexedTxenvp, _ = blkFileMgr.retrieveTransactionByID("txid-3")
   288  	require.Equal(t, txenvp3, indexedTxenvp)
   289  
   290  	blk, _ := blkFileMgr.retrieveBlockByTxID("txid-1")
   291  	require.Equal(t, block1, blk)
   292  	blk, _ = blkFileMgr.retrieveBlockByTxID("txid-2")
   293  	require.Equal(t, block1, blk)
   294  	blk, _ = blkFileMgr.retrieveBlockByTxID("txid-3")
   295  	require.Equal(t, block2, blk)
   296  
   297  	validationCode, blkNum, _ := blkFileMgr.retrieveTxValidationCodeByTxID("txid-1")
   298  	require.Equal(t, peer.TxValidationCode_VALID, validationCode)
   299  	require.Equal(t, uint64(1), blkNum)
   300  	validationCode, blkNum, _ = blkFileMgr.retrieveTxValidationCodeByTxID("txid-2")
   301  	require.Equal(t, peer.TxValidationCode_INVALID_OTHER_REASON, validationCode)
   302  	require.Equal(t, uint64(1), blkNum)
   303  	validationCode, blkNum, _ = blkFileMgr.retrieveTxValidationCodeByTxID("txid-3")
   304  	require.Equal(t, peer.TxValidationCode_VALID, validationCode)
   305  	require.Equal(t, uint64(2), blkNum)
   306  
   307  	// though we do not expose an API for retrieving all the txs by same id but we may in future
   308  	// and the data is persisted to support this. below code tests this behavior internally
   309  	w := &testBlockfileMgrWrapper{
   310  		t:            t,
   311  		blockfileMgr: blkFileMgr,
   312  	}
   313  	w.testGetMultipleDataByTxID(
   314  		"txid-1",
   315  		[]*expectedBlkTxValidationCode{
   316  			{
   317  				blk:            block1,
   318  				txEnv:          protoutil.ExtractEnvelopeOrPanic(block1, 0),
   319  				validationCode: peer.TxValidationCode_VALID,
   320  			},
   321  			{
   322  				blk:            block1,
   323  				txEnv:          protoutil.ExtractEnvelopeOrPanic(block1, 2),
   324  				validationCode: peer.TxValidationCode_DUPLICATE_TXID,
   325  			},
   326  			{
   327  				blk:            block2,
   328  				txEnv:          protoutil.ExtractEnvelopeOrPanic(block2, 1),
   329  				validationCode: peer.TxValidationCode_DUPLICATE_TXID,
   330  			},
   331  		},
   332  	)
   333  
   334  	w.testGetMultipleDataByTxID(
   335  		"txid-2",
   336  		[]*expectedBlkTxValidationCode{
   337  			{
   338  				blk:            block1,
   339  				txEnv:          protoutil.ExtractEnvelopeOrPanic(block1, 1),
   340  				validationCode: peer.TxValidationCode_INVALID_OTHER_REASON,
   341  			},
   342  		},
   343  	)
   344  
   345  	w.testGetMultipleDataByTxID(
   346  		"txid-3",
   347  		[]*expectedBlkTxValidationCode{
   348  			{
   349  				blk:            block2,
   350  				txEnv:          protoutil.ExtractEnvelopeOrPanic(block2, 0),
   351  				validationCode: peer.TxValidationCode_VALID,
   352  			},
   353  		},
   354  	)
   355  }
   356  
   357  func TestBlockfileMgrGetTxByBlockNumTranNum(t *testing.T) {
   358  	env := newTestEnv(t, NewConf(testPath(), 0))
   359  	defer env.Cleanup()
   360  	blkfileMgrWrapper := newTestBlockfileWrapper(env, "testLedger")
   361  	defer blkfileMgrWrapper.close()
   362  	blocks := testutil.ConstructTestBlocks(t, 10)
   363  	blkfileMgrWrapper.addBlocks(blocks)
   364  	for blockIndex, blk := range blocks {
   365  		for tranIndex, txEnvelopeBytes := range blk.Data.Data {
   366  			// blockNum and tranNum both start with 0
   367  			txEnvelopeFromFileMgr, err := blkfileMgrWrapper.blockfileMgr.retrieveTransactionByBlockNumTranNum(uint64(blockIndex), uint64(tranIndex))
   368  			require.NoError(t, err, "Error while retrieving tx from blkfileMgr")
   369  			txEnvelope, err := protoutil.GetEnvelopeFromBlock(txEnvelopeBytes)
   370  			require.NoError(t, err, "Error while unmarshalling tx")
   371  			require.Equal(t, txEnvelope, txEnvelopeFromFileMgr)
   372  		}
   373  	}
   374  }
   375  
   376  func TestBlockfileMgrRestart(t *testing.T) {
   377  	env := newTestEnv(t, NewConf(testPath(), 0))
   378  	defer env.Cleanup()
   379  	ledgerid := "testLedger"
   380  	blkfileMgrWrapper := newTestBlockfileWrapper(env, ledgerid)
   381  	blocks := testutil.ConstructTestBlocks(t, 10)
   382  	blkfileMgrWrapper.addBlocks(blocks)
   383  	expectedHeight := uint64(10)
   384  	require.Equal(t, expectedHeight, blkfileMgrWrapper.blockfileMgr.getBlockchainInfo().Height)
   385  	blkfileMgrWrapper.close()
   386  
   387  	blkfileMgrWrapper = newTestBlockfileWrapper(env, ledgerid)
   388  	defer blkfileMgrWrapper.close()
   389  	require.Equal(t, 9, int(blkfileMgrWrapper.blockfileMgr.blockfilesInfo.lastPersistedBlock))
   390  	blkfileMgrWrapper.testGetBlockByHash(blocks)
   391  	require.Equal(t, expectedHeight, blkfileMgrWrapper.blockfileMgr.getBlockchainInfo().Height)
   392  }
   393  
   394  func TestBlockfileMgrFileRolling(t *testing.T) {
   395  	blocks := testutil.ConstructTestBlocks(t, 200)
   396  	size := 0
   397  	for _, block := range blocks[:100] {
   398  		by, _, err := serializeBlock(block)
   399  		require.NoError(t, err, "Error while serializing block")
   400  		blockBytesSize := len(by)
   401  		encodedLen := proto.EncodeVarint(uint64(blockBytesSize))
   402  		size += blockBytesSize + len(encodedLen)
   403  	}
   404  
   405  	maxFileSie := int(0.75 * float64(size))
   406  	env := newTestEnv(t, NewConf(testPath(), maxFileSie))
   407  	defer env.Cleanup()
   408  	ledgerid := "testLedger"
   409  	blkfileMgrWrapper := newTestBlockfileWrapper(env, ledgerid)
   410  	blkfileMgrWrapper.addBlocks(blocks[:100])
   411  	require.Equal(t, 1, blkfileMgrWrapper.blockfileMgr.blockfilesInfo.latestFileNumber)
   412  	blkfileMgrWrapper.testGetBlockByHash(blocks[:100])
   413  	blkfileMgrWrapper.close()
   414  
   415  	blkfileMgrWrapper = newTestBlockfileWrapper(env, ledgerid)
   416  	defer blkfileMgrWrapper.close()
   417  	blkfileMgrWrapper.addBlocks(blocks[100:])
   418  	require.Equal(t, 2, blkfileMgrWrapper.blockfileMgr.blockfilesInfo.latestFileNumber)
   419  	blkfileMgrWrapper.testGetBlockByHash(blocks[100:])
   420  }
   421  
   422  func TestBlockfileMgrGetBlockByTxID(t *testing.T) {
   423  	env := newTestEnv(t, NewConf(testPath(), 0))
   424  	defer env.Cleanup()
   425  	blkfileMgrWrapper := newTestBlockfileWrapper(env, "testLedger")
   426  	defer blkfileMgrWrapper.close()
   427  	blocks := testutil.ConstructTestBlocks(t, 10)
   428  	blkfileMgrWrapper.addBlocks(blocks)
   429  	for _, blk := range blocks {
   430  		for j := range blk.Data.Data {
   431  			// blockNum starts with 1
   432  			txID, err := protoutil.GetOrComputeTxIDFromEnvelope(blk.Data.Data[j])
   433  			require.NoError(t, err)
   434  
   435  			blockFromFileMgr, err := blkfileMgrWrapper.blockfileMgr.retrieveBlockByTxID(txID)
   436  			require.NoError(t, err, "Error while retrieving block from blkfileMgr")
   437  			require.Equal(t, blk, blockFromFileMgr)
   438  		}
   439  	}
   440  }
   441  
   442  func TestBlockfileMgrSimulateCrashAtFirstBlockInFile(t *testing.T) {
   443  	t.Run("blockfilesInfo persisted", func(t *testing.T) {
   444  		testBlockfileMgrSimulateCrashAtFirstBlockInFile(t, false)
   445  	})
   446  
   447  	t.Run("blockfilesInfo to be computed from block files", func(t *testing.T) {
   448  		testBlockfileMgrSimulateCrashAtFirstBlockInFile(t, true)
   449  	})
   450  }
   451  
   452  func testBlockfileMgrSimulateCrashAtFirstBlockInFile(t *testing.T, deleteBlkfilesInfo bool) {
   453  	// open blockfileMgr and add 5 blocks
   454  	env := newTestEnv(t, NewConf(testPath(), 0))
   455  	defer env.Cleanup()
   456  
   457  	blkfileMgrWrapper := newTestBlockfileWrapper(env, "testLedger")
   458  	blockfileMgr := blkfileMgrWrapper.blockfileMgr
   459  	blocks := testutil.ConstructTestBlocks(t, 10)
   460  	for i := 0; i < 10; i++ {
   461  		fmt.Printf("blocks[i].Header.Number = %d\n", blocks[i].Header.Number)
   462  	}
   463  	blkfileMgrWrapper.addBlocks(blocks[:5])
   464  	firstFilePath := blockfileMgr.currentFileWriter.filePath
   465  	firstBlkFileSize := testutilGetFileSize(t, firstFilePath)
   466  
   467  	// move to next file and simulate crash scenario while writing the first block
   468  	blockfileMgr.moveToNextFile()
   469  	partialBytesForNextBlock := append(
   470  		proto.EncodeVarint(uint64(10000)),
   471  		[]byte("partialBytesForNextBlock depicting a crash during first block in file")...,
   472  	)
   473  	blockfileMgr.currentFileWriter.append(partialBytesForNextBlock, true)
   474  	if deleteBlkfilesInfo {
   475  		err := blockfileMgr.db.Delete(blkMgrInfoKey, true)
   476  		require.NoError(t, err)
   477  	}
   478  	blkfileMgrWrapper.close()
   479  
   480  	// verify that the block file number 1 has been created with partial bytes as a side-effect of crash
   481  	lastFilePath := blockfileMgr.currentFileWriter.filePath
   482  	lastFileContent, err := ioutil.ReadFile(lastFilePath)
   483  	require.NoError(t, err)
   484  	require.Equal(t, lastFileContent, partialBytesForNextBlock)
   485  
   486  	// simulate reopen after crash
   487  	blkfileMgrWrapper = newTestBlockfileWrapper(env, "testLedger")
   488  	defer blkfileMgrWrapper.close()
   489  
   490  	// last block file (block file number 1) should have been truncated to zero length and concluded as the next file to append to
   491  	require.Equal(t, 0, testutilGetFileSize(t, lastFilePath))
   492  	require.Equal(t,
   493  		&blockfilesInfo{
   494  			latestFileNumber:   1,
   495  			latestFileSize:     0,
   496  			lastPersistedBlock: 4,
   497  			noBlockFiles:       false,
   498  		},
   499  		blkfileMgrWrapper.blockfileMgr.blockfilesInfo,
   500  	)
   501  
   502  	// Add 5 more blocks and assert that they are added to last file (block file number 1) and full scanning across two files works as expected
   503  	blkfileMgrWrapper.addBlocks(blocks[5:])
   504  	require.True(t, testutilGetFileSize(t, lastFilePath) > 0)
   505  	require.Equal(t, firstBlkFileSize, testutilGetFileSize(t, firstFilePath))
   506  	blkfileMgrWrapper.testGetBlockByNumber(blocks)
   507  	testBlockfileMgrBlockIterator(t, blkfileMgrWrapper.blockfileMgr, 0, len(blocks)-1, blocks)
   508  }
   509  
   510  func testutilGetFileSize(t *testing.T, path string) int {
   511  	fi, err := os.Stat(path)
   512  	require.NoError(t, err)
   513  	return int(fi.Size())
   514  }