github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/hashcheck_pvtdata_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 "fmt" 11 "testing" 12 13 "github.com/golang/protobuf/proto" 14 "github.com/hechain20/hechain/common/ledger/testutil" 15 "github.com/hechain20/hechain/core/ledger" 16 "github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/rwsetutil" 17 "github.com/hyperledger/fabric-protos-go/ledger/rwset" 18 "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 19 "github.com/hyperledger/fabric-protos-go/peer" 20 "github.com/stretchr/testify/require" 21 ) 22 23 func TestConstructValidInvalidBlocksPvtData(t *testing.T) { 24 conf, cleanup := testConfig(t) 25 defer cleanup() 26 27 nsCollBtlConfs := []*nsCollBtlConfig{ 28 { 29 namespace: "ns-1", 30 btlConfig: map[string]uint64{ 31 "coll-1": 0, 32 "coll-2": 0, 33 }, 34 }, 35 { 36 namespace: "ns-2", 37 btlConfig: map[string]uint64{ 38 "coll-2": 0, 39 }, 40 }, 41 } 42 provider := testutilNewProviderWithCollectionConfig( 43 t, 44 nsCollBtlConfs, 45 conf, 46 ) 47 defer provider.Close() 48 49 blocksGenerator, gb := testutil.NewBlockGenerator(t, "testLedger", false) 50 lg, _ := provider.CreateFromGenesisBlock(gb) 51 kvledger := lg.(*kvLedger) 52 defer kvledger.Close() 53 54 // block-1 55 commitCollectionConfigsHistoryAndDummyBlock(t, provider, kvledger, nsCollBtlConfs, blocksGenerator) 56 57 pvtDataTx0, pubSimResTx0 := produceSamplePvtdata(t, 0, 58 [][4]string{ 59 {"ns-1", "coll-1", "tx0-key-1", "tx0-val-1"}, 60 {"ns-1", "coll-2", "tx0-key-2", "tx0-val-2"}, 61 }, 62 ) 63 64 pvtDataTx1, pubSimResTx1 := produceSamplePvtdata(t, 1, 65 [][4]string{ 66 {"ns-1", "coll-1", "tx1-key-1", "tx1-val-1"}, 67 {"ns-2", "coll-2", "tx1-key-2", "tx1-val-2"}, 68 {"ns-2", "coll-2", "tx1-key-3", "tx1-val-3"}, 69 {"ns-2", "coll-2", "tx1-key-4", "tx1-val-4"}, 70 {"ns-2", "coll-2", "tx1-key-5", "tx1-val-5"}, 71 }, 72 ) 73 74 pvtData := map[uint64]*ledger.TxPvtData{ 75 0: pvtDataTx0, 76 1: pvtDataTx1, 77 } 78 simulationResults := [][]byte{pubSimResTx0, pubSimResTx1} 79 missingData := make(ledger.TxMissingPvtData) 80 missingData.Add(0, "ns-1", "coll-1", true) 81 missingData.Add(0, "ns-1", "coll-2", true) 82 missingData.Add(1, "ns-1", "coll-1", true) 83 missingData.Add(1, "ns-2", "coll-2", true) 84 85 // commit block-2 86 blk2 := blocksGenerator.NextBlock([][]byte{pubSimResTx0, pubSimResTx1}) 87 blockAndPvtData1 := &ledger.BlockAndPvtData{ 88 Block: blk2, 89 PvtData: pvtData, 90 MissingPvtData: missingData, 91 } 92 require.NoError(t, kvledger.commit(blockAndPvtData1, &ledger.CommitOptions{})) 93 94 // generate snapshot at block-2 95 require.NoError(t, kvledger.generateSnapshot()) 96 freshConf, cleanup := testConfig(t) 97 defer cleanup() 98 99 freshProvider := testutilNewProviderWithCollectionConfig( 100 t, 101 nsCollBtlConfs, 102 freshConf, 103 ) 104 defer freshProvider.Close() 105 106 lgr, _, err := freshProvider.CreateFromSnapshot(SnapshotDirForLedgerBlockNum(conf.SnapshotsConfig.RootDir, "testLedger", 2)) 107 fmt.Printf("%+v", err) 108 require.NoError(t, err) 109 bootstrappedLedger := lgr.(*kvLedger) 110 defer bootstrappedLedger.Close() 111 112 // commit block-3 113 blk3 := blocksGenerator.NextBlock(simulationResults) 114 require.NoError(t, bootstrappedLedger.commit( 115 &ledger.BlockAndPvtData{ 116 Block: blk3, 117 PvtData: pvtData, 118 }, 119 &ledger.CommitOptions{}, 120 )) 121 122 pvtdataCopy := func() map[uint64]*ledger.TxPvtData { 123 m := make(map[uint64]*ledger.TxPvtData, len(pvtData)) 124 for k, v := range pvtData { 125 wsCopy := (proto.Clone(v.WriteSet)).(*rwset.TxPvtReadWriteSet) 126 m[k] = &ledger.TxPvtData{ 127 SeqInBlock: v.SeqInBlock, 128 WriteSet: wsCopy, 129 } 130 } 131 return m 132 } 133 134 t.Run("for-data-after-snapshot:extra-collection-is-ignored", func(t *testing.T) { 135 lgr := bootstrappedLedger 136 pvtdata := pvtdataCopy() 137 pvtdata[1], _ = produceSamplePvtdata(t, 1, 138 [][4]string{ 139 {"ns-1", "non-existing-collection", "randomValue", "randomValue"}, 140 }, 141 ) 142 143 blocksValidPvtData, hashMismatched, err := constructValidAndInvalidPvtData( 144 []*ledger.ReconciledPvtdata{ 145 { 146 BlockNum: 3, 147 WriteSets: pvtdata, 148 }, 149 }, 150 lgr.blockStore, 151 lgr.pvtdataStore, 152 2, 153 ) 154 require.NoError(t, err) 155 verifyBlocksPvtdata(t, 156 map[uint64][]*ledger.TxPvtData{ 157 3: { 158 pvtdata[0], 159 }, 160 }, 161 blocksValidPvtData, 162 ) 163 // should not include the pvtData passed for the tx1 even in hashmismatched as the collection does not exist in tx1 164 require.Len(t, hashMismatched, 0) 165 }) 166 167 t.Run("for-data-after-snapshot:hash-mismatch-is-reported", func(t *testing.T) { 168 lgr := bootstrappedLedger 169 pvtdata := pvtdataCopy() 170 171 pvtdata[1], _ = produceSamplePvtdata(t, 1, 172 [][4]string{ 173 {"ns-2", "coll-2", "key-2", "randomValue"}, 174 }, 175 ) 176 177 blocksValidPvtData, hashMismatches, err := constructValidAndInvalidPvtData( 178 []*ledger.ReconciledPvtdata{ 179 { 180 BlockNum: 3, 181 WriteSets: pvtdata, 182 }, 183 }, 184 lgr.blockStore, 185 lgr.pvtdataStore, 186 2, 187 ) 188 require.NoError(t, err) 189 verifyBlocksPvtdata(t, 190 map[uint64][]*ledger.TxPvtData{ 191 3: { 192 pvtdata[0], 193 }, 194 }, 195 blocksValidPvtData, 196 ) 197 require.Equal( 198 t, 199 []*ledger.PvtdataHashMismatch{ 200 { 201 BlockNum: 3, 202 TxNum: 1, 203 Namespace: "ns-2", 204 Collection: "coll-2", 205 }, 206 }, 207 hashMismatches, 208 ) 209 }) 210 211 t.Run("works-for-mixed-data-before-and-after-snapshot", func(t *testing.T) { 212 lgr := bootstrappedLedger 213 pvtdata := pvtdataCopy() 214 215 blocksValidPvtData, hashMismatches, err := constructValidAndInvalidPvtData( 216 []*ledger.ReconciledPvtdata{ 217 { 218 BlockNum: 2, 219 WriteSets: pvtdata, 220 }, 221 { 222 BlockNum: 3, 223 WriteSets: pvtdata, 224 }, 225 }, 226 lgr.blockStore, 227 lgr.pvtdataStore, 228 2, 229 ) 230 require.NoError(t, err) 231 verifyBlocksPvtdata(t, 232 map[uint64][]*ledger.TxPvtData{ 233 2: { 234 pvtdata[0], 235 pvtdata[1], 236 }, 237 3: { 238 pvtdata[0], 239 pvtdata[1], 240 }, 241 }, 242 blocksValidPvtData, 243 ) 244 require.Len(t, hashMismatches, 0) 245 }) 246 247 t.Run("for-data-before-snapshot:does-not-trim-the-extra-keys", func(t *testing.T) { 248 lgr := bootstrappedLedger 249 pvtdata := pvtdataCopy() 250 pvtdataWithExtraKey := pvtdataCopy() 251 pvtdataWithExtraKey[0], _ = produceSamplePvtdata(t, 0, 252 [][4]string{ 253 {"ns-1", "coll-1", "tx0-key-1", "tx0-val-1"}, 254 {"ns-1", "coll-2", "tx0-key-2", "tx0-val-2"}, 255 {"ns-1", "coll-2", "extra-key", "extra-val"}, 256 }, 257 ) 258 pvtdataWithExtraKey[2], _ = produceSamplePvtdata(t, 2, 259 [][4]string{ 260 {"ns-1", "coll-1", "tx2-key-1", "tx2-val-1"}, 261 }, 262 ) 263 264 blocksValidPvtData, hashMismatches, err := constructValidAndInvalidPvtData( 265 []*ledger.ReconciledPvtdata{ 266 { 267 BlockNum: 2, 268 WriteSets: pvtdataWithExtraKey, 269 }, 270 }, 271 lgr.blockStore, 272 lgr.pvtdataStore, 273 2, 274 ) 275 require.NoError(t, err) 276 277 verifyBlocksPvtdata(t, 278 map[uint64][]*ledger.TxPvtData{ 279 2: { 280 pvtdataWithExtraKey[0], 281 pvtdata[1], 282 }, 283 }, 284 blocksValidPvtData, 285 ) 286 require.Len(t, hashMismatches, 0) 287 }) 288 289 t.Run("for-data-before-snapshot:reports-hash-mismatch-and-partial-data-supplied", func(t *testing.T) { 290 lgr := bootstrappedLedger 291 temptered := pvtdataCopy() 292 temptered[0], _ = produceSamplePvtdata(t, 0, 293 [][4]string{ 294 {"ns-1", "coll-1", "tx0-key-1", "tx0-val-1-tempered"}, 295 }, 296 ) 297 temptered[1], _ = produceSamplePvtdata(t, 1, 298 [][4]string{ 299 {"ns-2", "coll-2", "tx1-key-2", "tx1-val-2-tempered"}, 300 {"ns-2", "coll-2", "tx1-key-3", "tx1-val-3-tempered"}, 301 }, 302 ) 303 304 blocksValidPvtData, hashMismatches, err := constructValidAndInvalidPvtData( 305 []*ledger.ReconciledPvtdata{ 306 { 307 BlockNum: 2, 308 WriteSets: temptered, 309 }, 310 }, 311 lgr.blockStore, 312 lgr.pvtdataStore, 313 2, 314 ) 315 require.NoError(t, err) 316 require.Len(t, blocksValidPvtData, 0) 317 require.ElementsMatch(t, 318 []*ledger.PvtdataHashMismatch{ 319 { 320 BlockNum: 2, 321 TxNum: 0, 322 Namespace: "ns-1", 323 Collection: "coll-1", 324 }, 325 { 326 BlockNum: 2, 327 TxNum: 1, 328 Namespace: "ns-2", 329 Collection: "coll-2", 330 }, 331 }, 332 hashMismatches, 333 ) 334 }) 335 336 t.Run("for-data-before-snapshot:reports-repeated-key", func(t *testing.T) { 337 lgr := bootstrappedLedger 338 pvtdata := pvtdataCopy() 339 repeatedKeyInTx0Ns1Coll1 := pvtdataCopy() 340 repeatedKeyWS := &kvrwset.KVRWSet{ 341 Writes: []*kvrwset.KVWrite{ 342 { 343 Key: "tx0-key-1", 344 Value: []byte("tx0-val-1"), 345 }, 346 { 347 Key: "tx0-key-1", 348 Value: []byte("tx0-val-1-tempered"), 349 }, 350 }, 351 } 352 353 repeatedKeyWSBytes, err := proto.Marshal(repeatedKeyWS) 354 require.NoError(t, err) 355 repeatedKeyInTx0Ns1Coll1[0].WriteSet.NsPvtRwset[0].CollectionPvtRwset[0].Rwset = repeatedKeyWSBytes 356 357 expectedPartOfTx0, _ := produceSamplePvtdata(t, 0, 358 [][4]string{ 359 {"ns-1", "coll-2", "tx0-key-2", "tx0-val-2"}, 360 }, 361 ) 362 363 blocksValidPvtData, hashMismatches, err := constructValidAndInvalidPvtData( 364 []*ledger.ReconciledPvtdata{ 365 { 366 BlockNum: 2, 367 WriteSets: repeatedKeyInTx0Ns1Coll1, 368 }, 369 }, 370 lgr.blockStore, 371 lgr.pvtdataStore, 372 2, 373 ) 374 require.NoError(t, err) 375 376 verifyBlocksPvtdata(t, 377 map[uint64][]*ledger.TxPvtData{ 378 2: { 379 expectedPartOfTx0, 380 pvtdata[1], 381 }, 382 }, 383 blocksValidPvtData, 384 ) 385 386 require.ElementsMatch(t, 387 []*ledger.PvtdataHashMismatch{ 388 { 389 BlockNum: 2, 390 TxNum: 0, 391 Namespace: "ns-1", 392 Collection: "coll-1", 393 }, 394 }, 395 hashMismatches, 396 ) 397 }) 398 399 t.Run("for-data-before-snapshot:ignores-bad-data-corrupted-writeset", func(t *testing.T) { 400 lgr := bootstrappedLedger 401 pvtdata := pvtdataCopy() 402 pvtdataWithBadData := pvtdataCopy() 403 pvtdataWithBadData[0].WriteSet.NsPvtRwset[0].CollectionPvtRwset[0].Rwset = []byte("bad-data") 404 405 blocksValidPvtData, hashMismatches, err := constructValidAndInvalidPvtData( 406 []*ledger.ReconciledPvtdata{ 407 { 408 BlockNum: 2, 409 WriteSets: pvtdataWithBadData, 410 }, 411 }, 412 lgr.blockStore, 413 lgr.pvtdataStore, 414 2, 415 ) 416 require.NoError(t, err) 417 418 verifyBlocksPvtdata(t, 419 map[uint64][]*ledger.TxPvtData{ 420 2: { 421 pvtdata[1], 422 }, 423 }, 424 blocksValidPvtData, 425 ) 426 require.Len(t, hashMismatches, 0) 427 }) 428 429 t.Run("for-data-before-snapshot:ignores-bad-data-empty-collections", func(t *testing.T) { 430 lgr := bootstrappedLedger 431 pvtdata := pvtdataCopy() 432 pvtdataWithEmptyCollections := pvtdataCopy() 433 pvtdataWithEmptyCollections[0].WriteSet.NsPvtRwset[0].CollectionPvtRwset[0].Rwset = nil 434 435 expectedValidDataForTx0, _ := produceSamplePvtdata(t, 0, 436 [][4]string{ 437 {"ns-1", "coll-2", "tx0-key-2", "tx0-val-2"}, 438 }, 439 ) 440 441 blocksValidPvtData, hashMismatches, err := constructValidAndInvalidPvtData( 442 []*ledger.ReconciledPvtdata{ 443 { 444 BlockNum: 2, 445 WriteSets: pvtdataWithEmptyCollections, 446 }, 447 }, 448 lgr.blockStore, 449 lgr.pvtdataStore, 450 2, 451 ) 452 require.NoError(t, err) 453 454 verifyBlocksPvtdata(t, 455 map[uint64][]*ledger.TxPvtData{ 456 2: { 457 expectedValidDataForTx0, 458 pvtdata[1], 459 }, 460 }, 461 blocksValidPvtData, 462 ) 463 require.Len(t, hashMismatches, 0) 464 }) 465 } 466 467 func verifyBlocksPvtdata(t *testing.T, expected, actual map[uint64][]*ledger.TxPvtData) { 468 require.Len(t, actual, len(expected)) 469 for blkNum, expectedTx := range expected { 470 actualTx := actual[blkNum] 471 require.Len(t, actualTx, len(expectedTx)) 472 m := map[uint64]*rwset.TxPvtReadWriteSet{} 473 for _, a := range actualTx { 474 m[a.SeqInBlock] = a.WriteSet 475 } 476 477 for _, e := range expectedTx { 478 require.NotNil(t, m[e.SeqInBlock]) 479 require.Equal(t, e.WriteSet, m[e.SeqInBlock]) 480 } 481 } 482 } 483 484 func produceSamplePvtdata(t *testing.T, txNum uint64, data [][4]string) (*ledger.TxPvtData, []byte) { 485 builder := rwsetutil.NewRWSetBuilder() 486 for _, d := range data { 487 builder.AddToPvtAndHashedWriteSet(d[0], d[1], d[2], []byte(d[3])) 488 } 489 simRes, err := builder.GetTxSimulationResults() 490 require.NoError(t, err) 491 pubSimulationResultsBytes, err := proto.Marshal(simRes.PubSimulationResults) 492 require.NoError(t, err) 493 return &ledger.TxPvtData{SeqInBlock: txNum, WriteSet: simRes.PvtSimulationResults}, pubSimulationResultsBytes 494 } 495 496 func commitCollectionConfigsHistoryAndDummyBlock(t *testing.T, provider *Provider, kvledger *kvLedger, 497 nsCollBtlConfs []*nsCollBtlConfig, blocksGenerator *testutil.BlockGenerator, 498 ) { 499 blockAndPvtdata := prepareNextBlockForTest(t, kvledger, blocksGenerator, "SimulateForBlk1", 500 map[string]string{ 501 "dummyKeyForCollectionConfig": "dummyValForCollectionConfig", 502 }, 503 nil, 504 ) 505 require.NoError(t, kvledger.commit(blockAndPvtdata, &ledger.CommitOptions{})) 506 507 for _, nsBTLConf := range nsCollBtlConfs { 508 namespace := nsBTLConf.namespace 509 collConfig := []*peer.StaticCollectionConfig{} 510 for coll, btl := range nsBTLConf.btlConfig { 511 collConfig = append(collConfig, 512 &peer.StaticCollectionConfig{ 513 Name: coll, 514 BlockToLive: btl, 515 }, 516 ) 517 } 518 addDummyEntryInCollectionConfigHistory( 519 t, 520 provider, 521 kvledger.ledgerID, 522 namespace, 523 blockAndPvtdata.Block.Header.Number, 524 collConfig, 525 ) 526 } 527 } 528 529 func TestRemoveCollFromTxPvtReadWriteSet(t *testing.T) { 530 txpvtrwset := testutilConstructSampleTxPvtRwset( 531 []*testNsColls{ 532 {ns: "ns-1", colls: []string{"coll-1", "coll-2"}}, 533 {ns: "ns-2", colls: []string{"coll-3", "coll-4"}}, 534 }, 535 ) 536 537 removeCollFromTxPvtReadWriteSet(txpvtrwset, "ns-1", "coll-1") 538 require.Equal( 539 t, 540 testutilConstructSampleTxPvtRwset( 541 []*testNsColls{ 542 {ns: "ns-1", colls: []string{"coll-2"}}, 543 {ns: "ns-2", colls: []string{"coll-3", "coll-4"}}, 544 }, 545 ), 546 txpvtrwset, 547 ) 548 549 removeCollFromTxPvtReadWriteSet(txpvtrwset, "ns-1", "coll-2") 550 require.Equal( 551 t, 552 testutilConstructSampleTxPvtRwset( 553 []*testNsColls{ 554 {ns: "ns-2", colls: []string{"coll-3", "coll-4"}}, 555 }, 556 ), 557 txpvtrwset, 558 ) 559 } 560 561 func testutilConstructSampleTxPvtRwset(nsCollsList []*testNsColls) *rwset.TxPvtReadWriteSet { 562 txPvtRwset := &rwset.TxPvtReadWriteSet{} 563 for _, nsColls := range nsCollsList { 564 ns := nsColls.ns 565 nsdata := &rwset.NsPvtReadWriteSet{ 566 Namespace: ns, 567 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{}, 568 } 569 txPvtRwset.NsPvtRwset = append(txPvtRwset.NsPvtRwset, nsdata) 570 for _, coll := range nsColls.colls { 571 nsdata.CollectionPvtRwset = append(nsdata.CollectionPvtRwset, 572 &rwset.CollectionPvtReadWriteSet{ 573 CollectionName: coll, 574 Rwset: []byte(fmt.Sprintf("pvtrwset-for-%s-%s", ns, coll)), 575 }, 576 ) 577 } 578 } 579 return txPvtRwset 580 } 581 582 type testNsColls struct { 583 ns string 584 colls []string 585 }