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