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