diff --git a/internal/pkg/gateway/api.go b/internal/pkg/gateway/api.go index 16d1007f96e..8a6d3488e27 100644 --- a/internal/pkg/gateway/api.go +++ b/internal/pkg/gateway/api.go @@ -334,9 +334,16 @@ func (gs *Server) planFromFirstEndorser(ctx context.Context, channel string, cha // 4. If transient data is involved, then we need to ensure that discovery only returns orgs which own the collections involved. // Do this by setting NoPrivateReads to false on each collection + originalInterest := &peer.ChaincodeInterest{} + var protectedCollections []string if hasTransientData { for _, call := range interest.GetChaincodes() { - call.NoPrivateReads = false + ccc := *call // shallow copy + originalInterest.Chaincodes = append(originalInterest.Chaincodes, &ccc) + if call.NoPrivateReads { + call.NoPrivateReads = false + protectedCollections = append(protectedCollections, call.CollectionNames...) + } } } @@ -344,6 +351,13 @@ func (gs *Server) planFromFirstEndorser(ctx context.Context, channel string, cha // The preferred discovery layout will contain the firstEndorser's Org. plan, err = gs.registry.endorsementPlan(channel, interest, firstEndorser) if err != nil { + if len(protectedCollections) > 0 { + // may have failed because of the cautious approach we are taking with transient data - check + _, err = gs.registry.endorsementPlan(channel, originalInterest, firstEndorser) + if err == nil { + return nil, status.Error(codes.FailedPrecondition, fmt.Sprintf("requires endorsement from organisation(s) that are not in the distribution policy of the private data collection(s): %v; retry specifying trusted endorsing organizations to protect transient data", protectedCollections)) + } + } return nil, status.Error(codes.FailedPrecondition, err.Error()) } diff --git a/internal/pkg/gateway/api_test.go b/internal/pkg/gateway/api_test.go index 972ec49c08f..d140993a680 100644 --- a/internal/pkg/gateway/api_test.go +++ b/internal/pkg/gateway/api_test.go @@ -712,6 +712,33 @@ func TestEndorse(t *testing.T) { errCode: codes.FailedPrecondition, errString: "no endorsers found in the gateway's organization; retry specifying endorsing organization(s) to protect transient data", }, + { + name: "local and non-local endorsers with transient data will fail", + plan: endorsementPlan{ + "g1": {{endorser: localhostMock, height: 3}}, // msp1 + "g2": {{endorser: peer3Mock, height: 4}}, // msp2 + }, + layouts: []endorsementLayout{ + {"g1": 1, "g2": 1}, + }, + members: []networkMember{ + {"id1", "localhost:7051", "msp1", 3}, + {"id3", "peer3:10051", "msp2", 4}, + }, + interest: &peer.ChaincodeInterest{ + Chaincodes: []*peer.ChaincodeCall{{ + Name: testChaincode, + CollectionNames: []string{"mycollection1", "mycollection2"}, + NoPrivateReads: true, + }}, + }, + transientData: map[string][]byte{"transient-key": []byte("transient-value")}, + postSetup: func(t *testing.T, def *preparedTest) { + def.discovery.PeersForEndorsementReturnsOnCall(0, nil, errors.New("protect-transient")) + }, + errCode: codes.FailedPrecondition, + errString: "requires endorsement from organisation(s) that are not in the distribution policy of the private data collection(s): [mycollection1 mycollection2]; retry specifying trusted endorsing organizations to protect transient data", + }, { name: "extra endorsers with transient data", plan: endorsementPlan{