From 807c6de8731ccfbcb13e1b984afb98036e653887 Mon Sep 17 00:00:00 2001 From: andrew-coleman Date: Tue, 24 May 2022 15:10:02 +0100 Subject: [PATCH] Improved gateway error for transient data failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When transient data is passed to the gateway in a transaction submit, the gateway is more cautious about which orgs it will select to endorse. It alters the ChaincodeInterest to only allow orgs that are in the distribution policy for the PDCs written to by the chaincode. This commit doesn’t change the behaviour, but gives a more helpful error message if this layer of protection caused the submit to fail due to the lack of endorsing orgs. This will help to diagnose issues such as #3328 which took a long time to resolve because of the generic error message. Signed-off-by: andrew-coleman --- internal/pkg/gateway/api.go | 16 +++++++++++++++- internal/pkg/gateway/api_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) 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{