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  }