Skip to content

Commit

Permalink
Gateway Evaluate() with transient data
Browse files Browse the repository at this point in the history
Add protection logic to ensure sensitive transient data is not unintentionally leaked to other org’s peers when evaluating transaction proposal.
If transient data is present and target_organizations is not specified, send request only to a local peer, or fail if one is not available for that channel/chaincode

Signed-off-by: andrew-coleman <andrew_coleman@uk.ibm.com>
  • Loading branch information
andrew-coleman authored and denyeart committed Aug 20, 2021
1 parent 540fff8 commit bc1898e
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 2 deletions.
14 changes: 12 additions & 2 deletions internal/pkg/gateway/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func (gs *Server) Evaluate(ctx context.Context, request *gp.EvaluateRequest) (*g
return nil, status.Error(codes.InvalidArgument, "an evaluate request is required")
}
signedProposal := request.GetProposedTransaction()
channel, chaincodeID, _, err := getChannelAndChaincodeFromSignedProposal(signedProposal)
channel, chaincodeID, hasTransientData, err := getChannelAndChaincodeFromSignedProposal(signedProposal)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "failed to unpack transaction proposal: %s", err)
}
Expand All @@ -42,8 +42,18 @@ func (gs *Server) Evaluate(ctx context.Context, request *gp.EvaluateRequest) (*g
return nil, status.Errorf(codes.Unavailable, "%s", err)
}

endorser, err := gs.registry.evaluator(channel, chaincodeID, request.GetTargetOrganizations())
targetOrgs := request.GetTargetOrganizations()
transientProtected := false
if hasTransientData && targetOrgs == nil {
targetOrgs = []string{gs.registry.localEndorser.mspid}
transientProtected = true
}

endorser, err := gs.registry.evaluator(channel, chaincodeID, targetOrgs)
if err != nil {
if transientProtected {
return nil, status.Error(codes.Unavailable, "no endorsers found in the gateway's organization; retry specifying target organization(s) to protect transient data")
}
return nil, status.Errorf(codes.Unavailable, "%s", err)
}

Expand Down
35 changes: 35 additions & 0 deletions internal/pkg/gateway/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,41 @@ func TestEvaluate(t *testing.T) {
endorsingOrgs: []string{"msp2", "msp3"},
expectedEndorsers: []string{"peer4:11051"},
},
{
name: "evaluate with transient data should select local org, highest block height",
members: []networkMember{
{"id1", "localhost:7051", "msp1", 4},
{"id2", "peer1:8051", "msp1", 5},
{"id3", "peer2:9051", "msp2", 6},
{"id4", "peer3:10051", "msp2", 5},
{"id5", "peer4:11051", "msp3", 7},
},
transientData: map[string][]byte{"transient-key": []byte("transient-value")},
expectedEndorsers: []string{"peer1:8051"},
},
{
name: "evaluate with transient data should fail if local org not available",
members: []networkMember{
{"id3", "peer2:9051", "msp2", 6},
{"id4", "peer3:10051", "msp2", 5},
{"id5", "peer4:11051", "msp3", 7},
},
transientData: map[string][]byte{"transient-key": []byte("transient-value")},
errString: "rpc error: code = Unavailable desc = no endorsers found in the gateway's organization; retry specifying target organization(s) to protect transient data",
},
{
name: "evaluate with transient data and target (non-local) orgs should select the highest block height peer",
members: []networkMember{
{"id1", "localhost:7051", "msp1", 11},
{"id2", "peer1:8051", "msp1", 5},
{"id3", "peer2:9051", "msp2", 6},
{"id4", "peer3:10051", "msp2", 9},
{"id5", "peer4:11051", "msp3", 7},
},
transientData: map[string][]byte{"transient-key": []byte("transient-value")},
endorsingOrgs: []string{"msp2", "msp3"},
expectedEndorsers: []string{"peer3:10051"},
},
{
name: "process proposal fails",
members: []networkMember{
Expand Down

0 comments on commit bc1898e

Please sign in to comment.