github.com/kaituanwang/hyperledger@v2.0.1+incompatible/discovery/client/client.go (about)

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