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 }