github.com/yous1230/fabric@v2.0.0-beta.0.20191224111736-74345bee6ac2+incompatible/gossip/privdata/coordinator_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package privdata 8 9 import ( 10 "encoding/asn1" 11 "encoding/hex" 12 "errors" 13 "fmt" 14 "io/ioutil" 15 "os" 16 "reflect" 17 "testing" 18 "time" 19 20 pb "github.com/golang/protobuf/proto" 21 "github.com/hyperledger/fabric-protos-go/common" 22 proto "github.com/hyperledger/fabric-protos-go/gossip" 23 "github.com/hyperledger/fabric-protos-go/ledger/rwset" 24 "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 25 mspproto "github.com/hyperledger/fabric-protos-go/msp" 26 "github.com/hyperledger/fabric-protos-go/peer" 27 tspb "github.com/hyperledger/fabric-protos-go/transientstore" 28 "github.com/hyperledger/fabric/bccsp/factory" 29 "github.com/hyperledger/fabric/common/metrics/disabled" 30 util2 "github.com/hyperledger/fabric/common/util" 31 "github.com/hyperledger/fabric/core/common/privdata" 32 "github.com/hyperledger/fabric/core/ledger" 33 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil" 34 "github.com/hyperledger/fabric/core/transientstore" 35 "github.com/hyperledger/fabric/gossip/metrics" 36 gmetricsmocks "github.com/hyperledger/fabric/gossip/metrics/mocks" 37 privdatacommon "github.com/hyperledger/fabric/gossip/privdata/common" 38 "github.com/hyperledger/fabric/gossip/privdata/mocks" 39 capabilitymock "github.com/hyperledger/fabric/gossip/privdata/mocks" 40 "github.com/hyperledger/fabric/gossip/util" 41 "github.com/hyperledger/fabric/msp" 42 "github.com/hyperledger/fabric/msp/mgmt" 43 mspmgmt "github.com/hyperledger/fabric/msp/mgmt" 44 msptesttools "github.com/hyperledger/fabric/msp/mgmt/testtools" 45 "github.com/hyperledger/fabric/protoutil" 46 "github.com/stretchr/testify/assert" 47 "github.com/stretchr/testify/mock" 48 "github.com/stretchr/testify/require" 49 ) 50 51 var testConfig = CoordinatorConfig{ 52 PullRetryThreshold: time.Second * 3, 53 TransientBlockRetention: 1000, 54 SkipPullingInvalidTransactions: false, 55 } 56 57 // CollectionCriteria aggregates criteria of 58 // a collection 59 type CollectionCriteria struct { 60 Channel string 61 Collection string 62 Namespace string 63 } 64 65 func fromCollectionCriteria(criteria privdata.CollectionCriteria) CollectionCriteria { 66 return CollectionCriteria{ 67 Collection: criteria.Collection, 68 Namespace: criteria.Namespace, 69 Channel: criteria.Channel, 70 } 71 } 72 73 type validatorMock struct { 74 err error 75 } 76 77 func (v *validatorMock) Validate(block *common.Block) error { 78 if v.err != nil { 79 return v.err 80 } 81 return nil 82 } 83 84 type digests []privdatacommon.DigKey 85 86 func (d digests) Equal(other digests) bool { 87 flatten := func(d digests) map[privdatacommon.DigKey]struct{} { 88 m := map[privdatacommon.DigKey]struct{}{} 89 for _, dig := range d { 90 m[dig] = struct{}{} 91 } 92 return m 93 } 94 return reflect.DeepEqual(flatten(d), flatten(other)) 95 } 96 97 type fetchCall struct { 98 fetcher *fetcherMock 99 *mock.Call 100 } 101 102 func (fc *fetchCall) expectingEndorsers(orgs ...string) *fetchCall { 103 if fc.fetcher.expectedEndorsers == nil { 104 fc.fetcher.expectedEndorsers = make(map[string]struct{}) 105 } 106 for _, org := range orgs { 107 sID := &mspproto.SerializedIdentity{Mspid: org, IdBytes: []byte(fmt.Sprintf("p0%s", org))} 108 b, _ := pb.Marshal(sID) 109 fc.fetcher.expectedEndorsers[string(b)] = struct{}{} 110 } 111 112 return fc 113 } 114 115 func (fc *fetchCall) expectingDigests(digests []privdatacommon.DigKey) *fetchCall { 116 fc.fetcher.expectedDigests = digests 117 return fc 118 } 119 120 func (fc *fetchCall) Return(returnArguments ...interface{}) *mock.Call { 121 122 return fc.Call.Return(returnArguments...) 123 } 124 125 type fetcherMock struct { 126 t *testing.T 127 mock.Mock 128 expectedDigests []privdatacommon.DigKey 129 expectedEndorsers map[string]struct{} 130 } 131 132 func (f *fetcherMock) On(methodName string, arguments ...interface{}) *fetchCall { 133 return &fetchCall{ 134 fetcher: f, 135 Call: f.Mock.On(methodName, arguments...), 136 } 137 } 138 139 func (f *fetcherMock) fetch(dig2src dig2sources) (*privdatacommon.FetchedPvtDataContainer, error) { 140 uniqueEndorsements := make(map[string]interface{}) 141 for _, endorsements := range dig2src { 142 for _, endorsement := range endorsements { 143 _, exists := f.expectedEndorsers[string(endorsement.Endorser)] 144 if !exists { 145 f.t.Fatalf("Encountered a non-expected endorser: %s", string(endorsement.Endorser)) 146 } 147 uniqueEndorsements[string(endorsement.Endorser)] = struct{}{} 148 } 149 } 150 assert.True(f.t, digests(f.expectedDigests).Equal(digests(dig2src.keys()))) 151 assert.Equal(f.t, len(f.expectedEndorsers), len(uniqueEndorsements)) 152 args := f.Called(dig2src) 153 if args.Get(1) == nil { 154 return args.Get(0).(*privdatacommon.FetchedPvtDataContainer), nil 155 } 156 return nil, args.Get(1).(error) 157 } 158 159 type testTransientStore struct { 160 storeProvider transientstore.StoreProvider 161 store *transientstore.Store 162 tempdir string 163 } 164 165 func newTransientStore(t *testing.T) *testTransientStore { 166 s := &testTransientStore{} 167 var err error 168 s.tempdir, err = ioutil.TempDir("", "ts") 169 if err != nil { 170 t.Fatalf("Failed to create test directory, got err %s", err) 171 return s 172 } 173 s.storeProvider, err = transientstore.NewStoreProvider(s.tempdir) 174 if err != nil { 175 t.Fatalf("Failed to open store, got err %s", err) 176 return s 177 } 178 s.store, err = s.storeProvider.OpenStore("testchannelid") 179 if err != nil { 180 t.Fatalf("Failed to open store, got err %s", err) 181 return s 182 } 183 return s 184 } 185 186 func (s *testTransientStore) tearDown() { 187 s.storeProvider.Close() 188 os.RemoveAll(s.tempdir) 189 } 190 191 func (s *testTransientStore) Persist(txid string, blockHeight uint64, 192 privateSimulationResultsWithConfig *tspb.TxPvtReadWriteSetWithConfigInfo) error { 193 return s.store.Persist(txid, blockHeight, privateSimulationResultsWithConfig) 194 } 195 196 func (s *testTransientStore) GetTxPvtRWSetByTxid(txid string, filter ledger.PvtNsCollFilter) (RWSetScanner, error) { 197 return s.store.GetTxPvtRWSetByTxid(txid, filter) 198 } 199 200 func createcollectionStore(expectedSignedData protoutil.SignedData) *collectionStore { 201 return &collectionStore{ 202 expectedSignedData: expectedSignedData, 203 policies: make(map[collectionAccessPolicy]CollectionCriteria), 204 store: make(map[CollectionCriteria]collectionAccessPolicy), 205 } 206 } 207 208 type collectionStore struct { 209 expectedSignedData protoutil.SignedData 210 acceptsAll bool 211 acceptsNone bool 212 lenient bool 213 mspIdentifier string 214 store map[CollectionCriteria]collectionAccessPolicy 215 policies map[collectionAccessPolicy]CollectionCriteria 216 } 217 218 func (cs *collectionStore) thatAcceptsAll() *collectionStore { 219 cs.acceptsAll = true 220 return cs 221 } 222 223 func (cs *collectionStore) thatAcceptsNone() *collectionStore { 224 cs.acceptsNone = true 225 return cs 226 } 227 228 func (cs *collectionStore) thatAccepts(cc CollectionCriteria) *collectionStore { 229 sp := collectionAccessPolicy{ 230 cs: cs, 231 n: util.RandomUInt64(), 232 } 233 cs.store[cc] = sp 234 cs.policies[sp] = cc 235 return cs 236 } 237 238 func (cs *collectionStore) withMSPIdentity(identifier string) *collectionStore { 239 cs.mspIdentifier = identifier 240 return cs 241 } 242 243 func (cs *collectionStore) RetrieveCollectionAccessPolicy(cc privdata.CollectionCriteria) (privdata.CollectionAccessPolicy, error) { 244 if sp, exists := cs.store[fromCollectionCriteria(cc)]; exists { 245 return &sp, nil 246 } 247 if cs.acceptsAll || cs.acceptsNone || cs.lenient { 248 return &collectionAccessPolicy{ 249 cs: cs, 250 n: util.RandomUInt64(), 251 }, nil 252 } 253 return nil, privdata.NoSuchCollectionError{} 254 } 255 256 func (cs *collectionStore) RetrieveCollection(privdata.CollectionCriteria) (privdata.Collection, error) { 257 panic("implement me") 258 } 259 260 func (cs *collectionStore) RetrieveCollectionConfig(cc privdata.CollectionCriteria) (*peer.StaticCollectionConfig, error) { 261 mspIdentifier := "different-org" 262 if _, exists := cs.store[fromCollectionCriteria(cc)]; exists || cs.acceptsAll { 263 mspIdentifier = cs.mspIdentifier 264 } 265 return &peer.StaticCollectionConfig{ 266 Name: cc.Collection, 267 MemberOnlyRead: true, 268 MemberOrgsPolicy: &peer.CollectionPolicyConfig{ 269 Payload: &peer.CollectionPolicyConfig_SignaturePolicy{ 270 SignaturePolicy: &common.SignaturePolicyEnvelope{ 271 Rule: &common.SignaturePolicy{ 272 Type: &common.SignaturePolicy_SignedBy{ 273 SignedBy: 0, 274 }, 275 }, 276 Identities: []*mspproto.MSPPrincipal{ 277 { 278 PrincipalClassification: mspproto.MSPPrincipal_ROLE, 279 Principal: protoutil.MarshalOrPanic(&mspproto.MSPRole{ 280 MspIdentifier: mspIdentifier, 281 Role: mspproto.MSPRole_MEMBER, 282 }), 283 }, 284 }, 285 }, 286 }, 287 }, 288 }, nil 289 } 290 291 func (cs *collectionStore) RetrieveReadWritePermission(cc privdata.CollectionCriteria, sp *peer.SignedProposal, qe ledger.QueryExecutor) (bool, bool, error) { 292 panic("implement me") 293 } 294 295 func (cs *collectionStore) RetrieveCollectionConfigPackage(cc privdata.CollectionCriteria) (*peer.CollectionConfigPackage, error) { 296 return &peer.CollectionConfigPackage{ 297 Config: []*peer.CollectionConfig{ 298 { 299 Payload: &peer.CollectionConfig_StaticCollectionConfig{ 300 StaticCollectionConfig: &peer.StaticCollectionConfig{ 301 Name: cc.Collection, 302 MaximumPeerCount: 1, 303 RequiredPeerCount: 1, 304 }, 305 }, 306 }, 307 }, 308 }, nil 309 } 310 311 func (cs *collectionStore) RetrieveCollectionPersistenceConfigs(cc privdata.CollectionCriteria) (privdata.CollectionPersistenceConfigs, error) { 312 panic("implement me") 313 } 314 315 func (cs *collectionStore) AccessFilter(channelName string, collectionPolicyConfig *peer.CollectionPolicyConfig) (privdata.Filter, error) { 316 panic("implement me") 317 } 318 319 type collectionAccessPolicy struct { 320 cs *collectionStore 321 n uint64 322 } 323 324 func (cap *collectionAccessPolicy) MemberOrgs() []string { 325 return []string{"org0", "org1"} 326 } 327 328 func (cap *collectionAccessPolicy) RequiredPeerCount() int { 329 return 1 330 } 331 332 func (cap *collectionAccessPolicy) MaximumPeerCount() int { 333 return 2 334 } 335 336 func (cap *collectionAccessPolicy) IsMemberOnlyRead() bool { 337 return false 338 } 339 340 func (cap *collectionAccessPolicy) IsMemberOnlyWrite() bool { 341 return false 342 } 343 344 func (cap *collectionAccessPolicy) AccessFilter() privdata.Filter { 345 return func(sd protoutil.SignedData) bool { 346 that, _ := asn1.Marshal(sd) 347 this, _ := asn1.Marshal(cap.cs.expectedSignedData) 348 if hex.EncodeToString(that) != hex.EncodeToString(this) { 349 panic(fmt.Errorf("self signed data passed isn't equal to expected:%v, %v", sd, cap.cs.expectedSignedData)) 350 } 351 352 if cap.cs.acceptsNone { 353 return false 354 } else if cap.cs.acceptsAll { 355 return true 356 } 357 358 _, exists := cap.cs.policies[*cap] 359 return exists 360 } 361 } 362 363 func TestPvtDataCollections_FailOnEmptyPayload(t *testing.T) { 364 collection := &util.PvtDataCollections{ 365 &ledger.TxPvtData{ 366 SeqInBlock: uint64(1), 367 WriteSet: &rwset.TxPvtReadWriteSet{ 368 DataModel: rwset.TxReadWriteSet_KV, 369 NsPvtRwset: []*rwset.NsPvtReadWriteSet{ 370 { 371 Namespace: "ns1", 372 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{ 373 { 374 CollectionName: "secretCollection", 375 Rwset: []byte{1, 2, 3, 4, 5, 6, 7}, 376 }, 377 }, 378 }, 379 }, 380 }, 381 }, 382 383 nil, 384 } 385 386 _, err := collection.Marshal() 387 assertion := assert.New(t) 388 assertion.Error(err, "Expected to fail since second item has nil payload") 389 assertion.Equal("Mallformed private data payload, rwset index 1 is nil", fmt.Sprintf("%s", err)) 390 } 391 392 func TestPvtDataCollections_FailMarshalingWriteSet(t *testing.T) { 393 collection := &util.PvtDataCollections{ 394 &ledger.TxPvtData{ 395 SeqInBlock: uint64(1), 396 WriteSet: nil, 397 }, 398 } 399 400 _, err := collection.Marshal() 401 assertion := assert.New(t) 402 assertion.Error(err, "Expected to fail since first item has nil writeset") 403 assertion.Contains(fmt.Sprintf("%s", err), "Could not marshal private rwset index 0") 404 } 405 406 func TestPvtDataCollections_Marshal(t *testing.T) { 407 collection := &util.PvtDataCollections{ 408 &ledger.TxPvtData{ 409 SeqInBlock: uint64(1), 410 WriteSet: &rwset.TxPvtReadWriteSet{ 411 DataModel: rwset.TxReadWriteSet_KV, 412 NsPvtRwset: []*rwset.NsPvtReadWriteSet{ 413 { 414 Namespace: "ns1", 415 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{ 416 { 417 CollectionName: "secretCollection", 418 Rwset: []byte{1, 2, 3, 4, 5, 6, 7}, 419 }, 420 }, 421 }, 422 }, 423 }, 424 }, 425 426 &ledger.TxPvtData{ 427 SeqInBlock: uint64(2), 428 WriteSet: &rwset.TxPvtReadWriteSet{ 429 DataModel: rwset.TxReadWriteSet_KV, 430 NsPvtRwset: []*rwset.NsPvtReadWriteSet{ 431 { 432 Namespace: "ns1", 433 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{ 434 { 435 CollectionName: "secretCollection", 436 Rwset: []byte{42, 42, 42, 42, 42, 42, 42}, 437 }, 438 }, 439 }, 440 { 441 Namespace: "ns2", 442 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{ 443 { 444 CollectionName: "otherCollection", 445 Rwset: []byte{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, 446 }, 447 }, 448 }, 449 }, 450 }, 451 }, 452 } 453 454 bytes, err := collection.Marshal() 455 456 assertion := assert.New(t) 457 assertion.NoError(err) 458 assertion.NotNil(bytes) 459 assertion.Equal(2, len(bytes)) 460 } 461 462 func TestPvtDataCollections_Unmarshal(t *testing.T) { 463 collection := util.PvtDataCollections{ 464 &ledger.TxPvtData{ 465 SeqInBlock: uint64(1), 466 WriteSet: &rwset.TxPvtReadWriteSet{ 467 DataModel: rwset.TxReadWriteSet_KV, 468 NsPvtRwset: []*rwset.NsPvtReadWriteSet{ 469 { 470 Namespace: "ns1", 471 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{ 472 { 473 CollectionName: "secretCollection", 474 Rwset: []byte{1, 2, 3, 4, 5, 6, 7}, 475 }, 476 }, 477 }, 478 }, 479 }, 480 }, 481 } 482 483 bytes, err := collection.Marshal() 484 485 assertion := assert.New(t) 486 assertion.NoError(err) 487 assertion.NotNil(bytes) 488 assertion.Equal(1, len(bytes)) 489 490 var newCol util.PvtDataCollections 491 492 err = newCol.Unmarshal(bytes) 493 assertion.NoError(err) 494 assertion.Equal(1, len(newCol)) 495 assertion.Equal(newCol[0].SeqInBlock, collection[0].SeqInBlock) 496 assertion.True(pb.Equal(newCol[0].WriteSet, collection[0].WriteSet)) 497 } 498 499 type rwsTriplet struct { 500 namespace string 501 collection string 502 rwset string 503 } 504 505 func flattenTxPvtDataMap(pd ledger.TxPvtDataMap) map[uint64]map[rwsTriplet]struct{} { 506 m := make(map[uint64]map[rwsTriplet]struct{}) 507 for seqInBlock, namespaces := range pd { 508 triplets := make(map[rwsTriplet]struct{}) 509 for _, namespace := range namespaces.WriteSet.NsPvtRwset { 510 for _, col := range namespace.CollectionPvtRwset { 511 triplets[rwsTriplet{ 512 namespace: namespace.Namespace, 513 collection: col.CollectionName, 514 rwset: hex.EncodeToString(col.Rwset), 515 }] = struct{}{} 516 } 517 } 518 m[seqInBlock] = triplets 519 } 520 return m 521 } 522 523 var expectedCommittedPrivateData1 = map[uint64]*ledger.TxPvtData{ 524 0: {SeqInBlock: 0, WriteSet: &rwset.TxPvtReadWriteSet{ 525 DataModel: rwset.TxReadWriteSet_KV, 526 NsPvtRwset: []*rwset.NsPvtReadWriteSet{ 527 { 528 Namespace: "ns1", 529 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{ 530 { 531 CollectionName: "c1", 532 Rwset: []byte("rws-pre-image"), 533 }, 534 { 535 CollectionName: "c2", 536 Rwset: []byte("rws-pre-image"), 537 }, 538 }, 539 }, 540 }, 541 }}, 542 1: {SeqInBlock: 1, WriteSet: &rwset.TxPvtReadWriteSet{ 543 DataModel: rwset.TxReadWriteSet_KV, 544 NsPvtRwset: []*rwset.NsPvtReadWriteSet{ 545 { 546 Namespace: "ns2", 547 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{ 548 { 549 CollectionName: "c1", 550 Rwset: []byte("rws-pre-image"), 551 }, 552 }, 553 }, 554 }, 555 }}, 556 } 557 558 var expectedCommittedPrivateData2 = map[uint64]*ledger.TxPvtData{ 559 0: {SeqInBlock: 0, WriteSet: &rwset.TxPvtReadWriteSet{ 560 DataModel: rwset.TxReadWriteSet_KV, 561 NsPvtRwset: []*rwset.NsPvtReadWriteSet{ 562 { 563 Namespace: "ns3", 564 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{ 565 { 566 CollectionName: "c3", 567 Rwset: []byte("rws-pre-image"), 568 }, 569 }, 570 }, 571 }, 572 }}, 573 } 574 575 var expectedCommittedPrivateData3 = map[uint64]*ledger.TxPvtData{} 576 577 func TestCoordinatorStoreInvalidBlock(t *testing.T) { 578 err := msptesttools.LoadMSPSetupForTesting() 579 require.NoError(t, err, fmt.Sprintf("Failed to setup local msp for testing, got err %s", err)) 580 identity := mspmgmt.GetLocalSigningIdentityOrPanic(factory.GetDefault()) 581 serializedID, err := identity.Serialize() 582 require.NoError(t, err, fmt.Sprintf("Serialize should have succeeded, got err %s", err)) 583 data := []byte{1, 2, 3} 584 signature, err := identity.Sign(data) 585 require.NoError(t, err, fmt.Sprintf("Could not sign identity, got err %s", err)) 586 peerSelfSignedData := protoutil.SignedData{ 587 Identity: serializedID, 588 Signature: signature, 589 Data: data, 590 } 591 592 metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics 593 594 hash := util2.ComputeSHA256([]byte("rws-pre-image")) 595 committer := &mocks.Committer{} 596 committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { 597 t.Fatal("Shouldn't have committed") 598 }).Return(nil) 599 cs := createcollectionStore(peerSelfSignedData).thatAcceptsAll().withMSPIdentity(identity.GetMSPIdentifier()) 600 601 store := newTransientStore(t) 602 defer store.tearDown() 603 604 assertPurged := func(txns ...string) { 605 for _, txn := range txns { 606 iterator, err := store.GetTxPvtRWSetByTxid(txn, nil) 607 if err != nil { 608 t.Fatalf("Failed iterating, got err %s", err) 609 iterator.Close() 610 return 611 } 612 res, err := iterator.Next() 613 if err != nil { 614 t.Fatalf("Failed iterating, got err %s", err) 615 iterator.Close() 616 return 617 } 618 assert.Nil(t, res) 619 iterator.Close() 620 } 621 } 622 fetcher := &fetcherMock{t: t} 623 pdFactory := &pvtDataFactory{} 624 bf := &blockFactory{ 625 channelID: "testchannelid", 626 } 627 628 idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer { 629 return mgmt.GetManagerForChain("testchannelid") 630 }) 631 block := bf.withoutMetadata().create() 632 // Scenario I: Block we got doesn't have any metadata with it 633 pvtData := pdFactory.create() 634 committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil) 635 capabilityProvider := &capabilitymock.CapabilityProvider{} 636 appCapability := &capabilitymock.AppCapabilities{} 637 capabilityProvider.On("Capabilities").Return(appCapability) 638 appCapability.On("StorePvtDataOfInvalidTx").Return(true) 639 coordinator := NewCoordinator(Support{ 640 ChainID: "testchannelid", 641 CollectionStore: cs, 642 Committer: committer, 643 Fetcher: fetcher, 644 Validator: &validatorMock{}, 645 CapabilityProvider: capabilityProvider, 646 }, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory) 647 err = coordinator.StoreBlock(block, pvtData) 648 assert.Error(t, err) 649 assert.Contains(t, err.Error(), "Block.Metadata is nil or Block.Metadata lacks a Tx filter bitmap") 650 651 // Scenario II: Validator has an error while validating the block 652 block = bf.create() 653 pvtData = pdFactory.create() 654 coordinator = NewCoordinator(Support{ 655 ChainID: "testchannelid", 656 CollectionStore: cs, 657 Committer: committer, 658 Fetcher: fetcher, 659 Validator: &validatorMock{fmt.Errorf("failed validating block")}, 660 CapabilityProvider: capabilityProvider, 661 }, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory) 662 err = coordinator.StoreBlock(block, pvtData) 663 assert.Error(t, err) 664 assert.Contains(t, err.Error(), "failed validating block") 665 666 // Scenario III: Block we got contains an inadequate length of Tx filter in the metadata 667 block = bf.withMetadataSize(100).create() 668 pvtData = pdFactory.create() 669 coordinator = NewCoordinator(Support{ 670 ChainID: "testchannelid", 671 CollectionStore: cs, 672 Committer: committer, 673 Fetcher: fetcher, 674 Validator: &validatorMock{}, 675 CapabilityProvider: capabilityProvider, 676 }, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory) 677 err = coordinator.StoreBlock(block, pvtData) 678 assert.Error(t, err) 679 assert.Contains(t, err.Error(), "block data size") 680 assert.Contains(t, err.Error(), "is different from Tx filter size") 681 682 // Scenario IV: The second transaction in the block we got is invalid, and we have no private data for that. 683 // As the StorePvtDataOfInvalidTx is set of false, if the coordinator would try to fetch private data, the 684 // test would fall because we haven't defined the mock operations for the transientstore (or for gossip) 685 // in this test. 686 var commitHappened bool 687 assertCommitHappened := func() { 688 assert.True(t, commitHappened) 689 commitHappened = false 690 } 691 digKeys := []privdatacommon.DigKey{ 692 { 693 TxId: "tx2", 694 Namespace: "ns2", 695 Collection: "c1", 696 BlockSeq: 1, 697 SeqInBlock: 1, 698 }, 699 } 700 fetcher = &fetcherMock{t: t} 701 fetcher.On("fetch", mock.Anything).expectingDigests(digKeys).expectingEndorsers(identity.GetMSPIdentifier()).Return(&privdatacommon.FetchedPvtDataContainer{ 702 AvailableElements: nil, 703 }, nil) 704 committer = &mocks.Committer{} 705 committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { 706 privateDataPassed2Ledger := args.Get(0).(*ledger.BlockAndPvtData).PvtData 707 commitHappened = true 708 // Only the first transaction's private data is passed to the ledger 709 assert.Len(t, privateDataPassed2Ledger, 1) 710 assert.Equal(t, 0, int(privateDataPassed2Ledger[0].SeqInBlock)) 711 // The private data passed to the ledger contains "ns1" and has 2 collections in it 712 assert.Len(t, privateDataPassed2Ledger[0].WriteSet.NsPvtRwset, 1) 713 assert.Equal(t, "ns1", privateDataPassed2Ledger[0].WriteSet.NsPvtRwset[0].Namespace) 714 assert.Len(t, privateDataPassed2Ledger[0].WriteSet.NsPvtRwset[0].CollectionPvtRwset, 2) 715 }).Return(nil) 716 block = bf.withInvalidTxns(1).AddTxn("tx1", "ns1", hash, "c1", "c2").AddTxn("tx2", "ns2", hash, "c1").create() 717 pvtData = pdFactory.addRWSet().addNSRWSet("ns1", "c1", "c2").create() 718 committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil) 719 720 capabilityProvider = &capabilitymock.CapabilityProvider{} 721 appCapability = &capabilitymock.AppCapabilities{} 722 capabilityProvider.On("Capabilities").Return(appCapability) 723 appCapability.On("StorePvtDataOfInvalidTx").Return(false) 724 coordinator = NewCoordinator(Support{ 725 ChainID: "testchannelid", 726 CollectionStore: cs, 727 Committer: committer, 728 Fetcher: fetcher, 729 Validator: &validatorMock{}, 730 CapabilityProvider: capabilityProvider, 731 }, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory) 732 err = coordinator.StoreBlock(block, pvtData) 733 assert.NoError(t, err) 734 assertCommitHappened() 735 // Ensure the 2nd transaction which is invalid and wasn't committed - is still purged. 736 // This is so that if we get a transaction via dissemination from an endorser, we purge it 737 // when its block comes. 738 assertPurged("tx1", "tx2") 739 740 // Scenario V: The second transaction in the block we got is invalid, and we have no private 741 // data for that in the transient store. As we have set StorePvtDataOfInvalidTx to true and 742 // configured the coordinator to skip pulling pvtData of invalid transactions from other peers, 743 // it should not store the pvtData of invalid transaction in the ledger instead a missing entry. 744 testConfig.SkipPullingInvalidTransactions = true 745 assertCommitHappened = func() { 746 assert.True(t, commitHappened) 747 commitHappened = false 748 } 749 committer = &mocks.Committer{} 750 committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { 751 blockAndPvtData := args.Get(0).(*ledger.BlockAndPvtData) 752 commitHappened = true 753 // Only the first transaction's private data is passed to the ledger 754 privateDataPassed2Ledger := blockAndPvtData.PvtData 755 assert.Len(t, privateDataPassed2Ledger, 1) 756 assert.Equal(t, 0, int(privateDataPassed2Ledger[0].SeqInBlock)) 757 // The private data passed to the ledger contains "ns1" and has 2 collections in it 758 assert.Len(t, privateDataPassed2Ledger[0].WriteSet.NsPvtRwset, 1) 759 assert.Equal(t, "ns1", privateDataPassed2Ledger[0].WriteSet.NsPvtRwset[0].Namespace) 760 assert.Len(t, privateDataPassed2Ledger[0].WriteSet.NsPvtRwset[0].CollectionPvtRwset, 2) 761 762 missingPrivateDataPassed2Ledger := blockAndPvtData.MissingPvtData 763 assert.Len(t, missingPrivateDataPassed2Ledger, 1) 764 assert.Len(t, missingPrivateDataPassed2Ledger[1], 1) 765 assert.Equal(t, missingPrivateDataPassed2Ledger[1][0].Namespace, "ns2") 766 assert.Equal(t, missingPrivateDataPassed2Ledger[1][0].Collection, "c1") 767 assert.Equal(t, missingPrivateDataPassed2Ledger[1][0].IsEligible, true) 768 769 commitOpts := args.Get(1).(*ledger.CommitOptions) 770 expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: false} 771 assert.Equal(t, expectedCommitOpts, commitOpts) 772 }).Return(nil) 773 774 block = bf.withInvalidTxns(1).AddTxn("tx1", "ns1", hash, "c1", "c2").AddTxn("tx2", "ns2", hash, "c1").create() 775 pvtData = pdFactory.addRWSet().addNSRWSet("ns1", "c1", "c2").create() 776 committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil) 777 capabilityProvider = &capabilitymock.CapabilityProvider{} 778 appCapability = &capabilitymock.AppCapabilities{} 779 capabilityProvider.On("Capabilities").Return(appCapability) 780 appCapability.On("StorePvtDataOfInvalidTx").Return(true) 781 digKeys = []privdatacommon.DigKey{} 782 fetcher = &fetcherMock{t: t} 783 fetcher.On("fetch", mock.Anything).expectingDigests(digKeys).Return(&privdatacommon.FetchedPvtDataContainer{ 784 AvailableElements: nil, 785 }, nil) 786 coordinator = NewCoordinator(Support{ 787 ChainID: "testchannelid", 788 CollectionStore: cs, 789 Committer: committer, 790 Fetcher: fetcher, 791 Validator: &validatorMock{}, 792 CapabilityProvider: capabilityProvider, 793 }, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory) 794 err = coordinator.StoreBlock(block, pvtData) 795 assert.NoError(t, err) 796 assertCommitHappened() 797 assertPurged("tx1", "tx2") 798 799 // Scenario VI: The second transaction in the block we got is invalid. As we have set the 800 // StorePvtDataOfInvalidTx to true and configured the coordinator to pull pvtData of invalid 801 // transactions, it should store the pvtData of invalid transactions in the ledger. 802 testConfig.SkipPullingInvalidTransactions = false 803 committer = &mocks.Committer{} 804 committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { 805 blockAndPvtData := args.Get(0).(*ledger.BlockAndPvtData) 806 commitHappened = true 807 // pvtData of both transactions must be present though the second transaction 808 // is invalid. 809 privateDataPassed2Ledger := blockAndPvtData.PvtData 810 assert.Len(t, privateDataPassed2Ledger, 2) 811 assert.Equal(t, 0, int(privateDataPassed2Ledger[0].SeqInBlock)) 812 assert.Equal(t, 1, int(privateDataPassed2Ledger[1].SeqInBlock)) 813 // The private data passed to the ledger for tx1 contains "ns1" and has 2 collections in it 814 assert.Len(t, privateDataPassed2Ledger[0].WriteSet.NsPvtRwset, 1) 815 assert.Equal(t, "ns1", privateDataPassed2Ledger[0].WriteSet.NsPvtRwset[0].Namespace) 816 assert.Len(t, privateDataPassed2Ledger[0].WriteSet.NsPvtRwset[0].CollectionPvtRwset, 2) 817 // The private data passed to the ledger for tx2 contains "ns2" and has 1 collection in it 818 assert.Len(t, privateDataPassed2Ledger[1].WriteSet.NsPvtRwset, 1) 819 assert.Equal(t, "ns2", privateDataPassed2Ledger[1].WriteSet.NsPvtRwset[0].Namespace) 820 assert.Len(t, privateDataPassed2Ledger[1].WriteSet.NsPvtRwset[0].CollectionPvtRwset, 1) 821 822 missingPrivateDataPassed2Ledger := blockAndPvtData.MissingPvtData 823 assert.Len(t, missingPrivateDataPassed2Ledger, 0) 824 825 commitOpts := args.Get(1).(*ledger.CommitOptions) 826 expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: false} 827 assert.Equal(t, expectedCommitOpts, commitOpts) 828 }).Return(nil) 829 830 fetcher = &fetcherMock{t: t} 831 fetcher.On("fetch", mock.Anything).expectingDigests([]privdatacommon.DigKey{ 832 { 833 TxId: "tx2", Namespace: "ns2", Collection: "c1", BlockSeq: 1, SeqInBlock: 1, 834 }, 835 }).Return(&privdatacommon.FetchedPvtDataContainer{ 836 AvailableElements: []*proto.PvtDataElement{ 837 { 838 Digest: &proto.PvtDataDigest{ 839 SeqInBlock: 1, 840 BlockSeq: 1, 841 Collection: "c1", 842 Namespace: "ns2", 843 TxId: "tx2", 844 }, 845 Payload: [][]byte{[]byte("rws-pre-image")}, 846 }, 847 }, 848 }, nil) 849 850 block = bf.withInvalidTxns(1).AddTxnWithEndorsement("tx1", "ns1", hash, "org1", true, "c1", "c2"). 851 AddTxnWithEndorsement("tx2", "ns2", hash, "org2", true, "c1").create() 852 pvtData = pdFactory.addRWSet().addNSRWSet("ns1", "c1", "c2").create() 853 committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil) 854 coordinator = NewCoordinator(Support{ 855 ChainID: "testchannelid", 856 CollectionStore: cs, 857 Committer: committer, 858 Fetcher: fetcher, 859 Validator: &validatorMock{}, 860 CapabilityProvider: capabilityProvider, 861 }, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory) 862 err = coordinator.StoreBlock(block, pvtData) 863 assert.NoError(t, err) 864 assertCommitHappened() 865 assertPurged("tx1", "tx2") 866 867 // Scenario VII: Block doesn't contain a header 868 block.Header = nil 869 err = coordinator.StoreBlock(block, pvtData) 870 assert.Error(t, err) 871 assert.Contains(t, err.Error(), "Block header is nil") 872 873 // Scenario VIII: Block doesn't contain Data 874 block.Data = nil 875 err = coordinator.StoreBlock(block, pvtData) 876 assert.Error(t, err) 877 assert.Contains(t, err.Error(), "Block data is empty") 878 } 879 880 func TestCoordinatorToFilterOutPvtRWSetsWithWrongHash(t *testing.T) { 881 /* 882 Test case, where peer receives new block for commit 883 it has ns1:c1 in transient store, while it has wrong 884 hash, hence it will fetch ns1:c1 from other peers 885 */ 886 err := msptesttools.LoadMSPSetupForTesting() 887 require.NoError(t, err, fmt.Sprintf("Failed to setup local msp for testing, got err %s", err)) 888 identity := mspmgmt.GetLocalSigningIdentityOrPanic(factory.GetDefault()) 889 serializedID, err := identity.Serialize() 890 require.NoError(t, err, fmt.Sprintf("Serialize should have succeeded, got err %s", err)) 891 data := []byte{1, 2, 3} 892 signature, err := identity.Sign(data) 893 require.NoError(t, err, fmt.Sprintf("Could not sign identity, got err %s", err)) 894 peerSelfSignedData := protoutil.SignedData{ 895 Identity: serializedID, 896 Signature: signature, 897 Data: data, 898 } 899 900 expectedPvtData := map[uint64]*ledger.TxPvtData{ 901 0: {SeqInBlock: 0, WriteSet: &rwset.TxPvtReadWriteSet{ 902 DataModel: rwset.TxReadWriteSet_KV, 903 NsPvtRwset: []*rwset.NsPvtReadWriteSet{ 904 { 905 Namespace: "ns1", 906 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{ 907 { 908 CollectionName: "c1", 909 Rwset: []byte("rws-original"), 910 }, 911 }, 912 }, 913 }, 914 }}, 915 } 916 917 cs := createcollectionStore(peerSelfSignedData).thatAcceptsAll().withMSPIdentity(identity.GetMSPIdentifier()) 918 committer := &mocks.Committer{} 919 920 store := newTransientStore(t) 921 defer store.tearDown() 922 923 assertPurged := func(txns ...string) { 924 for _, txn := range txns { 925 iterator, err := store.GetTxPvtRWSetByTxid(txn, nil) 926 if err != nil { 927 t.Fatalf("Failed iterating, got err %s", err) 928 iterator.Close() 929 return 930 } 931 res, err := iterator.Next() 932 if err != nil { 933 t.Fatalf("Failed iterating, got err %s", err) 934 iterator.Close() 935 return 936 } 937 assert.Nil(t, res) 938 iterator.Close() 939 } 940 } 941 942 fetcher := &fetcherMock{t: t} 943 944 var commitHappened bool 945 946 committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { 947 privateDataPassed2Ledger := args.Get(0).(*ledger.BlockAndPvtData).PvtData 948 assert.True(t, reflect.DeepEqual(flattenTxPvtDataMap(privateDataPassed2Ledger), 949 flattenTxPvtDataMap(expectedPvtData))) 950 commitHappened = true 951 952 commitOpts := args.Get(1).(*ledger.CommitOptions) 953 expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: false} 954 assert.Equal(t, expectedCommitOpts, commitOpts) 955 }).Return(nil) 956 957 hash := util2.ComputeSHA256([]byte("rws-original")) 958 bf := &blockFactory{ 959 channelID: "testchannelid", 960 } 961 962 idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer { 963 return mgmt.GetManagerForChain("testchannelid") 964 }) 965 966 block := bf.AddTxnWithEndorsement("tx1", "ns1", hash, "org1", true, "c1").create() 967 committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil) 968 969 metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics 970 971 capabilityProvider := &capabilitymock.CapabilityProvider{} 972 appCapability := &capabilitymock.AppCapabilities{} 973 capabilityProvider.On("Capabilities").Return(appCapability) 974 appCapability.On("StorePvtDataOfInvalidTx").Return(true) 975 coordinator := NewCoordinator(Support{ 976 ChainID: "testchannelid", 977 CollectionStore: cs, 978 Committer: committer, 979 Fetcher: fetcher, 980 Validator: &validatorMock{}, 981 CapabilityProvider: capabilityProvider, 982 }, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory) 983 984 fetcher.On("fetch", mock.Anything).expectingDigests([]privdatacommon.DigKey{ 985 { 986 TxId: "tx1", Namespace: "ns1", Collection: "c1", BlockSeq: 1, 987 }, 988 }).Return(&privdatacommon.FetchedPvtDataContainer{ 989 AvailableElements: []*proto.PvtDataElement{ 990 { 991 Digest: &proto.PvtDataDigest{ 992 BlockSeq: 1, 993 Collection: "c1", 994 Namespace: "ns1", 995 TxId: "tx1", 996 }, 997 Payload: [][]byte{[]byte("rws-original")}, 998 }, 999 }, 1000 }, nil) 1001 1002 coordinator.StoreBlock(block, nil) 1003 // Assert blocks was eventually committed 1004 assert.True(t, commitHappened) 1005 1006 // Assert transaction has been purged 1007 assertPurged("tx1") 1008 } 1009 1010 func TestCoordinatorStoreBlock(t *testing.T) { 1011 err := msptesttools.LoadMSPSetupForTesting() 1012 require.NoError(t, err, fmt.Sprintf("Failed to setup local msp for testing, got err %s", err)) 1013 identity := mspmgmt.GetLocalSigningIdentityOrPanic(factory.GetDefault()) 1014 serializedID, err := identity.Serialize() 1015 require.NoError(t, err, fmt.Sprintf("Serialize should have succeeded, got err %s", err)) 1016 data := []byte{1, 2, 3} 1017 signature, err := identity.Sign(data) 1018 require.NoError(t, err, fmt.Sprintf("Could not sign identity, got err %s", err)) 1019 peerSelfSignedData := protoutil.SignedData{ 1020 Identity: serializedID, 1021 Signature: signature, 1022 Data: data, 1023 } 1024 // Green path test, all private data should be obtained successfully 1025 1026 cs := createcollectionStore(peerSelfSignedData).thatAcceptsAll().withMSPIdentity(identity.GetMSPIdentifier()) 1027 1028 var commitHappened bool 1029 assertCommitHappened := func() { 1030 assert.True(t, commitHappened) 1031 commitHappened = false 1032 } 1033 committer := &mocks.Committer{} 1034 committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { 1035 privateDataPassed2Ledger := args.Get(0).(*ledger.BlockAndPvtData).PvtData 1036 assert.True(t, reflect.DeepEqual(flattenTxPvtDataMap(privateDataPassed2Ledger), 1037 flattenTxPvtDataMap(expectedCommittedPrivateData1))) 1038 commitHappened = true 1039 1040 commitOpts := args.Get(1).(*ledger.CommitOptions) 1041 expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: false} 1042 assert.Equal(t, expectedCommitOpts, commitOpts) 1043 }).Return(nil) 1044 1045 store := newTransientStore(t) 1046 defer store.tearDown() 1047 1048 assertPurged := func(txns ...string) { 1049 for _, txn := range txns { 1050 iterator, err := store.GetTxPvtRWSetByTxid(txn, nil) 1051 if err != nil { 1052 t.Fatalf("Failed iterating, got err %s", err) 1053 iterator.Close() 1054 return 1055 } 1056 res, err := iterator.Next() 1057 if err != nil { 1058 t.Fatalf("Failed iterating, got err %s", err) 1059 iterator.Close() 1060 return 1061 } 1062 assert.Nil(t, res) 1063 iterator.Close() 1064 } 1065 } 1066 1067 fetcher := &fetcherMock{t: t} 1068 1069 hash := util2.ComputeSHA256([]byte("rws-pre-image")) 1070 pdFactory := &pvtDataFactory{} 1071 bf := &blockFactory{ 1072 channelID: "testchannelid", 1073 } 1074 1075 idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer { 1076 return mgmt.GetManagerForChain("testchannelid") 1077 }) 1078 1079 block := bf.AddTxnWithEndorsement("tx1", "ns1", hash, "org1", true, "c1", "c2"). 1080 AddTxnWithEndorsement("tx2", "ns2", hash, "org2", true, "c1").create() 1081 1082 metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics 1083 1084 fmt.Println("Scenario I") 1085 // Scenario I: Block we got has sufficient private data alongside it. 1086 // If the coordinator tries fetching from the transientstore, or peers it would result in panic, 1087 // because we didn't define yet the "On(...)" invocation of the transient store or other peers. 1088 pvtData := pdFactory.addRWSet().addNSRWSet("ns1", "c1", "c2").addRWSet().addNSRWSet("ns2", "c1").create() 1089 committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil) 1090 1091 capabilityProvider := &capabilitymock.CapabilityProvider{} 1092 appCapability := &capabilitymock.AppCapabilities{} 1093 capabilityProvider.On("Capabilities").Return(appCapability) 1094 appCapability.On("StorePvtDataOfInvalidTx").Return(true) 1095 coordinator := NewCoordinator(Support{ 1096 ChainID: "testchannelid", 1097 CollectionStore: cs, 1098 Committer: committer, 1099 Fetcher: fetcher, 1100 Validator: &validatorMock{}, 1101 CapabilityProvider: capabilityProvider, 1102 }, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory) 1103 err = coordinator.StoreBlock(block, pvtData) 1104 assert.NoError(t, err) 1105 assertCommitHappened() 1106 assertPurged("tx1", "tx2") 1107 1108 fmt.Println("Scenario II") 1109 // Scenario II: Block we got doesn't have sufficient private data alongside it, 1110 // it is missing ns1: c2, but the data exists in the transient store 1111 store.Persist("tx1", 1, &tspb.TxPvtReadWriteSetWithConfigInfo{ 1112 PvtRwset: &rwset.TxPvtReadWriteSet{ 1113 NsPvtRwset: []*rwset.NsPvtReadWriteSet{ 1114 { 1115 Namespace: "ns1", 1116 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{ 1117 { 1118 CollectionName: "c2", 1119 Rwset: []byte("rws-pre-image"), 1120 }, 1121 }, 1122 }, 1123 }, 1124 }, 1125 CollectionConfigs: make(map[string]*peer.CollectionConfigPackage), 1126 }) 1127 pvtData = pdFactory.addRWSet().addNSRWSet("ns1", "c1").addRWSet().addNSRWSet("ns2", "c1").create() 1128 err = coordinator.StoreBlock(block, pvtData) 1129 assert.NoError(t, err) 1130 assertCommitHappened() 1131 assertPurged("tx1", "tx2") 1132 1133 fmt.Println("Scenario III") 1134 // Scenario III: Block doesn't have sufficient private data alongside it, 1135 // it is missing ns1: c2, and the data exists in the transient store, 1136 // but it is also missing ns2: c1, and that data doesn't exist in the transient store - but in a peer. 1137 // Additionally, the coordinator should pass an endorser identity of org1, but not of org2, since 1138 // the MemberOrgs() call doesn't return org2 but only org0 and org1. 1139 fetcher.On("fetch", mock.Anything).expectingDigests([]privdatacommon.DigKey{ 1140 { 1141 TxId: "tx1", Namespace: "ns1", Collection: "c2", BlockSeq: 1, 1142 }, 1143 { 1144 TxId: "tx2", Namespace: "ns2", Collection: "c1", BlockSeq: 1, SeqInBlock: 1, 1145 }, 1146 }).Return(&privdatacommon.FetchedPvtDataContainer{ 1147 AvailableElements: []*proto.PvtDataElement{ 1148 { 1149 Digest: &proto.PvtDataDigest{ 1150 BlockSeq: 1, 1151 Collection: "c2", 1152 Namespace: "ns1", 1153 TxId: "tx1", 1154 }, 1155 Payload: [][]byte{[]byte("rws-pre-image")}, 1156 }, 1157 { 1158 Digest: &proto.PvtDataDigest{ 1159 SeqInBlock: 1, 1160 BlockSeq: 1, 1161 Collection: "c1", 1162 Namespace: "ns2", 1163 TxId: "tx2", 1164 }, 1165 Payload: [][]byte{[]byte("rws-pre-image")}, 1166 }, 1167 }, 1168 }, nil) 1169 pvtData = pdFactory.addRWSet().addNSRWSet("ns1", "c1").create() 1170 err = coordinator.StoreBlock(block, pvtData) 1171 assertPurged("tx1", "tx2") 1172 assert.NoError(t, err) 1173 assertCommitHappened() 1174 1175 fmt.Println("Scenario IV") 1176 // Scenario IV: Block came with more than sufficient private data alongside it, some of it is redundant. 1177 pvtData = pdFactory.addRWSet().addNSRWSet("ns1", "c1", "c2", "c3"). 1178 addRWSet().addNSRWSet("ns2", "c1", "c3").addRWSet().addNSRWSet("ns1", "c4").create() 1179 err = coordinator.StoreBlock(block, pvtData) 1180 assertPurged("tx1", "tx2") 1181 assert.NoError(t, err) 1182 assertCommitHappened() 1183 1184 fmt.Println("Scenario V") 1185 // Scenario V: Block we got has private data alongside it but coordinator cannot retrieve collection access 1186 // policy of collections due to databse unavailability error. 1187 // we verify that the error propagates properly. 1188 mockCs := &mocks.CollectionStore{} 1189 mockCs.On("RetrieveCollectionConfig", mock.Anything).Return(nil, errors.New("test error")) 1190 coordinator = NewCoordinator(Support{ 1191 ChainID: "testchannelid", 1192 CollectionStore: mockCs, 1193 Committer: committer, 1194 Fetcher: fetcher, 1195 Validator: &validatorMock{}, 1196 CapabilityProvider: capabilityProvider, 1197 }, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory) 1198 err = coordinator.StoreBlock(block, nil) 1199 assert.Error(t, err) 1200 assert.Equal(t, "test error", err.Error()) 1201 1202 fmt.Println("Scenario VI") 1203 // Scenario VI: Block didn't get with any private data alongside it, and the transient store 1204 // has some problem. 1205 // In this case, we should try to fetch data from peers. 1206 block = bf.AddTxn("tx3", "ns3", hash, "c3").create() 1207 fetcher = &fetcherMock{t: t} 1208 fetcher.On("fetch", mock.Anything).expectingDigests([]privdatacommon.DigKey{ 1209 { 1210 TxId: "tx3", Namespace: "ns3", Collection: "c3", BlockSeq: 1, 1211 }, 1212 }).Return(&privdatacommon.FetchedPvtDataContainer{ 1213 AvailableElements: []*proto.PvtDataElement{ 1214 { 1215 Digest: &proto.PvtDataDigest{ 1216 BlockSeq: 1, 1217 Collection: "c3", 1218 Namespace: "ns3", 1219 TxId: "tx3", 1220 }, 1221 Payload: [][]byte{[]byte("rws-pre-image")}, 1222 }, 1223 }, 1224 }, nil) 1225 committer = &mocks.Committer{} 1226 committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { 1227 privateDataPassed2Ledger := args.Get(0).(*ledger.BlockAndPvtData).PvtData 1228 assert.True(t, reflect.DeepEqual(flattenTxPvtDataMap(privateDataPassed2Ledger), 1229 flattenTxPvtDataMap(expectedCommittedPrivateData2))) 1230 commitHappened = true 1231 1232 commitOpts := args.Get(1).(*ledger.CommitOptions) 1233 expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: false} 1234 assert.Equal(t, expectedCommitOpts, commitOpts) 1235 }).Return(nil) 1236 committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil) 1237 coordinator = NewCoordinator(Support{ 1238 ChainID: "testchannelid", 1239 CollectionStore: cs, 1240 Committer: committer, 1241 Fetcher: fetcher, 1242 Validator: &validatorMock{}, 1243 CapabilityProvider: capabilityProvider, 1244 }, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory) 1245 err = coordinator.StoreBlock(block, nil) 1246 assertPurged("tx3") 1247 assert.NoError(t, err) 1248 assertCommitHappened() 1249 1250 fmt.Println("Scenario VII") 1251 // Scenario VII: Block contains 2 transactions, and the peer is eligible for only tx3-ns3-c3. 1252 // Also, the blocks comes with a private data for tx3-ns3-c3 so that the peer won't have to fetch the 1253 // private data from the transient store or peers, and in fact- if it attempts to fetch the data it's not eligible 1254 // for from the transient store or from peers - the test would fail because the Mock wasn't initialized. 1255 block = bf.AddTxn("tx3", "ns3", hash, "c3", "c2", "c1").AddTxn("tx1", "ns1", hash, "c1").create() 1256 cs = createcollectionStore(peerSelfSignedData).thatAccepts(CollectionCriteria{ 1257 Collection: "c3", 1258 Namespace: "ns3", 1259 Channel: "testchannelid", 1260 }).withMSPIdentity(identity.GetMSPIdentifier()) 1261 fetcher = &fetcherMock{t: t} 1262 committer = &mocks.Committer{} 1263 committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { 1264 privateDataPassed2Ledger := args.Get(0).(*ledger.BlockAndPvtData).PvtData 1265 assert.True(t, reflect.DeepEqual(flattenTxPvtDataMap(privateDataPassed2Ledger), 1266 flattenTxPvtDataMap(expectedCommittedPrivateData2))) 1267 commitHappened = true 1268 1269 commitOpts := args.Get(1).(*ledger.CommitOptions) 1270 expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: false} 1271 assert.Equal(t, expectedCommitOpts, commitOpts) 1272 }).Return(nil) 1273 committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil) 1274 coordinator = NewCoordinator(Support{ 1275 ChainID: "testchannelid", 1276 CollectionStore: cs, 1277 Committer: committer, 1278 Fetcher: fetcher, 1279 Validator: &validatorMock{}, 1280 CapabilityProvider: capabilityProvider, 1281 }, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory) 1282 1283 pvtData = pdFactory.addRWSet().addNSRWSet("ns3", "c3").create() 1284 err = coordinator.StoreBlock(block, pvtData) 1285 assert.NoError(t, err) 1286 assertCommitHappened() 1287 // In any case, all transactions in the block are purged from the transient store 1288 assertPurged("tx3", "tx1") 1289 } 1290 1291 func TestCoordinatorStoreBlockWhenPvtDataExistInLedger(t *testing.T) { 1292 err := msptesttools.LoadMSPSetupForTesting() 1293 require.NoError(t, err, fmt.Sprintf("Failed to setup local msp for testing, got err %s", err)) 1294 identity := mspmgmt.GetLocalSigningIdentityOrPanic(factory.GetDefault()) 1295 serializedID, err := identity.Serialize() 1296 require.NoError(t, err, fmt.Sprintf("Serialize should have succeeded, got err %s", err)) 1297 data := []byte{1, 2, 3} 1298 signature, err := identity.Sign(data) 1299 require.NoError(t, err, fmt.Sprintf("Could not sign identity, got err %s", err)) 1300 peerSelfSignedData := protoutil.SignedData{ 1301 Identity: serializedID, 1302 Signature: signature, 1303 Data: data, 1304 } 1305 1306 var commitHappened bool 1307 assertCommitHappened := func() { 1308 assert.True(t, commitHappened) 1309 commitHappened = false 1310 } 1311 committer := &mocks.Committer{} 1312 committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { 1313 privateDataPassed2Ledger := args.Get(0).(*ledger.BlockAndPvtData).PvtData 1314 assert.Equal(t, ledger.TxPvtDataMap{}, privateDataPassed2Ledger) 1315 commitOpts := args.Get(1).(*ledger.CommitOptions) 1316 expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: true} 1317 assert.Equal(t, expectedCommitOpts, commitOpts) 1318 commitHappened = true 1319 }).Return(nil) 1320 1321 fetcher := &fetcherMock{t: t} 1322 1323 hash := util2.ComputeSHA256([]byte("rws-pre-image")) 1324 pdFactory := &pvtDataFactory{} 1325 bf := &blockFactory{ 1326 channelID: "testchannelid", 1327 } 1328 1329 idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer { 1330 return mgmt.GetManagerForChain("testchannelid") 1331 }) 1332 1333 block := bf.AddTxnWithEndorsement("tx1", "ns1", hash, "org1", true, "c1", "c2"). 1334 AddTxnWithEndorsement("tx2", "ns2", hash, "org2", true, "c1").create() 1335 1336 // Scenario: Block we got has been reprocessed and hence the sufficient pvtData is present 1337 // in the local pvtdataStore itself. The pvtData would be fetched from the local pvtdataStore. 1338 // If the coordinator tries fetching from the transientstore, or peers it would result in panic, 1339 // because we didn't define yet the "On(...)" invocation of the transient store or other peers. 1340 pvtData := pdFactory.addRWSet().addNSRWSet("ns1", "c1", "c2").addRWSet().addNSRWSet("ns2", "c1").create() 1341 committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(true, nil) 1342 1343 metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics 1344 1345 capabilityProvider := &capabilitymock.CapabilityProvider{} 1346 appCapability := &capabilitymock.AppCapabilities{} 1347 capabilityProvider.On("Capabilities").Return(appCapability) 1348 appCapability.On("StorePvtDataOfInvalidTx").Return(true) 1349 coordinator := NewCoordinator(Support{ 1350 ChainID: "testchannelid", 1351 CollectionStore: nil, 1352 Committer: committer, 1353 Fetcher: fetcher, 1354 Validator: &validatorMock{}, 1355 CapabilityProvider: capabilityProvider, 1356 }, nil, peerSelfSignedData, metrics, testConfig, idDeserializerFactory) 1357 err = coordinator.StoreBlock(block, pvtData) 1358 assert.NoError(t, err) 1359 assertCommitHappened() 1360 } 1361 1362 func TestProceedWithoutPrivateData(t *testing.T) { 1363 // Scenario: we are missing private data (c2 in ns3) and it cannot be obtained from any peer. 1364 // Block needs to be committed with missing private data. 1365 err := msptesttools.LoadMSPSetupForTesting() 1366 require.NoError(t, err, fmt.Sprintf("Failed to setup local msp for testing, got err %s", err)) 1367 identity := mspmgmt.GetLocalSigningIdentityOrPanic(factory.GetDefault()) 1368 serializedID, err := identity.Serialize() 1369 require.NoError(t, err, fmt.Sprintf("Serialize should have succeeded, got err %s", err)) 1370 data := []byte{1, 2, 3} 1371 signature, err := identity.Sign(data) 1372 require.NoError(t, err, fmt.Sprintf("Could not sign identity, got err %s", err)) 1373 peerSelfSignedData := protoutil.SignedData{ 1374 Identity: serializedID, 1375 Signature: signature, 1376 Data: data, 1377 } 1378 cs := createcollectionStore(peerSelfSignedData).thatAcceptsAll().withMSPIdentity(identity.GetMSPIdentifier()) 1379 var commitHappened bool 1380 assertCommitHappened := func() { 1381 assert.True(t, commitHappened) 1382 commitHappened = false 1383 } 1384 committer := &mocks.Committer{} 1385 committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { 1386 blockAndPrivateData := args.Get(0).(*ledger.BlockAndPvtData) 1387 privateDataPassed2Ledger := blockAndPrivateData.PvtData 1388 assert.True(t, reflect.DeepEqual(flattenTxPvtDataMap(privateDataPassed2Ledger), 1389 flattenTxPvtDataMap(expectedCommittedPrivateData2))) 1390 missingPrivateData := blockAndPrivateData.MissingPvtData 1391 expectedMissingPvtData := make(ledger.TxMissingPvtDataMap) 1392 expectedMissingPvtData.Add(0, "ns3", "c2", true) 1393 assert.Equal(t, expectedMissingPvtData, missingPrivateData) 1394 commitHappened = true 1395 1396 commitOpts := args.Get(1).(*ledger.CommitOptions) 1397 expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: false} 1398 assert.Equal(t, expectedCommitOpts, commitOpts) 1399 }).Return(nil) 1400 1401 store := newTransientStore(t) 1402 defer store.tearDown() 1403 1404 assertPurged := func(txns ...string) { 1405 for _, txn := range txns { 1406 iterator, err := store.GetTxPvtRWSetByTxid(txn, nil) 1407 if err != nil { 1408 t.Fatalf("Failed iterating, got err %s", err) 1409 iterator.Close() 1410 return 1411 } 1412 res, err := iterator.Next() 1413 if err != nil { 1414 t.Fatalf("Failed iterating, got err %s", err) 1415 iterator.Close() 1416 return 1417 } 1418 assert.Nil(t, res) 1419 iterator.Close() 1420 } 1421 } 1422 1423 fetcher := &fetcherMock{t: t} 1424 // Have the peer return in response to the pull, a private data with a non matching hash 1425 fetcher.On("fetch", mock.Anything).expectingDigests([]privdatacommon.DigKey{ 1426 { 1427 TxId: "tx1", Namespace: "ns3", Collection: "c2", BlockSeq: 1, 1428 }, 1429 }).Return(&privdatacommon.FetchedPvtDataContainer{ 1430 AvailableElements: []*proto.PvtDataElement{ 1431 { 1432 Digest: &proto.PvtDataDigest{ 1433 BlockSeq: 1, 1434 Collection: "c2", 1435 Namespace: "ns3", 1436 TxId: "tx1", 1437 }, 1438 Payload: [][]byte{[]byte("wrong pre-image")}, 1439 }, 1440 }, 1441 }, nil) 1442 1443 hash := util2.ComputeSHA256([]byte("rws-pre-image")) 1444 pdFactory := &pvtDataFactory{} 1445 bf := &blockFactory{ 1446 channelID: "testchannelid", 1447 } 1448 1449 idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer { 1450 return mgmt.GetManagerForChain("testchannelid") 1451 }) 1452 1453 metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics 1454 1455 block := bf.AddTxn("tx1", "ns3", hash, "c3", "c2").create() 1456 pvtData := pdFactory.addRWSet().addNSRWSet("ns3", "c3").create() 1457 committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil) 1458 1459 capabilityProvider := &capabilitymock.CapabilityProvider{} 1460 appCapability := &capabilitymock.AppCapabilities{} 1461 capabilityProvider.On("Capabilities").Return(appCapability) 1462 appCapability.On("StorePvtDataOfInvalidTx").Return(true) 1463 coordinator := NewCoordinator(Support{ 1464 ChainID: "testchannelid", 1465 CollectionStore: cs, 1466 Committer: committer, 1467 Fetcher: fetcher, 1468 Validator: &validatorMock{}, 1469 CapabilityProvider: capabilityProvider, 1470 }, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory) 1471 err = coordinator.StoreBlock(block, pvtData) 1472 assert.NoError(t, err) 1473 assertCommitHappened() 1474 assertPurged("tx1") 1475 } 1476 1477 func TestProceedWithInEligiblePrivateData(t *testing.T) { 1478 // Scenario: we are missing private data (c2 in ns3) and it cannot be obtained from any peer. 1479 // Block needs to be committed with missing private data. 1480 err := msptesttools.LoadMSPSetupForTesting() 1481 require.NoError(t, err, fmt.Sprintf("Failed to setup local msp for testing, got err %s", err)) 1482 identity := mspmgmt.GetLocalSigningIdentityOrPanic(factory.GetDefault()) 1483 serializedID, err := identity.Serialize() 1484 require.NoError(t, err, fmt.Sprintf("Serialize should have succeeded, got err %s", err)) 1485 data := []byte{1, 2, 3} 1486 signature, err := identity.Sign(data) 1487 require.NoError(t, err, fmt.Sprintf("Could not sign identity, got err %s", err)) 1488 peerSelfSignedData := protoutil.SignedData{ 1489 Identity: serializedID, 1490 Signature: signature, 1491 Data: data, 1492 } 1493 1494 cs := createcollectionStore(peerSelfSignedData).thatAcceptsNone().withMSPIdentity(identity.GetMSPIdentifier()) 1495 1496 var commitHappened bool 1497 assertCommitHappened := func() { 1498 assert.True(t, commitHappened) 1499 commitHappened = false 1500 } 1501 committer := &mocks.Committer{} 1502 committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { 1503 blockAndPrivateData := args.Get(0).(*ledger.BlockAndPvtData) 1504 privateDataPassed2Ledger := blockAndPrivateData.PvtData 1505 assert.True(t, reflect.DeepEqual(flattenTxPvtDataMap(privateDataPassed2Ledger), 1506 flattenTxPvtDataMap(expectedCommittedPrivateData3))) 1507 missingPrivateData := blockAndPrivateData.MissingPvtData 1508 expectedMissingPvtData := make(ledger.TxMissingPvtDataMap) 1509 expectedMissingPvtData.Add(0, "ns3", "c2", false) 1510 assert.Equal(t, expectedMissingPvtData, missingPrivateData) 1511 commitHappened = true 1512 1513 commitOpts := args.Get(1).(*ledger.CommitOptions) 1514 expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: false} 1515 assert.Equal(t, expectedCommitOpts, commitOpts) 1516 }).Return(nil) 1517 1518 hash := util2.ComputeSHA256([]byte("rws-pre-image")) 1519 bf := &blockFactory{ 1520 channelID: "testchannelid", 1521 } 1522 1523 idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer { 1524 return mgmt.GetManagerForChain("testchannelid") 1525 }) 1526 1527 block := bf.AddTxn("tx1", "ns3", hash, "c2").create() 1528 committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil) 1529 1530 metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics 1531 1532 capabilityProvider := &capabilitymock.CapabilityProvider{} 1533 appCapability := &capabilitymock.AppCapabilities{} 1534 capabilityProvider.On("Capabilities").Return(appCapability) 1535 appCapability.On("StorePvtDataOfInvalidTx").Return(true) 1536 coordinator := NewCoordinator(Support{ 1537 ChainID: "testchannelid", 1538 CollectionStore: cs, 1539 Committer: committer, 1540 Fetcher: nil, 1541 Validator: &validatorMock{}, 1542 CapabilityProvider: capabilityProvider, 1543 }, nil, peerSelfSignedData, metrics, testConfig, idDeserializerFactory) 1544 err = coordinator.StoreBlock(block, nil) 1545 assert.NoError(t, err) 1546 assertCommitHappened() 1547 } 1548 1549 func TestCoordinatorGetBlocks(t *testing.T) { 1550 metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics 1551 err := msptesttools.LoadMSPSetupForTesting() 1552 require.NoError(t, err, fmt.Sprintf("Failed to setup local msp for testing, got err %s", err)) 1553 identity := mspmgmt.GetLocalSigningIdentityOrPanic(factory.GetDefault()) 1554 serializedID, err := identity.Serialize() 1555 require.NoError(t, err, fmt.Sprintf("Serialize should have succeeded, got err %s", err)) 1556 data := []byte{1, 2, 3} 1557 signature, err := identity.Sign(data) 1558 require.NoError(t, err, fmt.Sprintf("Could not sign identity, got err %s", err)) 1559 peerSelfSignedData := protoutil.SignedData{ 1560 Identity: serializedID, 1561 Signature: signature, 1562 Data: data, 1563 } 1564 cs := createcollectionStore(peerSelfSignedData).thatAcceptsAll().withMSPIdentity(identity.GetMSPIdentifier()) 1565 committer := &mocks.Committer{} 1566 1567 store := newTransientStore(t) 1568 defer store.tearDown() 1569 1570 idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer { 1571 return mgmt.GetManagerForChain("testchannelid") 1572 }) 1573 1574 fetcher := &fetcherMock{t: t} 1575 committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil) 1576 1577 capabilityProvider := &capabilitymock.CapabilityProvider{} 1578 appCapability := &capabilitymock.AppCapabilities{} 1579 capabilityProvider.On("Capabilities").Return(appCapability) 1580 appCapability.On("StorePvtDataOfInvalidTx").Return(true) 1581 coordinator := NewCoordinator(Support{ 1582 ChainID: "testchannelid", 1583 CollectionStore: cs, 1584 Committer: committer, 1585 Fetcher: fetcher, 1586 Validator: &validatorMock{}, 1587 CapabilityProvider: capabilityProvider, 1588 }, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory) 1589 1590 hash := util2.ComputeSHA256([]byte("rws-pre-image")) 1591 bf := &blockFactory{ 1592 channelID: "testchannelid", 1593 } 1594 block := bf.AddTxn("tx1", "ns1", hash, "c1", "c2").AddTxn("tx2", "ns2", hash, "c1").create() 1595 1596 // Green path - block and private data is returned, but the requester isn't eligible for all the private data, 1597 // but only to a subset of it. 1598 cs = createcollectionStore(peerSelfSignedData).thatAccepts(CollectionCriteria{ 1599 Namespace: "ns1", 1600 Collection: "c2", 1601 Channel: "testchannelid", 1602 }).withMSPIdentity(identity.GetMSPIdentifier()) 1603 committer.Mock = mock.Mock{} 1604 committer.On("GetPvtDataAndBlockByNum", mock.Anything).Return(&ledger.BlockAndPvtData{ 1605 Block: block, 1606 PvtData: expectedCommittedPrivateData1, 1607 }, nil) 1608 committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil) 1609 coordinator = NewCoordinator(Support{ 1610 ChainID: "testchannelid", 1611 CollectionStore: cs, 1612 Committer: committer, 1613 Fetcher: fetcher, 1614 Validator: &validatorMock{}, 1615 CapabilityProvider: capabilityProvider, 1616 }, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory) 1617 expectedPrivData := (&pvtDataFactory{}).addRWSet().addNSRWSet("ns1", "c2").create() 1618 block2, returnedPrivateData, err := coordinator.GetPvtDataAndBlockByNum(1, peerSelfSignedData) 1619 assert.NoError(t, err) 1620 assert.Equal(t, block, block2) 1621 assert.Equal(t, expectedPrivData, []*ledger.TxPvtData(returnedPrivateData)) 1622 1623 // Bad path - error occurs when trying to retrieve the block and private data 1624 committer.Mock = mock.Mock{} 1625 committer.On("GetPvtDataAndBlockByNum", mock.Anything).Return(nil, errors.New("uh oh")) 1626 block2, returnedPrivateData, err = coordinator.GetPvtDataAndBlockByNum(1, peerSelfSignedData) 1627 assert.Nil(t, block2) 1628 assert.Empty(t, returnedPrivateData) 1629 assert.Error(t, err) 1630 } 1631 1632 func TestPurgeBelowHeight(t *testing.T) { 1633 conf := testConfig 1634 conf.TransientBlockRetention = 5 1635 peerSelfSignedData := protoutil.SignedData{} 1636 cs := createcollectionStore(peerSelfSignedData).thatAcceptsAll() 1637 1638 committer := &mocks.Committer{} 1639 committer.On("CommitLegacy", mock.Anything, mock.Anything).Return(nil) 1640 1641 store := newTransientStore(t) 1642 defer store.tearDown() 1643 1644 // store 9 data sets initially 1645 for i := 0; i < 9; i++ { 1646 txID := fmt.Sprintf("tx%d", i+1) 1647 store.Persist(txID, uint64(i), &tspb.TxPvtReadWriteSetWithConfigInfo{ 1648 PvtRwset: &rwset.TxPvtReadWriteSet{ 1649 NsPvtRwset: []*rwset.NsPvtReadWriteSet{ 1650 { 1651 Namespace: "ns1", 1652 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{ 1653 { 1654 CollectionName: "c1", 1655 Rwset: []byte("rws-pre-image"), 1656 }, 1657 }, 1658 }, 1659 }, 1660 }, 1661 CollectionConfigs: make(map[string]*peer.CollectionConfigPackage), 1662 }) 1663 } 1664 assertPurged := func(purged bool) { 1665 numTx := 9 1666 if purged { 1667 numTx = 10 1668 } 1669 for i := 1; i <= numTx; i++ { 1670 txID := fmt.Sprintf("tx%d", i) 1671 iterator, err := store.GetTxPvtRWSetByTxid(txID, nil) 1672 if err != nil { 1673 t.Fatalf("Failed iterating, got err %s", err) 1674 iterator.Close() 1675 return 1676 } 1677 res, err := iterator.Next() 1678 if err != nil { 1679 t.Fatalf("Failed iterating, got err %s", err) 1680 iterator.Close() 1681 return 1682 } 1683 if (i < 6 || i == numTx) && purged { 1684 assert.Nil(t, res) 1685 } else { 1686 assert.NotNil(t, res) 1687 } 1688 iterator.Close() 1689 } 1690 } 1691 1692 fetcher := &fetcherMock{t: t} 1693 1694 bf := &blockFactory{ 1695 channelID: "testchannelid", 1696 } 1697 1698 idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer { 1699 return mgmt.GetManagerForChain("testchannelid") 1700 }) 1701 1702 pdFactory := &pvtDataFactory{} 1703 1704 committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil) 1705 1706 metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics 1707 1708 capabilityProvider := &capabilitymock.CapabilityProvider{} 1709 appCapability := &capabilitymock.AppCapabilities{} 1710 capabilityProvider.On("Capabilities").Return(appCapability) 1711 appCapability.On("StorePvtDataOfInvalidTx").Return(true) 1712 coordinator := NewCoordinator(Support{ 1713 ChainID: "testchannelid", 1714 CollectionStore: cs, 1715 Committer: committer, 1716 Fetcher: fetcher, 1717 Validator: &validatorMock{}, 1718 CapabilityProvider: capabilityProvider, 1719 }, store.store, peerSelfSignedData, metrics, conf, idDeserializerFactory) 1720 1721 hash := util2.ComputeSHA256([]byte("rws-pre-image")) 1722 block := bf.AddTxn("tx10", "ns1", hash, "c1").create() 1723 block.Header.Number = 10 1724 pvtData := pdFactory.addRWSet().addNSRWSet("ns1", "c1").create() 1725 // test no blocks purged yet 1726 assertPurged(false) 1727 err := coordinator.StoreBlock(block, pvtData) 1728 assert.NoError(t, err) 1729 // test first 6 blocks were purged 1730 assertPurged(true) 1731 } 1732 1733 func TestCoordinatorStorePvtData(t *testing.T) { 1734 metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics 1735 cs := createcollectionStore(protoutil.SignedData{}).thatAcceptsAll() 1736 committer := &mocks.Committer{} 1737 1738 store := newTransientStore(t) 1739 defer store.tearDown() 1740 1741 idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer { 1742 return mgmt.GetManagerForChain("testchannelid") 1743 }) 1744 1745 fetcher := &fetcherMock{t: t} 1746 committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil) 1747 1748 capabilityProvider := &capabilitymock.CapabilityProvider{} 1749 appCapability := &capabilitymock.AppCapabilities{} 1750 capabilityProvider.On("Capabilities").Return(appCapability) 1751 appCapability.On("StorePvtDataOfInvalidTx").Return(true) 1752 coordinator := NewCoordinator(Support{ 1753 ChainID: "testchannelid", 1754 CollectionStore: cs, 1755 Committer: committer, 1756 Fetcher: fetcher, 1757 Validator: &validatorMock{}, 1758 CapabilityProvider: capabilityProvider, 1759 }, store.store, protoutil.SignedData{}, metrics, testConfig, idDeserializerFactory) 1760 pvtData := (&pvtDataFactory{}).addRWSet().addNSRWSet("ns1", "c1").create() 1761 // Green path: ledger height can be retrieved from ledger/committer 1762 err := coordinator.StorePvtData("tx1", &tspb.TxPvtReadWriteSetWithConfigInfo{ 1763 PvtRwset: pvtData[0].WriteSet, 1764 CollectionConfigs: make(map[string]*peer.CollectionConfigPackage), 1765 }, uint64(5)) 1766 assert.NoError(t, err) 1767 } 1768 1769 func TestContainsWrites(t *testing.T) { 1770 // Scenario I: Nil HashedRwSet in collection 1771 col := &rwsetutil.CollHashedRwSet{ 1772 CollectionName: "col1", 1773 } 1774 assert.False(t, containsWrites("tx", "ns", col)) 1775 1776 // Scenario II: No writes in collection 1777 col.HashedRwSet = &kvrwset.HashedRWSet{} 1778 assert.False(t, containsWrites("tx", "ns", col)) 1779 1780 // Scenario III: Some writes in collection 1781 col.HashedRwSet.HashedWrites = append(col.HashedRwSet.HashedWrites, &kvrwset.KVWriteHash{}) 1782 assert.True(t, containsWrites("tx", "ns", col)) 1783 } 1784 1785 func TestIgnoreReadOnlyColRWSets(t *testing.T) { 1786 // Scenario: The transaction has some ColRWSets that have only reads and no writes, 1787 // These should be ignored and not considered as missing private data that needs to be retrieved 1788 // from the transient store or other peers. 1789 // The gossip and transient store mocks in this test aren't initialized with 1790 // actions, so if the coordinator attempts to fetch private data from the 1791 // transient store or other peers, the test would fail. 1792 // Also - we check that at commit time - the coordinator concluded that 1793 // no missing private data was found. 1794 err := msptesttools.LoadMSPSetupForTesting() 1795 require.NoError(t, err, fmt.Sprintf("Failed to setup local msp for testing, got err %s", err)) 1796 identity := mspmgmt.GetLocalSigningIdentityOrPanic(factory.GetDefault()) 1797 serializedID, err := identity.Serialize() 1798 require.NoError(t, err, fmt.Sprintf("Serialize should have succeeded, got err %s", err)) 1799 data := []byte{1, 2, 3} 1800 signature, err := identity.Sign(data) 1801 require.NoError(t, err, fmt.Sprintf("Could not sign identity, got err %s", err)) 1802 peerSelfSignedData := protoutil.SignedData{ 1803 Identity: serializedID, 1804 Signature: signature, 1805 Data: data, 1806 } 1807 cs := createcollectionStore(peerSelfSignedData).thatAcceptsAll().withMSPIdentity(identity.GetMSPIdentifier()) 1808 var commitHappened bool 1809 assertCommitHappened := func() { 1810 assert.True(t, commitHappened) 1811 commitHappened = false 1812 } 1813 committer := &mocks.Committer{} 1814 committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { 1815 blockAndPrivateData := args.Get(0).(*ledger.BlockAndPvtData) 1816 // Ensure there is no private data to commit 1817 assert.Empty(t, blockAndPrivateData.PvtData) 1818 // Ensure there is no missing private data 1819 assert.Empty(t, blockAndPrivateData.MissingPvtData) 1820 commitHappened = true 1821 1822 commitOpts := args.Get(1).(*ledger.CommitOptions) 1823 expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: false} 1824 assert.Equal(t, expectedCommitOpts, commitOpts) 1825 }).Return(nil) 1826 1827 store := newTransientStore(t) 1828 defer store.tearDown() 1829 1830 fetcher := &fetcherMock{t: t} 1831 hash := util2.ComputeSHA256([]byte("rws-pre-image")) 1832 bf := &blockFactory{ 1833 channelID: "testchannelid", 1834 } 1835 1836 idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer { 1837 return mgmt.GetManagerForChain("testchannelid") 1838 }) 1839 1840 // The block contains a read only private data transaction 1841 block := bf.AddReadOnlyTxn("tx1", "ns3", hash, "c3", "c2").create() 1842 committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil) 1843 metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics 1844 1845 capabilityProvider := &capabilitymock.CapabilityProvider{} 1846 appCapability := &capabilitymock.AppCapabilities{} 1847 capabilityProvider.On("Capabilities").Return(appCapability) 1848 appCapability.On("StorePvtDataOfInvalidTx").Return(true) 1849 coordinator := NewCoordinator(Support{ 1850 ChainID: "testchannelid", 1851 CollectionStore: cs, 1852 Committer: committer, 1853 Fetcher: fetcher, 1854 Validator: &validatorMock{}, 1855 CapabilityProvider: capabilityProvider, 1856 }, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory) 1857 // We pass a nil private data slice to indicate no pre-images though the block contains 1858 // private data reads. 1859 err = coordinator.StoreBlock(block, nil) 1860 assert.NoError(t, err) 1861 assertCommitHappened() 1862 } 1863 1864 func TestCoordinatorMetrics(t *testing.T) { 1865 err := msptesttools.LoadMSPSetupForTesting() 1866 require.NoError(t, err, fmt.Sprintf("Failed to setup local msp for testing, got err %s", err)) 1867 identity := mspmgmt.GetLocalSigningIdentityOrPanic(factory.GetDefault()) 1868 serializedID, err := identity.Serialize() 1869 require.NoError(t, err, fmt.Sprintf("Serialize should have succeeded, got err %s", err)) 1870 data := []byte{1, 2, 3} 1871 signature, err := identity.Sign(data) 1872 require.NoError(t, err, fmt.Sprintf("Could not sign identity, got err %s", err)) 1873 peerSelfSignedData := protoutil.SignedData{ 1874 Identity: serializedID, 1875 Signature: signature, 1876 Data: data, 1877 } 1878 1879 cs := createcollectionStore(peerSelfSignedData).thatAcceptsAll().withMSPIdentity(identity.GetMSPIdentifier()) 1880 1881 committer := &mocks.Committer{} 1882 committer.On("CommitLegacy", mock.Anything, mock.Anything).Return(nil) 1883 1884 store := newTransientStore(t) 1885 defer store.tearDown() 1886 1887 hash := util2.ComputeSHA256([]byte("rws-pre-image")) 1888 pdFactory := &pvtDataFactory{} 1889 bf := &blockFactory{ 1890 channelID: "testchannelid", 1891 } 1892 1893 idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer { 1894 return mgmt.GetManagerForChain("testchannelid") 1895 }) 1896 1897 block := bf.AddTxnWithEndorsement("tx1", "ns1", hash, "org1", true, "c1", "c2"). 1898 AddTxnWithEndorsement("tx2", "ns2", hash, "org2", true, "c1"). 1899 AddTxnWithEndorsement("tx3", "ns3", hash, "org3", true, "c1").create() 1900 1901 pvtData := pdFactory.addRWSet().addNSRWSet("ns1", "c1", "c2").addRWSet().addNSRWSet("ns2", "c1").create() 1902 // fetch duration metric only reported when fetching from remote peer 1903 fetcher := &fetcherMock{t: t} 1904 fetcher.On("fetch", mock.Anything).expectingDigests([]privdatacommon.DigKey{ 1905 { 1906 TxId: "tx3", Namespace: "ns3", Collection: "c1", BlockSeq: 1, SeqInBlock: 2, 1907 }, 1908 }).Return(&privdatacommon.FetchedPvtDataContainer{ 1909 AvailableElements: []*proto.PvtDataElement{ 1910 { 1911 Digest: &proto.PvtDataDigest{ 1912 SeqInBlock: 2, 1913 BlockSeq: 1, 1914 Collection: "c1", 1915 Namespace: "ns3", 1916 TxId: "tx3", 1917 }, 1918 Payload: [][]byte{[]byte("rws-pre-image")}, 1919 }, 1920 }, 1921 }, nil) 1922 1923 testMetricProvider := gmetricsmocks.TestUtilConstructMetricProvider() 1924 metrics := metrics.NewGossipMetrics(testMetricProvider.FakeProvider).PrivdataMetrics 1925 1926 committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil) 1927 1928 capabilityProvider := &capabilitymock.CapabilityProvider{} 1929 appCapability := &capabilitymock.AppCapabilities{} 1930 capabilityProvider.On("Capabilities").Return(appCapability) 1931 appCapability.On("StorePvtDataOfInvalidTx").Return(true) 1932 coordinator := NewCoordinator(Support{ 1933 ChainID: "testchannelid", 1934 CollectionStore: cs, 1935 Committer: committer, 1936 Fetcher: fetcher, 1937 Validator: &validatorMock{}, 1938 CapabilityProvider: capabilityProvider, 1939 }, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory) 1940 err = coordinator.StoreBlock(block, pvtData) 1941 assert.NoError(t, err) 1942 1943 // make sure all coordinator metrics were reported 1944 1945 assert.Equal(t, 1946 []string{"channel", "testchannelid"}, 1947 testMetricProvider.FakeValidationDuration.WithArgsForCall(0), 1948 ) 1949 assert.True(t, testMetricProvider.FakeValidationDuration.ObserveArgsForCall(0) > 0) 1950 assert.Equal(t, 1951 []string{"channel", "testchannelid"}, 1952 testMetricProvider.FakeListMissingPrivateDataDuration.WithArgsForCall(0), 1953 ) 1954 assert.True(t, testMetricProvider.FakeListMissingPrivateDataDuration.ObserveArgsForCall(0) > 0) 1955 assert.Equal(t, 1956 []string{"channel", "testchannelid"}, 1957 testMetricProvider.FakeFetchDuration.WithArgsForCall(0), 1958 ) 1959 // fetch duration metric only reported when fetching from remote peer 1960 assert.True(t, testMetricProvider.FakeFetchDuration.ObserveArgsForCall(0) > 0) 1961 assert.Equal(t, 1962 []string{"channel", "testchannelid"}, 1963 testMetricProvider.FakeCommitPrivateDataDuration.WithArgsForCall(0), 1964 ) 1965 assert.True(t, testMetricProvider.FakeCommitPrivateDataDuration.ObserveArgsForCall(0) > 0) 1966 assert.Equal(t, 1967 []string{"channel", "testchannelid"}, 1968 testMetricProvider.FakePurgeDuration.WithArgsForCall(0), 1969 ) 1970 assert.True(t, testMetricProvider.FakePurgeDuration.ObserveArgsForCall(0) > 0) 1971 }