github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/core/ledger/confighistory/mgr_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package confighistory 8 9 import ( 10 "crypto/sha256" 11 "fmt" 12 "hash" 13 "io/ioutil" 14 "math" 15 "os" 16 "path/filepath" 17 "testing" 18 19 "github.com/golang/protobuf/proto" 20 "github.com/hyperledger/fabric-protos-go/common" 21 "github.com/hyperledger/fabric-protos-go/peer" 22 "github.com/osdi23p228/fabric/common/flogging" 23 "github.com/osdi23p228/fabric/common/ledger/snapshot" 24 "github.com/osdi23p228/fabric/core/ledger" 25 "github.com/osdi23p228/fabric/core/ledger/mock" 26 "github.com/stretchr/testify/require" 27 ) 28 29 var ( 30 testNewHashFunc = func() (hash.Hash, error) { 31 return sha256.New(), nil 32 } 33 ) 34 35 func TestMain(m *testing.M) { 36 flogging.ActivateSpec("confighistory=debug") 37 os.Exit(m.Run()) 38 } 39 40 func TestWithNoCollectionConfig(t *testing.T) { 41 dbPath, err := ioutil.TempDir("", "confighistory") 42 if err != nil { 43 t.Fatalf("Failed to create config history directory: %s", err) 44 } 45 defer os.RemoveAll(dbPath) 46 mockCCInfoProvider := &mock.DeployedChaincodeInfoProvider{} 47 mgr, err := NewMgr(dbPath, mockCCInfoProvider) 48 require.NoError(t, err) 49 testutilEquipMockCCInfoProviderToReturnDesiredCollConfig(mockCCInfoProvider, "chaincode1", nil) 50 err = mgr.HandleStateUpdates(&ledger.StateUpdateTrigger{ 51 LedgerID: "ledger1", 52 CommittingBlockNum: 50}, 53 ) 54 require.NoError(t, err) 55 dummyLedgerInfoRetriever := &dummyLedgerInfoRetriever{ 56 info: &common.BlockchainInfo{Height: 100}, 57 qe: &mock.QueryExecutor{}, 58 } 59 retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever) 60 collConfig, err := retriever.MostRecentCollectionConfigBelow(90, "chaincode1") 61 require.NoError(t, err) 62 require.Nil(t, collConfig) 63 } 64 65 func TestWithEmptyCollectionConfig(t *testing.T) { 66 dbPath, err := ioutil.TempDir("", "confighistory") 67 if err != nil { 68 t.Fatalf("Failed to create config history directory: %s", err) 69 } 70 defer os.RemoveAll(dbPath) 71 mockCCInfoProvider := &mock.DeployedChaincodeInfoProvider{} 72 mgr, err := NewMgr(dbPath, mockCCInfoProvider) 73 require.NoError(t, err) 74 testutilEquipMockCCInfoProviderToReturnDesiredCollConfig( 75 mockCCInfoProvider, 76 "chaincode1", 77 &peer.CollectionConfigPackage{}, 78 ) 79 err = mgr.HandleStateUpdates(&ledger.StateUpdateTrigger{ 80 LedgerID: "ledger1", 81 CommittingBlockNum: 50}, 82 ) 83 require.NoError(t, err) 84 dummyLedgerInfoRetriever := &dummyLedgerInfoRetriever{ 85 info: &common.BlockchainInfo{Height: 100}, 86 qe: &mock.QueryExecutor{}, 87 } 88 retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever) 89 collConfig, err := retriever.MostRecentCollectionConfigBelow(90, "chaincode1") 90 require.NoError(t, err) 91 require.Nil(t, collConfig) 92 } 93 94 func TestMgr(t *testing.T) { 95 dbPath, err := ioutil.TempDir("", "confighistory") 96 if err != nil { 97 t.Fatalf("Failed to create config history directory: %s", err) 98 } 99 defer os.RemoveAll(dbPath) 100 mockCCInfoProvider := &mock.DeployedChaincodeInfoProvider{} 101 mgr, err := NewMgr(dbPath, mockCCInfoProvider) 102 require.NoError(t, err) 103 chaincodeName := "chaincode1" 104 maxBlockNumberInLedger := uint64(2000) 105 dummyLedgerInfoRetriever := &dummyLedgerInfoRetriever{ 106 info: &common.BlockchainInfo{Height: maxBlockNumberInLedger + 1}, 107 qe: &mock.QueryExecutor{}, 108 } 109 configCommittingBlockNums := []uint64{5, 10, 15, 100} 110 ledgerIds := []string{"ledgerid1", "ledger2"} 111 112 // Populate collection config versions 113 for _, ledgerid := range ledgerIds { 114 for _, committingBlockNum := range configCommittingBlockNums { 115 // for each ledgerid and commitHeight combination, construct a unique collConfigPackage and induce a stateUpdate 116 collConfigPackage := sampleCollectionConfigPackage(ledgerid, committingBlockNum) 117 testutilEquipMockCCInfoProviderToReturnDesiredCollConfig(mockCCInfoProvider, chaincodeName, collConfigPackage) 118 mgr.HandleStateUpdates(&ledger.StateUpdateTrigger{ 119 LedgerID: ledgerid, 120 CommittingBlockNum: committingBlockNum}, 121 ) 122 } 123 } 124 125 t.Run("test-api-MostRecentCollectionConfigBelow()", func(t *testing.T) { 126 // A map that contains entries such that for each of the entries of type <K, V>, 127 // we retrieve the 'MostRecentCollectionConfigBelow' for 'K' and the expected value 128 // should be configuration committed at 'V' 129 m := map[uint64]uint64{math.MaxUint64: 100, 1000: 100, 50: 15, 12: 10, 7: 5} 130 for _, ledgerid := range ledgerIds { 131 retriever := mgr.GetRetriever(ledgerid, dummyLedgerInfoRetriever) 132 for testHeight, expectedHeight := range m { 133 retrievedConfig, err := retriever.MostRecentCollectionConfigBelow(testHeight, chaincodeName) 134 require.NoError(t, err) 135 expectedConfig := sampleCollectionConfigPackage(ledgerid, expectedHeight) 136 require.Equal(t, expectedConfig, retrievedConfig.CollectionConfig) 137 require.Equal(t, expectedHeight, retrievedConfig.CommittingBlockNum) 138 } 139 140 retrievedConfig, err := retriever.MostRecentCollectionConfigBelow(5, chaincodeName) 141 require.NoError(t, err) 142 require.Nil(t, retrievedConfig) 143 } 144 }) 145 146 t.Run("test-api-CollectionConfigAt()", func(t *testing.T) { 147 for _, ledgerid := range ledgerIds { 148 retriever := mgr.GetRetriever(ledgerid, dummyLedgerInfoRetriever) 149 for _, commitHeight := range configCommittingBlockNums { 150 retrievedConfig, err := retriever.CollectionConfigAt(commitHeight, chaincodeName) 151 require.NoError(t, err) 152 expectedConfig := sampleCollectionConfigPackage(ledgerid, commitHeight) 153 require.Equal(t, expectedConfig, retrievedConfig.CollectionConfig) 154 require.Equal(t, commitHeight, retrievedConfig.CommittingBlockNum) 155 } 156 } 157 }) 158 159 t.Run("test-api-CollectionConfigAt-BoundaryCases()", func(t *testing.T) { 160 retriever := mgr.GetRetriever("ledgerid1", dummyLedgerInfoRetriever) 161 retrievedConfig, err := retriever.CollectionConfigAt(4, chaincodeName) 162 require.NoError(t, err) 163 require.Nil(t, retrievedConfig) 164 165 _, err = retriever.CollectionConfigAt(5000, chaincodeName) 166 typedErr, ok := err.(*ledger.ErrCollectionConfigNotYetAvailable) 167 require.True(t, ok) 168 require.Equal(t, maxBlockNumberInLedger, typedErr.MaxBlockNumCommitted) 169 }) 170 } 171 172 func TestWithImplicitColls(t *testing.T) { 173 dbPath, err := ioutil.TempDir("", "confighistory") 174 if err != nil { 175 t.Fatalf("Failed to create config history directory: %s", err) 176 } 177 defer os.RemoveAll(dbPath) 178 collConfigPackage := testutilCreateCollConfigPkg([]string{"Explicit-coll-1", "Explicit-coll-2"}) 179 mockCCInfoProvider := &mock.DeployedChaincodeInfoProvider{} 180 mockCCInfoProvider.ImplicitCollectionsReturns( 181 []*peer.StaticCollectionConfig{ 182 { 183 Name: "Implicit-coll-1", 184 }, 185 { 186 Name: "Implicit-coll-2", 187 }, 188 }, 189 nil, 190 ) 191 p, err := newDBProvider(dbPath) 192 require.NoError(t, err) 193 194 mgr := &Mgr{ 195 ccInfoProvider: mockCCInfoProvider, 196 dbProvider: p, 197 } 198 dbHandle := mgr.dbProvider.getDB("ledger1") 199 batch := dbHandle.newBatch() 200 // add explicit collections at height 20 201 err = prepareDBBatch( 202 batch, 203 map[string]*peer.CollectionConfigPackage{ 204 "chaincode1": collConfigPackage, 205 }, 206 20, 207 ) 208 require.NoError(t, err) 209 require.NoError(t, dbHandle.writeBatch(batch, true)) 210 211 onlyImplicitCollections := testutilCreateCollConfigPkg( 212 []string{"Implicit-coll-1", "Implicit-coll-2"}, 213 ) 214 215 explicitAndImplicitCollections := testutilCreateCollConfigPkg( 216 []string{"Explicit-coll-1", "Explicit-coll-2", "Implicit-coll-1", "Implicit-coll-2"}, 217 ) 218 219 dummyLedgerInfoRetriever := &dummyLedgerInfoRetriever{ 220 info: &common.BlockchainInfo{Height: 1000}, 221 qe: &mock.QueryExecutor{}, 222 } 223 224 t.Run("CheckQueryExecutorCalls", func(t *testing.T) { 225 retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever) 226 // function MostRecentCollectionConfigBelow calls Done on query executor 227 _, err := retriever.MostRecentCollectionConfigBelow(50, "chaincode1") 228 require.NoError(t, err) 229 require.Equal(t, 1, dummyLedgerInfoRetriever.qe.DoneCallCount()) 230 // function CollectionConfigAt calls Done on query executor 231 _, err = retriever.CollectionConfigAt(50, "chaincode1") 232 require.NoError(t, err) 233 require.Equal(t, 2, dummyLedgerInfoRetriever.qe.DoneCallCount()) 234 }) 235 236 t.Run("MostRecentCollectionConfigBelow50", func(t *testing.T) { 237 // explicit collections added at height 20 should be merged with the implicit collections 238 retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever) 239 retrievedConfig, err := retriever.MostRecentCollectionConfigBelow(50, "chaincode1") 240 require.NoError(t, err) 241 require.True(t, proto.Equal(retrievedConfig.CollectionConfig, explicitAndImplicitCollections)) 242 }) 243 244 t.Run("MostRecentCollectionConfigBelow10", func(t *testing.T) { 245 // No explicit collections below height 10, should return only implicit collections 246 retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever) 247 retrievedConfig, err := retriever.MostRecentCollectionConfigBelow(10, "chaincode1") 248 require.NoError(t, err) 249 require.True(t, proto.Equal(retrievedConfig.CollectionConfig, onlyImplicitCollections)) 250 }) 251 252 t.Run("CollectionConfigAt50", func(t *testing.T) { 253 // No explicit collections at height 50, should return only implicit collections 254 retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever) 255 retrievedConfig, err := retriever.CollectionConfigAt(50, "chaincode1") 256 require.NoError(t, err) 257 require.True(t, proto.Equal(retrievedConfig.CollectionConfig, onlyImplicitCollections)) 258 }) 259 260 t.Run("CollectionConfigAt20", func(t *testing.T) { 261 // Explicit collections at height 20, should be merged with implicit collections 262 retriever := mgr.GetRetriever("ledger1", dummyLedgerInfoRetriever) 263 retrievedConfig, err := retriever.CollectionConfigAt(20, "chaincode1") 264 require.NoError(t, err) 265 require.True(t, proto.Equal(retrievedConfig.CollectionConfig, explicitAndImplicitCollections)) 266 }) 267 268 } 269 270 type testEnvForSnapshot struct { 271 mgr *Mgr 272 testSnapshotDir string 273 cleanup func() 274 } 275 276 func newTestEnvForSnapshot(t *testing.T) *testEnvForSnapshot { 277 dbPath, err := ioutil.TempDir("", "confighistory") 278 require.NoError(t, err) 279 mgr, err := NewMgr(dbPath, &mock.DeployedChaincodeInfoProvider{}) 280 if err != nil { 281 os.RemoveAll(dbPath) 282 t.Fatalf("Failed to create new config history manager: %s", err) 283 } 284 285 testSnapshotDir, err := ioutil.TempDir("", "confighistorysnapshot") 286 if err != nil { 287 os.RemoveAll(dbPath) 288 t.Fatalf("Failed to create config history snapshot directory: %s", err) 289 } 290 return &testEnvForSnapshot{ 291 mgr: mgr, 292 testSnapshotDir: testSnapshotDir, 293 cleanup: func() { 294 os.RemoveAll(dbPath) 295 os.RemoveAll(testSnapshotDir) 296 }, 297 } 298 } 299 300 func TestExportAndImportConfigHistory(t *testing.T) { 301 env := newTestEnvForSnapshot(t) 302 defer env.cleanup() 303 304 setup := func(ledgerID string) ([]*compositeKV, map[string][]*ledger.CollectionConfigInfo) { 305 cc1CollConfigPackage := testutilCreateCollConfigPkg([]string{"Explicit-cc1-coll-1", "Explicit-cc1-coll-2"}) 306 cc2CollConfigPackage := testutilCreateCollConfigPkg([]string{"Explicit-cc2-coll-1", "Explicit-cc2-coll-2"}) 307 cc3CollConfigPackage := testutilCreateCollConfigPkg([]string{"Explicit-cc3-coll-1", "Explicit-cc3-coll-2"}) 308 cc1CollConfigPackageNew := testutilCreateCollConfigPkg([]string{"Explicit-cc1-coll-1", "Explicit-cc1-coll-2", "Explicit-cc1-coll-3"}) 309 cc2CollConfigPackageNew := testutilCreateCollConfigPkg([]string{"Explicit-cc2-coll-1", "Explicit-cc2-coll-2", "Explicit-cc2-coll-3"}) 310 cc3CollConfigPackageNew := testutilCreateCollConfigPkg([]string{"Explicit-cc3-coll-1", "Explicit-cc3-coll-2", "Explicit-cc3-coll-3"}) 311 312 ccConfigInfo := map[string][]*ledger.CollectionConfigInfo{ 313 "chaincode1": { 314 { 315 CollectionConfig: cc1CollConfigPackage, 316 CommittingBlockNum: 50, 317 }, 318 { 319 CollectionConfig: cc1CollConfigPackageNew, 320 CommittingBlockNum: 100, 321 }, 322 }, 323 "chaincode2": { 324 { 325 CollectionConfig: cc2CollConfigPackage, 326 CommittingBlockNum: 50, 327 }, 328 { 329 CollectionConfig: cc2CollConfigPackageNew, 330 CommittingBlockNum: 100, 331 }, 332 }, 333 "chaincode3": { 334 { 335 CollectionConfig: cc3CollConfigPackage, 336 CommittingBlockNum: 50, 337 }, 338 { 339 CollectionConfig: cc3CollConfigPackageNew, 340 CommittingBlockNum: 100, 341 }, 342 }, 343 } 344 345 db := env.mgr.dbProvider.getDB(ledgerID) 346 batch := db.newBatch() 347 err := prepareDBBatch( 348 batch, 349 map[string]*peer.CollectionConfigPackage{ 350 "chaincode1": cc1CollConfigPackage, 351 "chaincode2": cc2CollConfigPackage, 352 "chaincode3": cc3CollConfigPackage, 353 }, 354 50, 355 ) 356 require.NoError(t, err) 357 require.NoError(t, db.writeBatch(batch, true)) 358 359 batch = db.newBatch() 360 err = prepareDBBatch( 361 batch, 362 map[string]*peer.CollectionConfigPackage{ 363 "chaincode1": cc1CollConfigPackageNew, 364 "chaincode2": cc2CollConfigPackageNew, 365 "chaincode3": cc3CollConfigPackageNew, 366 }, 367 100, 368 ) 369 require.NoError(t, err) 370 require.NoError(t, db.writeBatch(batch, true)) 371 372 cc1configBytes, err := proto.Marshal(cc1CollConfigPackage) 373 require.NoError(t, err) 374 cc2configBytes, err := proto.Marshal(cc2CollConfigPackage) 375 require.NoError(t, err) 376 cc3configBytes, err := proto.Marshal(cc3CollConfigPackage) 377 require.NoError(t, err) 378 cc1configBytesNew, err := proto.Marshal(cc1CollConfigPackageNew) 379 require.NoError(t, err) 380 cc2configBytesNew, err := proto.Marshal(cc2CollConfigPackageNew) 381 require.NoError(t, err) 382 cc3configBytesNew, err := proto.Marshal(cc3CollConfigPackageNew) 383 require.NoError(t, err) 384 385 storedKVs := []*compositeKV{ 386 {&compositeKey{ns: "lscc", key: "chaincode1~collection", blockNum: 100}, cc1configBytesNew}, 387 {&compositeKey{ns: "lscc", key: "chaincode1~collection", blockNum: 50}, cc1configBytes}, 388 {&compositeKey{ns: "lscc", key: "chaincode2~collection", blockNum: 100}, cc2configBytesNew}, 389 {&compositeKey{ns: "lscc", key: "chaincode2~collection", blockNum: 50}, cc2configBytes}, 390 {&compositeKey{ns: "lscc", key: "chaincode3~collection", blockNum: 100}, cc3configBytesNew}, 391 {&compositeKey{ns: "lscc", key: "chaincode3~collection", blockNum: 50}, cc3configBytes}, 392 } 393 394 return storedKVs, ccConfigInfo 395 } 396 397 cleanup := func() { 398 require.NoError(t, os.RemoveAll(filepath.Join(env.testSnapshotDir, snapshotDataFileName))) 399 require.NoError(t, os.RemoveAll(filepath.Join(env.testSnapshotDir, snapshotMetadataFileName))) 400 } 401 402 t.Run("confighistory is empty", func(t *testing.T) { 403 retriever := env.mgr.GetRetriever("ledger0", nil) 404 fileHashes, err := retriever.ExportConfigHistory(env.testSnapshotDir, testNewHashFunc) 405 require.NoError(t, err) 406 require.Empty(t, fileHashes) 407 files, err := ioutil.ReadDir(env.testSnapshotDir) 408 require.NoError(t, err) 409 require.Len(t, files, 0) 410 }) 411 412 t.Run("export confighistory", func(t *testing.T) { 413 // setup ledger1 => export ledger1 414 storedKVs, _ := setup("ledger1") 415 defer cleanup() 416 retriever := env.mgr.GetRetriever("ledger1", nil) 417 fileHashes, err := retriever.ExportConfigHistory(env.testSnapshotDir, testNewHashFunc) 418 require.NoError(t, err) 419 verifyExportedConfigHistory(t, env.testSnapshotDir, fileHashes, storedKVs) 420 }) 421 422 t.Run("import confighistory and verify queries", func(t *testing.T) { 423 // setup ledger2 => export ledger2 => import into ledger3 424 _, ccConfigInfo := setup("ledger2") 425 defer cleanup() 426 retriever := env.mgr.GetRetriever("ledger2", nil) 427 _, err := retriever.ExportConfigHistory(env.testSnapshotDir, testNewHashFunc) 428 require.NoError(t, err) 429 430 importConfigsBatchSize = 100 431 require.NoError(t, env.mgr.ImportConfigHistory("ledger3", env.testSnapshotDir)) 432 dummyLedgerInfoRetriever := &dummyLedgerInfoRetriever{ 433 info: &common.BlockchainInfo{Height: 1000}, 434 qe: &mock.QueryExecutor{}, 435 } 436 437 retriever = env.mgr.GetRetriever("ledger3", dummyLedgerInfoRetriever) 438 verifyImportedConfigHistory(t, retriever, ccConfigInfo) 439 }) 440 441 t.Run("export from an imported confighistory", func(t *testing.T) { 442 // setup ledger4 => export ledger4 => import into ledger5 => export ledger5 443 storedKVs, _ := setup("ledger4") 444 defer cleanup() 445 retriever := env.mgr.GetRetriever("ledger4", nil) 446 _, err := retriever.ExportConfigHistory(env.testSnapshotDir, testNewHashFunc) 447 require.NoError(t, err) 448 449 importConfigsBatchSize = 100 450 require.NoError(t, env.mgr.ImportConfigHistory("ledger5", env.testSnapshotDir)) 451 cleanup() 452 453 retriever = env.mgr.GetRetriever("ledger5", nil) 454 fileHashes, err := retriever.ExportConfigHistory(env.testSnapshotDir, testNewHashFunc) 455 require.NoError(t, err) 456 verifyExportedConfigHistory(t, env.testSnapshotDir, fileHashes, storedKVs) 457 }) 458 459 t.Run("import confighistory error cases", func(t *testing.T) { 460 setup("ledger6") 461 defer cleanup() 462 retriever := env.mgr.GetRetriever("ledger6", nil) 463 _, err := retriever.ExportConfigHistory(env.testSnapshotDir, testNewHashFunc) 464 require.NoError(t, err) 465 466 err = env.mgr.ImportConfigHistory("ledger6", env.testSnapshotDir) 467 expectedErrStr := "config history for ledger [ledger6] exists. Incremental import is not supported. Remove the existing ledger data before retry" 468 require.EqualError(t, err, expectedErrStr) 469 470 require.NoError(t, os.RemoveAll(filepath.Join(env.testSnapshotDir, snapshotDataFileName))) 471 err = env.mgr.ImportConfigHistory("ledger8", env.testSnapshotDir) 472 require.Contains(t, err.Error(), "confighistory.data: no such file or directory") 473 474 dataFileWriter, err := snapshot.CreateFile(filepath.Join(env.testSnapshotDir, snapshotDataFileName), snapshotFileFormat, testNewHashFunc) 475 require.NoError(t, err) 476 defer dataFileWriter.Close() 477 require.NoError(t, dataFileWriter.EncodeUVarint(1)) 478 err = env.mgr.ImportConfigHistory("ledger8", env.testSnapshotDir) 479 require.Contains(t, err.Error(), "error while reading from the snapshot file") 480 require.Contains(t, err.Error(), "confighistory.data: EOF") 481 482 require.NoError(t, os.RemoveAll(filepath.Join(env.testSnapshotDir, snapshotMetadataFileName))) 483 err = env.mgr.ImportConfigHistory("ledger8", env.testSnapshotDir) 484 require.Contains(t, err.Error(), "confighistory.metadata: no such file or directory") 485 486 dataFileWriter, err = snapshot.CreateFile(filepath.Join(env.testSnapshotDir, snapshotMetadataFileName), snapshotFileFormat, testNewHashFunc) 487 require.NoError(t, err) 488 defer dataFileWriter.Close() 489 require.NoError(t, dataFileWriter.EncodeBytes([]byte("junk"))) 490 err = env.mgr.ImportConfigHistory("ledger8", env.testSnapshotDir) 491 require.Contains(t, err.Error(), "error while reading from the snapshot file") 492 require.Contains(t, err.Error(), "confighistory.metadata: EOF") 493 494 env.mgr.dbProvider.Close() 495 err = env.mgr.ImportConfigHistory("ledger8", env.testSnapshotDir) 496 require.EqualError(t, err, "internal leveldb error while obtaining db iterator: leveldb: closed") 497 }) 498 } 499 500 func verifyExportedConfigHistory(t *testing.T, dir string, fileHashes map[string][]byte, expectedCollectionConfigs []*compositeKV) { 501 require.Len(t, fileHashes, 2) 502 require.Contains(t, fileHashes, snapshotDataFileName) 503 require.Contains(t, fileHashes, snapshotMetadataFileName) 504 505 dataFile := filepath.Join(dir, snapshotDataFileName) 506 dataFileContent, err := ioutil.ReadFile(dataFile) 507 require.NoError(t, err) 508 dataFileHash := sha256.Sum256(dataFileContent) 509 require.Equal(t, dataFileHash[:], fileHashes[snapshotDataFileName]) 510 511 metadataFile := filepath.Join(dir, snapshotMetadataFileName) 512 metadataFileContent, err := ioutil.ReadFile(metadataFile) 513 require.NoError(t, err) 514 metadataFileHash := sha256.Sum256(metadataFileContent) 515 require.Equal(t, metadataFileHash[:], fileHashes[snapshotMetadataFileName]) 516 517 metadataReader, err := snapshot.OpenFile(metadataFile, snapshotFileFormat) 518 require.NoError(t, err) 519 defer metadataReader.Close() 520 521 dataReader, err := snapshot.OpenFile(dataFile, snapshotFileFormat) 522 require.NoError(t, err) 523 defer dataReader.Close() 524 525 numCollectionConfigs, err := metadataReader.DecodeUVarInt() 526 require.NoError(t, err) 527 528 var retrievedCollectionConfigs []*compositeKV 529 for i := uint64(0); i < numCollectionConfigs; i++ { 530 key, err := dataReader.DecodeBytes() 531 require.NoError(t, err) 532 val, err := dataReader.DecodeBytes() 533 require.NoError(t, err) 534 retrievedCollectionConfigs = append(retrievedCollectionConfigs, 535 &compositeKV{decodeCompositeKey(key), val}, 536 ) 537 } 538 require.Equal(t, expectedCollectionConfigs, retrievedCollectionConfigs) 539 } 540 541 func verifyImportedConfigHistory(t *testing.T, retriever *Retriever, expectedCCConfigInfo map[string][]*ledger.CollectionConfigInfo) { 542 for chaincodeName, ccConfigInfos := range expectedCCConfigInfo { 543 for _, expectedCCConfig := range ccConfigInfos { 544 ccConfig, err := retriever.CollectionConfigAt(expectedCCConfig.CommittingBlockNum, chaincodeName) 545 require.NoError(t, err) 546 require.True(t, proto.Equal(expectedCCConfig.CollectionConfig, ccConfig.CollectionConfig)) 547 require.Equal(t, expectedCCConfig.CommittingBlockNum, ccConfig.CommittingBlockNum) 548 549 ccConfig, err = retriever.MostRecentCollectionConfigBelow(expectedCCConfig.CommittingBlockNum+1, chaincodeName) 550 require.NoError(t, err) 551 require.True(t, proto.Equal(expectedCCConfig.CollectionConfig, ccConfig.CollectionConfig)) 552 require.Equal(t, expectedCCConfig.CommittingBlockNum, ccConfig.CommittingBlockNum) 553 } 554 } 555 } 556 557 func TestExportConfigHistoryErrorCase(t *testing.T) { 558 env := newTestEnvForSnapshot(t) 559 defer env.cleanup() 560 561 db := env.mgr.dbProvider.getDB("ledger1") 562 cc1collConfigPackage := testutilCreateCollConfigPkg([]string{"Explicit-cc1-coll-1", "Explicit-cc1-coll-2"}) 563 batch := db.newBatch() 564 err := prepareDBBatch( 565 batch, 566 map[string]*peer.CollectionConfigPackage{ 567 "chaincode1": cc1collConfigPackage, 568 }, 569 50, 570 ) 571 require.NoError(t, err) 572 require.NoError(t, db.writeBatch(batch, true)) 573 574 // error during data file creation 575 dataFilePath := filepath.Join(env.testSnapshotDir, snapshotDataFileName) 576 _, err = os.Create(dataFilePath) 577 require.NoError(t, err) 578 579 retriever := env.mgr.GetRetriever("ledger1", nil) 580 _, err = retriever.ExportConfigHistory(env.testSnapshotDir, testNewHashFunc) 581 require.Contains(t, err.Error(), "error while creating the snapshot file: "+dataFilePath) 582 os.RemoveAll(env.testSnapshotDir) 583 584 // error during metadata file creation 585 require.NoError(t, os.MkdirAll(env.testSnapshotDir, 0700)) 586 metadataFilePath := filepath.Join(env.testSnapshotDir, snapshotMetadataFileName) 587 _, err = os.Create(metadataFilePath) 588 require.NoError(t, err) 589 _, err = retriever.ExportConfigHistory(env.testSnapshotDir, testNewHashFunc) 590 require.Contains(t, err.Error(), "error while creating the snapshot file: "+metadataFilePath) 591 os.RemoveAll(env.testSnapshotDir) 592 593 // error while reading from leveldb 594 require.NoError(t, os.MkdirAll(env.testSnapshotDir, 0700)) 595 env.mgr.dbProvider.Close() 596 _, err = retriever.ExportConfigHistory(env.testSnapshotDir, testNewHashFunc) 597 require.EqualError(t, err, "internal leveldb error while obtaining db iterator: leveldb: closed") 598 os.RemoveAll(env.testSnapshotDir) 599 } 600 601 func sampleCollectionConfigPackage(collNamePart1 string, collNamePart2 uint64) *peer.CollectionConfigPackage { 602 collName := fmt.Sprintf("%s-%d", collNamePart1, collNamePart2) 603 return testutilCreateCollConfigPkg([]string{collName}) 604 } 605 606 func testutilEquipMockCCInfoProviderToReturnDesiredCollConfig( 607 mockCCInfoProvider *mock.DeployedChaincodeInfoProvider, 608 chaincodeName string, 609 collConfigPackage *peer.CollectionConfigPackage) { 610 mockCCInfoProvider.UpdatedChaincodesReturns( 611 []*ledger.ChaincodeLifecycleInfo{ 612 {Name: chaincodeName}, 613 }, 614 nil, 615 ) 616 mockCCInfoProvider.ChaincodeInfoReturns( 617 &ledger.DeployedChaincodeInfo{Name: chaincodeName, ExplicitCollectionConfigPkg: collConfigPackage}, 618 nil, 619 ) 620 } 621 622 func testutilCreateCollConfigPkg(collNames []string) *peer.CollectionConfigPackage { 623 pkg := &peer.CollectionConfigPackage{ 624 Config: []*peer.CollectionConfig{}, 625 } 626 for _, collName := range collNames { 627 pkg.Config = append(pkg.Config, 628 &peer.CollectionConfig{ 629 Payload: &peer.CollectionConfig_StaticCollectionConfig{ 630 StaticCollectionConfig: &peer.StaticCollectionConfig{ 631 Name: collName, 632 }, 633 }, 634 }, 635 ) 636 } 637 return pkg 638 } 639 640 type dummyLedgerInfoRetriever struct { 641 info *common.BlockchainInfo 642 qe *mock.QueryExecutor 643 } 644 645 func (d *dummyLedgerInfoRetriever) GetBlockchainInfo() (*common.BlockchainInfo, error) { 646 return d.info, nil 647 } 648 649 func (d *dummyLedgerInfoRetriever) NewQueryExecutor() (ledger.QueryExecutor, error) { 650 return d.qe, nil 651 }