github.com/kaituanwang/hyperledger@v2.0.1+incompatible/common/ledger/blkstorage/fsblkstorage/reset_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 SPDX-License-Identifier: Apache-2.0 4 */ 5 6 package fsblkstorage 7 8 import ( 9 "io/ioutil" 10 "os" 11 "path" 12 "testing" 13 14 "github.com/davecgh/go-spew/spew" 15 "github.com/golang/protobuf/proto" 16 "github.com/hyperledger/fabric-protos-go/common" 17 "github.com/hyperledger/fabric/common/ledger/blkstorage" 18 "github.com/hyperledger/fabric/common/ledger/testutil" 19 "github.com/hyperledger/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.CreateBlockStore("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.CreateBlockStore("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.(*fsBlockStore).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.Len(t, files, 5) 77 resetToGenesisBlk(ledgerDir) 78 assertBlocksDirOnlyFileWithGenesisBlock(t, ledgerDir, blocks[0]) 79 } 80 81 func TestResetBlockStore(t *testing.T) { 82 blockStoreRootDir := "/tmp/testBlockStoreReset" 83 os.RemoveAll(blockStoreRootDir) 84 blocks1 := testutil.ConstructTestBlocks(t, 20) // 20 blocks persisted in ~5 block files 85 blocks2 := testutil.ConstructTestBlocks(t, 40) // 40 blocks persisted in ~5 block files 86 maxFileSie := int(0.2 * float64(testutilEstimateTotalSizeOnDisk(t, blocks1))) 87 88 env := newTestEnv(t, NewConf(blockStoreRootDir, maxFileSie)) 89 defer env.Cleanup() 90 provider := env.provider 91 store1, err := provider.OpenBlockStore("ledger1") 92 require.NoError(t, err) 93 store2, _ := provider.CreateBlockStore("ledger2") 94 95 for _, b := range blocks1 { 96 store1.AddBlock(b) 97 } 98 99 for _, b := range blocks2 { 100 store2.AddBlock(b) 101 } 102 103 store1.Shutdown() 104 store2.Shutdown() 105 provider.Close() 106 107 require.NoError(t, ResetBlockStore(blockStoreRootDir)) 108 // test load and clear preResetHeight for ledger1 and ledger2 109 ledgerIDs := []string{"ledger1", "ledger2"} 110 h, err := LoadPreResetHeight(blockStoreRootDir, ledgerIDs) 111 require.Equal(t, 112 map[string]uint64{ 113 "ledger1": 20, 114 "ledger2": 40, 115 }, 116 h, 117 ) 118 119 env = newTestEnv(t, NewConf(blockStoreRootDir, maxFileSie)) 120 provider = env.provider 121 store1, _ = provider.OpenBlockStore("ledger1") 122 store2, _ = provider.CreateBlockStore("ledger2") 123 assertBlockStorePostReset(t, store1, blocks1) 124 assertBlockStorePostReset(t, store2, blocks2) 125 126 require.NoError(t, ClearPreResetHeight(blockStoreRootDir, ledgerIDs)) 127 h, err = LoadPreResetHeight(blockStoreRootDir, ledgerIDs) 128 require.Equal(t, 129 map[string]uint64{}, 130 h, 131 ) 132 133 // reset again to test load and clear preResetHeight for ledger2 134 require.NoError(t, ResetBlockStore(blockStoreRootDir)) 135 ledgerIDs = []string{"ledger2"} 136 h, err = LoadPreResetHeight(blockStoreRootDir, ledgerIDs) 137 require.Equal(t, 138 map[string]uint64{ 139 "ledger2": 40, 140 }, 141 h, 142 ) 143 require.NoError(t, ClearPreResetHeight(blockStoreRootDir, ledgerIDs)) 144 // verify that ledger1 has preResetHeight file is not deleted 145 h, err = LoadPreResetHeight(blockStoreRootDir, []string{"ledger1", "ledger2"}) 146 require.Equal(t, 147 map[string]uint64{ 148 "ledger1": 20, 149 }, 150 h, 151 ) 152 } 153 154 func TestRecordHeight(t *testing.T) { 155 blockStoreRootDir := "/tmp/testBlockStoreReset" 156 require.NoError(t, os.RemoveAll(blockStoreRootDir)) 157 env := newTestEnv(t, NewConf(blockStoreRootDir, 0)) 158 defer env.Cleanup() 159 provider := env.provider 160 store, err := provider.CreateBlockStore("ledger1") 161 require.NoError(t, err) 162 163 blocks := testutil.ConstructTestBlocks(t, 60) 164 165 // Add 50 blocks, record, and require the recording of the current height 166 for _, b := range blocks[:50] { 167 require.NoError(t, store.AddBlock(b)) 168 } 169 ledgerDir := (&Conf{blockStorageDir: blockStoreRootDir}).getLedgerBlockDir("ledger1") 170 require.NoError(t, recordHeightIfGreaterThanPreviousRecording(ledgerDir)) 171 assertRecordedHeight(t, ledgerDir, "50") 172 173 // Add 10 more blocks, record again and require that the previous recorded info is overwritten with new current height 174 for _, b := range blocks[50:] { 175 require.NoError(t, store.AddBlock(b)) 176 } 177 require.NoError(t, recordHeightIfGreaterThanPreviousRecording(ledgerDir)) 178 assertRecordedHeight(t, ledgerDir, "60") 179 180 // truncate the most recent block file to half 181 // record again and require that the previous recorded info is NOT overwritten with new current height 182 // because the current height is less than the previously recorded height 183 lastFileNum, err := retrieveLastFileSuffix(ledgerDir) 184 require.NoError(t, err) 185 lastFile := deriveBlockfilePath(ledgerDir, lastFileNum) 186 fileInfo, err := os.Stat(lastFile) 187 require.NoError(t, err) 188 require.NoError(t, os.Truncate(lastFile, fileInfo.Size()/2)) 189 checkpointInfo, err := constructCheckpointInfoFromBlockFiles(ledgerDir) 190 require.NoError(t, err) 191 require.True(t, checkpointInfo.lastBlockNumber < 59) 192 require.NoError(t, recordHeightIfGreaterThanPreviousRecording(ledgerDir)) 193 assertRecordedHeight(t, ledgerDir, "60") 194 } 195 196 func assertBlocksDirOnlyFileWithGenesisBlock(t *testing.T, ledgerDir string, genesisBlock *common.Block) { 197 files, err := ioutil.ReadDir(ledgerDir) 198 require.Len(t, files, 2) 199 require.Equal(t, "__backupGenesisBlockBytes", files[0].Name()) 200 require.Equal(t, "blockfile_000000", files[1].Name()) 201 blockBytes, lastOffset, numBlocks, err := scanForLastCompleteBlock(ledgerDir, 0, 0) 202 require.NoError(t, err) 203 t.Logf("lastOffset=%d", lastOffset) 204 require.Equal(t, 1, numBlocks) 205 block0, err := deserializeBlock(blockBytes) 206 require.NoError(t, err) 207 require.Equal(t, genesisBlock, block0) 208 fileInfo, err := os.Stat(deriveBlockfilePath(ledgerDir, 0)) 209 require.NoError(t, err) 210 require.Equal(t, fileInfo.Size(), lastOffset) 211 } 212 213 func assertBlockStorePostReset(t *testing.T, store blkstorage.BlockStore, originallyCommittedBlocks []*common.Block) { 214 bcInfo, _ := store.GetBlockchainInfo() 215 t.Logf("bcInfo = %s", spew.Sdump(bcInfo)) 216 require.Equal(t, 217 &common.BlockchainInfo{ 218 Height: 1, 219 CurrentBlockHash: protoutil.BlockHeaderHash(originallyCommittedBlocks[0].Header), 220 PreviousBlockHash: nil, 221 }, 222 bcInfo) 223 224 blk, err := store.RetrieveBlockByNumber(0) 225 require.NoError(t, err) 226 require.Equal(t, originallyCommittedBlocks[0], blk) 227 228 blk, err = store.RetrieveBlockByNumber(1) 229 require.Error(t, err) 230 require.Equal(t, err, blkstorage.ErrNotFoundInIndex) 231 232 err = store.AddBlock(originallyCommittedBlocks[0]) 233 require.EqualError(t, err, "block number should have been 1 but was 0") 234 235 for _, b := range originallyCommittedBlocks[1:] { 236 require.NoError(t, store.AddBlock(b)) 237 } 238 239 for i := 0; i < len(originallyCommittedBlocks); i++ { 240 blk, err := store.RetrieveBlockByNumber(uint64(i)) 241 require.NoError(t, err) 242 require.Equal(t, originallyCommittedBlocks[i], blk) 243 } 244 } 245 246 func assertRecordedHeight(t *testing.T, ledgerDir, expectedRecordedHt string) { 247 bytes, err := ioutil.ReadFile(path.Join(ledgerDir, fileNamePreRestHt)) 248 require.NoError(t, err) 249 require.Equal(t, expectedRecordedHt, string(bytes)) 250 } 251 252 func testutilEstimateTotalSizeOnDisk(t *testing.T, blocks []*common.Block) int { 253 size := 0 254 for _, block := range blocks { 255 by, _, err := serializeBlock(block) 256 require.NoError(t, err) 257 blockBytesSize := len(by) 258 encodedLen := proto.EncodeVarint(uint64(blockBytesSize)) 259 size += blockBytesSize + len(encodedLen) 260 } 261 return size 262 }