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 }