github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/ledgermgmt/ledger_mgmt_test.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package ledgermgmt 8 9 import ( 10 "fmt" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 "testing" 15 "time" 16 17 "github.com/hechain20/hechain/bccsp/sw" 18 "github.com/hechain20/hechain/common/configtx/test" 19 "github.com/hechain20/hechain/common/ledger/testutil" 20 "github.com/hechain20/hechain/common/metrics/disabled" 21 "github.com/hechain20/hechain/core/ledger" 22 "github.com/hechain20/hechain/core/ledger/cceventmgmt" 23 "github.com/hechain20/hechain/core/ledger/kvledger" 24 "github.com/hechain20/hechain/core/ledger/mock" 25 "github.com/hechain20/hechain/protoutil" 26 "github.com/hyperledger/fabric-protos-go/common" 27 "github.com/stretchr/testify/require" 28 ) 29 30 func TestLedgerMgmt(t *testing.T) { 31 initializer, ledgerMgr, cleanup := setup(t, "ledgermgmt") 32 defer cleanup() 33 34 numLedgers := 10 35 ledgers := make([]ledger.PeerLedger, numLedgers) 36 for i := 0; i < numLedgers; i++ { 37 cid := constructTestLedgerID(i) 38 gb, _ := test.MakeGenesisBlock(cid) 39 l, err := ledgerMgr.CreateLedger(cid, gb) 40 require.NoError(t, err) 41 ledgers[i] = l 42 } 43 44 ids, _ := ledgerMgr.GetLedgerIDs() 45 require.Len(t, ids, numLedgers) 46 for i := 0; i < numLedgers; i++ { 47 require.Equal(t, constructTestLedgerID(i), ids[i]) 48 } 49 50 ledgerID := constructTestLedgerID(2) 51 t.Logf("Ledger selected for test = %s", ledgerID) 52 _, err := ledgerMgr.OpenLedger(ledgerID) 53 require.Equal(t, ErrLedgerAlreadyOpened, err) 54 55 l := ledgers[2] 56 l.Close() 57 // attempt to close the same ledger twice and ensure it doesn't panic 58 require.NotPanics(t, l.Close) 59 60 _, err = ledgerMgr.OpenLedger(ledgerID) 61 require.NoError(t, err) 62 63 _, err = ledgerMgr.OpenLedger(ledgerID) 64 require.Equal(t, ErrLedgerAlreadyOpened, err) 65 // close all opened ledgers and ledger mgmt 66 ledgerMgr.Close() 67 68 // Recreate LedgerMgr with existing ledgers 69 ledgerMgr = NewLedgerMgr(initializer) 70 _, err = ledgerMgr.OpenLedger(ledgerID) 71 require.NoError(t, err) 72 ledgerMgr.Close() 73 } 74 75 // TestCreateLedgerFromSnapshot first creates a ledger using a genesis block and generates a snapshot. 76 // After it, it tests creating ledger from the snapshot. 77 func TestCreateLedgerFromSnapshot(t *testing.T) { 78 initializer, lgrMgr, cleanup := setup(t, "createledgerfromsnapshot") 79 defer cleanup() 80 81 channelID := "testcreatefromsnapshot" 82 snapshotDir, gb := generateSnapshot(t, lgrMgr, initializer, channelID) 83 84 t.Run("create_ledger_from_snapshot_internal", func(t *testing.T) { 85 _, ledgerMgr, cleanup := setup(t, "createledgerfromsnapshot_internal") 86 defer cleanup() 87 88 l, _, err := ledgerMgr.createFromSnapshot(snapshotDir) 89 require.NoError(t, err) 90 bcInfo, _ := l.GetBlockchainInfo() 91 require.Equal(t, &common.BlockchainInfo{ 92 Height: 1, 93 CurrentBlockHash: protoutil.BlockHeaderHash(gb.Header), 94 PreviousBlockHash: nil, 95 BootstrappingSnapshotInfo: &common.BootstrappingSnapshotInfo{ 96 LastBlockInSnapshot: 0, 97 }, 98 }, bcInfo) 99 }) 100 101 t.Run("create_ledger_from_snapshot_async", func(t *testing.T) { 102 _, ledgerMgr, cleanup := setup(t, "createledgerfromsnapshot_async") 103 defer cleanup() 104 105 callbackCounter := 0 106 callback := func(l ledger.PeerLedger, cid string) { callbackCounter++ } 107 108 require.NoError(t, ledgerMgr.CreateLedgerFromSnapshot(snapshotDir, callback)) 109 110 ledgerCreated := func() bool { 111 status := ledgerMgr.JoinBySnapshotStatus() 112 return !status.InProgress && status.BootstrappingSnapshotDir == "" 113 } 114 115 require.Eventually(t, ledgerCreated, time.Minute, time.Second) 116 require.Equal(t, 1, callbackCounter) 117 118 ledgerids, err := ledgerMgr.GetLedgerIDs() 119 require.NoError(t, err) 120 require.Equal(t, ledgerids, []string{channelID}) 121 }) 122 123 t.Run("create_existing_ledger_returns_error", func(t *testing.T) { 124 // create the ledger from snapshot under the same rootdir should return error because the ledger already exists 125 _, _, err := lgrMgr.createFromSnapshot(snapshotDir) 126 require.EqualError(t, err, "error while creating ledger id: ledger [testcreatefromsnapshot] already exists with state [ACTIVE]") 127 }) 128 129 t.Run("create_ledger_from_nonexist_or_empty_dir_returns_error", func(t *testing.T) { 130 testDir, err := ioutil.TempDir("", "invalidsnapshotdir") 131 require.NoError(t, err) 132 defer os.RemoveAll(testDir) 133 134 nonExistDir := filepath.Join(testDir, "nonexistdir") 135 require.EqualError(t, lgrMgr.CreateLedgerFromSnapshot(nonExistDir, nil), 136 fmt.Sprintf("error opening dir [%s]: open %s: no such file or directory", nonExistDir, nonExistDir)) 137 138 require.EqualError(t, lgrMgr.CreateLedgerFromSnapshot(testDir, nil), 139 fmt.Sprintf("snapshot dir %s is empty", testDir)) 140 }) 141 142 t.Run("callback_func_is_not_called_if_create_ledger_from_snapshot_failed", func(t *testing.T) { 143 initializer, ledgerMgr, cleanup := setup(t, "callbackfuncisnotcalled") 144 defer cleanup() 145 146 // copy snapshotDir to a new dir and remove a metadata file so that kvledger.CreateFromSnapshot will fail 147 require.NoError(t, os.MkdirAll(initializer.Config.SnapshotsConfig.RootDir, 0o755)) 148 require.NoError(t, testutil.CopyDir(snapshotDir, initializer.Config.SnapshotsConfig.RootDir, false)) 149 newSnapshotDir := filepath.Join(initializer.Config.SnapshotsConfig.RootDir, "0") 150 require.NoError(t, os.Remove(filepath.Join(newSnapshotDir, "_snapshot_signable_metadata.json"))) 151 152 callbackCounter := 0 153 callback := func(l ledger.PeerLedger, cid string) { callbackCounter++ } 154 155 require.NoError(t, ledgerMgr.CreateLedgerFromSnapshot(newSnapshotDir, callback)) 156 157 // wait until CreateFromSnapshot is done 158 ledgerCreated := func() bool { 159 status := ledgerMgr.JoinBySnapshotStatus() 160 return !status.InProgress && status.BootstrappingSnapshotDir == "" 161 } 162 require.Eventually(t, ledgerCreated, time.Minute, time.Second) 163 164 // callback should not be called and ledger should not be generated 165 require.Equal(t, 0, callbackCounter) 166 167 ids, err := ledgerMgr.GetLedgerIDs() 168 require.NoError(t, err) 169 require.Equal(t, 0, len(ids)) 170 }) 171 } 172 173 func TestConcurrentCreateLedgerFromGB(t *testing.T) { 174 _, ledgerMgr, cleanup := setup(t, "concurrentcreateledgerfromgb") 175 defer cleanup() 176 177 var err error 178 gbs := make([]*common.Block, 0, 5) 179 for i := 0; i < len(gbs); i++ { 180 gbs[i], err = test.MakeGenesisBlock(fmt.Sprintf("l%d", i)) 181 require.NoError(t, err) 182 } 183 184 // verify CreateLedger (from genesisblock) can be called concurrently 185 for i := 0; i < len(gbs); i++ { 186 gb := gbs[i] 187 ledgerID := fmt.Sprintf("l%d", i) 188 go func() { 189 _, err := ledgerMgr.CreateLedger(ledgerID, gb) 190 require.NoError(t, err) 191 }() 192 } 193 194 ledgersGenerated := func() bool { 195 ledgerIds, err := ledgerMgr.GetLedgerIDs() 196 require.NoError(t, err) 197 return len(ledgerIds) == len(gbs) 198 } 199 require.Eventually(t, ledgersGenerated, time.Minute, time.Second) 200 } 201 202 func TestConcurrentCreateLedgerFromSnapshot(t *testing.T) { 203 initializer, ledgerMgr, cleanup := setup(t, "concurrentcreateledgerfromsnapshot") 204 defer cleanup() 205 206 // generate 2 snapshots for 2 channels 207 channelID1 := "testcreatefromsnapshot1" 208 snapshotDir1, _ := generateSnapshot(t, ledgerMgr, initializer, channelID1) 209 channelID2 := "testcreatefromsnapshot2" 210 snapshotDir2, _ := generateSnapshot(t, ledgerMgr, initializer, channelID2) 211 212 ledgerMgr.Close() 213 214 // create a new ledger mgr to import snapshot 215 _, ledgerMgr2, cleanup2 := setup(t, "concurrentcreateledgerfromsnapshot2") 216 defer cleanup2() 217 218 // use a channel to keep the callback func waiting so that we can test concurrent CreateLedger/CreateLedgerBySnapshot calls 219 waitCh := make(chan struct{}) 220 callback := func(l ledger.PeerLedger, cid string) { 221 <-waitCh 222 } 223 require.NoError(t, ledgerMgr2.CreateLedgerFromSnapshot(snapshotDir1, callback)) 224 225 // concurrent CreateLedger call should fail 226 channelID3 := "ledgerfromgb" 227 gb, err := test.MakeGenesisBlock(channelID3) 228 require.NoError(t, err) 229 _, err = ledgerMgr2.CreateLedger(channelID3, gb) 230 require.EqualError(t, err, fmt.Sprintf("a ledger is being created from a snapshot at %s. Call ledger creation again after it is done.", snapshotDir1)) 231 232 // concurrent CreateLedgerBySnapshot call should fail 233 err = ledgerMgr2.CreateLedgerFromSnapshot(snapshotDir2, callback) 234 require.EqualError(t, err, fmt.Sprintf("a ledger is being created from a snapshot at %s. Call ledger creation again after it is done.", snapshotDir1)) 235 236 waitCh <- struct{}{} 237 ledgerCreated := func() bool { 238 status := ledgerMgr.JoinBySnapshotStatus() 239 return !status.InProgress && status.BootstrappingSnapshotDir == "" 240 } 241 require.Eventually(t, ledgerCreated, time.Minute, time.Second) 242 243 ledgerIDs, err := ledgerMgr2.GetLedgerIDs() 244 require.NoError(t, err) 245 require.Equal(t, ledgerIDs, []string{channelID1}) 246 247 // CreateLedger should work after the previous CreateLedgerFromSnapshot is done 248 _, err = ledgerMgr2.CreateLedger(channelID3, gb) 249 require.NoError(t, err, "creating ledger for %s should have succeeded", channelID3) 250 251 // CreateLedgerFromSnapshot should work after the previous CreateLedgerFromSnapshot is done 252 callback = func(l ledger.PeerLedger, cid string) {} 253 require.NoError(t, ledgerMgr2.CreateLedgerFromSnapshot(snapshotDir2, callback)) 254 255 // wait until ledger is created from snapshotDir2 256 ledgerCreated = func() bool { 257 status := ledgerMgr2.JoinBySnapshotStatus() 258 return !status.InProgress && status.BootstrappingSnapshotDir == "" 259 } 260 require.Eventually(t, ledgerCreated, time.Minute, time.Second) 261 262 ledgerIDs, err = ledgerMgr2.GetLedgerIDs() 263 require.NoError(t, err) 264 require.ElementsMatch(t, ledgerIDs, []string{channelID1, channelID2, channelID3}) 265 } 266 267 func TestChaincodeInfoProvider(t *testing.T) { 268 _, ledgerMgr, cleanup := setup(t, "chaincodeinfoprovider") 269 defer cleanup() 270 271 gb, _ := test.MakeGenesisBlock("ledger1") 272 _, err := ledgerMgr.CreateLedger("ledger1", gb) 273 require.NoError(t, err) 274 275 mockDeployedCCInfoProvider := &mock.DeployedChaincodeInfoProvider{} 276 mockDeployedCCInfoProvider.ChaincodeInfoStub = func(channelName, ccName string, qe ledger.SimpleQueryExecutor) (*ledger.DeployedChaincodeInfo, error) { 277 return constructTestCCInfo(ccName, ccName, ccName), nil 278 } 279 280 ccInfoProvider := &chaincodeInfoProviderImpl{ 281 ledgerMgr, 282 mockDeployedCCInfoProvider, 283 } 284 _, err = ccInfoProvider.GetDeployedChaincodeInfo("ledger2", constructTestCCDef("cc2", "1.0", "cc2Hash")) 285 t.Logf("Expected error received = %s", err) 286 require.Error(t, err) 287 288 ccInfo, err := ccInfoProvider.GetDeployedChaincodeInfo("ledger1", constructTestCCDef("cc1", "non-matching-version", "cc1")) 289 require.NoError(t, err) 290 require.Nil(t, ccInfo) 291 292 ccInfo, err = ccInfoProvider.GetDeployedChaincodeInfo("ledger1", constructTestCCDef("cc1", "cc1", "non-matching-hash")) 293 require.NoError(t, err) 294 require.Nil(t, ccInfo) 295 296 ccInfo, err = ccInfoProvider.GetDeployedChaincodeInfo("ledger1", constructTestCCDef("cc1", "cc1", "cc1")) 297 require.NoError(t, err) 298 require.Equal(t, constructTestCCInfo("cc1", "cc1", "cc1"), ccInfo) 299 } 300 301 func setup(t *testing.T, basename string) (*Initializer, *LedgerMgr, func()) { 302 testDir, err := ioutil.TempDir("", basename) 303 require.NoError(t, err) 304 initializer, err := constructDefaultInitializer(testDir) 305 require.NoError(t, err) 306 ledgerMgr := NewLedgerMgr(initializer) 307 cleanup := func() { 308 ledgerMgr.Close() 309 os.Remove(testDir) 310 } 311 return initializer, ledgerMgr, cleanup 312 } 313 314 func constructDefaultInitializer(testDir string) (*Initializer, error) { 315 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 316 if err != nil { 317 return nil, err 318 } 319 return &Initializer{ 320 Config: &ledger.Config{ 321 RootFSPath: testDir, 322 StateDBConfig: &ledger.StateDBConfig{}, 323 PrivateDataConfig: &ledger.PrivateDataConfig{ 324 MaxBatchSize: 5000, 325 BatchesInterval: 1000, 326 PurgeInterval: 100, 327 }, 328 HistoryDBConfig: &ledger.HistoryDBConfig{ 329 Enabled: true, 330 }, 331 SnapshotsConfig: &ledger.SnapshotsConfig{ 332 RootDir: filepath.Join(testDir, "snapshots"), 333 }, 334 }, 335 336 MetricsProvider: &disabled.Provider{}, 337 DeployedChaincodeInfoProvider: &mock.DeployedChaincodeInfoProvider{}, 338 HashProvider: cryptoProvider, 339 }, nil 340 } 341 342 func constructTestLedgerID(i int) string { 343 return fmt.Sprintf("ledger_%06d", i) 344 } 345 346 func constructTestCCInfo(ccName, version, hash string) *ledger.DeployedChaincodeInfo { 347 return &ledger.DeployedChaincodeInfo{ 348 Name: ccName, 349 Hash: []byte(hash), 350 Version: version, 351 } 352 } 353 354 func constructTestCCDef(ccName, version, hash string) *cceventmgmt.ChaincodeDefinition { 355 return &cceventmgmt.ChaincodeDefinition{ 356 Name: ccName, 357 Hash: []byte(hash), 358 Version: version, 359 } 360 } 361 362 // generateSnapshot creates a ledger with genesis block and generates a snapshot when the ledger only has genesisblock 363 func generateSnapshot(t *testing.T, ledgerMgr *LedgerMgr, initializer *Initializer, channelID string) (string, *common.Block) { 364 _, gb := testutil.NewBlockGenerator(t, channelID, false) 365 l, err := ledgerMgr.CreateLedger(channelID, gb) 366 require.NoError(t, err) 367 368 require.NoError(t, l.SubmitSnapshotRequest(0)) 369 370 snapshotDir := kvledger.SnapshotDirForLedgerBlockNum(initializer.Config.SnapshotsConfig.RootDir, channelID, 0) 371 snapshotGenerated := func() bool { 372 pendingRequests, err := l.PendingSnapshotRequests() 373 require.NoError(t, err) 374 return len(pendingRequests) == 0 375 } 376 require.Eventually(t, snapshotGenerated, 30*time.Second, 100*time.Millisecond) 377 378 return snapshotDir, gb 379 }