github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/common/ledger/blkstorage/rollback_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  	"os"
    11  	"testing"
    12  
    13  	"github.com/hyperledger/fabric-protos-go/common"
    14  	"github.com/hyperledger/fabric-protos-go/peer"
    15  	"github.com/osdi23p228/fabric/common/ledger/testutil"
    16  	"github.com/osdi23p228/fabric/common/metrics/disabled"
    17  	"github.com/osdi23p228/fabric/protoutil"
    18  	"github.com/pkg/errors"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  func TestRollback(t *testing.T) {
    23  	path := testPath()
    24  	blocks := testutil.ConstructTestBlocks(t, 50) // 50 blocks persisted in ~5 block files
    25  	blocksPerFile := 50 / 5
    26  	env := newTestEnv(t, NewConf(path, 0))
    27  	defer env.Cleanup()
    28  	blkfileMgrWrapper := newTestBlockfileWrapper(env, "testLedger")
    29  	blkfileMgr := blkfileMgrWrapper.blockfileMgr
    30  	// 1. Store blocks
    31  	for i, b := range blocks {
    32  		require.NoError(t, blkfileMgr.addBlock(b))
    33  		if i != 0 && i%blocksPerFile == 0 {
    34  			// block ranges in files [(0, 10):file0, (11,20):file1, (21,30):file2, (31, 40):file3, (41,49):file4]
    35  			blkfileMgr.moveToNextFile()
    36  		}
    37  	}
    38  
    39  	// 2. Check the BlockchainInfo
    40  	expectedBlockchainInfo := &common.BlockchainInfo{
    41  		Height:            50,
    42  		CurrentBlockHash:  protoutil.BlockHeaderHash(blocks[49].Header),
    43  		PreviousBlockHash: protoutil.BlockHeaderHash(blocks[48].Header),
    44  	}
    45  	actualBlockchainInfo := blkfileMgrWrapper.blockfileMgr.getBlockchainInfo()
    46  	require.Equal(t, expectedBlockchainInfo, actualBlockchainInfo)
    47  
    48  	// 3. Check the BlockfileInfo
    49  	expectedblkfilesInfoLastBlockNumber := uint64(49)
    50  	expectedBlkfilesInfoIsNoFiles := false
    51  	actualBlkfilesInfo, err := blkfileMgrWrapper.blockfileMgr.loadBlkfilesInfo()
    52  	require.NoError(t, err)
    53  	require.Equal(t, expectedblkfilesInfoLastBlockNumber, actualBlkfilesInfo.lastPersistedBlock)
    54  	require.Equal(t, expectedBlkfilesInfoIsNoFiles, actualBlkfilesInfo.noBlockFiles)
    55  	require.Equal(t, actualBlkfilesInfo.latestFileNumber, 4)
    56  
    57  	// 4. Check whether all blocks are stored correctly
    58  	blkfileMgrWrapper.testGetBlockByNumber(blocks, 0, nil)
    59  	blkfileMgrWrapper.testGetBlockByHash(blocks, nil)
    60  	blkfileMgrWrapper.testGetBlockByTxID(blocks, nil)
    61  
    62  	// 5. Close the blkfileMgrWrapper
    63  	env.provider.Close()
    64  	blkfileMgrWrapper.close()
    65  	lastBlockNumberInLastFile := uint64(49)
    66  	middleBlockNumberInLastFile := uint64(45)
    67  	firstBlockNumberInLastFile := uint64(41)
    68  
    69  	// 7. Rollback to one before the lastBlockNumberInLastFile
    70  	indexConfig := &IndexConfig{AttrsToIndex: attrsToIndex}
    71  	err = Rollback(path, "testLedger", lastBlockNumberInLastFile-uint64(1), indexConfig)
    72  	require.NoError(t, err)
    73  	assertBlockStoreRollback(t, path, "testLedger", blocks, lastBlockNumberInLastFile-uint64(1), 4, indexConfig)
    74  
    75  	// 8. Rollback to middleBlockNumberInLastFile
    76  	err = Rollback(path, "testLedger", middleBlockNumberInLastFile, indexConfig)
    77  	require.NoError(t, err)
    78  	assertBlockStoreRollback(t, path, "testLedger", blocks, middleBlockNumberInLastFile, 4, indexConfig)
    79  
    80  	// 9. Rollback to firstBlockNumberInLastFile
    81  	err = Rollback(path, "testLedger", firstBlockNumberInLastFile, indexConfig)
    82  	require.NoError(t, err)
    83  	assertBlockStoreRollback(t, path, "testLedger", blocks, firstBlockNumberInLastFile, 4, indexConfig)
    84  
    85  	// 10. Rollback to one before the firstBlockNumberInLastFile
    86  	err = Rollback(path, "testLedger", firstBlockNumberInLastFile-1, indexConfig)
    87  	require.NoError(t, err)
    88  	assertBlockStoreRollback(t, path, "testLedger", blocks, firstBlockNumberInLastFile-1, 3, indexConfig)
    89  
    90  	// 11. In the middle block file (among a range of block files), find the middle block number
    91  	middleBlockNumberInMiddleFile := uint64(25)
    92  
    93  	// 12. Rollback to middleBlockNumberInMiddleFile
    94  	err = Rollback(path, "testLedger", middleBlockNumberInMiddleFile, indexConfig)
    95  	require.NoError(t, err)
    96  	assertBlockStoreRollback(t, path, "testLedger", blocks, middleBlockNumberInMiddleFile, 2, indexConfig)
    97  
    98  	// 13. Rollback to block 5
    99  	err = Rollback(path, "testLedger", 5, indexConfig)
   100  	require.NoError(t, err)
   101  	assertBlockStoreRollback(t, path, "testLedger", blocks, 5, 0, indexConfig)
   102  
   103  	// 14. Rollback to block 1
   104  	err = Rollback(path, "testLedger", 1, indexConfig)
   105  	require.NoError(t, err)
   106  	assertBlockStoreRollback(t, path, "testLedger", blocks, 1, 0, indexConfig)
   107  }
   108  
   109  // TestRollbackWithOnlyBlockIndexAttributes mimics the scenario when ledger is used for orderer
   110  // i.e., only block is index and transancations are not indexed
   111  func TestRollbackWithOnlyBlockIndexAttributes(t *testing.T) {
   112  	path := testPath()
   113  	blocks := testutil.ConstructTestBlocks(t, 50) // 50 blocks persisted in ~5 block files
   114  	blocksPerFile := 50 / 5
   115  	onlyBlockNumIndex := []IndexableAttr{
   116  		IndexableAttrBlockNum,
   117  	}
   118  	env := newTestEnvSelectiveIndexing(t, NewConf(path, 0), onlyBlockNumIndex, &disabled.Provider{})
   119  	defer env.Cleanup()
   120  	blkfileMgrWrapper := newTestBlockfileWrapper(env, "testLedger")
   121  	blkfileMgr := blkfileMgrWrapper.blockfileMgr
   122  
   123  	// 1. Store blocks
   124  	for i, b := range blocks {
   125  		require.NoError(t, blkfileMgr.addBlock(b))
   126  		if i != 0 && i%blocksPerFile == 0 {
   127  			// block ranges in files [(0, 10):file0, (11,20):file1, (21,30):file2, (31, 40):file3, (41,49):file4]
   128  			blkfileMgr.moveToNextFile()
   129  		}
   130  	}
   131  
   132  	// 2. Check the BlockchainInfo
   133  	expectedBlockchainInfo := &common.BlockchainInfo{
   134  		Height:            50,
   135  		CurrentBlockHash:  protoutil.BlockHeaderHash(blocks[49].Header),
   136  		PreviousBlockHash: protoutil.BlockHeaderHash(blocks[48].Header),
   137  	}
   138  	actualBlockchainInfo := blkfileMgrWrapper.blockfileMgr.getBlockchainInfo()
   139  	require.Equal(t, expectedBlockchainInfo, actualBlockchainInfo)
   140  
   141  	// 3. Check the BlockfilesInfo
   142  	expectedBlkfilesInfoLastBlockNumber := uint64(49)
   143  	expectedBlkfilesInfoIsNoBlkFiles := false
   144  	actualBlkfilesInfo, err := blkfileMgrWrapper.blockfileMgr.loadBlkfilesInfo()
   145  	require.NoError(t, err)
   146  	require.Equal(t, expectedBlkfilesInfoLastBlockNumber, actualBlkfilesInfo.lastPersistedBlock)
   147  	require.Equal(t, expectedBlkfilesInfoIsNoBlkFiles, actualBlkfilesInfo.noBlockFiles)
   148  	require.Equal(t, actualBlkfilesInfo.latestFileNumber, 4)
   149  
   150  	// 4. Close the blkfileMgrWrapper
   151  	env.provider.Close()
   152  	blkfileMgrWrapper.close()
   153  
   154  	// 5. Rollback to block 2
   155  	onlyBlockNumIndexCfg := &IndexConfig{
   156  		AttrsToIndex: onlyBlockNumIndex,
   157  	}
   158  	err = Rollback(path, "testLedger", 2, onlyBlockNumIndexCfg)
   159  	require.NoError(t, err)
   160  	assertBlockStoreRollback(t, path, "testLedger", blocks, 2, 0, onlyBlockNumIndexCfg)
   161  }
   162  
   163  func TestRollbackWithNoIndexDir(t *testing.T) {
   164  	path := testPath()
   165  	blocks := testutil.ConstructTestBlocks(t, 50)
   166  	blocksPerFile := 50 / 5
   167  	conf := NewConf(path, 0)
   168  	env := newTestEnv(t, conf)
   169  	defer env.Cleanup()
   170  	blkfileMgrWrapper := newTestBlockfileWrapper(env, "testLedger")
   171  	blkfileMgr := blkfileMgrWrapper.blockfileMgr
   172  
   173  	// 1. Store blocks
   174  	for i, b := range blocks {
   175  		require.NoError(t, blkfileMgr.addBlock(b))
   176  		if i != 0 && i%blocksPerFile == 0 {
   177  			// block ranges in files [(0, 10):file0, (11,20):file1, (21,30):file2, (31, 40):file3, (41,49):file4]
   178  			blkfileMgr.moveToNextFile()
   179  		}
   180  	}
   181  
   182  	// 2. Check the BlockchainInfo
   183  	expectedBlockchainInfo := &common.BlockchainInfo{
   184  		Height:            50,
   185  		CurrentBlockHash:  protoutil.BlockHeaderHash(blocks[49].Header),
   186  		PreviousBlockHash: protoutil.BlockHeaderHash(blocks[48].Header),
   187  	}
   188  	actualBlockchainInfo := blkfileMgrWrapper.blockfileMgr.getBlockchainInfo()
   189  	require.Equal(t, expectedBlockchainInfo, actualBlockchainInfo)
   190  
   191  	// 3. Check the BlockfilesInfo
   192  	expectedBlkfilesInfoLastBlockNumber := uint64(49)
   193  	expectedBlkfilesInfoIsChainEmpty := false
   194  	actualBlkfilesInfo, err := blkfileMgrWrapper.blockfileMgr.loadBlkfilesInfo()
   195  	require.NoError(t, err)
   196  	require.Equal(t, expectedBlkfilesInfoLastBlockNumber, actualBlkfilesInfo.lastPersistedBlock)
   197  	require.Equal(t, expectedBlkfilesInfoIsChainEmpty, actualBlkfilesInfo.noBlockFiles)
   198  	require.Equal(t, actualBlkfilesInfo.latestFileNumber, 4)
   199  
   200  	// 4. Close the blkfileMgrWrapper
   201  	env.provider.Close()
   202  	blkfileMgrWrapper.close()
   203  
   204  	// 5. Remove the index directory
   205  	indexDir := conf.getIndexDir()
   206  	err = os.RemoveAll(indexDir)
   207  	require.NoError(t, err)
   208  
   209  	// 6. Rollback to block 2
   210  	indexConfig := &IndexConfig{AttrsToIndex: attrsToIndex}
   211  	err = Rollback(path, "testLedger", 2, indexConfig)
   212  	require.NoError(t, err)
   213  	assertBlockStoreRollback(t, path, "testLedger", blocks, 2, 0, indexConfig)
   214  }
   215  
   216  func TestValidateRollbackParams(t *testing.T) {
   217  	path := testPath()
   218  	env := newTestEnv(t, NewConf(path, 1024*24))
   219  	defer env.Cleanup()
   220  
   221  	blkfileMgrWrapper := newTestBlockfileWrapper(env, "testLedger")
   222  
   223  	// 1. Create 10 blocks
   224  	blocks := testutil.ConstructTestBlocks(t, 10)
   225  	blkfileMgrWrapper.addBlocks(blocks)
   226  
   227  	// 2. Valid inputs
   228  	err := ValidateRollbackParams(path, "testLedger", 5)
   229  	require.NoError(t, err)
   230  
   231  	// 3. ledgerID does not exist
   232  	err = ValidateRollbackParams(path, "noLedger", 5)
   233  	require.Equal(t, "ledgerID [noLedger] does not exist", err.Error())
   234  
   235  	err = ValidateRollbackParams(path, "testLedger", 15)
   236  	require.Equal(t, "target block number [15] should be less than the biggest block number [9]", err.Error())
   237  }
   238  
   239  func TestDuplicateTxIDDuringRollback(t *testing.T) {
   240  	path := testPath()
   241  	blocks := testutil.ConstructTestBlocks(t, 4)
   242  	maxFileSize := 1024 * 1024 * 4
   243  	env := newTestEnv(t, NewConf(path, maxFileSize))
   244  	defer env.Cleanup()
   245  	blkfileMgrWrapper := newTestBlockfileWrapper(env, "testLedger")
   246  	blocks[3].Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER][0] = byte(peer.TxValidationCode_DUPLICATE_TXID)
   247  	testutil.SetTxID(t, blocks[3], 0, "tx0")
   248  	testutil.SetTxID(t, blocks[2], 0, "tx0")
   249  
   250  	// 1. Store blocks
   251  	blkfileMgrWrapper.addBlocks(blocks)
   252  
   253  	// 2. Check the BlockchainInfo
   254  	expectedBlockchainInfo := &common.BlockchainInfo{
   255  		Height:            4,
   256  		CurrentBlockHash:  protoutil.BlockHeaderHash(blocks[3].Header),
   257  		PreviousBlockHash: protoutil.BlockHeaderHash(blocks[2].Header),
   258  	}
   259  	actualBlockchainInfo := blkfileMgrWrapper.blockfileMgr.getBlockchainInfo()
   260  	require.Equal(t, expectedBlockchainInfo, actualBlockchainInfo)
   261  
   262  	// 3. Retrieve tx
   263  	blkfileMgrWrapper.testGetTransactionByTxID("tx0", blocks[2].Data.Data[0], nil)
   264  
   265  	// 4. Close the blkfileMgrWrapper
   266  	env.provider.Close()
   267  	blkfileMgrWrapper.close()
   268  
   269  	// 5. Rollback to block 2
   270  	indexConfig := &IndexConfig{AttrsToIndex: attrsToIndex}
   271  	err := Rollback(path, "testLedger", 2, indexConfig)
   272  	require.NoError(t, err)
   273  
   274  	env = newTestEnv(t, NewConf(path, maxFileSize))
   275  	blkfileMgrWrapper = newTestBlockfileWrapper(env, "testLedger")
   276  
   277  	// 6. Check the BlockchainInfo
   278  	expectedBlockchainInfo = &common.BlockchainInfo{
   279  		Height:            3,
   280  		CurrentBlockHash:  protoutil.BlockHeaderHash(blocks[2].Header),
   281  		PreviousBlockHash: protoutil.BlockHeaderHash(blocks[1].Header),
   282  	}
   283  	actualBlockchainInfo = blkfileMgrWrapper.blockfileMgr.getBlockchainInfo()
   284  	require.Equal(t, expectedBlockchainInfo, actualBlockchainInfo)
   285  
   286  	// 8. Retrieve tx (should not have been deleted)
   287  	blkfileMgrWrapper.testGetTransactionByTxID("tx0", blocks[2].Data.Data[0], nil)
   288  }
   289  
   290  func assertBlockStoreRollback(t *testing.T, path, ledgerID string, blocks []*common.Block,
   291  	rollbackedToBlkNum uint64, lastFileSuffixNum int, indexConfig *IndexConfig) {
   292  
   293  	env := newTestEnvSelectiveIndexing(t, NewConf(path, 0), indexConfig.AttrsToIndex, &disabled.Provider{})
   294  	blkfileMgrWrapper := newTestBlockfileWrapper(env, ledgerID)
   295  
   296  	// 1. Check the BlockchainInfo after the rollback
   297  	expectedBlockchainInfo := &common.BlockchainInfo{
   298  		Height:            rollbackedToBlkNum + 1,
   299  		CurrentBlockHash:  protoutil.BlockHeaderHash(blocks[rollbackedToBlkNum].Header),
   300  		PreviousBlockHash: protoutil.BlockHeaderHash(blocks[rollbackedToBlkNum-1].Header),
   301  	}
   302  	actualBlockchainInfo := blkfileMgrWrapper.blockfileMgr.getBlockchainInfo()
   303  	require.Equal(t, expectedBlockchainInfo, actualBlockchainInfo)
   304  
   305  	// 2. Check the BlockfilesInfo after the rollback
   306  	expectedBlkfilesInfoLastBlockNumber := rollbackedToBlkNum
   307  	expectedBlkfilesInfoIsNoBlkfiles := false
   308  	expectedBlockchainInfoLastFileSuffixNum := lastFileSuffixNum
   309  	actualBlkfilesInfo, err := blkfileMgrWrapper.blockfileMgr.loadBlkfilesInfo()
   310  	require.NoError(t, err)
   311  	require.Equal(t, expectedBlkfilesInfoLastBlockNumber, actualBlkfilesInfo.lastPersistedBlock)
   312  	require.Equal(t, expectedBlkfilesInfoIsNoBlkfiles, actualBlkfilesInfo.noBlockFiles)
   313  	require.Equal(t, expectedBlockchainInfoLastFileSuffixNum, actualBlkfilesInfo.latestFileNumber)
   314  
   315  	// 3. Check whether all blocks till the target block number are stored correctly
   316  	if blkfileMgrWrapper.blockfileMgr.index.isAttributeIndexed(IndexableAttrBlockNum) {
   317  		blkfileMgrWrapper.testGetBlockByNumber(blocks[:rollbackedToBlkNum+1], 0, nil)
   318  	}
   319  	if blkfileMgrWrapper.blockfileMgr.index.isAttributeIndexed(IndexableAttrBlockHash) {
   320  		blkfileMgrWrapper.testGetBlockByHash(blocks[:rollbackedToBlkNum+1], nil)
   321  	}
   322  	if blkfileMgrWrapper.blockfileMgr.index.isAttributeIndexed(IndexableAttrTxID) {
   323  		blkfileMgrWrapper.testGetBlockByTxID(blocks[:rollbackedToBlkNum+1], nil)
   324  	}
   325  
   326  	// 4. Check whether all blocks with number greater than target block number
   327  	// are removed including index entries
   328  	expectedErr := errors.New("Entry not found in index")
   329  	if blkfileMgrWrapper.blockfileMgr.index.isAttributeIndexed(IndexableAttrBlockHash) {
   330  		blkfileMgrWrapper.testGetBlockByHash(blocks[rollbackedToBlkNum+1:], expectedErr)
   331  	}
   332  	if blkfileMgrWrapper.blockfileMgr.index.isAttributeIndexed(IndexableAttrTxID) {
   333  		blkfileMgrWrapper.testGetBlockByTxID(blocks[rollbackedToBlkNum+1:], expectedErr)
   334  	}
   335  
   336  	// 5. Close the blkfileMgrWrapper
   337  	env.provider.Close()
   338  	blkfileMgrWrapper.close()
   339  }