github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/common/ledger/blkstorage/reset_test.go (about)

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