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