@@ -13,6 +13,7 @@ package discovery
1313import (
1414 "bytes"
1515 "context"
16+ "encoding/json"
1617 "math/rand"
1718 "time"
1819
@@ -38,8 +39,9 @@ type Client struct {
3839// NewRequest creates a new request
3940func NewRequest () * Request {
4041 r := & Request {
41- queryMapping : make (map [discovery.QueryType ]map [string ]int ),
42- Request : & discovery.Request {},
42+ invocationChainMapping : make (map [int ][]InvocationChain ),
43+ queryMapping : make (map [discovery.QueryType ]map [string ]int ),
44+ Request : & discovery.Request {},
4345 }
4446 // pre-populate types
4547 for _ , queryType := range configTypes {
@@ -52,8 +54,10 @@ func NewRequest() *Request {
5254type Request struct {
5355 lastChannel string
5456 lastIndex int
55- // map from query type to channel (or channel + chaincode) to expected index in response
57+ // map from query type to channel to expected index in response
5658 queryMapping map [discovery.QueryType ]map [string ]int
59+ // map from expected index in response to invocation chains
60+ invocationChainMapping map [int ][]InvocationChain
5761 * discovery.Request
5862}
5963
@@ -72,22 +76,29 @@ func (req *Request) AddConfigQuery() *Request {
7276}
7377
7478// AddEndorsersQuery adds to the request a query for given chaincodes
75- func (req * Request ) AddEndorsersQuery (chaincodes ... string ) * Request {
79+ // interests are the chaincode interests that the client wants to query for.
80+ // All interests for a given channel should be supplied in an aggregated slice
81+ func (req * Request ) AddEndorsersQuery (interests ... * discovery.ChaincodeInterest ) (* Request , error ) {
82+ if err := validateInterests (interests ... ); err != nil {
83+ return nil , err
84+ }
7685 ch := req .lastChannel
7786 q := & discovery.Query_CcQuery {
78- CcQuery : & discovery.ChaincodeQuery {},
79- }
80- for _ , cc := range chaincodes {
81- q .CcQuery .Interests = append (q .CcQuery .Interests , & discovery.ChaincodeInterest {
82- Chaincodes : []* discovery.ChaincodeCall {{Name : cc }},
83- })
87+ CcQuery : & discovery.ChaincodeQuery {
88+ Interests : interests ,
89+ },
8490 }
8591 req .Queries = append (req .Queries , & discovery.Query {
8692 Channel : ch ,
8793 Query : q ,
8894 })
95+ var invocationChains []InvocationChain
96+ for _ , interest := range interests {
97+ invocationChains = append (invocationChains , interest .Chaincodes )
98+ }
99+ req .addChaincodeQueryMapping (invocationChains )
89100 req .addQueryMapping (discovery .ChaincodeQueryType , ch )
90- return req
101+ return req , nil
91102}
92103
93104// AddLocalPeersQuery adds to the request a local peer query
@@ -122,6 +133,10 @@ func (req *Request) OfChannel(ch string) *Request {
122133 return req
123134}
124135
136+ func (req * Request ) addChaincodeQueryMapping (invocationChains []InvocationChain ) {
137+ req .invocationChainMapping [req .lastIndex ] = invocationChains
138+ }
139+
125140func (req * Request ) addQueryMapping (queryType discovery.QueryType , key string ) {
126141 req.queryMapping [queryType ][key ] = req .lastIndex
127142 req .lastIndex ++
@@ -169,7 +184,7 @@ func (c *Client) Send(ctx context.Context, req *Request, auth *discovery.AuthInf
169184 if n := len (resp .Results ); n != req .lastIndex {
170185 return nil , errors .Errorf ("Sent %d queries but received %d responses back" , req .lastIndex , n )
171186 }
172- return computeResponse ( req .queryMapping , resp )
187+ return req .computeResponse ( resp )
173188}
174189
175190type resultOrError interface {
@@ -228,7 +243,7 @@ func (cr *channelResponse) Peers() ([]*Peer, error) {
228243 return parsePeers (discovery .PeerMembershipQueryType , cr .response , cr .channel )
229244}
230245
231- func (cr * channelResponse ) Endorsers (cc string , ps PrioritySelector , ef ExclusionFilter ) (Endorsers , error ) {
246+ func (cr * channelResponse ) Endorsers (invocationChain InvocationChain , ps PrioritySelector , ef ExclusionFilter ) (Endorsers , error ) {
232247 // If we have a key that has no chaincode field,
233248 // it means it's an error returned from the service
234249 if err , exists := cr .response [key {
@@ -240,9 +255,9 @@ func (cr *channelResponse) Endorsers(cc string, ps PrioritySelector, ef Exclusio
240255
241256 // Else, the service returned a response that isn't an error
242257 res , exists := cr .response [key {
243- queryType : discovery .ChaincodeQueryType ,
244- channel : cr .channel ,
245- chaincode : cc ,
258+ queryType : discovery .ChaincodeQueryType ,
259+ channel : cr .channel ,
260+ invocationChain : invocationChain . String () ,
246261 }]
247262
248263 if ! exists {
@@ -294,20 +309,20 @@ func (resp response) ForChannel(ch string) ChannelResponse {
294309}
295310
296311type key struct {
297- queryType discovery.QueryType
298- channel string
299- chaincode string
312+ queryType discovery.QueryType
313+ channel string
314+ invocationChain string
300315}
301316
302- func computeResponse ( queryMapping map [discovery. QueryType ] map [ string ] int , r * discovery.Response ) (response , error ) {
317+ func ( req * Request ) computeResponse ( r * discovery.Response ) (response , error ) {
303318 var err error
304319 resp := make (response )
305- for configType , channel2index := range queryMapping {
320+ for configType , channel2index := range req . queryMapping {
306321 switch configType {
307322 case discovery .ConfigQueryType :
308323 err = resp .mapConfig (channel2index , r )
309324 case discovery .ChaincodeQueryType :
310- err = resp .mapEndorsers (channel2index , r )
325+ err = resp .mapEndorsers (channel2index , r , req . queryMapping , req . invocationChainMapping )
311326 case discovery .PeerMembershipQueryType :
312327 err = resp .mapPeerMembership (channel2index , r , discovery .PeerMembershipQueryType )
313328 case discovery .LocalMembershipQueryType :
@@ -404,36 +419,46 @@ func isStateInfoExpected(qt discovery.QueryType) bool {
404419 return qt != discovery .LocalMembershipQueryType
405420}
406421
407- func (resp response ) mapEndorsers (channel2index map [string ]int , r * discovery.Response ) error {
422+ func (resp response ) mapEndorsers (
423+ channel2index map [string ]int ,
424+ r * discovery.Response ,
425+ queryMapping map [discovery.QueryType ]map [string ]int ,
426+ chaincodeQueryMapping map [int ][]InvocationChain ) error {
408427 for ch , index := range channel2index {
409428 ccQueryRes , err := r .EndorsersAt (index )
410429 if ccQueryRes == nil && err == nil {
411430 return errors .Errorf ("expected QueryResult of either ChaincodeQueryResult or Error but got %v instead" , r .Results [index ])
412431 }
413432
414- key := key {
415- queryType : discovery .ChaincodeQueryType ,
416- channel : ch ,
417- }
418-
419433 if err != nil {
434+ key := key {
435+ queryType : discovery .ChaincodeQueryType ,
436+ channel : ch ,
437+ }
420438 resp [key ] = errors .New (err .Content )
421439 continue
422440 }
423441
424- if err := resp .mapEndorsersOfChannel (ccQueryRes , ch ); err != nil {
442+ if err := resp .mapEndorsersOfChannel (ccQueryRes , ch , chaincodeQueryMapping [ index ] ); err != nil {
425443 return errors .Wrapf (err , "failed assembling endorsers of channel %s" , ch )
426444 }
427445 }
428446 return nil
429447}
430448
431- func (resp response ) mapEndorsersOfChannel (ccRs * discovery.ChaincodeQueryResult , channel string ) error {
432- for _ , desc := range ccRs .Content {
449+ func (resp response ) mapEndorsersOfChannel (ccRs * discovery.ChaincodeQueryResult , channel string , invocationChain []InvocationChain ) error {
450+ if len (ccRs .Content ) < len (invocationChain ) {
451+ return errors .Errorf ("expected %d endorsement descriptors but got only %d" , len (invocationChain ), len (ccRs .Content ))
452+ }
453+ for i , desc := range ccRs .Content {
454+ expectedCCName := invocationChain [i ][0 ].Name
455+ if desc .Chaincode != expectedCCName {
456+ return errors .Errorf ("expected chaincode %s but got endorsement descriptor for %s" , expectedCCName , desc .Chaincode )
457+ }
433458 key := key {
434- queryType : discovery .ChaincodeQueryType ,
435- channel : channel ,
436- chaincode : desc . Chaincode ,
459+ queryType : discovery .ChaincodeQueryType ,
460+ channel : channel ,
461+ invocationChain : invocationChain [ i ]. String () ,
437462 }
438463
439464 descriptor , err := resp .createEndorsementDescriptor (desc , channel )
@@ -548,3 +573,40 @@ func validateStateInfoMessage(message *gossip.SignedGossipMessage) error {
548573 }
549574 return nil
550575}
576+
577+ func validateInterests (interests ... * discovery.ChaincodeInterest ) error {
578+ if len (interests ) == 0 {
579+ return errors .New ("no chaincode interests given" )
580+ }
581+ for _ , interest := range interests {
582+ if interest == nil {
583+ return errors .New ("chaincode interest is nil" )
584+ }
585+ if err := InvocationChain (interest .Chaincodes ).ValidateInvocationChain (); err != nil {
586+ return err
587+ }
588+ }
589+ return nil
590+ }
591+
592+ // InvocationChain aggregates ChaincodeCalls
593+ type InvocationChain []* discovery.ChaincodeCall
594+
595+ // String returns a string representation of this invocation chain
596+ func (ic InvocationChain ) String () string {
597+ s , _ := json .Marshal (ic )
598+ return string (s )
599+ }
600+
601+ // ValidateInvocationChain validates the InvocationChain's structure
602+ func (ic InvocationChain ) ValidateInvocationChain () error {
603+ if len (ic ) == 0 {
604+ return errors .New ("invocation chain should not be empty" )
605+ }
606+ for _ , cc := range ic {
607+ if cc .Name == "" {
608+ return errors .New ("chaincode name should not be empty" )
609+ }
610+ }
611+ return nil
612+ }
0 commit comments