github.com/kaituanwang/hyperledger@v2.0.1+incompatible/core/ledger/ledgerstorage/store_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package ledgerstorage 8 9 import ( 10 "io/ioutil" 11 "os" 12 "path/filepath" 13 "testing" 14 15 "github.com/golang/protobuf/proto" 16 "github.com/hyperledger/fabric-protos-go/common" 17 "github.com/hyperledger/fabric-protos-go/ledger/rwset" 18 pb "github.com/hyperledger/fabric-protos-go/peer" 19 "github.com/hyperledger/fabric/common/flogging" 20 "github.com/hyperledger/fabric/common/ledger/blkstorage" 21 "github.com/hyperledger/fabric/common/ledger/blkstorage/fsblkstorage" 22 "github.com/hyperledger/fabric/common/ledger/testutil" 23 "github.com/hyperledger/fabric/common/metrics/disabled" 24 "github.com/hyperledger/fabric/core/ledger" 25 "github.com/hyperledger/fabric/core/ledger/pvtdatapolicy" 26 btltestutil "github.com/hyperledger/fabric/core/ledger/pvtdatapolicy/testutil" 27 "github.com/hyperledger/fabric/core/ledger/pvtdatastorage" 28 lutil "github.com/hyperledger/fabric/core/ledger/util" 29 "github.com/stretchr/testify/assert" 30 ) 31 32 var metricsProvider = &disabled.Provider{} 33 34 func TestMain(m *testing.M) { 35 flogging.ActivateSpec("ledgerstorage,pvtdatastorage=debug") 36 os.Exit(m.Run()) 37 } 38 39 func TestStore(t *testing.T) { 40 storeDir, err := ioutil.TempDir("", "lstore") 41 if err != nil { 42 t.Fatalf("Failed to create ledger storage directory: %s", err) 43 } 44 defer os.RemoveAll(storeDir) 45 conf := buildPrivateDataConfig(storeDir) 46 blockStoreDir := filepath.Join(storeDir, "chains") 47 provider, err := NewProvider(blockStoreDir, conf, metricsProvider) 48 assert.NoError(t, err) 49 defer provider.Close() 50 store, err := provider.Open("testLedger") 51 store.Init(btlPolicyForSampleData()) 52 defer store.Shutdown() 53 54 assert.NoError(t, err) 55 sampleData := sampleDataWithPvtdataForSelectiveTx(t) 56 for _, sampleDatum := range sampleData { 57 assert.NoError(t, store.CommitWithPvtData(sampleDatum)) 58 } 59 60 // block 1 has no pvt data 61 pvtdata, err := store.GetPvtDataByNum(1, nil) 62 assert.NoError(t, err) 63 assert.Nil(t, pvtdata) 64 65 // block 4 has no pvt data 66 pvtdata, err = store.GetPvtDataByNum(4, nil) 67 assert.NoError(t, err) 68 assert.Nil(t, pvtdata) 69 70 // block 2 has pvt data for tx 3, 5 and 6. Though the tx 6 71 // is marked as invalid in the block, the pvtData should 72 // have been stored 73 pvtdata, err = store.GetPvtDataByNum(2, nil) 74 assert.NoError(t, err) 75 assert.Equal(t, 3, len(pvtdata)) 76 assert.Equal(t, uint64(3), pvtdata[0].SeqInBlock) 77 assert.Equal(t, uint64(5), pvtdata[1].SeqInBlock) 78 assert.Equal(t, uint64(6), pvtdata[2].SeqInBlock) 79 80 // block 3 has pvt data for tx 4 and 6 only 81 pvtdata, err = store.GetPvtDataByNum(3, nil) 82 assert.NoError(t, err) 83 assert.Equal(t, 2, len(pvtdata)) 84 assert.Equal(t, uint64(4), pvtdata[0].SeqInBlock) 85 assert.Equal(t, uint64(6), pvtdata[1].SeqInBlock) 86 87 blockAndPvtdata, err := store.GetPvtDataAndBlockByNum(2, nil) 88 assert.NoError(t, err) 89 assert.True(t, proto.Equal(sampleData[2].Block, blockAndPvtdata.Block)) 90 91 blockAndPvtdata, err = store.GetPvtDataAndBlockByNum(3, nil) 92 assert.NoError(t, err) 93 assert.True(t, proto.Equal(sampleData[3].Block, blockAndPvtdata.Block)) 94 95 // pvt data retrieval for block 3 with filter should return filtered pvtdata 96 filter := ledger.NewPvtNsCollFilter() 97 filter.Add("ns-1", "coll-1") 98 blockAndPvtdata, err = store.GetPvtDataAndBlockByNum(3, filter) 99 assert.NoError(t, err) 100 assert.Equal(t, sampleData[3].Block, blockAndPvtdata.Block) 101 // two transactions should be present 102 assert.Equal(t, 2, len(blockAndPvtdata.PvtData)) 103 // both tran number 4 and 6 should have only one collection because of filter 104 assert.Equal(t, 1, len(blockAndPvtdata.PvtData[4].WriteSet.NsPvtRwset)) 105 assert.Equal(t, 1, len(blockAndPvtdata.PvtData[6].WriteSet.NsPvtRwset)) 106 // any other transaction entry should be nil 107 assert.Nil(t, blockAndPvtdata.PvtData[2]) 108 109 // test missing data retrieval in the presence of invalid tx. Block 5 had 110 // missing data (for tx4 and tx5). Though tx5 was marked as invalid tx, 111 // both tx4 and tx5 missing data should be returned 112 expectedMissingDataInfo := make(ledger.MissingPvtDataInfo) 113 expectedMissingDataInfo.Add(5, 4, "ns-4", "coll-4") 114 expectedMissingDataInfo.Add(5, 5, "ns-5", "coll-5") 115 missingDataInfo, err := store.GetMissingPvtDataInfoForMostRecentBlocks(1) 116 assert.NoError(t, err) 117 assert.Equal(t, expectedMissingDataInfo, missingDataInfo) 118 } 119 120 func TestStoreWithExistingBlockchain(t *testing.T) { 121 testLedgerid := "test-ledger" 122 storeDir, err := ioutil.TempDir("", "lstore") 123 if err != nil { 124 t.Fatalf("Failed to create ledger storage directory: %s", err) 125 } 126 defer os.RemoveAll(storeDir) 127 128 // Construct a block storage 129 attrsToIndex := []blkstorage.IndexableAttr{ 130 blkstorage.IndexableAttrBlockHash, 131 blkstorage.IndexableAttrBlockNum, 132 blkstorage.IndexableAttrTxID, 133 blkstorage.IndexableAttrBlockNumTranNum, 134 } 135 indexConfig := &blkstorage.IndexConfig{AttrsToIndex: attrsToIndex} 136 blockStoreProvider, err := fsblkstorage.NewProvider( 137 fsblkstorage.NewConf(filepath.Join(storeDir, "chains"), maxBlockFileSize), 138 indexConfig, 139 metricsProvider, 140 ) 141 assert.NoError(t, err) 142 blkStore, err := blockStoreProvider.OpenBlockStore(testLedgerid) 143 assert.NoError(t, err) 144 testBlocks := testutil.ConstructTestBlocks(t, 10) 145 146 existingBlocks := testBlocks[0:9] 147 blockToAdd := testBlocks[9:][0] 148 149 // Add existingBlocks to the block storage directly without involving pvtdata store and close the block storage 150 for _, blk := range existingBlocks { 151 assert.NoError(t, blkStore.AddBlock(blk)) 152 } 153 blockStoreProvider.Close() 154 155 // Simulating the upgrade from 1.0 situation: 156 // Open the ledger storage - pvtdata store is opened for the first time with an existing block storage 157 conf := buildPrivateDataConfig(storeDir) 158 blockStoreDir := filepath.Join(storeDir, "chains") 159 provider, err := NewProvider(blockStoreDir, conf, metricsProvider) 160 assert.NoError(t, err) 161 defer provider.Close() 162 store, err := provider.Open(testLedgerid) 163 assert.NoError(t, err) 164 store.Init(btlPolicyForSampleData()) 165 defer store.Shutdown() 166 167 // test that pvtdata store is updated with info from existing block storage 168 pvtdataBlockHt, err := store.pvtdataStore.LastCommittedBlockHeight() 169 assert.NoError(t, err) 170 assert.Equal(t, uint64(9), pvtdataBlockHt) 171 172 // Add one more block with ovtdata associated with one of the trans and commit in the normal course 173 pvtdata := samplePvtData(t, []uint64{0}) 174 assert.NoError(t, store.CommitWithPvtData(&ledger.BlockAndPvtData{Block: blockToAdd, PvtData: pvtdata})) 175 pvtdataBlockHt, err = store.pvtdataStore.LastCommittedBlockHeight() 176 assert.NoError(t, err) 177 assert.Equal(t, uint64(10), pvtdataBlockHt) 178 } 179 180 func TestCrashAfterPvtdataStoreCommit(t *testing.T) { 181 storeDir, err := ioutil.TempDir("", "lstore") 182 if err != nil { 183 t.Fatalf("Failed to create ledger storage directory: %s", err) 184 } 185 defer os.RemoveAll(storeDir) 186 conf := buildPrivateDataConfig(storeDir) 187 blockStoreDir := filepath.Join(storeDir, "chains") 188 provider, err := NewProvider(blockStoreDir, conf, metricsProvider) 189 assert.NoError(t, err) 190 defer provider.Close() 191 store, err := provider.Open("testLedger") 192 store.Init(btlPolicyForSampleData()) 193 defer store.Shutdown() 194 assert.NoError(t, err) 195 196 sampleData := sampleDataWithPvtdataForAllTxs(t) 197 dataBeforeCrash := sampleData[0:3] 198 dataAtCrash := sampleData[3] 199 200 for _, sampleDatum := range dataBeforeCrash { 201 assert.NoError(t, store.CommitWithPvtData(sampleDatum)) 202 } 203 blockNumAtCrash := dataAtCrash.Block.Header.Number 204 var pvtdataAtCrash []*ledger.TxPvtData 205 for _, p := range dataAtCrash.PvtData { 206 pvtdataAtCrash = append(pvtdataAtCrash, p) 207 } 208 // call Commit on pvt data store and mimic a crash before committing the block to block store 209 store.pvtdataStore.Commit(blockNumAtCrash, pvtdataAtCrash, nil) 210 store.Shutdown() 211 provider.Close() 212 provider, err = NewProvider(blockStoreDir, conf, metricsProvider) 213 assert.NoError(t, err) 214 store, err = provider.Open("testLedger") 215 assert.NoError(t, err) 216 store.Init(btlPolicyForSampleData()) 217 218 // When starting the storage after a crash, we should be able to fetch the pvtData from pvtStore 219 testVerifyPvtData(t, store, blockNumAtCrash, dataAtCrash.PvtData) 220 bcInfo, err := store.GetBlockchainInfo() 221 assert.NoError(t, err) 222 assert.Equal(t, blockNumAtCrash, bcInfo.Height) 223 224 // we should be able to write the last block again 225 // to ensure that the pvtdataStore is not updated, we send a different pvtData for 226 // the same block such that we can retrieve the pvtData and compare. 227 expectedPvtData := dataAtCrash.PvtData 228 dataAtCrash.PvtData = make(ledger.TxPvtDataMap) 229 dataAtCrash.PvtData[0] = &ledger.TxPvtData{ 230 SeqInBlock: 0, 231 WriteSet: &rwset.TxPvtReadWriteSet{ 232 NsPvtRwset: []*rwset.NsPvtReadWriteSet{ 233 { 234 Namespace: "ns-1", 235 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{ 236 { 237 CollectionName: "coll-1", 238 Rwset: []byte("pvtdata"), 239 }, 240 }, 241 }, 242 }, 243 }, 244 } 245 assert.NoError(t, store.CommitWithPvtData(dataAtCrash)) 246 testVerifyPvtData(t, store, blockNumAtCrash, expectedPvtData) 247 bcInfo, err = store.GetBlockchainInfo() 248 assert.NoError(t, err) 249 assert.Equal(t, blockNumAtCrash+1, bcInfo.Height) 250 251 } 252 253 func testVerifyPvtData(t *testing.T, store *Store, blockNum uint64, expectedPvtData ledger.TxPvtDataMap) { 254 pvtdata, err := store.GetPvtDataByNum(blockNum, nil) 255 assert.NoError(t, err) 256 constructed := constructPvtdataMap(pvtdata) 257 assert.Equal(t, len(expectedPvtData), len(constructed)) 258 for k, v := range expectedPvtData { 259 ov, ok := constructed[k] 260 assert.True(t, ok) 261 assert.Equal(t, v.SeqInBlock, ov.SeqInBlock) 262 assert.True(t, proto.Equal(v.WriteSet, ov.WriteSet)) 263 } 264 } 265 266 func TestAddAfterPvtdataStoreError(t *testing.T) { 267 storeDir, err := ioutil.TempDir("", "lstore") 268 if err != nil { 269 t.Fatalf("Failed to create ledger storage directory: %s", err) 270 } 271 defer os.RemoveAll(storeDir) 272 conf := buildPrivateDataConfig(storeDir) 273 blockStoreDir := filepath.Join(storeDir, "chains") 274 provider, err := NewProvider(blockStoreDir, conf, metricsProvider) 275 assert.NoError(t, err) 276 defer provider.Close() 277 store, err := provider.Open("testLedger") 278 store.Init(btlPolicyForSampleData()) 279 defer store.Shutdown() 280 assert.NoError(t, err) 281 282 sampleData := sampleDataWithPvtdataForAllTxs(t) 283 for _, d := range sampleData[0:9] { 284 assert.NoError(t, store.CommitWithPvtData(d)) 285 } 286 // try to write the last block again. The function should skip adding block to the private store 287 // as the pvt store but the block storage should return error 288 assert.Error(t, store.CommitWithPvtData(sampleData[8])) 289 290 // At the end, the pvt store status should not have changed 291 pvtStoreCommitHt, err := store.pvtdataStore.LastCommittedBlockHeight() 292 assert.NoError(t, err) 293 assert.Equal(t, uint64(9), pvtStoreCommitHt) 294 295 // commit the rightful next block 296 assert.NoError(t, store.CommitWithPvtData(sampleData[9])) 297 pvtStoreCommitHt, err = store.pvtdataStore.LastCommittedBlockHeight() 298 assert.NoError(t, err) 299 assert.Equal(t, uint64(10), pvtStoreCommitHt) 300 } 301 302 func TestAddAfterBlkStoreError(t *testing.T) { 303 storeDir, err := ioutil.TempDir("", "lstore") 304 if err != nil { 305 t.Fatalf("Failed to create ledger storage directory: %s", err) 306 } 307 defer os.RemoveAll(storeDir) 308 conf := buildPrivateDataConfig(storeDir) 309 blockStoreDir := filepath.Join(storeDir, "chains") 310 provider, err := NewProvider(blockStoreDir, conf, metricsProvider) 311 assert.NoError(t, err) 312 defer provider.Close() 313 store, err := provider.Open("testLedger") 314 store.Init(btlPolicyForSampleData()) 315 defer store.Shutdown() 316 assert.NoError(t, err) 317 318 sampleData := sampleDataWithPvtdataForAllTxs(t) 319 for _, d := range sampleData[0:9] { 320 assert.NoError(t, store.CommitWithPvtData(d)) 321 } 322 lastBlkAndPvtData := sampleData[9] 323 // Add the block directly to blockstore 324 store.BlockStore.AddBlock(lastBlkAndPvtData.Block) 325 // Adding the same block should cause passing on the error caused by the block storgae 326 assert.Error(t, store.CommitWithPvtData(lastBlkAndPvtData)) 327 // At the end, the pvt store status should be changed 328 pvtStoreCommitHt, err := store.pvtdataStore.LastCommittedBlockHeight() 329 assert.NoError(t, err) 330 assert.Equal(t, uint64(10), pvtStoreCommitHt) 331 } 332 333 func TestPvtStoreAheadOfBlockStore(t *testing.T) { 334 storeDir, err := ioutil.TempDir("", "lstore") 335 if err != nil { 336 t.Fatalf("Failed to create ledger storage directory: %s", err) 337 } 338 defer os.RemoveAll(storeDir) 339 conf := buildPrivateDataConfig(storeDir) 340 blockStoreDir := filepath.Join(storeDir, "chains") 341 provider, err := NewProvider(blockStoreDir, conf, metricsProvider) 342 assert.NoError(t, err) 343 defer provider.Close() 344 store, err := provider.Open("testLedger") 345 store.Init(btlPolicyForSampleData()) 346 defer store.Shutdown() 347 assert.NoError(t, err) 348 349 // when both stores are empty, isPvtstoreAheadOfBlockstore should be false 350 assert.False(t, store.IsPvtStoreAheadOfBlockStore()) 351 352 sampleData := sampleDataWithPvtdataForSelectiveTx(t) 353 for _, d := range sampleData[0:9] { // commit block number 0 to 8 354 assert.NoError(t, store.CommitWithPvtData(d)) 355 } 356 assert.False(t, store.IsPvtStoreAheadOfBlockStore()) 357 358 // close and reopen 359 store.Shutdown() 360 provider.Close() 361 provider, err = NewProvider(blockStoreDir, conf, metricsProvider) 362 assert.NoError(t, err) 363 store, err = provider.Open("testLedger") 364 assert.NoError(t, err) 365 store.Init(btlPolicyForSampleData()) 366 367 // as both stores are at the same block height, isPvtstoreAheadOfBlockstore should be false 368 info, err := store.GetBlockchainInfo() 369 assert.NoError(t, err) 370 assert.Equal(t, uint64(9), info.Height) 371 pvtStoreHt, err := store.pvtdataStore.LastCommittedBlockHeight() 372 assert.NoError(t, err) 373 assert.Equal(t, uint64(9), pvtStoreHt) 374 assert.False(t, store.IsPvtStoreAheadOfBlockStore()) 375 376 lastBlkAndPvtData := sampleData[9] 377 // Add the last block directly to the pvtdataStore but not to blockstore. This would make 378 // the pvtdatastore height greater than the block store height. 379 validTxPvtData, validTxMissingPvtData := constructPvtDataAndMissingData(lastBlkAndPvtData) 380 err = store.pvtdataStore.Commit(lastBlkAndPvtData.Block.Header.Number, validTxPvtData, validTxMissingPvtData) 381 assert.NoError(t, err) 382 383 // close and reopen 384 store.Shutdown() 385 provider.Close() 386 provider, err = NewProvider(blockStoreDir, conf, metricsProvider) 387 assert.NoError(t, err) 388 store, err = provider.Open("testLedger") 389 assert.NoError(t, err) 390 store.Init(btlPolicyForSampleData()) 391 392 // pvtdataStore should be ahead of blockstore 393 info, err = store.GetBlockchainInfo() 394 assert.NoError(t, err) 395 assert.Equal(t, uint64(9), info.Height) 396 pvtStoreHt, err = store.pvtdataStore.LastCommittedBlockHeight() 397 assert.NoError(t, err) 398 assert.Equal(t, uint64(10), pvtStoreHt) 399 assert.True(t, store.IsPvtStoreAheadOfBlockStore()) 400 401 // bring the height of BlockStore equal to pvtdataStore 402 assert.NoError(t, store.CommitWithPvtData(lastBlkAndPvtData)) 403 info, err = store.GetBlockchainInfo() 404 assert.NoError(t, err) 405 assert.Equal(t, uint64(10), info.Height) 406 pvtStoreHt, err = store.pvtdataStore.LastCommittedBlockHeight() 407 assert.NoError(t, err) 408 assert.Equal(t, uint64(10), pvtStoreHt) 409 assert.False(t, store.IsPvtStoreAheadOfBlockStore()) 410 } 411 412 func TestConstructPvtdataMap(t *testing.T) { 413 assert.Nil(t, constructPvtdataMap(nil)) 414 } 415 416 func sampleDataWithPvtdataForSelectiveTx(t *testing.T) []*ledger.BlockAndPvtData { 417 var blockAndpvtdata []*ledger.BlockAndPvtData 418 blocks := testutil.ConstructTestBlocks(t, 10) 419 for i := 0; i < 10; i++ { 420 blockAndpvtdata = append(blockAndpvtdata, &ledger.BlockAndPvtData{Block: blocks[i]}) 421 } 422 423 // txNum 3, 5, 6 in block 2 has pvtdata but txNum 6 is invalid 424 blockAndpvtdata[2].PvtData = samplePvtData(t, []uint64{3, 5, 6}) 425 txFilter := lutil.TxValidationFlags(blockAndpvtdata[2].Block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER]) 426 txFilter.SetFlag(6, pb.TxValidationCode_INVALID_WRITESET) 427 blockAndpvtdata[2].Block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txFilter 428 429 // txNum 4, 6 in block 3 has pvtdata 430 blockAndpvtdata[3].PvtData = samplePvtData(t, []uint64{4, 6}) 431 432 // txNum 4, 5 in block 5 has missing pvt data but txNum 5 is invalid 433 missingData := make(ledger.TxMissingPvtDataMap) 434 missingData.Add(4, "ns-4", "coll-4", true) 435 missingData.Add(5, "ns-5", "coll-5", true) 436 blockAndpvtdata[5].MissingPvtData = missingData 437 txFilter = lutil.TxValidationFlags(blockAndpvtdata[5].Block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER]) 438 txFilter.SetFlag(5, pb.TxValidationCode_INVALID_WRITESET) 439 blockAndpvtdata[5].Block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txFilter 440 441 return blockAndpvtdata 442 } 443 444 func sampleDataWithPvtdataForAllTxs(t *testing.T) []*ledger.BlockAndPvtData { 445 var blockAndpvtdata []*ledger.BlockAndPvtData 446 blocks := testutil.ConstructTestBlocks(t, 10) 447 for i := 0; i < 10; i++ { 448 blockAndpvtdata = append(blockAndpvtdata, 449 &ledger.BlockAndPvtData{ 450 Block: blocks[i], 451 PvtData: samplePvtData(t, []uint64{uint64(i), uint64(i + 1)}), 452 }, 453 ) 454 } 455 return blockAndpvtdata 456 } 457 458 func samplePvtData(t *testing.T, txNums []uint64) map[uint64]*ledger.TxPvtData { 459 pvtWriteSet := &rwset.TxPvtReadWriteSet{DataModel: rwset.TxReadWriteSet_KV} 460 pvtWriteSet.NsPvtRwset = []*rwset.NsPvtReadWriteSet{ 461 { 462 Namespace: "ns-1", 463 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{ 464 { 465 CollectionName: "coll-1", 466 Rwset: []byte("RandomBytes-PvtRWSet-ns1-coll1"), 467 }, 468 { 469 CollectionName: "coll-2", 470 Rwset: []byte("RandomBytes-PvtRWSet-ns1-coll2"), 471 }, 472 }, 473 }, 474 } 475 var pvtData []*ledger.TxPvtData 476 for _, txNum := range txNums { 477 pvtData = append(pvtData, &ledger.TxPvtData{SeqInBlock: txNum, WriteSet: pvtWriteSet}) 478 } 479 return constructPvtdataMap(pvtData) 480 } 481 482 func btlPolicyForSampleData() pvtdatapolicy.BTLPolicy { 483 return btltestutil.SampleBTLPolicy( 484 map[[2]string]uint64{ 485 {"ns-1", "coll-1"}: 0, 486 {"ns-1", "coll-2"}: 0, 487 }, 488 ) 489 } 490 491 func buildPrivateDataConfig(rootFSPath string) *pvtdatastorage.PrivateDataConfig { 492 return &pvtdatastorage.PrivateDataConfig{ 493 PrivateDataConfig: &ledger.PrivateDataConfig{ 494 PurgeInterval: 1, 495 }, 496 StorePath: filepath.Join(rootFSPath, "pvtdataStore"), 497 } 498 }