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 }