github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/common/ledger/blkstorage/snapshot_test.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  	"fmt"
    11  	"io/ioutil"
    12  	"os"
    13  	"path/filepath"
    14  	"sort"
    15  	"testing"
    16  
    17  	"github.com/hechain20/hechain/common/ledger/snapshot"
    18  	"github.com/hechain20/hechain/common/ledger/testutil"
    19  	"github.com/hechain20/hechain/internal/pkg/txflags"
    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/stretchr/testify/require"
    24  )
    25  
    26  type testBlockDetails struct {
    27  	txIDs           []string
    28  	validationCodes []peer.TxValidationCode
    29  }
    30  
    31  func TestImportFromSnapshot(t *testing.T) {
    32  	var testDir string
    33  	var env *testEnv
    34  	var blocksDetailsBeforeSnapshot, blocksDetailsAfterSnapshot []*testBlockDetails
    35  	var blocksBeforeSnapshot, blocksAfterSnapshot []*common.Block
    36  	var blocksGenerator *testutil.BlockGenerator
    37  
    38  	var snapshotDir string
    39  	var snapshotInfo *SnapshotInfo
    40  	var bootstrappedBlockStore *BlockStore
    41  
    42  	bootstrappedLedgerName := "bootstrappedLedger"
    43  
    44  	setup := func() {
    45  		testDir = testPath()
    46  		env = newTestEnv(t, NewConf(testDir, 0))
    47  		snapshotDir = filepath.Join(testDir, "snapshot")
    48  		require.NoError(t, os.Mkdir(snapshotDir, 0o755))
    49  
    50  		bg, genesisBlock := testutil.NewBlockGenerator(t, "testLedger", false)
    51  		blocksGenerator = bg
    52  		originalBlockStore, err := env.provider.Open("originalLedger")
    53  		require.NoError(t, err)
    54  		txIDGenesisTx, err := protoutil.GetOrComputeTxIDFromEnvelope(genesisBlock.Data.Data[0])
    55  		require.NoError(t, err)
    56  
    57  		blocksDetailsBeforeSnapshot = []*testBlockDetails{
    58  			{
    59  				txIDs: []string{txIDGenesisTx},
    60  			},
    61  			{
    62  				txIDs: []string{"txid1", "txid2"},
    63  			},
    64  			{
    65  				txIDs: []string{"txid3", "txid4", "txid5"},
    66  			},
    67  		}
    68  
    69  		blocksBeforeSnapshot = []*common.Block{
    70  			genesisBlock,
    71  			generateNextTestBlock(blocksGenerator, blocksDetailsBeforeSnapshot[1]),
    72  			generateNextTestBlock(blocksGenerator, blocksDetailsBeforeSnapshot[2]),
    73  		}
    74  
    75  		blocksDetailsAfterSnapshot = []*testBlockDetails{
    76  			{
    77  				txIDs: []string{"txid7", "txid8"},
    78  			},
    79  			{
    80  				txIDs: []string{"txid9", "txid10", "txid11"},
    81  				validationCodes: []peer.TxValidationCode{
    82  					peer.TxValidationCode_BAD_CHANNEL_HEADER,
    83  					peer.TxValidationCode_BAD_CREATOR_SIGNATURE,
    84  				},
    85  			},
    86  		}
    87  
    88  		blocksAfterSnapshot = []*common.Block{
    89  			generateNextTestBlock(blocksGenerator, blocksDetailsAfterSnapshot[0]),
    90  			generateNextTestBlock(blocksGenerator, blocksDetailsAfterSnapshot[1]),
    91  		}
    92  
    93  		for _, b := range blocksBeforeSnapshot {
    94  			err := originalBlockStore.AddBlock(b)
    95  			require.NoError(t, err)
    96  		}
    97  
    98  		// verify blockchain info for original store (created by genesisblock)
    99  		lastBlock := blocksBeforeSnapshot[len(blocksBeforeSnapshot)-1]
   100  		prevBlock := blocksBeforeSnapshot[len(blocksBeforeSnapshot)-2]
   101  		bcInfo, err := originalBlockStore.GetBlockchainInfo()
   102  		require.NoError(t, err)
   103  		require.Equal(t, &common.BlockchainInfo{
   104  			Height:            uint64(len(blocksBeforeSnapshot)),
   105  			CurrentBlockHash:  protoutil.BlockHeaderHash(lastBlock.Header),
   106  			PreviousBlockHash: protoutil.BlockHeaderHash(prevBlock.Header),
   107  		}, bcInfo)
   108  
   109  		_, err = originalBlockStore.ExportTxIds(snapshotDir, testNewHashFunc)
   110  		require.NoError(t, err)
   111  		lastBlockInSnapshot := blocksBeforeSnapshot[len(blocksBeforeSnapshot)-1]
   112  
   113  		snapshotInfo = &SnapshotInfo{
   114  			LastBlockHash:     protoutil.BlockHeaderHash(lastBlockInSnapshot.Header),
   115  			LastBlockNum:      lastBlockInSnapshot.Header.Number,
   116  			PreviousBlockHash: lastBlockInSnapshot.Header.PreviousHash,
   117  		}
   118  
   119  		// bootstrap another blockstore from the snapshot and verify its APIs
   120  		importTxIDsBatchSize = uint64(2) // smaller batch size for testing
   121  
   122  		err = env.provider.ImportFromSnapshot(bootstrappedLedgerName, snapshotDir, snapshotInfo)
   123  		require.NoError(t, err)
   124  		bootstrappedBlockStore, err = env.provider.Open(bootstrappedLedgerName)
   125  		require.NoError(t, err)
   126  	}
   127  
   128  	cleanup := func() {
   129  		env.Cleanup()
   130  	}
   131  
   132  	closeBlockStore := func() {
   133  		env.provider.Close()
   134  	}
   135  
   136  	reopenBlockStore := func() error {
   137  		env = newTestEnv(t, env.provider.conf)
   138  		s, err := env.provider.Open(bootstrappedLedgerName)
   139  		bootstrappedBlockStore = s
   140  		return err
   141  	}
   142  
   143  	t.Run("query-just-after-bootstrap", func(t *testing.T) {
   144  		setup()
   145  		defer cleanup()
   146  
   147  		verifyQueriesOnBlocksPriorToSnapshot(
   148  			t,
   149  			bootstrappedBlockStore,
   150  			&common.BlockchainInfo{
   151  				Height:            snapshotInfo.LastBlockNum + 1,
   152  				CurrentBlockHash:  snapshotInfo.LastBlockHash,
   153  				PreviousBlockHash: snapshotInfo.PreviousBlockHash,
   154  				BootstrappingSnapshotInfo: &common.BootstrappingSnapshotInfo{
   155  					LastBlockInSnapshot: snapshotInfo.LastBlockNum,
   156  				},
   157  			},
   158  			blocksDetailsBeforeSnapshot,
   159  			blocksBeforeSnapshot,
   160  		)
   161  	})
   162  
   163  	t.Run("add-more-blocks", func(t *testing.T) {
   164  		setup()
   165  		defer cleanup()
   166  
   167  		require.EqualError(t,
   168  			bootstrappedBlockStore.AddBlock(blocksAfterSnapshot[1]),
   169  			"block number should have been 3 but was 4",
   170  		)
   171  
   172  		for _, b := range blocksAfterSnapshot {
   173  			require.NoError(t, bootstrappedBlockStore.AddBlock(b))
   174  		}
   175  		finalBlock := blocksAfterSnapshot[len(blocksAfterSnapshot)-1]
   176  		expectedBCInfo := &common.BlockchainInfo{
   177  			Height:            finalBlock.Header.Number + 1,
   178  			CurrentBlockHash:  protoutil.BlockHeaderHash(finalBlock.Header),
   179  			PreviousBlockHash: finalBlock.Header.PreviousHash,
   180  			BootstrappingSnapshotInfo: &common.BootstrappingSnapshotInfo{
   181  				LastBlockInSnapshot: snapshotInfo.LastBlockNum,
   182  			},
   183  		}
   184  		verifyQueriesOnBlocksPriorToSnapshot(t,
   185  			bootstrappedBlockStore,
   186  			expectedBCInfo,
   187  			blocksDetailsBeforeSnapshot,
   188  			blocksBeforeSnapshot,
   189  		)
   190  		verifyQueriesOnBlocksAddedAfterBootstrapping(t,
   191  			bootstrappedBlockStore,
   192  			expectedBCInfo,
   193  			blocksDetailsAfterSnapshot,
   194  			blocksAfterSnapshot,
   195  		)
   196  	})
   197  
   198  	t.Run("close-and-reopen", func(t *testing.T) {
   199  		setup()
   200  		defer cleanup()
   201  
   202  		closeBlockStore()
   203  		require.NoError(t, reopenBlockStore())
   204  		verifyQueriesOnBlocksPriorToSnapshot(t,
   205  			bootstrappedBlockStore,
   206  			&common.BlockchainInfo{
   207  				Height:            snapshotInfo.LastBlockNum + 1,
   208  				CurrentBlockHash:  snapshotInfo.LastBlockHash,
   209  				PreviousBlockHash: snapshotInfo.PreviousBlockHash,
   210  				BootstrappingSnapshotInfo: &common.BootstrappingSnapshotInfo{
   211  					LastBlockInSnapshot: snapshotInfo.LastBlockNum,
   212  				},
   213  			},
   214  			blocksDetailsBeforeSnapshot,
   215  			blocksBeforeSnapshot,
   216  		)
   217  
   218  		for _, b := range blocksAfterSnapshot {
   219  			require.NoError(t, bootstrappedBlockStore.AddBlock(b))
   220  		}
   221  		closeBlockStore()
   222  		require.NoError(t, reopenBlockStore())
   223  		finalBlock := blocksAfterSnapshot[len(blocksAfterSnapshot)-1]
   224  		expectedBCInfo := &common.BlockchainInfo{
   225  			Height:            finalBlock.Header.Number + 1,
   226  			CurrentBlockHash:  protoutil.BlockHeaderHash(finalBlock.Header),
   227  			PreviousBlockHash: finalBlock.Header.PreviousHash,
   228  			BootstrappingSnapshotInfo: &common.BootstrappingSnapshotInfo{
   229  				LastBlockInSnapshot: snapshotInfo.LastBlockNum,
   230  			},
   231  		}
   232  		verifyQueriesOnBlocksAddedAfterBootstrapping(t,
   233  			bootstrappedBlockStore,
   234  			expectedBCInfo,
   235  			blocksDetailsAfterSnapshot,
   236  			blocksAfterSnapshot,
   237  		)
   238  	})
   239  
   240  	t.Run("export-txids", func(t *testing.T) {
   241  		setup()
   242  		defer cleanup()
   243  
   244  		anotherSnapshotDir := filepath.Join(testDir, "anotherSnapshot")
   245  		require.NoError(t, os.Mkdir(anotherSnapshotDir, 0o755))
   246  
   247  		for _, b := range blocksAfterSnapshot {
   248  			require.NoError(t, bootstrappedBlockStore.AddBlock(b))
   249  		}
   250  
   251  		fileHashes, err := bootstrappedBlockStore.ExportTxIds(anotherSnapshotDir, testNewHashFunc)
   252  		require.NoError(t, err)
   253  		expectedTxIDs := []string{}
   254  		for _, b := range append(blocksDetailsBeforeSnapshot, blocksDetailsAfterSnapshot...) {
   255  			expectedTxIDs = append(expectedTxIDs, b.txIDs...)
   256  		}
   257  		sort.Slice(expectedTxIDs, func(i, j int) bool {
   258  			ith := expectedTxIDs[i]
   259  			jth := expectedTxIDs[j]
   260  			if len(ith) == len(jth) {
   261  				return ith < jth
   262  			}
   263  			return len(ith) < len(jth)
   264  		})
   265  		verifyExportedTxIDs(t, anotherSnapshotDir, fileHashes, expectedTxIDs...)
   266  	})
   267  
   268  	t.Run("sync-up-indexes", func(t *testing.T) {
   269  		setup()
   270  		defer cleanup()
   271  
   272  		blockDetails := []*testBlockDetails{}
   273  		blocks := []*common.Block{}
   274  		for i, blockDetail := range blocksDetailsAfterSnapshot {
   275  			block := blocksAfterSnapshot[i]
   276  			blockDetails = append(blockDetails, blockDetail)
   277  			blocks = append(blocks, block)
   278  
   279  			// redirect index writes to some random place and add two blocks and then set the original index back
   280  			blkfileMgr := bootstrappedBlockStore.fileMgr
   281  			originalIndexDB := blkfileMgr.index.db
   282  			bootstrappedBlockStore.fileMgr.index.db = env.provider.leveldbProvider.GetDBHandle(filepath.Join(testDir, "someRandomPlace"))
   283  			require.NoError(t, blkfileMgr.addBlock(block))
   284  			blkfileMgr.index.db = originalIndexDB
   285  
   286  			// before, we test for index sync-up, verify that the last set of blocks not indexed in the original index
   287  			_, err := blkfileMgr.retrieveBlockByNumber(block.Header.Number)
   288  			require.EqualError(t, err, fmt.Sprintf("no such block number [%d] in index", block.Header.Number))
   289  
   290  			// close and open should be able to sync-up the index
   291  			closeBlockStore()
   292  			require.NoError(t, reopenBlockStore())
   293  
   294  			finalBlock := blocks[len(blocks)-1]
   295  			verifyQueriesOnBlocksAddedAfterBootstrapping(
   296  				t,
   297  				bootstrappedBlockStore,
   298  				&common.BlockchainInfo{
   299  					Height:            finalBlock.Header.Number + 1,
   300  					CurrentBlockHash:  protoutil.BlockHeaderHash(finalBlock.Header),
   301  					PreviousBlockHash: finalBlock.Header.PreviousHash,
   302  					BootstrappingSnapshotInfo: &common.BootstrappingSnapshotInfo{
   303  						LastBlockInSnapshot: snapshotInfo.LastBlockNum,
   304  					},
   305  				},
   306  				blockDetails,
   307  				blocks,
   308  			)
   309  		}
   310  	})
   311  
   312  	t.Run("error-when-indexes-deleted", func(t *testing.T) {
   313  		setup()
   314  		defer cleanup()
   315  
   316  		closeBlockStore()
   317  		require.NoError(t, os.RemoveAll(env.provider.conf.getIndexDir()))
   318  		err := reopenBlockStore()
   319  		require.EqualError(t, err,
   320  			fmt.Sprintf(
   321  				"cannot sync index with block files. blockstore is bootstrapped from a snapshot and first available block=[%d]",
   322  				len(blocksBeforeSnapshot),
   323  			),
   324  		)
   325  	})
   326  }
   327  
   328  func TestBootstrapFromSnapshotErrorPaths(t *testing.T) {
   329  	testPath := testPath()
   330  	env := newTestEnv(t, NewConf(testPath, 0))
   331  	defer func() {
   332  		env.Cleanup()
   333  	}()
   334  
   335  	ledgerID := "bootstrappedLedger"
   336  	snapshotDir := filepath.Join(testPath, "snapshot")
   337  	metadataFile := filepath.Join(snapshotDir, snapshotMetadataFileName)
   338  	dataFile := filepath.Join(snapshotDir, snapshotDataFileName)
   339  	ledgerDir := filepath.Join(testPath, "chains", "bootstrappedLedger")
   340  	bootstrappingSnapshotInfoFile := filepath.Join(ledgerDir, bootstrappingSnapshotInfoFile)
   341  
   342  	snapshotInfo := &SnapshotInfo{
   343  		LastBlockHash:     []byte("LastBlockHash"),
   344  		LastBlockNum:      5,
   345  		PreviousBlockHash: []byte("PreviousBlockHash"),
   346  	}
   347  
   348  	cleanupDirs := func() {
   349  		require.NoError(t, os.RemoveAll(ledgerDir))
   350  		require.NoError(t, os.RemoveAll(snapshotDir))
   351  		require.NoError(t, os.Mkdir(snapshotDir, 0o755))
   352  	}
   353  
   354  	createSnapshotMetadataFile := func(content uint64) {
   355  		mf, err := snapshot.CreateFile(metadataFile, snapshotFileFormat, testNewHashFunc)
   356  		require.NoError(t, err)
   357  		require.NoError(t, mf.EncodeUVarint(content))
   358  		_, err = mf.Done()
   359  		require.NoError(t, err)
   360  	}
   361  	createSnapshotDataFile := func(content ...string) {
   362  		df, err := snapshot.CreateFile(dataFile, snapshotFileFormat, testNewHashFunc)
   363  		require.NoError(t, err)
   364  		for _, c := range content {
   365  			require.NoError(t, df.EncodeString(c))
   366  		}
   367  		_, err = df.Done()
   368  		require.NoError(t, err)
   369  	}
   370  
   371  	t.Run("metadata-file-missing", func(t *testing.T) {
   372  		cleanupDirs()
   373  		err := env.provider.ImportFromSnapshot(ledgerID, snapshotDir, snapshotInfo)
   374  		require.Contains(t, err.Error(), "error while opening the snapshot file: "+metadataFile)
   375  	})
   376  
   377  	t.Run("bootstapping-more-than-once", func(t *testing.T) {
   378  		cleanupDirs()
   379  		env.provider.ImportFromSnapshot(ledgerID, snapshotDir, snapshotInfo)
   380  		err := env.provider.ImportFromSnapshot(ledgerID, snapshotDir, snapshotInfo)
   381  		require.EqualError(t, err, "dir "+ledgerDir+" not empty")
   382  	})
   383  
   384  	t.Run("metadata-file-corrupted", func(t *testing.T) {
   385  		cleanupDirs()
   386  		mf, err := snapshot.CreateFile(metadataFile, snapshotFileFormat, testNewHashFunc)
   387  		require.NoError(t, err)
   388  		require.NoError(t, mf.Close())
   389  		err = env.provider.ImportFromSnapshot(ledgerID, snapshotDir, snapshotInfo)
   390  		require.Contains(t, err.Error(), "error while reading from the snapshot file: "+metadataFile)
   391  	})
   392  
   393  	t.Run("data-file-missing", func(t *testing.T) {
   394  		cleanupDirs()
   395  		createSnapshotMetadataFile(1)
   396  		err := env.provider.ImportFromSnapshot(ledgerID, snapshotDir, snapshotInfo)
   397  		require.Contains(t, err.Error(), "error while opening the snapshot file: "+dataFile)
   398  	})
   399  
   400  	t.Run("data-file-corrupt", func(t *testing.T) {
   401  		cleanupDirs()
   402  		createSnapshotMetadataFile(2)
   403  		createSnapshotDataFile("single-tx-id")
   404  		err := env.provider.ImportFromSnapshot(ledgerID, snapshotDir, snapshotInfo)
   405  		require.Contains(t, err.Error(), "error while reading from snapshot file: "+dataFile)
   406  	})
   407  
   408  	t.Run("db-error", func(t *testing.T) {
   409  		cleanupDirs()
   410  		createSnapshotMetadataFile(1)
   411  		createSnapshotDataFile("single-tx-id")
   412  		env.provider.Close()
   413  		defer func() {
   414  			env = newTestEnv(t, NewConf(testPath, 0))
   415  		}()
   416  		err := env.provider.ImportFromSnapshot(ledgerID, snapshotDir, snapshotInfo)
   417  		require.Contains(t, err.Error(), "error writing batch to leveldb")
   418  	})
   419  
   420  	t.Run("bootstrappedsnapshotInfo-file-corrupt", func(t *testing.T) {
   421  		cleanupDirs()
   422  		createSnapshotMetadataFile(1)
   423  		createSnapshotDataFile("single-tx-id")
   424  		err := env.provider.ImportFromSnapshot(ledgerID, snapshotDir, snapshotInfo)
   425  		require.NoError(t, err)
   426  		env.provider.Close()
   427  		env = newTestEnv(t, NewConf(testPath, 0))
   428  		require.NoError(t, ioutil.WriteFile(bootstrappingSnapshotInfoFile, []byte("junk-data"), 0o644))
   429  		_, err = env.provider.Open(ledgerID)
   430  		require.Contains(t, err.Error(), "error while unmarshalling bootstrappingSnapshotInfo")
   431  	})
   432  }
   433  
   434  func generateNextTestBlock(bg *testutil.BlockGenerator, d *testBlockDetails) *common.Block {
   435  	txContents := [][]byte{}
   436  	for _, txID := range d.txIDs {
   437  		txContents = append(txContents, []byte("dummy content for txid = "+txID))
   438  	}
   439  	block := bg.NextBlockWithTxid(txContents, d.txIDs)
   440  	for i, validationCode := range d.validationCodes {
   441  		txflags.ValidationFlags(block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER]).SetFlag(i, validationCode)
   442  	}
   443  	return block
   444  }
   445  
   446  func verifyQueriesOnBlocksPriorToSnapshot(
   447  	t *testing.T,
   448  	bootstrappedBlockStore *BlockStore,
   449  	expectedBCInfo *common.BlockchainInfo,
   450  	blocksDetailsBeforeSnapshot []*testBlockDetails,
   451  	blocksBeforeSnapshot []*common.Block,
   452  ) {
   453  	bci, err := bootstrappedBlockStore.GetBlockchainInfo()
   454  	require.NoError(t, err)
   455  	require.Equal(t, expectedBCInfo, bci)
   456  
   457  	for _, b := range blocksBeforeSnapshot {
   458  		blockNum := b.Header.Number
   459  		blockHash := protoutil.BlockHeaderHash(b.Header)
   460  		expectedErrStr := fmt.Sprintf(
   461  			"cannot serve block [%d]. The ledger is bootstrapped from a snapshot. First available block = [%d]",
   462  			blockNum, len(blocksBeforeSnapshot),
   463  		)
   464  
   465  		_, err := bootstrappedBlockStore.RetrieveBlockByNumber(blockNum)
   466  		require.EqualError(t, err, expectedErrStr)
   467  
   468  		_, err = bootstrappedBlockStore.RetrieveBlocks(blockNum)
   469  		require.EqualError(t, err, expectedErrStr)
   470  
   471  		_, err = bootstrappedBlockStore.RetrieveTxByBlockNumTranNum(blockNum, 0)
   472  		require.EqualError(t, err, expectedErrStr)
   473  
   474  		_, err = bootstrappedBlockStore.RetrieveBlockByHash(blockHash)
   475  		require.EqualError(t, err, fmt.Sprintf("no such block hash [%x] in index", blockHash))
   476  	}
   477  
   478  	bootstrappingSnapshotHeight := uint64(len(blocksDetailsBeforeSnapshot))
   479  	for _, d := range blocksDetailsBeforeSnapshot {
   480  		for _, txID := range d.txIDs {
   481  			expectedErrorStr := fmt.Sprintf(
   482  				"details for the TXID [%s] not available. Ledger bootstrapped from a snapshot. First available block = [%d]",
   483  				txID, bootstrappingSnapshotHeight,
   484  			)
   485  			_, err := bootstrappedBlockStore.RetrieveBlockByTxID(txID)
   486  			require.EqualError(t, err, expectedErrorStr)
   487  
   488  			_, err = bootstrappedBlockStore.RetrieveTxByID(txID)
   489  			require.EqualError(t, err, expectedErrorStr)
   490  
   491  			_, _, err = bootstrappedBlockStore.RetrieveTxValidationCodeByTxID(txID)
   492  			require.EqualError(t, err, expectedErrorStr)
   493  
   494  			exists, err := bootstrappedBlockStore.TxIDExists(txID)
   495  			require.NoError(t, err)
   496  			require.True(t, exists)
   497  		}
   498  	}
   499  }
   500  
   501  func verifyQueriesOnBlocksAddedAfterBootstrapping(t *testing.T,
   502  	bootstrappedBlockStore *BlockStore,
   503  	expectedBCInfo *common.BlockchainInfo,
   504  	blocksDetailsAfterSnapshot []*testBlockDetails,
   505  	blocksAfterSnapshot []*common.Block,
   506  ) {
   507  	bci, err := bootstrappedBlockStore.GetBlockchainInfo()
   508  	require.NoError(t, err)
   509  	require.Equal(t, expectedBCInfo, bci)
   510  
   511  	for _, b := range blocksAfterSnapshot {
   512  		retrievedBlock, err := bootstrappedBlockStore.RetrieveBlockByNumber(b.Header.Number)
   513  		require.NoError(t, err)
   514  		require.Equal(t, b, retrievedBlock)
   515  
   516  		retrievedBlock, err = bootstrappedBlockStore.RetrieveBlockByHash(protoutil.BlockHeaderHash(b.Header))
   517  		require.NoError(t, err)
   518  		require.Equal(t, b, retrievedBlock)
   519  
   520  		itr, err := bootstrappedBlockStore.RetrieveBlocks(b.Header.Number)
   521  		require.NoError(t, err)
   522  		blk, err := itr.Next()
   523  		require.NoError(t, err)
   524  		require.Equal(t, b, blk)
   525  		itr.Close()
   526  
   527  		retrievedTxEnv, err := bootstrappedBlockStore.RetrieveTxByBlockNumTranNum(b.Header.Number, 0)
   528  		require.NoError(t, err)
   529  		expectedTxEnv, err := protoutil.GetEnvelopeFromBlock(b.Data.Data[0])
   530  		require.NoError(t, err)
   531  		require.Equal(t, expectedTxEnv, retrievedTxEnv)
   532  	}
   533  
   534  	for i, d := range blocksDetailsAfterSnapshot {
   535  		block := blocksAfterSnapshot[i]
   536  		for j, txID := range d.txIDs {
   537  			retrievedBlock, err := bootstrappedBlockStore.RetrieveBlockByTxID(txID)
   538  			require.NoError(t, err)
   539  			require.Equal(t, block, retrievedBlock)
   540  
   541  			retrievedTxEnv, err := bootstrappedBlockStore.RetrieveTxByID(txID)
   542  			require.NoError(t, err)
   543  			expectedTxEnv, err := protoutil.GetEnvelopeFromBlock(block.Data.Data[j])
   544  			require.NoError(t, err)
   545  			require.Equal(t, expectedTxEnv, retrievedTxEnv)
   546  
   547  			exists, err := bootstrappedBlockStore.TxIDExists(txID)
   548  			require.NoError(t, err)
   549  			require.True(t, exists)
   550  		}
   551  
   552  		for j, validationCode := range d.validationCodes {
   553  			retrievedValidationCode, blkNum, err := bootstrappedBlockStore.RetrieveTxValidationCodeByTxID(d.txIDs[j])
   554  			require.NoError(t, err)
   555  			require.Equal(t, validationCode, retrievedValidationCode)
   556  			require.Equal(t, block.Header.Number, blkNum)
   557  		}
   558  	}
   559  }