github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/core/ledger/kvledger/snapshot_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package kvledger 8 9 import ( 10 "encoding/hex" 11 "encoding/json" 12 "fmt" 13 "io/ioutil" 14 "os" 15 "path/filepath" 16 "testing" 17 18 "github.com/hyperledger/fabric-protos-go/peer" 19 "github.com/osdi23p228/fabric/bccsp/sw" 20 "github.com/osdi23p228/fabric/common/ledger/testutil" 21 "github.com/osdi23p228/fabric/common/metrics/disabled" 22 "github.com/osdi23p228/fabric/common/util" 23 "github.com/osdi23p228/fabric/core/ledger" 24 lgr "github.com/osdi23p228/fabric/core/ledger" 25 "github.com/osdi23p228/fabric/core/ledger/mock" 26 "github.com/osdi23p228/fabric/protoutil" 27 "github.com/stretchr/testify/require" 28 ) 29 30 func TestGenerateSnapshot(t *testing.T) { 31 conf, cleanup := testConfig(t) 32 defer cleanup() 33 snapshotRootDir := conf.SnapshotsConfig.RootDir 34 nsCollBtlConfs := []*nsCollBtlConfig{ 35 { 36 namespace: "ns", 37 btlConfig: map[string]uint64{"coll": 0}, 38 }, 39 } 40 provider := testutilNewProviderWithCollectionConfig( 41 t, 42 nsCollBtlConfs, 43 conf, 44 ) 45 defer provider.Close() 46 47 // add the genesis block and generate the snapshot 48 blkGenerator, genesisBlk := testutil.NewBlockGenerator(t, "testLedgerid", false) 49 lgr, err := provider.Create(genesisBlk) 50 require.NoError(t, err) 51 defer lgr.Close() 52 kvlgr := lgr.(*kvLedger) 53 require.NoError(t, kvlgr.generateSnapshot()) 54 verifySnapshotOutput(t, 55 snapshotRootDir, 56 kvlgr.ledgerID, 57 1, 58 protoutil.BlockHeaderHash(genesisBlk.Header), 59 kvlgr.commitHash, 60 "txids.data", "txids.metadata", 61 ) 62 63 // add block-1 only with public state data and generate the snapshot 64 blockAndPvtdata1 := prepareNextBlockForTest(t, kvlgr, blkGenerator, "SimulateForBlk1", 65 map[string]string{"key1": "value1.1", "key2": "value2.1", "key3": "value3.1"}, 66 nil, 67 ) 68 require.NoError(t, kvlgr.CommitLegacy(blockAndPvtdata1, &ledger.CommitOptions{})) 69 require.NoError(t, kvlgr.generateSnapshot()) 70 verifySnapshotOutput(t, 71 snapshotRootDir, 72 kvlgr.ledgerID, 73 2, 74 protoutil.BlockHeaderHash(blockAndPvtdata1.Block.Header), 75 kvlgr.commitHash, 76 "txids.data", "txids.metadata", 77 "public_state.data", "public_state.metadata", 78 ) 79 80 // add block-2 only with public and private data and generate the snapshot 81 blockAndPvtdata2 := prepareNextBlockForTest(t, kvlgr, blkGenerator, "SimulateForBlk2", 82 map[string]string{"key1": "value1.2", "key2": "value2.2", "key3": "value3.2"}, 83 map[string]string{"key1": "pvtValue1.2", "key2": "pvtValue2.2", "key3": "pvtValue3.2"}, 84 ) 85 require.NoError(t, kvlgr.CommitLegacy(blockAndPvtdata2, &ledger.CommitOptions{})) 86 require.NoError(t, kvlgr.generateSnapshot()) 87 verifySnapshotOutput(t, 88 snapshotRootDir, 89 kvlgr.ledgerID, 90 3, 91 protoutil.BlockHeaderHash(blockAndPvtdata2.Block.Header), 92 kvlgr.commitHash, 93 "txids.data", "txids.metadata", 94 "public_state.data", "public_state.metadata", 95 "private_state_hashes.data", "private_state_hashes.metadata", 96 ) 97 98 // add dummy entry in collection config history and commit block-3 and generate the snapshot 99 addDummyEntryInCollectionConfigHistory(t, provider, kvlgr.ledgerID) 100 blockAndPvtdata3 := prepareNextBlockForTest(t, kvlgr, blkGenerator, "SimulateForBlk3", 101 map[string]string{"key1": "value1.3", "key2": "value2.3", "key3": "value3.3"}, 102 nil, 103 ) 104 require.NoError(t, kvlgr.CommitLegacy(blockAndPvtdata3, &ledger.CommitOptions{})) 105 require.NoError(t, kvlgr.generateSnapshot()) 106 verifySnapshotOutput(t, 107 snapshotRootDir, 108 kvlgr.ledgerID, 109 4, 110 protoutil.BlockHeaderHash(blockAndPvtdata3.Block.Header), 111 kvlgr.commitHash, 112 "txids.data", "txids.metadata", 113 "public_state.data", "public_state.metadata", 114 "private_state_hashes.data", "private_state_hashes.metadata", 115 "confighistory.data", "confighistory.metadata", 116 ) 117 } 118 119 func TestSnapshotDirPaths(t *testing.T) { 120 require.Equal(t, "/peerFSPath/snapshotRootDir/underConstruction", InProgressSnapshotsPath("/peerFSPath/snapshotRootDir")) 121 require.Equal(t, "/peerFSPath/snapshotRootDir/completed", CompletedSnapshotsPath("/peerFSPath/snapshotRootDir")) 122 require.Equal(t, "/peerFSPath/snapshotRootDir/completed/myLedger", SnapshotsDirForLedger("/peerFSPath/snapshotRootDir", "myLedger")) 123 require.Equal(t, "/peerFSPath/snapshotRootDir/completed/myLedger/2000", SnapshotDirForLedgerHeight("/peerFSPath/snapshotRootDir", "myLedger", 2000)) 124 } 125 126 func TestSnapshotDirPathsCreation(t *testing.T) { 127 conf, cleanup := testConfig(t) 128 defer cleanup() 129 provider := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{}) 130 defer func() { 131 provider.Close() 132 }() 133 134 inProgressSnapshotsPath := InProgressSnapshotsPath(conf.SnapshotsConfig.RootDir) 135 completedSnapshotsPath := CompletedSnapshotsPath(conf.SnapshotsConfig.RootDir) 136 137 // verify that upon first time start, kvledgerProvider creates an empty temp dir and an empty final dir for the snapshots 138 for _, dir := range [2]string{inProgressSnapshotsPath, completedSnapshotsPath} { 139 f, err := ioutil.ReadDir(dir) 140 require.NoError(t, err) 141 require.Len(t, f, 0) 142 } 143 144 // add a file in each of the above folders 145 for _, dir := range [2]string{inProgressSnapshotsPath, completedSnapshotsPath} { 146 ioutil.WriteFile(filepath.Join(dir, "testFile"), []byte("some junk data"), 0644) 147 f, err := ioutil.ReadDir(dir) 148 require.NoError(t, err) 149 require.Len(t, f, 1) 150 } 151 152 // verify that upon subsequent opening, kvledgerProvider removes any under-processing snapshots, 153 // potentially from a previous crash, from the temp dir but it does not remove any files from the final dir 154 provider.Close() 155 provider = testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{}) 156 f, err := ioutil.ReadDir(inProgressSnapshotsPath) 157 require.NoError(t, err) 158 require.Len(t, f, 0) 159 f, err = ioutil.ReadDir(completedSnapshotsPath) 160 require.NoError(t, err) 161 require.Len(t, f, 1) 162 } 163 164 func TestSnapshotsDirInitializingErrors(t *testing.T) { 165 initKVLedgerProvider := func(conf *ledger.Config) error { 166 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 167 require.NoError(t, err) 168 _, err = NewProvider( 169 &lgr.Initializer{ 170 DeployedChaincodeInfoProvider: &mock.DeployedChaincodeInfoProvider{}, 171 MetricsProvider: &disabled.Provider{}, 172 Config: conf, 173 HashProvider: cryptoProvider, 174 }, 175 ) 176 return err 177 } 178 179 t.Run("invalid-path", func(t *testing.T) { 180 conf, cleanup := testConfig(t) 181 defer cleanup() 182 conf.SnapshotsConfig.RootDir = "./a-relative-path" 183 err := initKVLedgerProvider(conf) 184 require.EqualError(t, err, "invalid path: ./a-relative-path. The path for the snapshot dir is expected to be an absolute path") 185 }) 186 187 t.Run("snapshots final dir creation returns error", func(t *testing.T) { 188 conf, cleanup := testConfig(t) 189 defer cleanup() 190 191 completedSnapshotsPath := CompletedSnapshotsPath(conf.SnapshotsConfig.RootDir) 192 require.NoError(t, os.MkdirAll(filepath.Dir(completedSnapshotsPath), 0755)) 193 require.NoError(t, ioutil.WriteFile(completedSnapshotsPath, []byte("some data"), 0644)) 194 err := initKVLedgerProvider(conf) 195 require.Error(t, err) 196 require.Contains(t, err.Error(), "while creating the dir: "+completedSnapshotsPath) 197 }) 198 } 199 200 func TestGenerateSnapshotErrors(t *testing.T) { 201 conf, cleanup := testConfig(t) 202 defer cleanup() 203 provider := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{}) 204 defer func() { 205 provider.Close() 206 }() 207 208 // create a ledger 209 _, genesisBlk := testutil.NewBlockGenerator(t, "testLedgerid", false) 210 lgr, err := provider.Create(genesisBlk) 211 require.NoError(t, err) 212 kvlgr := lgr.(*kvLedger) 213 214 closeAndReopenLedgerProvider := func() { 215 provider.Close() 216 provider = testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{}) 217 lgr, err = provider.Open("testLedgerid") 218 require.NoError(t, err) 219 kvlgr = lgr.(*kvLedger) 220 } 221 222 t.Run("snapshot tmp dir creation returns error", func(t *testing.T) { 223 closeAndReopenLedgerProvider() 224 require.NoError(t, os.RemoveAll( // remove the base tempdir so that the snapshot tempdir creation fails 225 InProgressSnapshotsPath(conf.SnapshotsConfig.RootDir), 226 )) 227 err := kvlgr.generateSnapshot() 228 require.Error(t, err) 229 require.Contains(t, err.Error(), "error while creating temp dir") 230 }) 231 232 t.Run("block store returns error", func(t *testing.T) { 233 closeAndReopenLedgerProvider() 234 provider.blkStoreProvider.Close() // close the blockstore provider to trigger the error 235 err := kvlgr.generateSnapshot() 236 require.Error(t, err) 237 errStackTrace := fmt.Sprintf("%+v", err) 238 require.Contains(t, errStackTrace, "internal leveldb error while obtaining db iterator") 239 require.Contains(t, errStackTrace, "fabric/common/ledger/blkstorage/blockindex.go") 240 }) 241 242 t.Run("config history mgr returns error", func(t *testing.T) { 243 closeAndReopenLedgerProvider() 244 provider.configHistoryMgr.Close() // close the configHistoryMgr to trigger the error 245 err := kvlgr.generateSnapshot() 246 require.Error(t, err) 247 errStackTrace := fmt.Sprintf("%+v", err) 248 require.Contains(t, errStackTrace, "internal leveldb error while obtaining db iterator") 249 require.Contains(t, errStackTrace, "fabric/core/ledger/confighistory/mgr.go") 250 }) 251 252 t.Run("statedb returns error", func(t *testing.T) { 253 closeAndReopenLedgerProvider() 254 provider.dbProvider.Close() // close the dbProvider to trigger the error 255 err := kvlgr.generateSnapshot() 256 require.Error(t, err) 257 errStackTrace := fmt.Sprintf("%+v", err) 258 require.Contains(t, errStackTrace, "internal leveldb error while obtaining db iterator") 259 require.Contains(t, errStackTrace, "statedb/stateleveldb/stateleveldb.go") 260 }) 261 262 t.Run("renaming to the final snapshot dir returns error", func(t *testing.T) { 263 closeAndReopenLedgerProvider() 264 snapshotFinalDir := SnapshotDirForLedgerHeight(conf.SnapshotsConfig.RootDir, "testLedgerid", 1) 265 require.NoError(t, os.MkdirAll(snapshotFinalDir, 0744)) 266 defer os.RemoveAll(snapshotFinalDir) 267 require.NoError(t, ioutil.WriteFile( // make a non-empty snapshotFinalDir to trigger failure on rename 268 filepath.Join(snapshotFinalDir, "dummyFile"), 269 []byte("dummy file"), 0444), 270 ) 271 err := kvlgr.generateSnapshot() 272 require.Contains(t, err.Error(), "error while renaming dir") 273 }) 274 } 275 276 func TestFileUtilFunctionsErrors(t *testing.T) { 277 t.Run("syncDir-openfile", func(t *testing.T) { 278 err := syncDir("non-existent-dir") 279 require.Error(t, err) 280 require.Contains(t, err.Error(), "error while opening dir:non-existent-dir") 281 }) 282 283 t.Run("createAndSyncFile-openfile", func(t *testing.T) { 284 path, err := ioutil.TempDir("", "kvledger") 285 err = createAndSyncFile(path, []byte("dummy content")) 286 require.Error(t, err) 287 require.Contains(t, err.Error(), "error while creating file:"+path) 288 }) 289 } 290 291 func verifySnapshotOutput( 292 t *testing.T, 293 snapshotRootDir string, 294 ledgerID string, 295 ledgerHeight uint64, 296 lastBlockHash []byte, 297 lastCommitHash []byte, 298 expectedBinaryFiles ...string, 299 ) { 300 inProgressSnapshotsPath := InProgressSnapshotsPath(snapshotRootDir) 301 f, err := ioutil.ReadDir(inProgressSnapshotsPath) 302 require.NoError(t, err) 303 require.Len(t, f, 0) 304 305 snapshotDir := SnapshotDirForLedgerHeight(snapshotRootDir, ledgerID, ledgerHeight) 306 files, err := ioutil.ReadDir(snapshotDir) 307 require.NoError(t, err) 308 require.Len(t, files, len(expectedBinaryFiles)+2) // + 2 JSON files 309 310 filesAndHashes := map[string]string{} 311 for _, f := range expectedBinaryFiles { 312 c, err := ioutil.ReadFile(filepath.Join(snapshotDir, f)) 313 require.NoError(t, err) 314 filesAndHashes[f] = hex.EncodeToString(util.ComputeSHA256(c)) 315 } 316 317 // verify the contents of the file snapshot_metadata.json 318 m := &snapshotSignableMetadata{} 319 mJSON, err := ioutil.ReadFile(filepath.Join(snapshotDir, snapshotMetadataFileName)) 320 require.NoError(t, err) 321 require.NoError(t, json.Unmarshal(mJSON, m)) 322 require.Equal(t, 323 &snapshotSignableMetadata{ 324 ChannelName: ledgerID, 325 ChannelHeight: ledgerHeight, 326 LastBlockHashInHex: hex.EncodeToString(lastBlockHash), 327 FilesAndHashes: filesAndHashes, 328 }, 329 m, 330 ) 331 332 // verify the contents of the file snapshot_metadata_hash.json 333 mh := &snapshotAdditionalInfo{} 334 mhJSON, err := ioutil.ReadFile(filepath.Join(snapshotDir, snapshotMetadataHashFileName)) 335 require.NoError(t, err) 336 require.NoError(t, json.Unmarshal(mhJSON, mh)) 337 require.Equal(t, 338 &snapshotAdditionalInfo{ 339 SnapshotHashInHex: hex.EncodeToString(util.ComputeSHA256(mJSON)), 340 LastBlockCommitHashInHex: hex.EncodeToString(lastCommitHash), 341 }, 342 mh, 343 ) 344 } 345 346 func addDummyEntryInCollectionConfigHistory(t *testing.T, provider *Provider, ledgerID string) { 347 // configure mock to cause data entry in collection config history 348 ccInfoProviderMock := provider.initializer.DeployedChaincodeInfoProvider.(*mock.DeployedChaincodeInfoProvider) 349 ccInfoProviderMock.UpdatedChaincodesReturns( 350 []*ledger.ChaincodeLifecycleInfo{ 351 { 352 Name: "ns", 353 }, 354 }, 355 nil, 356 ) 357 358 ccInfoProviderMock.ChaincodeInfoReturns( 359 &ledger.DeployedChaincodeInfo{ 360 Name: "ns", 361 ExplicitCollectionConfigPkg: &peer.CollectionConfigPackage{ 362 Config: []*peer.CollectionConfig{ 363 { 364 Payload: &peer.CollectionConfig_StaticCollectionConfig{ 365 StaticCollectionConfig: &peer.StaticCollectionConfig{ 366 Name: "coll1", 367 }, 368 }, 369 }, 370 }, 371 }, 372 }, 373 nil, 374 ) 375 provider.configHistoryMgr.HandleStateUpdates( 376 &ledger.StateUpdateTrigger{ 377 LedgerID: ledgerID, 378 StateUpdates: map[string]*ledger.KVStateUpdates{ 379 "ns": {}, 380 }, 381 }, 382 ) 383 }