github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/discovery/client/client.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package discovery 8 9 import ( 10 "context" 11 "encoding/json" 12 "fmt" 13 "math/rand" 14 "time" 15 16 "github.com/hyperledger/fabric-protos-go/peer" 17 18 "github.com/golang/protobuf/proto" 19 "github.com/hechain20/hechain/discovery/protoext" 20 gprotoext "github.com/hechain20/hechain/gossip/protoext" 21 "github.com/hyperledger/fabric-protos-go/discovery" 22 "github.com/hyperledger/fabric-protos-go/msp" 23 "github.com/pkg/errors" 24 ) 25 26 var configTypes = []protoext.QueryType{ 27 protoext.ConfigQueryType, 28 protoext.PeerMembershipQueryType, 29 protoext.ChaincodeQueryType, 30 protoext.LocalMembershipQueryType, 31 } 32 33 // Client interacts with the discovery server 34 type Client struct { 35 createConnection Dialer 36 signRequest Signer 37 } 38 39 // NewRequest creates a new request 40 func NewRequest() *Request { 41 r := &Request{ 42 invocationChainMapping: make(map[int][]InvocationChain), 43 queryMapping: make(map[protoext.QueryType]map[string]int), 44 Request: &discovery.Request{}, 45 } 46 // pre-populate types 47 for _, queryType := range configTypes { 48 r.queryMapping[queryType] = make(map[string]int) 49 } 50 return r 51 } 52 53 // Request aggregates several queries inside it 54 type Request struct { 55 lastChannel string 56 lastIndex int 57 // map from query type to channel to expected index in response 58 queryMapping map[protoext.QueryType]map[string]int 59 // map from expected index in response to invocation chains 60 invocationChainMapping map[int][]InvocationChain 61 *discovery.Request 62 } 63 64 // AddConfigQuery adds to the request a config query 65 func (req *Request) AddConfigQuery() *Request { 66 ch := req.lastChannel 67 q := &discovery.Query_ConfigQuery{ 68 ConfigQuery: &discovery.ConfigQuery{}, 69 } 70 req.Queries = append(req.Queries, &discovery.Query{ 71 Channel: ch, 72 Query: q, 73 }) 74 req.addQueryMapping(protoext.ConfigQueryType, ch) 75 return req 76 } 77 78 // AddEndorsersQuery adds to the request a query for given chaincodes 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 ...*peer.ChaincodeInterest) (*Request, error) { 82 if err := validateInterests(interests...); err != nil { 83 return nil, err 84 } 85 ch := req.lastChannel 86 q := &discovery.Query_CcQuery{ 87 CcQuery: &discovery.ChaincodeQuery{ 88 Interests: interests, 89 }, 90 } 91 req.Queries = append(req.Queries, &discovery.Query{ 92 Channel: ch, 93 Query: q, 94 }) 95 var invocationChains []InvocationChain 96 for _, interest := range interests { 97 invocationChains = append(invocationChains, interest.Chaincodes) 98 } 99 req.addChaincodeQueryMapping(invocationChains) 100 req.addQueryMapping(protoext.ChaincodeQueryType, ch) 101 return req, nil 102 } 103 104 // AddLocalPeersQuery adds to the request a local peer query 105 func (req *Request) AddLocalPeersQuery() *Request { 106 q := &discovery.Query_LocalPeers{ 107 LocalPeers: &discovery.LocalPeerQuery{}, 108 } 109 req.Queries = append(req.Queries, &discovery.Query{ 110 Query: q, 111 }) 112 var ic InvocationChain 113 req.addQueryMapping(protoext.LocalMembershipQueryType, channnelAndInvocationChain("", ic)) 114 return req 115 } 116 117 // AddPeersQuery adds to the request a peer query 118 func (req *Request) AddPeersQuery(invocationChain ...*peer.ChaincodeCall) *Request { 119 ch := req.lastChannel 120 q := &discovery.Query_PeerQuery{ 121 PeerQuery: &discovery.PeerMembershipQuery{ 122 Filter: &peer.ChaincodeInterest{ 123 Chaincodes: invocationChain, 124 }, 125 }, 126 } 127 req.Queries = append(req.Queries, &discovery.Query{ 128 Channel: ch, 129 Query: q, 130 }) 131 var ic InvocationChain 132 if len(invocationChain) > 0 { 133 ic = InvocationChain(invocationChain) 134 } 135 req.addChaincodeQueryMapping([]InvocationChain{ic}) 136 req.addQueryMapping(protoext.PeerMembershipQueryType, channnelAndInvocationChain(ch, ic)) 137 return req 138 } 139 140 func channnelAndInvocationChain(ch string, ic InvocationChain) string { 141 return fmt.Sprintf("%s %s", ch, ic.String()) 142 } 143 144 // OfChannel sets the next queries added to be in the given channel's context 145 func (req *Request) OfChannel(ch string) *Request { 146 req.lastChannel = ch 147 return req 148 } 149 150 func (req *Request) addChaincodeQueryMapping(invocationChains []InvocationChain) { 151 req.invocationChainMapping[req.lastIndex] = invocationChains 152 } 153 154 func (req *Request) addQueryMapping(queryType protoext.QueryType, key string) { 155 req.queryMapping[queryType][key] = req.lastIndex 156 req.lastIndex++ 157 } 158 159 // Send sends the request and returns the response, or error on failure 160 func (c *Client) Send(ctx context.Context, req *Request, auth *discovery.AuthInfo) (Response, error) { 161 reqToBeSent := *req.Request 162 reqToBeSent.Authentication = auth 163 payload, err := proto.Marshal(&reqToBeSent) 164 if err != nil { 165 return nil, errors.Wrap(err, "failed marshaling Request to bytes") 166 } 167 168 sig, err := c.signRequest(payload) 169 if err != nil { 170 return nil, errors.Wrap(err, "failed signing Request") 171 } 172 173 conn, err := c.createConnection() 174 if err != nil { 175 return nil, errors.Wrap(err, "failed connecting to discovery service") 176 } 177 178 cl := discovery.NewDiscoveryClient(conn) 179 resp, err := cl.Discover(ctx, &discovery.SignedRequest{ 180 Payload: payload, 181 Signature: sig, 182 }) 183 if err != nil { 184 return nil, errors.Wrap(err, "discovery service refused our Request") 185 } 186 if n := len(resp.Results); n != req.lastIndex { 187 return nil, errors.Errorf("Sent %d queries but received %d responses back", req.lastIndex, n) 188 } 189 return req.computeResponse(resp) 190 } 191 192 type resultOrError interface{} 193 194 type response map[key]resultOrError 195 196 type localResponse struct { 197 response 198 } 199 200 func (cr *localResponse) Peers() ([]*Peer, error) { 201 return parsePeers(protoext.LocalMembershipQueryType, cr.response, "") 202 } 203 204 type channelResponse struct { 205 response 206 channel string 207 } 208 209 func (cr *channelResponse) Config() (*discovery.ConfigResult, error) { 210 res, exists := cr.response[key{ 211 queryType: protoext.ConfigQueryType, 212 k: cr.channel, 213 }] 214 215 if !exists { 216 return nil, ErrNotFound 217 } 218 219 if config, isConfig := res.(*discovery.ConfigResult); isConfig { 220 return config, nil 221 } 222 223 return nil, res.(error) 224 } 225 226 func parsePeers(queryType protoext.QueryType, r response, channel string, invocationChain ...*peer.ChaincodeCall) ([]*Peer, error) { 227 peerKeys := key{ 228 queryType: queryType, 229 k: fmt.Sprintf("%s %s", channel, InvocationChain(invocationChain).String()), 230 } 231 res, exists := r[peerKeys] 232 233 if !exists { 234 return nil, ErrNotFound 235 } 236 237 if peers, isPeers := res.([]*Peer); isPeers { 238 return peers, nil 239 } 240 241 return nil, res.(error) 242 } 243 244 func (cr *channelResponse) Peers(invocationChain ...*peer.ChaincodeCall) ([]*Peer, error) { 245 return parsePeers(protoext.PeerMembershipQueryType, cr.response, cr.channel, invocationChain...) 246 } 247 248 func (cr *channelResponse) Endorsers(invocationChain InvocationChain, f Filter) (Endorsers, error) { 249 // If we have a key that has no chaincode field, 250 // it means it's an error returned from the service 251 if err, exists := cr.response[key{ 252 queryType: protoext.ChaincodeQueryType, 253 k: cr.channel, 254 }]; exists { 255 return nil, err.(error) 256 } 257 258 // Else, the service returned a response that isn't an error 259 res, exists := cr.response[key{ 260 queryType: protoext.ChaincodeQueryType, 261 k: cr.channel, 262 invocationChain: invocationChain.String(), 263 }] 264 265 if !exists { 266 return nil, ErrNotFound 267 } 268 269 desc := res.(*endorsementDescriptor) 270 rand.Seed(time.Now().Unix()) 271 // We iterate over all layouts to find one that we have enough peers to select 272 for _, index := range rand.Perm(len(desc.layouts)) { 273 layout := desc.layouts[index] 274 endorsers, canLayoutBeSatisfied := selectPeersForLayout(desc.endorsersByGroups, layout, f) 275 if canLayoutBeSatisfied { 276 return endorsers, nil 277 } 278 } 279 return nil, errors.New("no endorsement combination can be satisfied") 280 } 281 282 type filter struct { 283 ef ExclusionFilter 284 ps PrioritySelector 285 } 286 287 // NewFilter returns an endorser filter that uses the given exclusion filter and priority selector 288 // to filter and sort the endorsers 289 func NewFilter(ps PrioritySelector, ef ExclusionFilter) Filter { 290 return &filter{ 291 ef: ef, 292 ps: ps, 293 } 294 } 295 296 // Filter returns a filtered and sorted list of endorsers 297 func (f *filter) Filter(endorsers Endorsers) Endorsers { 298 return endorsers.Shuffle().Filter(f.ef).Sort(f.ps) 299 } 300 301 // NoFilter returns a noop Filter 302 var NoFilter = NewFilter(NoPriorities, NoExclusion) 303 304 func selectPeersForLayout(endorsersByGroups map[string][]*Peer, layout map[string]int, f Filter) (Endorsers, bool) { 305 var endorsers []*Peer 306 for grp, count := range layout { 307 endorsersOfGrp := f.Filter(Endorsers(endorsersByGroups[grp])) 308 309 // We couldn't select enough peers for this layout because the current group 310 // requires more peers than we have available to be selected 311 if len(endorsersOfGrp) < count { 312 return nil, false 313 } 314 endorsersOfGrp = endorsersOfGrp[:count] 315 endorsers = append(endorsers, endorsersOfGrp...) 316 } 317 // The current (randomly chosen) layout can be satisfied, so return it 318 // instead of checking the next one. 319 return endorsers, true 320 } 321 322 func (resp response) ForLocal() LocalResponse { 323 return &localResponse{ 324 response: resp, 325 } 326 } 327 328 func (resp response) ForChannel(ch string) ChannelResponse { 329 return &channelResponse{ 330 channel: ch, 331 response: resp, 332 } 333 } 334 335 type key struct { 336 queryType protoext.QueryType 337 k string 338 invocationChain string 339 } 340 341 func (req *Request) computeResponse(r *discovery.Response) (response, error) { 342 var err error 343 resp := make(response) 344 for configType, channel2index := range req.queryMapping { 345 switch configType { 346 case protoext.ConfigQueryType: 347 err = resp.mapConfig(channel2index, r) 348 case protoext.ChaincodeQueryType: 349 err = resp.mapEndorsers(channel2index, r, req.invocationChainMapping) 350 case protoext.PeerMembershipQueryType: 351 err = resp.mapPeerMembership(channel2index, r, protoext.PeerMembershipQueryType) 352 case protoext.LocalMembershipQueryType: 353 err = resp.mapPeerMembership(channel2index, r, protoext.LocalMembershipQueryType) 354 } 355 if err != nil { 356 return nil, err 357 } 358 } 359 360 return resp, err 361 } 362 363 func (resp response) mapConfig(channel2index map[string]int, r *discovery.Response) error { 364 for ch, index := range channel2index { 365 config, err := protoext.ResponseConfigAt(r, index) 366 if config == nil && err == nil { 367 return errors.Errorf("expected QueryResult of either ConfigResult or Error but got %v instead", r.Results[index]) 368 } 369 key := key{ 370 queryType: protoext.ConfigQueryType, 371 k: ch, 372 } 373 374 if err != nil { 375 resp[key] = errors.New(err.Content) 376 continue 377 } 378 379 resp[key] = config 380 } 381 return nil 382 } 383 384 func (resp response) mapPeerMembership(key2Index map[string]int, r *discovery.Response, qt protoext.QueryType) error { 385 for k, index := range key2Index { 386 membersRes, err := protoext.ResponseMembershipAt(r, index) 387 if membersRes == nil && err == nil { 388 return errors.Errorf("expected QueryResult of either PeerMembershipResult or Error but got %v instead", r.Results[index]) 389 } 390 391 key := key{ 392 queryType: qt, 393 k: k, 394 } 395 396 if err != nil { 397 resp[key] = errors.New(err.Content) 398 continue 399 } 400 401 peers, err2 := peersForChannel(membersRes, qt) 402 if err2 != nil { 403 return errors.Wrap(err2, "failed constructing peer membership out of response") 404 } 405 406 resp[key] = peers 407 } 408 return nil 409 } 410 411 func peersForChannel(membersRes *discovery.PeerMembershipResult, qt protoext.QueryType) ([]*Peer, error) { 412 var peers []*Peer 413 for org, peersOfCurrentOrg := range membersRes.PeersByOrg { 414 for _, peer := range peersOfCurrentOrg.Peers { 415 aliveMsg, err := gprotoext.EnvelopeToGossipMessage(peer.MembershipInfo) 416 if err != nil { 417 return nil, errors.Wrap(err, "failed unmarshalling alive message") 418 } 419 var stateInfoMsg *gprotoext.SignedGossipMessage 420 if isStateInfoExpected(qt) { 421 stateInfoMsg, err = gprotoext.EnvelopeToGossipMessage(peer.StateInfo) 422 if err != nil { 423 return nil, errors.Wrap(err, "failed unmarshalling stateInfo message") 424 } 425 if err := validateStateInfoMessage(stateInfoMsg); err != nil { 426 return nil, errors.Wrap(err, "failed validating stateInfo message") 427 } 428 } 429 if err := validateAliveMessage(aliveMsg); err != nil { 430 return nil, errors.Wrap(err, "failed validating alive message") 431 } 432 peers = append(peers, &Peer{ 433 MSPID: org, 434 Identity: peer.Identity, 435 AliveMessage: aliveMsg, 436 StateInfoMessage: stateInfoMsg, 437 }) 438 } 439 } 440 return peers, nil 441 } 442 443 func isStateInfoExpected(qt protoext.QueryType) bool { 444 return qt != protoext.LocalMembershipQueryType 445 } 446 447 func (resp response) mapEndorsers( 448 channel2index map[string]int, 449 r *discovery.Response, 450 chaincodeQueryMapping map[int][]InvocationChain) error { 451 for ch, index := range channel2index { 452 ccQueryRes, err := protoext.ResponseEndorsersAt(r, index) 453 if ccQueryRes == nil && err == nil { 454 return errors.Errorf("expected QueryResult of either ChaincodeQueryResult or Error but got %v instead", r.Results[index]) 455 } 456 457 if err != nil { 458 key := key{ 459 queryType: protoext.ChaincodeQueryType, 460 k: ch, 461 } 462 resp[key] = errors.New(err.Content) 463 continue 464 } 465 466 if err := resp.mapEndorsersOfChannel(ccQueryRes, ch, chaincodeQueryMapping[index]); err != nil { 467 return errors.Wrapf(err, "failed assembling endorsers of channel %s", ch) 468 } 469 } 470 return nil 471 } 472 473 func (resp response) mapEndorsersOfChannel(ccRs *discovery.ChaincodeQueryResult, channel string, invocationChain []InvocationChain) error { 474 if len(ccRs.Content) < len(invocationChain) { 475 return errors.Errorf("expected %d endorsement descriptors but got only %d", len(invocationChain), len(ccRs.Content)) 476 } 477 for i, desc := range ccRs.Content { 478 expectedCCName := invocationChain[i][0].Name 479 if desc.Chaincode != expectedCCName { 480 return errors.Errorf("expected chaincode %s but got endorsement descriptor for %s", expectedCCName, desc.Chaincode) 481 } 482 key := key{ 483 queryType: protoext.ChaincodeQueryType, 484 k: channel, 485 invocationChain: invocationChain[i].String(), 486 } 487 488 descriptor, err := resp.createEndorsementDescriptor(desc, channel) 489 if err != nil { 490 return err 491 } 492 resp[key] = descriptor 493 } 494 495 return nil 496 } 497 498 func (resp response) createEndorsementDescriptor(desc *discovery.EndorsementDescriptor, channel string) (*endorsementDescriptor, error) { 499 descriptor := &endorsementDescriptor{ 500 layouts: []map[string]int{}, 501 endorsersByGroups: make(map[string][]*Peer), 502 } 503 for _, l := range desc.Layouts { 504 currentLayout := make(map[string]int) 505 descriptor.layouts = append(descriptor.layouts, currentLayout) 506 for grp, count := range l.QuantitiesByGroup { 507 if _, exists := desc.EndorsersByGroups[grp]; !exists { 508 return nil, errors.Errorf("group %s isn't mapped to endorsers, but exists in a layout", grp) 509 } 510 currentLayout[grp] = int(count) 511 } 512 } 513 514 for grp, peers := range desc.EndorsersByGroups { 515 var endorsers []*Peer 516 for _, p := range peers.Peers { 517 peer, err := endorser(p, desc.Chaincode, channel) 518 if err != nil { 519 return nil, errors.Wrap(err, "failed creating endorser object") 520 } 521 endorsers = append(endorsers, peer) 522 } 523 descriptor.endorsersByGroups[grp] = endorsers 524 } 525 526 return descriptor, nil 527 } 528 529 func endorser(peer *discovery.Peer, chaincode, channel string) (*Peer, error) { 530 if peer.MembershipInfo == nil || peer.StateInfo == nil { 531 return nil, errors.Errorf("received empty envelope(s) for endorsers for chaincode %s, channel %s", chaincode, channel) 532 } 533 aliveMsg, err := gprotoext.EnvelopeToGossipMessage(peer.MembershipInfo) 534 if err != nil { 535 return nil, errors.Wrap(err, "failed unmarshalling gossip envelope to alive message") 536 } 537 stateInfMsg, err := gprotoext.EnvelopeToGossipMessage(peer.StateInfo) 538 if err != nil { 539 return nil, errors.Wrap(err, "failed unmarshalling gossip envelope to state info message") 540 } 541 if err := validateAliveMessage(aliveMsg); err != nil { 542 return nil, errors.Wrap(err, "failed validating alive message") 543 } 544 if err := validateStateInfoMessage(stateInfMsg); err != nil { 545 return nil, errors.Wrap(err, "failed validating stateInfo message") 546 } 547 sID := &msp.SerializedIdentity{} 548 if err := proto.Unmarshal(peer.Identity, sID); err != nil { 549 return nil, errors.Wrap(err, "failed unmarshalling peer's identity") 550 } 551 return &Peer{ 552 Identity: peer.Identity, 553 StateInfoMessage: stateInfMsg, 554 AliveMessage: aliveMsg, 555 MSPID: sID.Mspid, 556 }, nil 557 } 558 559 type endorsementDescriptor struct { 560 endorsersByGroups map[string][]*Peer 561 layouts []map[string]int 562 } 563 564 // NewClient creates a new Client instance 565 func NewClient(createConnection Dialer, s Signer, signerCacheSize uint) *Client { 566 return &Client{ 567 createConnection: createConnection, 568 signRequest: NewMemoizeSigner(s, signerCacheSize).Sign, 569 } 570 } 571 572 func validateAliveMessage(message *gprotoext.SignedGossipMessage) error { 573 am := message.GetAliveMsg() 574 if am == nil { 575 return errors.New("message isn't an alive message") 576 } 577 m := am.Membership 578 if m == nil { 579 return errors.New("membership is empty") 580 } 581 if am.Timestamp == nil { 582 return errors.New("timestamp is nil") 583 } 584 return nil 585 } 586 587 func validateStateInfoMessage(message *gprotoext.SignedGossipMessage) error { 588 si := message.GetStateInfo() 589 if si == nil { 590 return errors.New("message isn't a stateInfo message") 591 } 592 if si.Timestamp == nil { 593 return errors.New("timestamp is nil") 594 } 595 if si.Properties == nil { 596 return errors.New("properties is nil") 597 } 598 return nil 599 } 600 601 func validateInterests(interests ...*peer.ChaincodeInterest) error { 602 if len(interests) == 0 { 603 return errors.New("no chaincode interests given") 604 } 605 for _, interest := range interests { 606 if interest == nil { 607 return errors.New("chaincode interest is nil") 608 } 609 if err := InvocationChain(interest.Chaincodes).ValidateInvocationChain(); err != nil { 610 return err 611 } 612 } 613 return nil 614 } 615 616 // InvocationChain aggregates ChaincodeCalls 617 type InvocationChain []*peer.ChaincodeCall 618 619 // String returns a string representation of this invocation chain 620 func (ic InvocationChain) String() string { 621 s, _ := json.Marshal(ic) 622 return string(s) 623 } 624 625 // ValidateInvocationChain validates the InvocationChain's structure 626 func (ic InvocationChain) ValidateInvocationChain() error { 627 if len(ic) == 0 { 628 return errors.New("invocation chain should not be empty") 629 } 630 for _, cc := range ic { 631 if cc.Name == "" { 632 return errors.New("chaincode name should not be empty") 633 } 634 } 635 return nil 636 }