github.com/kaituanwang/hyperledger@v2.0.1+incompatible/common/ledger/blkstorage/fsblkstorage/reset_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  SPDX-License-Identifier: Apache-2.0
     4  */
     5  
     6  package fsblkstorage
     7  
     8  import (
     9  	"io/ioutil"
    10  	"os"
    11  	"path"
    12  	"testing"
    13  
    14  	"github.com/davecgh/go-spew/spew"
    15  	"github.com/golang/protobuf/proto"
    16  	"github.com/hyperledger/fabric-protos-go/common"
    17  	"github.com/hyperledger/fabric/common/ledger/blkstorage"
    18  	"github.com/hyperledger/fabric/common/ledger/testutil"
    19  	"github.com/hyperledger/fabric/protoutil"
    20  	"github.com/stretchr/testify/require"
    21  )
    22  
    23  func TestResetToGenesisBlkSingleBlkFile(t *testing.T) {
    24  	blockStoreRootDir := "/tmp/testBlockStoreReset"
    25  	require.NoError(t, os.RemoveAll(blockStoreRootDir))
    26  	env := newTestEnv(t, NewConf(blockStoreRootDir, 0))
    27  	defer env.Cleanup()
    28  	provider := env.provider
    29  	store, err := provider.CreateBlockStore("ledger1")
    30  	require.NoError(t, err)
    31  
    32  	// Add 50 blocks and shutdown blockstore store
    33  	blocks := testutil.ConstructTestBlocks(t, 50)
    34  	for _, b := range blocks {
    35  		require.NoError(t, store.AddBlock(b))
    36  	}
    37  	store.Shutdown()
    38  	provider.Close()
    39  
    40  	ledgerDir := (&Conf{blockStorageDir: blockStoreRootDir}).getLedgerBlockDir("ledger1")
    41  
    42  	_, lastOffsetOriginal, numBlocksOriginal, err := scanForLastCompleteBlock(ledgerDir, 0, 0)
    43  	t.Logf("lastOffsetOriginal=%d", lastOffsetOriginal)
    44  	require.NoError(t, err)
    45  	require.Equal(t, 50, numBlocksOriginal)
    46  	fileInfo, err := os.Stat(deriveBlockfilePath(ledgerDir, 0))
    47  	require.NoError(t, err)
    48  	require.Equal(t, fileInfo.Size(), lastOffsetOriginal)
    49  
    50  	resetToGenesisBlk(ledgerDir)
    51  	assertBlocksDirOnlyFileWithGenesisBlock(t, ledgerDir, blocks[0])
    52  }
    53  
    54  func TestResetToGenesisBlkMultipleBlkFiles(t *testing.T) {
    55  	blockStoreRootDir := "/tmp/testBlockStoreReset"
    56  	require.NoError(t, os.RemoveAll(blockStoreRootDir))
    57  	blocks := testutil.ConstructTestBlocks(t, 20) // 20 blocks persisted in ~5 block files
    58  	blocksPerFile := 20 / 5
    59  	env := newTestEnv(t, NewConf(blockStoreRootDir, 0))
    60  	defer env.Cleanup()
    61  	provider := env.provider
    62  	store, err := provider.CreateBlockStore("ledger1")
    63  	require.NoError(t, err)
    64  	for i, b := range blocks {
    65  		require.NoError(t, store.AddBlock(b))
    66  		if i != 0 && i%blocksPerFile == 0 {
    67  			// block ranges in files [(0, 4):file0, (5,8):file1, (9,12):file2, (13, 16):file3, (17,19):file4]
    68  			store.(*fsBlockStore).fileMgr.moveToNextFile()
    69  		}
    70  	}
    71  	store.Shutdown()
    72  	provider.Close()
    73  
    74  	ledgerDir := (&Conf{blockStorageDir: blockStoreRootDir}).getLedgerBlockDir("ledger1")
    75  	files, err := ioutil.ReadDir(ledgerDir)
    76  	require.Len(t, files, 5)
    77  	resetToGenesisBlk(ledgerDir)
    78  	assertBlocksDirOnlyFileWithGenesisBlock(t, ledgerDir, blocks[0])
    79  }
    80  
    81  func TestResetBlockStore(t *testing.T) {
    82  	blockStoreRootDir := "/tmp/testBlockStoreReset"
    83  	os.RemoveAll(blockStoreRootDir)
    84  	blocks1 := testutil.ConstructTestBlocks(t, 20) // 20 blocks persisted in ~5 block files
    85  	blocks2 := testutil.ConstructTestBlocks(t, 40) // 40 blocks persisted in ~5 block files
    86  	maxFileSie := int(0.2 * float64(testutilEstimateTotalSizeOnDisk(t, blocks1)))
    87  
    88  	env := newTestEnv(t, NewConf(blockStoreRootDir, maxFileSie))
    89  	defer env.Cleanup()
    90  	provider := env.provider
    91  	store1, err := provider.OpenBlockStore("ledger1")
    92  	require.NoError(t, err)
    93  	store2, _ := provider.CreateBlockStore("ledger2")
    94  
    95  	for _, b := range blocks1 {
    96  		store1.AddBlock(b)
    97  	}
    98  
    99  	for _, b := range blocks2 {
   100  		store2.AddBlock(b)
   101  	}
   102  
   103  	store1.Shutdown()
   104  	store2.Shutdown()
   105  	provider.Close()
   106  
   107  	require.NoError(t, ResetBlockStore(blockStoreRootDir))
   108  	// test load and clear preResetHeight for ledger1 and ledger2
   109  	ledgerIDs := []string{"ledger1", "ledger2"}
   110  	h, err := LoadPreResetHeight(blockStoreRootDir, ledgerIDs)
   111  	require.Equal(t,
   112  		map[string]uint64{
   113  			"ledger1": 20,
   114  			"ledger2": 40,
   115  		},
   116  		h,
   117  	)
   118  
   119  	env = newTestEnv(t, NewConf(blockStoreRootDir, maxFileSie))
   120  	provider = env.provider
   121  	store1, _ = provider.OpenBlockStore("ledger1")
   122  	store2, _ = provider.CreateBlockStore("ledger2")
   123  	assertBlockStorePostReset(t, store1, blocks1)
   124  	assertBlockStorePostReset(t, store2, blocks2)
   125  
   126  	require.NoError(t, ClearPreResetHeight(blockStoreRootDir, ledgerIDs))
   127  	h, err = LoadPreResetHeight(blockStoreRootDir, ledgerIDs)
   128  	require.Equal(t,
   129  		map[string]uint64{},
   130  		h,
   131  	)
   132  
   133  	// reset again to test load and clear preResetHeight for ledger2
   134  	require.NoError(t, ResetBlockStore(blockStoreRootDir))
   135  	ledgerIDs = []string{"ledger2"}
   136  	h, err = LoadPreResetHeight(blockStoreRootDir, ledgerIDs)
   137  	require.Equal(t,
   138  		map[string]uint64{
   139  			"ledger2": 40,
   140  		},
   141  		h,
   142  	)
   143  	require.NoError(t, ClearPreResetHeight(blockStoreRootDir, ledgerIDs))
   144  	// verify that ledger1 has preResetHeight file is not deleted
   145  	h, err = LoadPreResetHeight(blockStoreRootDir, []string{"ledger1", "ledger2"})
   146  	require.Equal(t,
   147  		map[string]uint64{
   148  			"ledger1": 20,
   149  		},
   150  		h,
   151  	)
   152  }
   153  
   154  func TestRecordHeight(t *testing.T) {
   155  	blockStoreRootDir := "/tmp/testBlockStoreReset"
   156  	require.NoError(t, os.RemoveAll(blockStoreRootDir))
   157  	env := newTestEnv(t, NewConf(blockStoreRootDir, 0))
   158  	defer env.Cleanup()
   159  	provider := env.provider
   160  	store, err := provider.CreateBlockStore("ledger1")
   161  	require.NoError(t, err)
   162  
   163  	blocks := testutil.ConstructTestBlocks(t, 60)
   164  
   165  	// Add 50 blocks, record, and require the recording of the current height
   166  	for _, b := range blocks[:50] {
   167  		require.NoError(t, store.AddBlock(b))
   168  	}
   169  	ledgerDir := (&Conf{blockStorageDir: blockStoreRootDir}).getLedgerBlockDir("ledger1")
   170  	require.NoError(t, recordHeightIfGreaterThanPreviousRecording(ledgerDir))
   171  	assertRecordedHeight(t, ledgerDir, "50")
   172  
   173  	// Add 10 more blocks, record again and require that the previous recorded info is overwritten with new current height
   174  	for _, b := range blocks[50:] {
   175  		require.NoError(t, store.AddBlock(b))
   176  	}
   177  	require.NoError(t, recordHeightIfGreaterThanPreviousRecording(ledgerDir))
   178  	assertRecordedHeight(t, ledgerDir, "60")
   179  
   180  	// truncate the most recent block file to half
   181  	// record again and require that the previous recorded info is NOT overwritten with new current height
   182  	// because the current height is less than the previously recorded height
   183  	lastFileNum, err := retrieveLastFileSuffix(ledgerDir)
   184  	require.NoError(t, err)
   185  	lastFile := deriveBlockfilePath(ledgerDir, lastFileNum)
   186  	fileInfo, err := os.Stat(lastFile)
   187  	require.NoError(t, err)
   188  	require.NoError(t, os.Truncate(lastFile, fileInfo.Size()/2))
   189  	checkpointInfo, err := constructCheckpointInfoFromBlockFiles(ledgerDir)
   190  	require.NoError(t, err)
   191  	require.True(t, checkpointInfo.lastBlockNumber < 59)
   192  	require.NoError(t, recordHeightIfGreaterThanPreviousRecording(ledgerDir))
   193  	assertRecordedHeight(t, ledgerDir, "60")
   194  }
   195  
   196  func assertBlocksDirOnlyFileWithGenesisBlock(t *testing.T, ledgerDir string, genesisBlock *common.Block) {
   197  	files, err := ioutil.ReadDir(ledgerDir)
   198  	require.Len(t, files, 2)
   199  	require.Equal(t, "__backupGenesisBlockBytes", files[0].Name())
   200  	require.Equal(t, "blockfile_000000", files[1].Name())
   201  	blockBytes, lastOffset, numBlocks, err := scanForLastCompleteBlock(ledgerDir, 0, 0)
   202  	require.NoError(t, err)
   203  	t.Logf("lastOffset=%d", lastOffset)
   204  	require.Equal(t, 1, numBlocks)
   205  	block0, err := deserializeBlock(blockBytes)
   206  	require.NoError(t, err)
   207  	require.Equal(t, genesisBlock, block0)
   208  	fileInfo, err := os.Stat(deriveBlockfilePath(ledgerDir, 0))
   209  	require.NoError(t, err)
   210  	require.Equal(t, fileInfo.Size(), lastOffset)
   211  }
   212  
   213  func assertBlockStorePostReset(t *testing.T, store blkstorage.BlockStore, originallyCommittedBlocks []*common.Block) {
   214  	bcInfo, _ := store.GetBlockchainInfo()
   215  	t.Logf("bcInfo = %s", spew.Sdump(bcInfo))
   216  	require.Equal(t,
   217  		&common.BlockchainInfo{
   218  			Height:            1,
   219  			CurrentBlockHash:  protoutil.BlockHeaderHash(originallyCommittedBlocks[0].Header),
   220  			PreviousBlockHash: nil,
   221  		},
   222  		bcInfo)
   223  
   224  	blk, err := store.RetrieveBlockByNumber(0)
   225  	require.NoError(t, err)
   226  	require.Equal(t, originallyCommittedBlocks[0], blk)
   227  
   228  	blk, err = store.RetrieveBlockByNumber(1)
   229  	require.Error(t, err)
   230  	require.Equal(t, err, blkstorage.ErrNotFoundInIndex)
   231  
   232  	err = store.AddBlock(originallyCommittedBlocks[0])
   233  	require.EqualError(t, err, "block number should have been 1 but was 0")
   234  
   235  	for _, b := range originallyCommittedBlocks[1:] {
   236  		require.NoError(t, store.AddBlock(b))
   237  	}
   238  
   239  	for i := 0; i < len(originallyCommittedBlocks); i++ {
   240  		blk, err := store.RetrieveBlockByNumber(uint64(i))
   241  		require.NoError(t, err)
   242  		require.Equal(t, originallyCommittedBlocks[i], blk)
   243  	}
   244  }
   245  
   246  func assertRecordedHeight(t *testing.T, ledgerDir, expectedRecordedHt string) {
   247  	bytes, err := ioutil.ReadFile(path.Join(ledgerDir, fileNamePreRestHt))
   248  	require.NoError(t, err)
   249  	require.Equal(t, expectedRecordedHt, string(bytes))
   250  }
   251  
   252  func testutilEstimateTotalSizeOnDisk(t *testing.T, blocks []*common.Block) int {
   253  	size := 0
   254  	for _, block := range blocks {
   255  		by, _, err := serializeBlock(block)
   256  		require.NoError(t, err)
   257  		blockBytesSize := len(by)
   258  		encodedLen := proto.EncodeVarint(uint64(blockBytesSize))
   259  		size += blockBytesSize + len(encodedLen)
   260  	}
   261  	return size
   262  }