github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/snapshot_test.go (about) 1 /* 2 Copyright hechain. 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 "math" 15 "os" 16 "path/filepath" 17 "testing" 18 "time" 19 20 "github.com/golang/protobuf/proto" 21 "github.com/hechain20/hechain/bccsp/sw" 22 "github.com/hechain20/hechain/common/ledger/testutil" 23 "github.com/hechain20/hechain/common/metrics/disabled" 24 "github.com/hechain20/hechain/common/util" 25 "github.com/hechain20/hechain/core/chaincode/implicitcollection" 26 "github.com/hechain20/hechain/core/ledger" 27 "github.com/hechain20/hechain/core/ledger/cceventmgmt" 28 "github.com/hechain20/hechain/core/ledger/confighistory/confighistorytest" 29 "github.com/hechain20/hechain/core/ledger/internal/version" 30 kvledgermock "github.com/hechain20/hechain/core/ledger/kvledger/mock" 31 "github.com/hechain20/hechain/core/ledger/kvledger/msgs" 32 "github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/statedb/statecouchdb" 33 "github.com/hechain20/hechain/core/ledger/mock" 34 "github.com/hechain20/hechain/internal/fileutil" 35 "github.com/hechain20/hechain/protoutil" 36 "github.com/hyperledger/fabric-protos-go/common" 37 "github.com/hyperledger/fabric-protos-go/ledger/queryresult" 38 "github.com/hyperledger/fabric-protos-go/peer" 39 "github.com/stretchr/testify/require" 40 ) 41 42 func TestSnapshotGenerationAndNewLedgerCreation(t *testing.T) { 43 conf, cleanup := testConfig(t) 44 defer cleanup() 45 snapshotRootDir := conf.SnapshotsConfig.RootDir 46 nsCollBtlConfs := []*nsCollBtlConfig{ 47 { 48 namespace: "ns", 49 btlConfig: map[string]uint64{"coll": 0}, 50 }, 51 } 52 provider := testutilNewProviderWithCollectionConfig( 53 t, 54 nsCollBtlConfs, 55 conf, 56 ) 57 defer provider.Close() 58 59 // add the genesis block and generate the snapshot 60 blkGenerator, genesisBlk := testutil.NewBlockGenerator(t, "testLedgerid", false) 61 lgr, err := provider.CreateFromGenesisBlock(genesisBlk) 62 require.NoError(t, err) 63 defer lgr.Close() 64 kvlgr := lgr.(*kvLedger) 65 require.NoError(t, kvlgr.generateSnapshot()) 66 verifySnapshotOutput(t, 67 &expectedSnapshotOutput{ 68 snapshotRootDir: snapshotRootDir, 69 ledgerID: kvlgr.ledgerID, 70 lastBlockNumber: 0, 71 lastBlockHash: protoutil.BlockHeaderHash(genesisBlk.Header), 72 previousBlockHash: genesisBlk.Header.PreviousHash, 73 lastCommitHash: kvlgr.commitHash, 74 stateDBType: simpleKeyValueDB, 75 expectedBinaryFiles: []string{ 76 "txids.data", "txids.metadata", 77 }, 78 }, 79 ) 80 81 // add block-1 only with public state data and generate the snapshot 82 blockAndPvtdata1 := prepareNextBlockForTest(t, kvlgr, blkGenerator, "SimulateForBlk1", 83 map[string]string{ 84 "key1": "value1.1", 85 "key2": "value2.1", 86 "key3": "value3.1", 87 }, 88 nil, 89 ) 90 require.NoError(t, kvlgr.CommitLegacy(blockAndPvtdata1, &ledger.CommitOptions{})) 91 require.NoError(t, kvlgr.generateSnapshot()) 92 verifySnapshotOutput(t, 93 &expectedSnapshotOutput{ 94 snapshotRootDir: snapshotRootDir, 95 ledgerID: kvlgr.ledgerID, 96 lastBlockNumber: 1, 97 lastBlockHash: protoutil.BlockHeaderHash(blockAndPvtdata1.Block.Header), 98 previousBlockHash: blockAndPvtdata1.Block.Header.PreviousHash, 99 lastCommitHash: kvlgr.commitHash, 100 stateDBType: simpleKeyValueDB, 101 expectedBinaryFiles: []string{ 102 "txids.data", "txids.metadata", 103 "public_state.data", "public_state.metadata", 104 }, 105 }, 106 ) 107 108 // add dummy entry in collection config history and commit block-2 and generate the snapshot 109 addDummyEntryInCollectionConfigHistory(t, provider, kvlgr.ledgerID, "ns", 1, []*peer.StaticCollectionConfig{{Name: "coll"}}) 110 111 // add block-2 only with public and private data and generate the snapshot 112 blockAndPvtdata2 := prepareNextBlockForTest(t, kvlgr, blkGenerator, "SimulateForBlk2", 113 map[string]string{ 114 "key1": "value1.2", 115 "key2": "value2.2", 116 "key3": "value3.2", 117 }, 118 map[string]string{ 119 "key1": "pvtValue1.2", 120 "key2": "pvtValue2.2", 121 "key3": "pvtValue3.2", 122 }, 123 ) 124 require.NoError(t, kvlgr.CommitLegacy(blockAndPvtdata2, &ledger.CommitOptions{})) 125 require.NoError(t, kvlgr.generateSnapshot()) 126 verifySnapshotOutput(t, 127 &expectedSnapshotOutput{ 128 snapshotRootDir: snapshotRootDir, 129 ledgerID: kvlgr.ledgerID, 130 lastBlockNumber: 2, 131 lastBlockHash: protoutil.BlockHeaderHash(blockAndPvtdata2.Block.Header), 132 previousBlockHash: blockAndPvtdata2.Block.Header.PreviousHash, 133 lastCommitHash: kvlgr.commitHash, 134 stateDBType: simpleKeyValueDB, 135 expectedBinaryFiles: []string{ 136 "txids.data", "txids.metadata", 137 "public_state.data", "public_state.metadata", 138 "private_state_hashes.data", "private_state_hashes.metadata", 139 "confighistory.data", "confighistory.metadata", 140 }, 141 }, 142 ) 143 144 blockAndPvtdata3 := prepareNextBlockForTest(t, kvlgr, blkGenerator, "SimulateForBlk3", 145 map[string]string{ 146 "key1": "value1.3", 147 "key2": "value2.3", 148 "key3": "value3.3", 149 }, 150 nil, 151 ) 152 require.NoError(t, kvlgr.CommitLegacy(blockAndPvtdata3, &ledger.CommitOptions{})) 153 require.NoError(t, kvlgr.generateSnapshot()) 154 verifySnapshotOutput(t, 155 &expectedSnapshotOutput{ 156 snapshotRootDir: snapshotRootDir, 157 ledgerID: kvlgr.ledgerID, 158 lastBlockNumber: 3, 159 lastBlockHash: protoutil.BlockHeaderHash(blockAndPvtdata3.Block.Header), 160 previousBlockHash: blockAndPvtdata3.Block.Header.PreviousHash, 161 lastCommitHash: kvlgr.commitHash, 162 stateDBType: simpleKeyValueDB, 163 expectedBinaryFiles: []string{ 164 "txids.data", "txids.metadata", 165 "public_state.data", "public_state.metadata", 166 "private_state_hashes.data", "private_state_hashes.metadata", 167 "confighistory.data", "confighistory.metadata", 168 }, 169 }, 170 ) 171 172 snapshotDir := SnapshotDirForLedgerBlockNum(snapshotRootDir, kvlgr.ledgerID, 3) 173 174 t.Run("create-ledger-from-snapshot", func(t *testing.T) { 175 createdLedger := testCreateLedgerFromSnapshot(t, snapshotDir, kvlgr.ledgerID) 176 verifyCreatedLedger(t, 177 provider, 178 createdLedger, 179 &expectedLegderState{ 180 lastBlockNumber: 3, 181 lastBlockHash: protoutil.BlockHeaderHash(blockAndPvtdata3.Block.Header), 182 previousBlockHash: blockAndPvtdata3.Block.Header.PreviousHash, 183 lastCommitHash: kvlgr.commitHash, 184 namespace: "ns", 185 publicState: map[string]string{ 186 "key1": "value1.3", 187 "key2": "value2.3", 188 "key3": "value3.3", 189 }, 190 collectionConfig: map[uint64]*peer.CollectionConfigPackage{ 191 1: { 192 Config: []*peer.CollectionConfig{ 193 { 194 Payload: &peer.CollectionConfig_StaticCollectionConfig{ 195 StaticCollectionConfig: &peer.StaticCollectionConfig{ 196 Name: "coll", 197 }, 198 }, 199 }, 200 }, 201 }, 202 }, 203 }, 204 ) 205 }) 206 207 t.Run("create-ledger-from-snapshot-error-paths", func(t *testing.T) { 208 testCreateLedgerFromSnapshotErrorPaths(t, snapshotDir) 209 }) 210 } 211 212 func TestSnapshotDBTypeCouchDB(t *testing.T) { 213 conf, cleanup := testConfig(t) 214 fmt.Printf("snapshotRootDir %s\n", conf.SnapshotsConfig.RootDir) 215 defer cleanup() 216 provider := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{}) 217 defer provider.Close() 218 219 _, genesisBlk := testutil.NewBlockGenerator(t, "testLedgerid", false) 220 lgr, err := provider.CreateFromGenesisBlock(genesisBlk) 221 require.NoError(t, err) 222 defer lgr.Close() 223 kvlgr := lgr.(*kvLedger) 224 225 // artificially set the db type 226 kvlgr.config.StateDBConfig.StateDatabase = ledger.CouchDB 227 require.NoError(t, kvlgr.generateSnapshot()) 228 verifySnapshotOutput(t, 229 &expectedSnapshotOutput{ 230 snapshotRootDir: conf.SnapshotsConfig.RootDir, 231 ledgerID: kvlgr.ledgerID, 232 stateDBType: ledger.CouchDB, 233 lastBlockHash: protoutil.BlockHeaderHash(genesisBlk.Header), 234 expectedBinaryFiles: []string{ 235 "txids.data", "txids.metadata", 236 }, 237 }, 238 ) 239 } 240 241 func TestSnapshotCouchDBIndexCreation(t *testing.T) { 242 setup := func() (string, *ledger.CouchDBConfig, *Provider) { 243 conf, cleanup := testConfig(t) 244 t.Cleanup(cleanup) 245 246 snapshotRootDir := conf.SnapshotsConfig.RootDir 247 provider := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{}) 248 t.Cleanup(provider.Close) 249 250 // add the genesis block and generate the snapshot 251 blkGenerator, genesisBlk := testutil.NewBlockGenerator(t, "test_ledger", false) 252 lgr, err := provider.CreateFromGenesisBlock(genesisBlk) 253 require.NoError(t, err) 254 t.Cleanup(lgr.Close) 255 kvlgr := lgr.(*kvLedger) 256 257 blockAndPvtdata := prepareNextBlockForTest(t, kvlgr, blkGenerator, "SimulateForBlk1", 258 map[string]string{ 259 "key1": `{"asset_name": "marble1", "color": "blue", "size": 1, "owner": "tom"}`, 260 "key2": `{"asset_name": "marble2", "color": "red", "size": 2, "owner": "jerry"}`, 261 }, 262 nil, 263 ) 264 require.NoError(t, kvlgr.CommitLegacy(blockAndPvtdata, &ledger.CommitOptions{})) 265 require.NoError(t, kvlgr.generateSnapshot()) 266 snapshotDir := SnapshotDirForLedgerBlockNum(snapshotRootDir, kvlgr.ledgerID, 1) 267 268 cceventmgmt.Initialize(nil) 269 if couchDBAddress == "" { 270 couchDBAddress, stopCouchDBFunc = statecouchdb.StartCouchDB(t, nil) 271 } 272 couchDBConfig := &ledger.CouchDBConfig{ 273 Address: couchDBAddress, 274 Username: "admin", 275 Password: "adminpw", 276 MaxRetries: 3, 277 MaxRetriesOnStartup: 3, 278 RequestTimeout: 10 * time.Second, 279 InternalQueryLimit: 1000, 280 RedoLogPath: filepath.Join(conf.RootFSPath, "couchdbRedoLogs"), 281 } 282 283 destConf, destCleanup := testConfig(t) 284 t.Cleanup(destCleanup) 285 destConf.StateDBConfig = &ledger.StateDBConfig{ 286 StateDatabase: ledger.CouchDB, 287 CouchDB: couchDBConfig, 288 } 289 destinationProvider := testutilNewProvider(destConf, t, &mock.DeployedChaincodeInfoProvider{}) 290 return snapshotDir, couchDBConfig, destinationProvider 291 } 292 293 dbArtifactsBytes := testutil.CreateTarBytesForTest( 294 []*testutil.TarFileEntry{ 295 { 296 Name: "META-INF/statedb/couchdb/indexes/indexSizeSortName.json", 297 Body: `{"index":{"fields":[{"size":"desc"}]},"ddoc":"indexSizeSortName","name":"indexSizeSortName","type":"json"}`, 298 }, 299 }, 300 ) 301 302 verifyIndexCreatedOnMarbleSize := func(lgr ledger.PeerLedger) { 303 qe, err := lgr.NewQueryExecutor() 304 require.NoError(t, err) 305 defer qe.Done() 306 iter, err := qe.ExecuteQuery("ns", `{"selector":{"owner":"tom"}, "sort": [{"size": "desc"}]}`) 307 require.NoError(t, err) 308 defer iter.Close() 309 actualResults := []*queryresult.KV{} 310 for { 311 queryResults, err := iter.Next() 312 require.NoError(t, err) 313 if queryResults == nil { 314 break 315 } 316 actualResults = append(actualResults, queryResults.(*queryresult.KV)) 317 } 318 require.Len(t, actualResults, 1) 319 _, err = qe.ExecuteQuery("ns", `{"selector":{"owner":"tom"}, "sort": [{"color": "desc"}]}`) 320 require.Contains(t, err.Error(), "No index exists for this sort") 321 } 322 323 t.Run("create_indexes_on_couchdb_for_new_lifecycle", func(t *testing.T) { 324 snapshotDir, couchDBConfig, provider := setup() 325 defer func() { 326 require.NoError(t, statecouchdb.DropApplicationDBs(couchDBConfig)) 327 }() 328 329 // mimic new lifecycle chaincode "ns" installed and defiend and the package contains an index definition "sort index" 330 ccLifecycleEventProvider := provider.initializer.ChaincodeLifecycleEventProvider.(*mock.ChaincodeLifecycleEventProvider) 331 ccLifecycleEventProvider.RegisterListenerStub = 332 func( 333 channelID string, 334 listener ledger.ChaincodeLifecycleEventListener, 335 callback bool, 336 ) error { 337 if callback { 338 err := listener.HandleChaincodeDeploy( 339 &ledger.ChaincodeDefinition{ 340 Name: "ns", 341 }, 342 dbArtifactsBytes, 343 ) 344 require.NoError(t, err) 345 } 346 return nil 347 } 348 349 lgr, _, err := provider.CreateFromSnapshot(snapshotDir) 350 require.NoError(t, err) 351 verifyIndexCreatedOnMarbleSize(lgr) 352 }) 353 354 t.Run("create_indexes_on_couchdb_for_legacy_lifecycle", func(t *testing.T) { 355 snapshotDir, couchDBConfig, provider := setup() 356 defer func() { 357 require.NoError(t, statecouchdb.DropApplicationDBs(couchDBConfig)) 358 }() 359 360 // mimic legacy chaincode "ns" installed and defiend and the package contains an index definition "sort index" 361 deployedCCInfoProvider := provider.initializer.DeployedChaincodeInfoProvider.(*mock.DeployedChaincodeInfoProvider) 362 deployedCCInfoProvider.AllChaincodesInfoReturns( 363 map[string]*ledger.DeployedChaincodeInfo{ 364 "ns": { 365 Name: "ns", 366 Version: "version", 367 Hash: []byte("hash"), 368 IsLegacy: true, 369 }, 370 "anotherNs": { 371 Name: "anotherNs", 372 Version: "version", 373 Hash: []byte("hash"), 374 IsLegacy: false, 375 }, 376 }, 377 nil, 378 ) 379 380 installedChaincodeInfoProvider := &kvledgermock.ChaincodeInfoProvider{} 381 installedChaincodeInfoProvider.RetrieveChaincodeArtifactsReturns( 382 true, dbArtifactsBytes, nil, 383 ) 384 cceventmgmt.Initialize(installedChaincodeInfoProvider) 385 lgr, _, err := provider.CreateFromSnapshot(snapshotDir) 386 require.NoError(t, err) 387 388 require.Equal(t, 1, installedChaincodeInfoProvider.RetrieveChaincodeArtifactsCallCount()) 389 require.Equal(t, 390 &cceventmgmt.ChaincodeDefinition{ 391 Name: "ns", 392 Version: "version", 393 Hash: []byte("hash"), 394 }, 395 installedChaincodeInfoProvider.RetrieveChaincodeArtifactsArgsForCall(0), 396 ) 397 verifyIndexCreatedOnMarbleSize(lgr) 398 }) 399 400 t.Run("errors-propagation", func(t *testing.T) { 401 snapshotDir, couchDBConfig, provider := setup() 402 defer func() { 403 require.NoError(t, statecouchdb.DropApplicationDBs(couchDBConfig)) 404 }() 405 406 t.Run("deployedChaincodeInfoProvider-returns-error", func(t *testing.T) { 407 deployedCCInfoProvider := provider.initializer.DeployedChaincodeInfoProvider.(*mock.DeployedChaincodeInfoProvider) 408 deployedCCInfoProvider.AllChaincodesInfoReturns(nil, fmt.Errorf("error-retrieving-all-defined-chaincodes")) 409 410 installedChaincodeInfoProvider := &kvledgermock.ChaincodeInfoProvider{} 411 installedChaincodeInfoProvider.RetrieveChaincodeArtifactsReturns( 412 true, dbArtifactsBytes, nil, 413 ) 414 cceventmgmt.Initialize(installedChaincodeInfoProvider) 415 _, _, err := provider.CreateFromSnapshot(snapshotDir) 416 require.EqualError(t, err, "error while opening ledger: error while creating statdb indexes after bootstrapping from snapshot: error-retrieving-all-defined-chaincodes") 417 }) 418 419 t.Run("legacychaincodes-dbartifacts-retriever-returns-error", func(t *testing.T) { 420 deployedCCInfoProvider := provider.initializer.DeployedChaincodeInfoProvider.(*mock.DeployedChaincodeInfoProvider) 421 deployedCCInfoProvider.AllChaincodesInfoReturns( 422 map[string]*ledger.DeployedChaincodeInfo{ 423 "ns": { 424 Name: "ns", 425 Version: "version", 426 Hash: []byte("hash"), 427 IsLegacy: true, 428 }, 429 }, 430 nil, 431 ) 432 433 installedChaincodeInfoProvider := &kvledgermock.ChaincodeInfoProvider{} 434 installedChaincodeInfoProvider.RetrieveChaincodeArtifactsReturns(false, nil, fmt.Errorf("error-retrieving-db-artifacts")) 435 cceventmgmt.Initialize(installedChaincodeInfoProvider) 436 _, _, err := provider.CreateFromSnapshot(snapshotDir) 437 require.EqualError(t, err, "error while opening ledger: error while creating statdb indexes after bootstrapping from snapshot: error-retrieving-db-artifacts") 438 }) 439 440 t.Run("chaincodeLifecycleEventProvider-returns-error", func(t *testing.T) { 441 chaincodeLifecycleEventProvider := provider.initializer.ChaincodeLifecycleEventProvider.(*mock.ChaincodeLifecycleEventProvider) 442 chaincodeLifecycleEventProvider.RegisterListenerReturns(fmt.Errorf("error-calling-back")) 443 cceventmgmt.Initialize(nil) 444 _, _, err := provider.CreateFromSnapshot(snapshotDir) 445 require.EqualError(t, err, "error while opening ledger: error while creating statdb indexes after bootstrapping from snapshot: error-calling-back") 446 }) 447 }) 448 } 449 450 func TestSnapshotDirPaths(t *testing.T) { 451 require.Equal(t, "/peerFSPath/snapshotRootDir/temp", SnapshotsTempDirPath("/peerFSPath/snapshotRootDir")) 452 require.Equal(t, "/peerFSPath/snapshotRootDir/completed", CompletedSnapshotsPath("/peerFSPath/snapshotRootDir")) 453 require.Equal(t, "/peerFSPath/snapshotRootDir/completed/myLedger", SnapshotsDirForLedger("/peerFSPath/snapshotRootDir", "myLedger")) 454 require.Equal(t, "/peerFSPath/snapshotRootDir/completed/myLedger/2000", SnapshotDirForLedgerBlockNum("/peerFSPath/snapshotRootDir", "myLedger", 2000)) 455 } 456 457 func TestSnapshotDirPathsCreation(t *testing.T) { 458 conf, cleanup := testConfig(t) 459 defer cleanup() 460 provider := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{}) 461 defer func() { 462 provider.Close() 463 }() 464 465 inProgressSnapshotsPath := SnapshotsTempDirPath(conf.SnapshotsConfig.RootDir) 466 completedSnapshotsPath := CompletedSnapshotsPath(conf.SnapshotsConfig.RootDir) 467 468 // verify that upon first time start, kvledgerProvider creates an empty temp dir and an empty final dir for the snapshots 469 for _, dir := range [2]string{inProgressSnapshotsPath, completedSnapshotsPath} { 470 f, err := ioutil.ReadDir(dir) 471 require.NoError(t, err) 472 require.Len(t, f, 0) 473 } 474 475 // add a file in each of the above folders 476 for _, dir := range [2]string{inProgressSnapshotsPath, completedSnapshotsPath} { 477 err := ioutil.WriteFile(filepath.Join(dir, "testFile"), []byte("some junk data"), 0o644) 478 require.NoError(t, err) 479 f, err := ioutil.ReadDir(dir) 480 require.NoError(t, err) 481 require.Len(t, f, 1) 482 } 483 484 // verify that upon subsequent opening, kvledgerProvider removes any under-processing snapshots, 485 // potentially from a previous crash, from the temp dir but it does not remove any files from the final dir 486 provider.Close() 487 provider = testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{}) 488 f, err := ioutil.ReadDir(inProgressSnapshotsPath) 489 require.NoError(t, err) 490 require.Len(t, f, 0) 491 f, err = ioutil.ReadDir(completedSnapshotsPath) 492 require.NoError(t, err) 493 require.Len(t, f, 1) 494 } 495 496 func TestSnapshotsDirInitializingErrors(t *testing.T) { 497 initKVLedgerProvider := func(conf *ledger.Config) error { 498 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 499 require.NoError(t, err) 500 _, err = NewProvider( 501 &ledger.Initializer{ 502 DeployedChaincodeInfoProvider: &mock.DeployedChaincodeInfoProvider{}, 503 MetricsProvider: &disabled.Provider{}, 504 Config: conf, 505 HashProvider: cryptoProvider, 506 }, 507 ) 508 return err 509 } 510 511 t.Run("invalid-path", func(t *testing.T) { 512 conf, cleanup := testConfig(t) 513 defer cleanup() 514 conf.SnapshotsConfig.RootDir = "./a-relative-path" 515 err := initKVLedgerProvider(conf) 516 require.EqualError(t, err, "invalid path: ./a-relative-path. The path for the snapshot dir is expected to be an absolute path") 517 }) 518 519 t.Run("snapshots final dir creation returns error", func(t *testing.T) { 520 conf, cleanup := testConfig(t) 521 defer cleanup() 522 523 completedSnapshotsPath := CompletedSnapshotsPath(conf.SnapshotsConfig.RootDir) 524 require.NoError(t, os.MkdirAll(filepath.Dir(completedSnapshotsPath), 0o755)) 525 require.NoError(t, ioutil.WriteFile(completedSnapshotsPath, []byte("some data"), 0o644)) 526 err := initKVLedgerProvider(conf) 527 require.Error(t, err) 528 require.Contains(t, err.Error(), "while creating the dir: "+completedSnapshotsPath) 529 }) 530 } 531 532 func TestGenerateSnapshotErrors(t *testing.T) { 533 conf, cleanup := testConfig(t) 534 defer cleanup() 535 provider := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{}) 536 defer func() { 537 provider.Close() 538 }() 539 540 // create a ledger 541 _, genesisBlk := testutil.NewBlockGenerator(t, "testLedgerid", false) 542 lgr, err := provider.CreateFromGenesisBlock(genesisBlk) 543 require.NoError(t, err) 544 kvlgr := lgr.(*kvLedger) 545 546 closeAndReopenLedgerProvider := func() { 547 provider.Close() 548 provider = testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{}) 549 lgr, err = provider.Open("testLedgerid") 550 require.NoError(t, err) 551 kvlgr = lgr.(*kvLedger) 552 } 553 554 t.Run("snapshot tmp dir creation returns error", func(t *testing.T) { 555 closeAndReopenLedgerProvider() 556 require.NoError(t, os.RemoveAll( // remove the base tempdir so that the snapshot tempdir creation fails 557 SnapshotsTempDirPath(conf.SnapshotsConfig.RootDir), 558 )) 559 err := kvlgr.generateSnapshot() 560 require.Error(t, err) 561 require.Contains(t, err.Error(), "error while creating temp dir") 562 }) 563 564 t.Run("block store returns error", func(t *testing.T) { 565 closeAndReopenLedgerProvider() 566 provider.blkStoreProvider.Close() // close the blockstore provider to trigger the error 567 err := kvlgr.generateSnapshot() 568 require.Error(t, err) 569 errStackTrace := fmt.Sprintf("%+v", err) 570 require.Contains(t, errStackTrace, "internal leveldb error while obtaining db iterator") 571 require.Contains(t, errStackTrace, "github.com/hechain20/hechain/common/ledger/blkstorage") 572 }) 573 574 t.Run("config history mgr returns error", func(t *testing.T) { 575 closeAndReopenLedgerProvider() 576 provider.configHistoryMgr.Close() // close the configHistoryMgr to trigger the error 577 err := kvlgr.generateSnapshot() 578 require.Error(t, err) 579 errStackTrace := fmt.Sprintf("%+v", err) 580 require.Contains(t, errStackTrace, "internal leveldb error while obtaining db iterator") 581 require.Contains(t, errStackTrace, "github.com/hechain20/hechain/core/ledger/confighistory") 582 }) 583 584 t.Run("statedb returns error", func(t *testing.T) { 585 closeAndReopenLedgerProvider() 586 provider.dbProvider.Close() // close the dbProvider to trigger the error 587 err := kvlgr.generateSnapshot() 588 require.Error(t, err) 589 errStackTrace := fmt.Sprintf("%+v", err) 590 require.Contains(t, errStackTrace, "internal leveldb error while obtaining db iterator") 591 require.Contains(t, errStackTrace, "statedb/stateleveldb/stateleveldb.go") 592 }) 593 594 t.Run("renaming to the final snapshot dir returns error", func(t *testing.T) { 595 closeAndReopenLedgerProvider() 596 snapshotFinalDir := SnapshotDirForLedgerBlockNum(conf.SnapshotsConfig.RootDir, "testLedgerid", 0) 597 require.NoError(t, os.MkdirAll(snapshotFinalDir, 0o744)) 598 defer os.RemoveAll(snapshotFinalDir) 599 require.NoError(t, ioutil.WriteFile( // make a non-empty snapshotFinalDir to trigger failure on rename 600 filepath.Join(snapshotFinalDir, "dummyFile"), 601 []byte("dummy file"), 0o444), 602 ) 603 err := kvlgr.generateSnapshot() 604 require.Contains(t, err.Error(), "error while renaming dir") 605 }) 606 607 t.Run("deletes the temp folder upon error", func(t *testing.T) { 608 closeAndReopenLedgerProvider() 609 provider.blkStoreProvider.Close() // close the blockstore provider to trigger an error 610 err := kvlgr.generateSnapshot() 611 require.Error(t, err) 612 613 empty, err := fileutil.DirEmpty(SnapshotsTempDirPath(conf.SnapshotsConfig.RootDir)) 614 require.NoError(t, err) 615 require.True(t, empty) 616 }) 617 } 618 619 func testCreateLedgerFromSnapshotErrorPaths(t *testing.T, originalSnapshotDir string) { 620 var provider *Provider 621 var snapshotDirForTest string 622 var cleanup func() 623 624 var metadata *SnapshotMetadata 625 var signableMetadataFile string 626 var additionalMetadataFile string 627 628 init := func(t *testing.T) { 629 conf, cleanupFunc := testConfig(t) 630 // make a copy of originalSnapshotDir 631 snapshotDirForTest = filepath.Join(conf.RootFSPath, "snapshot") 632 require.NoError(t, os.MkdirAll(snapshotDirForTest, 0o700)) 633 files, err := ioutil.ReadDir(originalSnapshotDir) 634 require.NoError(t, err) 635 for _, f := range files { 636 content, err := ioutil.ReadFile(filepath.Join(originalSnapshotDir, f.Name())) 637 require.NoError(t, err) 638 err = ioutil.WriteFile(filepath.Join(snapshotDirForTest, f.Name()), content, 0o600) 639 require.NoError(t, err) 640 } 641 642 metadataJSONs, err := loadSnapshotMetadataJSONs(snapshotDirForTest) 643 require.NoError(t, err) 644 metadata, err = metadataJSONs.ToMetadata() 645 require.NoError(t, err) 646 647 signableMetadataFile = filepath.Join(snapshotDirForTest, SnapshotSignableMetadataFileName) 648 additionalMetadataFile = filepath.Join(snapshotDirForTest, snapshotAdditionalMetadataFileName) 649 650 provider = testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{}) 651 cleanup = func() { 652 provider.Close() 653 cleanupFunc() 654 } 655 } 656 657 overwriteModifiedSignableMetadata := func() { 658 signaleMetadataJSON, err := metadata.SnapshotSignableMetadata.ToJSON() 659 require.NoError(t, err) 660 require.NoError(t, ioutil.WriteFile(signableMetadataFile, signaleMetadataJSON, 0o600)) 661 662 metadata.snapshotAdditionalMetadata.SnapshotHashInHex = computeHashForTest(t, provider, signaleMetadataJSON) 663 additionalMetadataJSON, err := metadata.snapshotAdditionalMetadata.ToJSON() 664 require.NoError(t, err) 665 require.NoError(t, ioutil.WriteFile(additionalMetadataFile, additionalMetadataJSON, 0o600)) 666 } 667 668 overwriteDataFile := func(fileName string, content []byte) { 669 filePath := filepath.Join(snapshotDirForTest, fileName) 670 require.NoError(t, ioutil.WriteFile(filePath, content, 0o600)) 671 metadata.SnapshotSignableMetadata.FilesAndHashes[fileName] = computeHashForTest(t, provider, content) 672 overwriteModifiedSignableMetadata() 673 } 674 675 t.Run("singable-metadata-file-missing", func(t *testing.T) { 676 init(t) 677 defer cleanup() 678 679 require.NoError(t, os.Remove(filepath.Join(snapshotDirForTest, SnapshotSignableMetadataFileName))) 680 _, _, err := provider.CreateFromSnapshot(snapshotDirForTest) 681 require.EqualError(t, 682 err, 683 fmt.Sprintf( 684 "error while loading metadata: open %s/_snapshot_signable_metadata.json: no such file or directory", 685 snapshotDirForTest, 686 ), 687 ) 688 verifyLedgerDoesNotExist(t, provider, metadata.ChannelName) 689 }) 690 691 t.Run("additional-metadata-file-missing", func(t *testing.T) { 692 init(t) 693 defer cleanup() 694 695 require.NoError(t, os.Remove(filepath.Join(snapshotDirForTest, snapshotAdditionalMetadataFileName))) 696 _, _, err := provider.CreateFromSnapshot(snapshotDirForTest) 697 require.EqualError(t, 698 err, 699 fmt.Sprintf("error while loading metadata: open %s/_snapshot_additional_metadata.json: no such file or directory", snapshotDirForTest), 700 ) 701 verifyLedgerDoesNotExist(t, provider, metadata.ChannelName) 702 }) 703 704 t.Run("singable-metadata-file-invalid-json", func(t *testing.T) { 705 init(t) 706 defer cleanup() 707 708 require.NoError(t, ioutil.WriteFile(signableMetadataFile, []byte(""), 0o600)) 709 _, _, err := provider.CreateFromSnapshot(snapshotDirForTest) 710 require.EqualError(t, 711 err, 712 "error while unmarshalling metadata: error while unmarshalling signable metadata: unexpected end of JSON input", 713 ) 714 verifyLedgerDoesNotExist(t, provider, metadata.ChannelName) 715 }) 716 717 t.Run("additional-metadata-file-invalid-json", func(t *testing.T) { 718 init(t) 719 defer cleanup() 720 721 require.NoError(t, ioutil.WriteFile(additionalMetadataFile, []byte(""), 0o600)) 722 _, _, err := provider.CreateFromSnapshot(snapshotDirForTest) 723 require.EqualError(t, 724 err, 725 "error while unmarshalling metadata: error while unmarshalling additional metadata: unexpected end of JSON input", 726 ) 727 verifyLedgerDoesNotExist(t, provider, metadata.ChannelName) 728 }) 729 730 t.Run("snapshot-hash-mismatch", func(t *testing.T) { 731 init(t) 732 defer cleanup() 733 734 require.NoError(t, ioutil.WriteFile(signableMetadataFile, []byte("{}"), 0o600)) 735 _, _, err := provider.CreateFromSnapshot(snapshotDirForTest) 736 require.Contains(t, 737 err.Error(), 738 "error while verifying snapshot: hash mismatch for file [_snapshot_signable_metadata.json]", 739 ) 740 verifyLedgerDoesNotExist(t, provider, metadata.ChannelName) 741 }) 742 743 t.Run("datafile-missing", func(t *testing.T) { 744 init(t) 745 defer cleanup() 746 747 err := os.Remove(filepath.Join(snapshotDirForTest, "txids.data")) 748 require.NoError(t, err) 749 750 _, _, err = provider.CreateFromSnapshot(snapshotDirForTest) 751 require.EqualError(t, err, 752 fmt.Sprintf( 753 "error while verifying snapshot: open %s/txids.data: no such file or directory", 754 snapshotDirForTest, 755 ), 756 ) 757 verifyLedgerDoesNotExist(t, provider, metadata.ChannelName) 758 }) 759 760 t.Run("datafile-hash-mismatch", func(t *testing.T) { 761 init(t) 762 defer cleanup() 763 764 err := ioutil.WriteFile(filepath.Join(snapshotDirForTest, "txids.data"), []byte("random content"), 0o600) 765 require.NoError(t, err) 766 767 _, _, err = provider.CreateFromSnapshot(snapshotDirForTest) 768 require.Contains(t, err.Error(), "error while verifying snapshot: hash mismatch for file [txids.data]") 769 verifyLedgerDoesNotExist(t, provider, metadata.ChannelName) 770 }) 771 772 t.Run("hex-decoding-error-for-lastBlkHash", func(t *testing.T) { 773 init(t) 774 defer cleanup() 775 776 metadata.SnapshotSignableMetadata.LastBlockHashInHex = "invalid-hex" 777 overwriteModifiedSignableMetadata() 778 779 _, _, err := provider.CreateFromSnapshot(snapshotDirForTest) 780 require.Contains(t, err.Error(), "error while decoding last block hash") 781 verifyLedgerDoesNotExist(t, provider, metadata.ChannelName) 782 }) 783 784 t.Run("hex-decoding-error-for-previousBlkHash", func(t *testing.T) { 785 init(t) 786 defer cleanup() 787 788 metadata.SnapshotSignableMetadata.PreviousBlockHashInHex = "invalid-hex" 789 overwriteModifiedSignableMetadata() 790 791 _, _, err := provider.CreateFromSnapshot(snapshotDirForTest) 792 require.Contains(t, err.Error(), "error while decoding previous block hash") 793 verifyLedgerDoesNotExist(t, provider, metadata.ChannelName) 794 }) 795 796 t.Run("idStore-returns-error", func(t *testing.T) { 797 init(t) 798 defer cleanup() 799 800 provider.idStore.close() 801 _, _, err := provider.CreateFromSnapshot(snapshotDirForTest) 802 require.Contains(t, err.Error(), "error while creating ledger id") 803 }) 804 805 t.Run("blkstore-provider-returns-error", func(t *testing.T) { 806 init(t) 807 defer cleanup() 808 809 overwriteDataFile("txids.data", []byte("")) 810 _, _, err := provider.CreateFromSnapshot(snapshotDirForTest) 811 require.Contains(t, err.Error(), "error while importing data into block store") 812 verifyLedgerDoesNotExist(t, provider, metadata.ChannelName) 813 }) 814 815 t.Run("config-history-mgr-returns-error", func(t *testing.T) { 816 init(t) 817 defer cleanup() 818 819 overwriteDataFile("confighistory.data", []byte("")) 820 _, _, err := provider.CreateFromSnapshot(snapshotDirForTest) 821 require.Contains(t, err.Error(), "error while importing data into config history Mgr") 822 verifyLedgerDoesNotExist(t, provider, metadata.ChannelName) 823 }) 824 825 t.Run("statedb-provider-returns-error", func(t *testing.T) { 826 init(t) 827 defer cleanup() 828 829 overwriteDataFile("public_state.data", []byte("")) 830 _, _, err := provider.CreateFromSnapshot(snapshotDirForTest) 831 require.Contains(t, err.Error(), "error while importing data into state db") 832 verifyLedgerDoesNotExist(t, provider, metadata.ChannelName) 833 }) 834 835 t.Run("error-while-deleting-partially-created-ledger", func(t *testing.T) { 836 init(t) 837 defer cleanup() 838 839 provider.historydbProvider.Close() 840 841 _, _, err := provider.CreateFromSnapshot(snapshotDirForTest) 842 require.Contains(t, err.Error(), "error while preparing history db") 843 require.Contains(t, err.Error(), "error while deleting data from ledger") 844 verifyLedgerIDExists(t, provider, metadata.ChannelName, msgs.Status_UNDER_CONSTRUCTION) 845 }) 846 } 847 848 func computeHashForTest(t *testing.T, provider *Provider, content []byte) string { 849 hasher, err := provider.initializer.HashProvider.GetHash(snapshotHashOpts) 850 require.NoError(t, err) 851 _, err = hasher.Write(content) 852 require.NoError(t, err) 853 return hex.EncodeToString(hasher.Sum(nil)) 854 } 855 856 type expectedSnapshotOutput struct { 857 snapshotRootDir string 858 ledgerID string 859 lastBlockNumber uint64 860 lastBlockHash []byte 861 previousBlockHash []byte 862 lastCommitHash []byte 863 stateDBType string 864 expectedBinaryFiles []string 865 } 866 867 func verifySnapshotOutput( 868 t *testing.T, 869 o *expectedSnapshotOutput, 870 ) { 871 inProgressSnapshotsPath := SnapshotsTempDirPath(o.snapshotRootDir) 872 f, err := ioutil.ReadDir(inProgressSnapshotsPath) 873 require.NoError(t, err) 874 require.Len(t, f, 0) 875 876 snapshotDir := SnapshotDirForLedgerBlockNum(o.snapshotRootDir, o.ledgerID, o.lastBlockNumber) 877 files, err := ioutil.ReadDir(snapshotDir) 878 require.NoError(t, err) 879 require.Len(t, files, len(o.expectedBinaryFiles)+2) // + 2 JSON files 880 881 filesAndHashes := map[string]string{} 882 for _, f := range o.expectedBinaryFiles { 883 c, err := ioutil.ReadFile(filepath.Join(snapshotDir, f)) 884 require.NoError(t, err) 885 filesAndHashes[f] = hex.EncodeToString(util.ComputeSHA256(c)) 886 } 887 888 // verify the contents of the file snapshot_metadata.json 889 m := &SnapshotSignableMetadata{} 890 mJSON, err := ioutil.ReadFile(filepath.Join(snapshotDir, SnapshotSignableMetadataFileName)) 891 require.NoError(t, err) 892 require.NoError(t, json.Unmarshal(mJSON, m)) 893 894 previousBlockHashHex := "" 895 if o.previousBlockHash != nil { 896 previousBlockHashHex = hex.EncodeToString(o.previousBlockHash) 897 } 898 require.Equal(t, 899 &SnapshotSignableMetadata{ 900 ChannelName: o.ledgerID, 901 LastBlockNumber: o.lastBlockNumber, 902 LastBlockHashInHex: hex.EncodeToString(o.lastBlockHash), 903 PreviousBlockHashInHex: previousBlockHashHex, 904 StateDBType: o.stateDBType, 905 FilesAndHashes: filesAndHashes, 906 }, 907 m, 908 ) 909 910 // verify the contents of the file snapshot_metadata_hash.json 911 mh := &snapshotAdditionalMetadata{} 912 mhJSON, err := ioutil.ReadFile(filepath.Join(snapshotDir, snapshotAdditionalMetadataFileName)) 913 require.NoError(t, err) 914 require.NoError(t, json.Unmarshal(mhJSON, mh)) 915 require.Equal(t, 916 &snapshotAdditionalMetadata{ 917 SnapshotHashInHex: hex.EncodeToString(util.ComputeSHA256(mJSON)), 918 LastBlockCommitHashInHex: hex.EncodeToString(o.lastCommitHash), 919 }, 920 mh, 921 ) 922 } 923 924 func testCreateLedgerFromSnapshot(t *testing.T, snapshotDir string, expectedChannelID string) *kvLedger { 925 conf, cleanup := testConfig(t) 926 defer cleanup() 927 p := testutilNewProvider(conf, t, &mock.DeployedChaincodeInfoProvider{}) 928 destLedger, channelID, err := p.CreateFromSnapshot(snapshotDir) 929 require.NoError(t, err) 930 require.Equal(t, expectedChannelID, channelID) 931 return destLedger.(*kvLedger) 932 } 933 934 type expectedLegderState struct { 935 lastBlockNumber uint64 936 lastBlockHash []byte 937 previousBlockHash []byte 938 lastCommitHash []byte 939 namespace string 940 publicState map[string]string 941 collectionConfig map[uint64]*peer.CollectionConfigPackage 942 } 943 944 func verifyCreatedLedger(t *testing.T, 945 p *Provider, 946 l *kvLedger, 947 e *expectedLegderState, 948 ) { 949 verifyLedgerIDExists(t, p, l.ledgerID, msgs.Status_ACTIVE) 950 951 destBCInfo, err := l.GetBlockchainInfo() 952 require.NoError(t, err) 953 require.Equal(t, 954 &common.BlockchainInfo{ 955 Height: e.lastBlockNumber + 1, 956 CurrentBlockHash: e.lastBlockHash, 957 PreviousBlockHash: e.previousBlockHash, 958 BootstrappingSnapshotInfo: &common.BootstrappingSnapshotInfo{ 959 LastBlockInSnapshot: e.lastBlockNumber, 960 }, 961 }, 962 destBCInfo, 963 ) 964 965 statedbSavepoint, err := l.txmgr.GetLastSavepoint() 966 require.NoError(t, err) 967 require.Equal(t, version.NewHeight(e.lastBlockNumber, math.MaxUint64), statedbSavepoint) 968 969 historydbSavepoint, err := l.historyDB.GetLastSavepoint() 970 require.NoError(t, err) 971 require.Equal(t, version.NewHeight(e.lastBlockNumber, math.MaxUint64), historydbSavepoint) 972 973 qe, err := l.txmgr.NewQueryExecutor("dummyTxId") 974 require.NoError(t, err) 975 defer qe.Done() 976 for k, v := range e.publicState { 977 val, err := qe.GetState(e.namespace, k) 978 require.NoError(t, err) 979 require.Equal(t, v, string(val)) 980 } 981 for committingBlock, collConfigPkg := range e.collectionConfig { 982 collConfigInfo, err := l.configHistoryRetriever.MostRecentCollectionConfigBelow(committingBlock+1, e.namespace) 983 require.NoError(t, err) 984 require.Equal(t, committingBlock, collConfigInfo.CommittingBlockNum) 985 require.True(t, proto.Equal(collConfigPkg, collConfigInfo.CollectionConfig)) 986 } 987 } 988 989 func addDummyEntryInCollectionConfigHistory( 990 t *testing.T, 991 provider *Provider, 992 ledgerID string, 993 namespace string, 994 committingBlockNumber uint64, 995 collectionConfig []*peer.StaticCollectionConfig, 996 ) { 997 configHistory := &confighistorytest.Mgr{ 998 Mgr: provider.configHistoryMgr, 999 MockCCInfoProvider: provider.initializer.DeployedChaincodeInfoProvider.(*mock.DeployedChaincodeInfoProvider), 1000 } 1001 require.NoError(t, 1002 configHistory.Setup(ledgerID, namespace, 1003 map[uint64][]*peer.StaticCollectionConfig{ 1004 committingBlockNumber: collectionConfig, 1005 }, 1006 ), 1007 ) 1008 } 1009 1010 func TestMostRecentCollectionConfigFetcher(t *testing.T) { 1011 conf, cleanup := testConfig(t) 1012 defer cleanup() 1013 1014 ledgerID := "test-ledger" 1015 chaincodeName := "test-chaincode" 1016 1017 implicitCollectionName := implicitcollection.NameForOrg("test-org") 1018 implicitCollection := &peer.StaticCollectionConfig{ 1019 Name: implicitCollectionName, 1020 } 1021 mockDeployedCCInfoProvider := &mock.DeployedChaincodeInfoProvider{} 1022 mockDeployedCCInfoProvider.GenerateImplicitCollectionForOrgReturns(implicitCollection) 1023 1024 provider := testutilNewProvider(conf, t, mockDeployedCCInfoProvider) 1025 explicitCollectionName := "explicit-coll" 1026 explicitCollection := &peer.StaticCollectionConfig{ 1027 Name: explicitCollectionName, 1028 } 1029 testutilPersistExplicitCollectionConfig( 1030 t, 1031 provider, 1032 mockDeployedCCInfoProvider, 1033 ledgerID, 1034 chaincodeName, 1035 testutilCollConfigPkg( 1036 []*peer.StaticCollectionConfig{ 1037 explicitCollection, 1038 }, 1039 ), 1040 10, 1041 ) 1042 1043 fetcher := &mostRecentCollectionConfigFetcher{ 1044 DeployedChaincodeInfoProvider: mockDeployedCCInfoProvider, 1045 Retriever: provider.configHistoryMgr.GetRetriever(ledgerID), 1046 } 1047 1048 testcases := []struct { 1049 name string 1050 lookupCollectionName string 1051 expectedOutput *peer.StaticCollectionConfig 1052 }{ 1053 { 1054 name: "lookup-implicit-collection", 1055 lookupCollectionName: implicitCollectionName, 1056 expectedOutput: implicitCollection, 1057 }, 1058 1059 { 1060 name: "lookup-explicit-collection", 1061 lookupCollectionName: explicitCollectionName, 1062 expectedOutput: explicitCollection, 1063 }, 1064 1065 { 1066 name: "lookup-non-existing-explicit-collection", 1067 lookupCollectionName: "non-existing-explicit-collection", 1068 expectedOutput: nil, 1069 }, 1070 } 1071 1072 for _, testcase := range testcases { 1073 t.Run( 1074 testcase.name, 1075 func(t *testing.T) { 1076 config, err := fetcher.CollectionInfo(chaincodeName, testcase.lookupCollectionName) 1077 require.NoError(t, err) 1078 require.True(t, proto.Equal(testcase.expectedOutput, config)) 1079 }, 1080 ) 1081 } 1082 1083 t.Run("explicit-collection-lookup-causes-error", func(t *testing.T) { 1084 provider.configHistoryMgr.Close() 1085 _, err := fetcher.CollectionInfo(chaincodeName, explicitCollectionName) 1086 require.Contains(t, err.Error(), "error while fetching most recent collection config") 1087 }) 1088 }