From 2c2274d0519a1ce1eba596ff0a43636dce64d926 Mon Sep 17 00:00:00 2001 From: Wenjian Qiao Date: Thu, 9 Apr 2020 10:24:43 -0400 Subject: [PATCH] [FAB-17716] Fix test flake due to too many requests for /protos.Deliver Currently peer.limits.concurrency.deliverService value is set to 1 in IT core_template.go. This value is too low and may cause test flakes because some tests have multiple goroutines sending requests to DeliverService. This PR sets a higher value for all tests in IT. Meanwhile, it overwrites the value to 1 in pvtdata_test.go where maximum 1 goroutine sends requests to DeliverService. Signed-off-by: Wenjian Qiao --- integration/nwo/core_template.go | 2 +- integration/nwo/fabricconfig/core.go | 10 + integration/pvtdata/pvtdata_test.go | 321 +++++++++++++++------------ 3 files changed, 188 insertions(+), 145 deletions(-) diff --git a/integration/nwo/core_template.go b/integration/nwo/core_template.go index 26939be6808..29df215bc04 100644 --- a/integration/nwo/core_template.go +++ b/integration/nwo/core_template.go @@ -140,7 +140,7 @@ peer: limits: concurrency: endorserService: 100 - deliverService: 1 + deliverService: 100 vm: endpoint: unix:///var/run/docker.sock diff --git a/integration/nwo/fabricconfig/core.go b/integration/nwo/fabricconfig/core.go index 5c1180f5e36..77e653c13c2 100644 --- a/integration/nwo/fabricconfig/core.go +++ b/integration/nwo/fabricconfig/core.go @@ -50,6 +50,7 @@ type Peer struct { Handlers *Handlers `yaml:"handlers,omitempty"` ValidatorPoolSize int `yaml:"validatorPoolSize,omitempty"` Discovery *Discovery `yaml:"discovery,omitempty"` + Limits *Limits `yaml:"limits,omitempty"` ExtraProperties map[string]interface{} `yaml:",inline,omitempty"` } @@ -212,6 +213,15 @@ type Discovery struct { OrgMembersAllowedAccess bool `yaml:"orgMembersAllowedAccess"` } +type Limits struct { + Concurrency *Concurrency `yaml:"concurrency,omitempty"` +} + +type Concurrency struct { + EndorserService int `yaml:"endorserService,omitempty"` + DeliverService int `yaml:"deliverService,omitempty"` +} + type VM struct { Endpoint string `yaml:"endpoint,omitempty"` Docker *Docker `yaml:"docker,omitempty"` diff --git a/integration/pvtdata/pvtdata_test.go b/integration/pvtdata/pvtdata_test.go index 0fe79ff632a..b8ede70b3dc 100644 --- a/integration/pvtdata/pvtdata_test.go +++ b/integration/pvtdata/pvtdata_test.go @@ -624,150 +624,6 @@ var _ bool = Describe("PrivateData", func() { }) }) - Describe("marble APIs invocation and private data delivery", func() { - // call marble APIs: getMarblesByRange, transferMarble, delete, getMarbleHash, getMarblePrivateDetailsHash and verify ACL Behavior - assertMarbleAPIs := func() { - eligiblePeer := network.Peer("Org2", "peer0") - ccName := testChaincode.Name - - // Verifies marble private chaincode APIs: getMarblesByRange, transferMarble, delete - - By("adding five marbles") - for i := 0; i < 5; i++ { - addMarble(network, orderer, ccName, fmt.Sprintf(`{"name":"test-marble-%d", "color":"blue", "size":35, "owner":"tom", "price":99}`, i), eligiblePeer) - } - - By("getting marbles by range") - assertGetMarblesByRange(network, ccName, `"test-marble-0", "test-marble-2"`, eligiblePeer) - - By("transferring test-marble-0 to jerry") - transferMarble(network, orderer, ccName, `{"name":"test-marble-0", "owner":"jerry"}`, eligiblePeer) - - By("verifying the new ownership of test-marble-0") - assertOwnershipInCollectionM(network, ccName, `test-marble-0`, eligiblePeer) - - By("deleting test-marble-0") - deleteMarble(network, orderer, ccName, `{"name":"test-marble-0"}`, eligiblePeer) - - By("verifying the deletion of test-marble-0") - assertDoesNotExistInCollectionM(network, ccName, `test-marble-0`, eligiblePeer) - - // This section verifies that chaincode can return private data hash. - // Unlike private data that can only be accessed from authorized peers as defined in the collection config, - // private data hash can be queried on any peer in the channel that has the chaincode instantiated. - // When calling QueryChaincode with "getMarbleHash", the cc will return the private data hash in collectionMarbles. - // When calling QueryChaincode with "getMarblePrivateDetailsHash", the cc will return the private data hash in collectionMarblePrivateDetails. - - peerList := []*nwo.Peer{ - network.Peer("Org1", "peer0"), - network.Peer("Org2", "peer0"), - network.Peer("Org3", "peer0")} - - By("verifying getMarbleHash is accessible from all peers that has the chaincode instantiated") - expectedBytes := util.ComputeStringHash(`{"docType":"marble","name":"test-marble-1","color":"blue","size":35,"owner":"tom"}`) - assertMarblesPrivateHashM(network, ccName, "test-marble-1", expectedBytes, peerList) - - By("verifying getMarblePrivateDetailsHash is accessible from all peers that has the chaincode instantiated") - expectedBytes = util.ComputeStringHash(`{"docType":"marblePrivateDetails","name":"test-marble-1","price":99}`) - assertMarblesPrivateDetailsHashMPD(network, ccName, "test-marble-1", expectedBytes, peerList) - - // collection ACL while reading private data: not allowed to non-members - // collections_config3: collectionMarblePrivateDetails - member_only_read is set to true - - By("querying collectionMarblePrivateDetails on org1-peer0 by org1-user1, shouldn't have read access") - assertNoReadAccessToCollectionMPD(network, testChaincode.Name, "test-marble-1", network.Peer("Org1", "peer0")) - } - - // verify DeliverWithPrivateData sends private data based on the ACL in collection config - // before and after upgrade. - assertDeliverWithPrivateDataACLBehavior := func() { - By("getting signing identity for a user in org1") - signingIdentity := getSigningIdentity(network, "Org1", "User1", "Org1MSP", "bccsp") - - By("adding a marble") - peer := network.Peer("Org2", "peer0") - addMarble(network, orderer, testChaincode.Name, `{"name":"marble11", "color":"blue", "size":35, "owner":"tom", "price":99}`, peer) - - By("getting the deliver event for newest block") - event := getEventFromDeliverService(network, peer, channelID, signingIdentity, 0) - - By("verifying private data in deliver event contains 'collectionMarbles' only") - // it should receive pvtdata for 'collectionMarbles' only because memberOnlyRead is true - expectedKVWritesMap := map[string]map[string][]byte{ - "collectionMarbles": { - "\000color~name\000blue\000marble11\000": []byte("\000"), - "marble11": getValueForCollectionMarbles("marble11", "blue", "tom", 35), - }, - } - assertPrivateDataAsExpected(event.BlockAndPvtData.PrivateDataMap, expectedKVWritesMap) - - By("upgrading chaincode with collections_config1.json where isMemberOnlyRead is false") - testChaincode.CollectionsConfig = collectionConfig("collections_config1.json") - testChaincode.Version = "1.1" - if !testChaincode.isLegacy { - testChaincode.Sequence = "2" - } - upgradeChaincode(network, orderer, testChaincode) - - By("getting the deliver event for an old block committed before upgrade") - event = getEventFromDeliverService(network, peer, channelID, signingIdentity, event.BlockNum) - - By("verifying the deliver event for the old block uses old config") - assertPrivateDataAsExpected(event.BlockAndPvtData.PrivateDataMap, expectedKVWritesMap) - - By("adding a new marble after upgrade") - addMarble(network, orderer, testChaincode.Name, - `{"name":"marble12", "color":"blue", "size":35, "owner":"tom", "price":99}`, - network.Peer("Org1", "peer0"), - ) - By("getting the deliver event for a new block committed after upgrade") - event = getEventFromDeliverService(network, peer, channelID, signingIdentity, 0) - - // it should receive pvtdata for both collections because memberOnlyRead is false - By("verifying the deliver event for the new block uses new config") - expectedKVWritesMap = map[string]map[string][]byte{ - "collectionMarbles": { - "\000color~name\000blue\000marble12\000": []byte("\000"), - "marble12": getValueForCollectionMarbles("marble12", "blue", "tom", 35), - }, - "collectionMarblePrivateDetails": { - "marble12": getValueForCollectionMarblePrivateDetails("marble12", 99), - }, - } - assertPrivateDataAsExpected(event.BlockAndPvtData.PrivateDataMap, expectedKVWritesMap) - } - - It("calls marbles APIs and delivers private data", func() { - By("deploying new lifecycle chaincode") - testChaincode = chaincode{ - Chaincode: newLifecycleChaincode, - isLegacy: false, - } - nwo.EnableCapabilities(network, channelID, "Application", "V2_0", orderer, network.Peers...) - testChaincode.CollectionsConfig = collectionConfig("collections_config3.json") - deployChaincode(network, orderer, testChaincode) - - By("attempting to invoke chaincode from a user (org1) not in any collection member orgs (org2 and org3)") - peer2 := network.Peer("Org2", "peer0") - marbleDetailsBase64 := base64.StdEncoding.EncodeToString([]byte(`{"name":"memberonly-marble", "color":"blue", "size":35, "owner":"tom", "price":99}`)) - command := commands.ChaincodeInvoke{ - ChannelID: channelID, - Orderer: network.OrdererAddress(orderer, nwo.ListenPort), - Name: "marblesp", - Ctor: fmt.Sprintf(`{"Args":["initMarble"]}`), - Transient: fmt.Sprintf(`{"marble":"%s"}`, marbleDetailsBase64), - PeerAddresses: []string{network.PeerAddress(peer2, nwo.ListenPort)}, - WaitForEvent: true, - } - peer1 := network.Peer("Org1", "peer0") - expectedErrMsg := "tx creator does not have write access permission" - invokeChaincodeExpectErr(network, peer1, command, expectedErrMsg) - - assertMarbleAPIs() - assertDeliverWithPrivateDataACLBehavior() - }) - }) - Describe("Collection Config Endorsement Policy", func() { When("using legacy lifecycle chaincode", func() { It("ignores the collection config endorsement policy and successfully invokes the chaincode", func() { @@ -905,6 +761,183 @@ var _ bool = Describe("PrivateData", func() { }) }) }) + + Describe("marble APIs invocation and private data delivery", func() { + var ( + newLifecycleChaincode nwo.Chaincode + testChaincode chaincode + ) + + BeforeEach(func() { + By("setting up the network") + network = initThreeOrgsSetup(true) + + newLifecycleChaincode = nwo.Chaincode{ + Name: "marblesp", + Version: "1.0", + Path: components.Build("github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd"), + Lang: "binary", + PackageFile: filepath.Join(network.RootDir, "marbles-pvtdata.tar.gz"), + Label: "marbles-private-20", + SignaturePolicy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`, + CollectionsConfig: collectionConfig("collections_config1.json"), + Sequence: "1", + } + + // In assertDeliverWithPrivateDataACLBehavior, there is a single goroutine that communicates to DeliverService. + // Set DeliverService concurrency limit to 1 in order to verify that the grpc server in a peer works correctly + // when reaching (but not exceeding) the concurrency limit. + By("setting deliverservice concurrency limit to 1") + for _, p := range network.Peers { + core := network.ReadPeerConfig(p) + core.Peer.Limits.Concurrency.DeliverService = 1 + network.WritePeerConfig(p, core) + } + process, orderer = startNetwork(network) + }) + + // call marble APIs: getMarblesByRange, transferMarble, delete, getMarbleHash, getMarblePrivateDetailsHash and verify ACL Behavior + assertMarbleAPIs := func() { + eligiblePeer := network.Peer("Org2", "peer0") + ccName := testChaincode.Name + + // Verifies marble private chaincode APIs: getMarblesByRange, transferMarble, delete + + By("adding five marbles") + for i := 0; i < 5; i++ { + addMarble(network, orderer, ccName, fmt.Sprintf(`{"name":"test-marble-%d", "color":"blue", "size":35, "owner":"tom", "price":99}`, i), eligiblePeer) + } + + By("getting marbles by range") + assertGetMarblesByRange(network, ccName, `"test-marble-0", "test-marble-2"`, eligiblePeer) + + By("transferring test-marble-0 to jerry") + transferMarble(network, orderer, ccName, `{"name":"test-marble-0", "owner":"jerry"}`, eligiblePeer) + + By("verifying the new ownership of test-marble-0") + assertOwnershipInCollectionM(network, ccName, `test-marble-0`, eligiblePeer) + + By("deleting test-marble-0") + deleteMarble(network, orderer, ccName, `{"name":"test-marble-0"}`, eligiblePeer) + + By("verifying the deletion of test-marble-0") + assertDoesNotExistInCollectionM(network, ccName, `test-marble-0`, eligiblePeer) + + // This section verifies that chaincode can return private data hash. + // Unlike private data that can only be accessed from authorized peers as defined in the collection config, + // private data hash can be queried on any peer in the channel that has the chaincode instantiated. + // When calling QueryChaincode with "getMarbleHash", the cc will return the private data hash in collectionMarbles. + // When calling QueryChaincode with "getMarblePrivateDetailsHash", the cc will return the private data hash in collectionMarblePrivateDetails. + + peerList := []*nwo.Peer{ + network.Peer("Org1", "peer0"), + network.Peer("Org2", "peer0"), + network.Peer("Org3", "peer0")} + + By("verifying getMarbleHash is accessible from all peers that has the chaincode instantiated") + expectedBytes := util.ComputeStringHash(`{"docType":"marble","name":"test-marble-1","color":"blue","size":35,"owner":"tom"}`) + assertMarblesPrivateHashM(network, ccName, "test-marble-1", expectedBytes, peerList) + + By("verifying getMarblePrivateDetailsHash is accessible from all peers that has the chaincode instantiated") + expectedBytes = util.ComputeStringHash(`{"docType":"marblePrivateDetails","name":"test-marble-1","price":99}`) + assertMarblesPrivateDetailsHashMPD(network, ccName, "test-marble-1", expectedBytes, peerList) + + // collection ACL while reading private data: not allowed to non-members + // collections_config3: collectionMarblePrivateDetails - member_only_read is set to true + + By("querying collectionMarblePrivateDetails on org1-peer0 by org1-user1, shouldn't have read access") + assertNoReadAccessToCollectionMPD(network, testChaincode.Name, "test-marble-1", network.Peer("Org1", "peer0")) + } + + // verify DeliverWithPrivateData sends private data based on the ACL in collection config + // before and after upgrade. + assertDeliverWithPrivateDataACLBehavior := func() { + By("getting signing identity for a user in org1") + signingIdentity := getSigningIdentity(network, "Org1", "User1", "Org1MSP", "bccsp") + + By("adding a marble") + peer := network.Peer("Org2", "peer0") + addMarble(network, orderer, testChaincode.Name, `{"name":"marble11", "color":"blue", "size":35, "owner":"tom", "price":99}`, peer) + + By("getting the deliver event for newest block") + event := getEventFromDeliverService(network, peer, channelID, signingIdentity, 0) + + By("verifying private data in deliver event contains 'collectionMarbles' only") + // it should receive pvtdata for 'collectionMarbles' only because memberOnlyRead is true + expectedKVWritesMap := map[string]map[string][]byte{ + "collectionMarbles": { + "\000color~name\000blue\000marble11\000": []byte("\000"), + "marble11": getValueForCollectionMarbles("marble11", "blue", "tom", 35), + }, + } + assertPrivateDataAsExpected(event.BlockAndPvtData.PrivateDataMap, expectedKVWritesMap) + + By("upgrading chaincode with collections_config1.json where isMemberOnlyRead is false") + testChaincode.CollectionsConfig = collectionConfig("collections_config1.json") + testChaincode.Version = "1.1" + if !testChaincode.isLegacy { + testChaincode.Sequence = "2" + } + upgradeChaincode(network, orderer, testChaincode) + + By("getting the deliver event for an old block committed before upgrade") + event = getEventFromDeliverService(network, peer, channelID, signingIdentity, event.BlockNum) + + By("verifying the deliver event for the old block uses old config") + assertPrivateDataAsExpected(event.BlockAndPvtData.PrivateDataMap, expectedKVWritesMap) + + By("adding a new marble after upgrade") + addMarble(network, orderer, testChaincode.Name, + `{"name":"marble12", "color":"blue", "size":35, "owner":"tom", "price":99}`, + network.Peer("Org1", "peer0"), + ) + By("getting the deliver event for a new block committed after upgrade") + event = getEventFromDeliverService(network, peer, channelID, signingIdentity, 0) + + // it should receive pvtdata for both collections because memberOnlyRead is false + By("verifying the deliver event for the new block uses new config") + expectedKVWritesMap = map[string]map[string][]byte{ + "collectionMarbles": { + "\000color~name\000blue\000marble12\000": []byte("\000"), + "marble12": getValueForCollectionMarbles("marble12", "blue", "tom", 35), + }, + "collectionMarblePrivateDetails": { + "marble12": getValueForCollectionMarblePrivateDetails("marble12", 99), + }, + } + assertPrivateDataAsExpected(event.BlockAndPvtData.PrivateDataMap, expectedKVWritesMap) + } + + It("calls marbles APIs and delivers private data", func() { + By("deploying new lifecycle chaincode") + testChaincode = chaincode{ + Chaincode: newLifecycleChaincode, + isLegacy: false, + } + nwo.EnableCapabilities(network, channelID, "Application", "V2_0", orderer, network.Peers...) + testChaincode.CollectionsConfig = collectionConfig("collections_config3.json") + deployChaincode(network, orderer, testChaincode) + + By("attempting to invoke chaincode from a user (org1) not in any collection member orgs (org2 and org3)") + peer2 := network.Peer("Org2", "peer0") + marbleDetailsBase64 := base64.StdEncoding.EncodeToString([]byte(`{"name":"memberonly-marble", "color":"blue", "size":35, "owner":"tom", "price":99}`)) + command := commands.ChaincodeInvoke{ + ChannelID: channelID, + Orderer: network.OrdererAddress(orderer, nwo.ListenPort), + Name: "marblesp", + Ctor: fmt.Sprintf(`{"Args":["initMarble"]}`), + Transient: fmt.Sprintf(`{"marble":"%s"}`, marbleDetailsBase64), + PeerAddresses: []string{network.PeerAddress(peer2, nwo.ListenPort)}, + WaitForEvent: true, + } + peer1 := network.Peer("Org1", "peer0") + expectedErrMsg := "tx creator does not have write access permission" + invokeChaincodeExpectErr(network, peer1, command, expectedErrMsg) + + assertMarbleAPIs() + assertDeliverWithPrivateDataACLBehavior() + }) + }) }) func initThreeOrgsSetup(removePeer1 bool) *nwo.Network {