github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/internal/pkg/gateway/registry.go (about)

     1  /*
     2  Copyright 2021 IBM All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package gateway
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"math/rand"
    13  	"sort"
    14  	"strings"
    15  	"sync"
    16  
    17  	"github.com/golang/protobuf/proto"
    18  	"github.com/hechain20/hechain/common/channelconfig"
    19  	"github.com/hechain20/hechain/common/flogging"
    20  	gossipapi "github.com/hechain20/hechain/gossip/api"
    21  	gossipcommon "github.com/hechain20/hechain/gossip/common"
    22  	gossipdiscovery "github.com/hechain20/hechain/gossip/discovery"
    23  	dp "github.com/hyperledger/fabric-protos-go/discovery"
    24  	"github.com/hyperledger/fabric-protos-go/gossip"
    25  	"github.com/hyperledger/fabric-protos-go/peer"
    26  )
    27  
    28  type Discovery interface {
    29  	Config(channel string) (*dp.ConfigResult, error)
    30  	IdentityInfo() gossipapi.PeerIdentitySet
    31  	PeersForEndorsement(channel gossipcommon.ChannelID, interest *peer.ChaincodeInterest) (*dp.EndorsementDescriptor, error)
    32  	PeersOfChannel(gossipcommon.ChannelID) gossipdiscovery.Members
    33  }
    34  
    35  type registry struct {
    36  	localEndorser      *endorser
    37  	discovery          Discovery
    38  	logger             *flogging.FabricLogger
    39  	endpointFactory    *endpointFactory
    40  	remoteEndorsers    map[string]*endorser
    41  	broadcastClients   sync.Map // orderer address (string) -> client connection (orderer)
    42  	channelInitialized map[string]bool
    43  	configLock         sync.RWMutex
    44  	channelOrderers    sync.Map // channel (string) -> orderer addresses (endpointConfig)
    45  }
    46  
    47  type endorserState struct {
    48  	peer     *dp.Peer
    49  	endorser *endorser
    50  	height   uint64
    51  }
    52  
    53  // Returns an endorsementPlan for the given chaincode on a channel.
    54  func (reg *registry) endorsementPlan(channel string, interest *peer.ChaincodeInterest, preferredEndorser *endorser) (*plan, error) {
    55  	descriptor, err := reg.discovery.PeersForEndorsement(gossipcommon.ChannelID(channel), interest)
    56  	if err != nil {
    57  		logger.Errorw("PeersForEndorsement failed.", "error", err, "channel", channel, "ChaincodeInterest", proto.MarshalTextString(interest))
    58  		return nil, fmt.Errorf("no combination of peers can be derived which satisfy the endorsement policy: %s", err)
    59  	}
    60  
    61  	// There are two parts to an endorsement plan:
    62  	// 1) List of endorsers in each group
    63  	// 2) Layouts consisting of a number of endorsers from each group
    64  
    65  	// Firstly, process the endorsers by group
    66  	// Create a map of groupIds to list of endorsers, sorted by decreasing block height
    67  	// Also build a map of endorser pkiid to groupId
    68  	groupEndorsers := map[string][]*endorser{}
    69  	var preferredGroup string
    70  	var unavailableEndorsers []string
    71  
    72  	for group, peers := range descriptor.GetEndorsersByGroups() {
    73  		var groupPeers []*endorserState
    74  		for _, peer := range peers.GetPeers() {
    75  			// extract block height
    76  			msg := &gossip.GossipMessage{}
    77  			err = proto.Unmarshal(peer.GetStateInfo().GetPayload(), msg)
    78  			if err != nil {
    79  				return nil, err
    80  			}
    81  			height := msg.GetStateInfo().GetProperties().GetLedgerHeight()
    82  
    83  			// extract endpoint
    84  			err = proto.Unmarshal(peer.GetMembershipInfo().GetPayload(), msg)
    85  			if err != nil {
    86  				return nil, err
    87  			}
    88  			member := msg.GetAliveMsg().GetMembership()
    89  
    90  			// find the endorser in the registry for this endpoint
    91  			endorser := reg.lookupEndorser(member.GetEndpoint(), member.GetPkiId(), channel)
    92  			if endorser == nil {
    93  				unavailableEndorsers = append(unavailableEndorsers, member.GetEndpoint())
    94  				continue
    95  			}
    96  			if endorser == preferredEndorser {
    97  				preferredGroup = group
    98  			}
    99  			groupPeers = append(groupPeers, &endorserState{peer: peer, endorser: endorser, height: height})
   100  		}
   101  		// sort by decreasing height
   102  		sort.Slice(groupPeers, sorter(groupPeers, reg.localEndorser.address))
   103  
   104  		if len(groupPeers) > 0 {
   105  			var endorsers []*endorser
   106  			for _, peer := range groupPeers {
   107  				endorsers = append(endorsers, peer.endorser)
   108  			}
   109  			groupEndorsers[group] = endorsers
   110  		}
   111  	}
   112  
   113  	// Second, process the layouts
   114  	// Group them by groupId and arrange them so that layouts containing the 'preferredOrg' are at the front of the list
   115  	var preferredLayouts []*layout
   116  	var otherLayouts []*layout
   117  layout:
   118  	for _, lo := range descriptor.GetLayouts() {
   119  		hasPreferredGroup := false
   120  		quantityByGroup := map[string]int{}
   121  		for group, quantity := range lo.GetQuantitiesByGroup() {
   122  			// if there's no entry in this map, meaning there aren't any available endorsers for this group
   123  			if len(groupEndorsers[group]) < int(quantity) {
   124  				// The number of available endorsers less than the quantity required, so abandon this layout
   125  				continue layout
   126  			}
   127  			quantityByGroup[group] = int(quantity)
   128  			if group == preferredGroup {
   129  				hasPreferredGroup = true
   130  			}
   131  		}
   132  		if len(quantityByGroup) == 0 {
   133  			// empty layout
   134  			break
   135  		}
   136  		if hasPreferredGroup {
   137  			preferredLayouts = append(preferredLayouts, &layout{required: quantityByGroup})
   138  		} else {
   139  			otherLayouts = append(otherLayouts, &layout{required: quantityByGroup})
   140  		}
   141  	}
   142  
   143  	// shuffle the layouts - keep the preferred ones first
   144  	rand.Shuffle(len(preferredLayouts), func(i, j int) { preferredLayouts[i], preferredLayouts[j] = preferredLayouts[j], preferredLayouts[i] })
   145  	rand.Shuffle(len(otherLayouts), func(i, j int) { otherLayouts[i], otherLayouts[j] = otherLayouts[j], otherLayouts[i] })
   146  	layouts := append(preferredLayouts, otherLayouts...)
   147  
   148  	if len(layouts) == 0 {
   149  		return nil, fmt.Errorf("failed to select a set of endorsers that satisfy the endorsement policy due to unavailability of peers: %v", unavailableEndorsers)
   150  	}
   151  
   152  	return newPlan(layouts, groupEndorsers), nil
   153  }
   154  
   155  // planForOrgs generates an endorsement plan with a single layout requiring one peer from each of the given orgs for the given chaincode on a channel.
   156  func (reg *registry) planForOrgs(channel string, chaincode string, endorsingOrgs []string) (*plan, error) {
   157  	endorsersByOrg := reg.endorsersByOrg(channel, chaincode)
   158  
   159  	required := map[string]int{}
   160  	groupEndorsers := map[string][]*endorser{}
   161  	missingOrgs := []string{}
   162  	for _, org := range endorsingOrgs {
   163  		if es, ok := endorsersByOrg[org]; ok {
   164  			for _, e := range es {
   165  				groupEndorsers[org] = append(groupEndorsers[org], e.endorser)
   166  			}
   167  			required[org] = 1
   168  		} else {
   169  			missingOrgs = append(missingOrgs, org)
   170  		}
   171  	}
   172  	if len(missingOrgs) > 0 {
   173  		return nil, fmt.Errorf("failed to find any endorsing peers for org(s): %s", strings.Join(missingOrgs, ", "))
   174  	}
   175  
   176  	return newPlan([]*layout{{required: required}}, groupEndorsers), nil
   177  }
   178  
   179  func (reg *registry) endorsersByOrg(channel string, chaincode string) map[string][]*endorserState {
   180  	endorsersByOrg := make(map[string][]*endorserState)
   181  
   182  	members := reg.discovery.PeersOfChannel(gossipcommon.ChannelID(channel))
   183  
   184  	for _, member := range members {
   185  		endorser := reg.lookupEndorser(member.PreferredEndpoint(), member.PKIid, channel)
   186  		if endorser == nil {
   187  			continue
   188  		}
   189  		for _, installedChaincode := range member.Properties.GetChaincodes() {
   190  			// only consider the peers that have our chaincode installed
   191  			if installedChaincode.GetName() == chaincode {
   192  				endorsersByOrg[endorser.mspid] = append(endorsersByOrg[endorser.mspid], &endorserState{endorser: endorser, height: member.Properties.GetLedgerHeight()})
   193  			}
   194  		}
   195  		for _, es := range endorsersByOrg {
   196  			// sort by decreasing height in each org
   197  			sort.Slice(es, sorter(es, reg.localEndorser.address))
   198  		}
   199  	}
   200  	return endorsersByOrg
   201  }
   202  
   203  // evaluator returns a plan representing a single endorsement, preferably from local org, if available
   204  // targetOrgs specifies the orgs that are allowed receive the request, due to private data restrictions
   205  func (reg *registry) evaluator(channel string, chaincode string, targetOrgs []string) (*plan, error) {
   206  	endorsersByOrg := reg.endorsersByOrg(channel, chaincode)
   207  
   208  	// If no targetOrgs are specified (i.e. no restrictions), then populate with all available orgs
   209  	if len(targetOrgs) == 0 {
   210  		for org := range endorsersByOrg {
   211  			targetOrgs = append(targetOrgs, org)
   212  		}
   213  	}
   214  
   215  	localOrgEndorsers := []*endorserState{}
   216  	otherOrgEndorsers := []*endorserState{}
   217  	for _, org := range targetOrgs {
   218  		if es, ok := endorsersByOrg[org]; ok {
   219  			if org == reg.localEndorser.mspid {
   220  				localOrgEndorsers = es
   221  			} else {
   222  				otherOrgEndorsers = append(otherOrgEndorsers, es...)
   223  			}
   224  		}
   225  	}
   226  	// sort all the 'other orgs' endorsers by decreasing block height
   227  	sort.Slice(otherOrgEndorsers, sorter(otherOrgEndorsers, ""))
   228  
   229  	var allEndorsers []*endorser
   230  	for _, e := range append(localOrgEndorsers, otherOrgEndorsers...) {
   231  		allEndorsers = append(allEndorsers, e.endorser)
   232  	}
   233  	if len(allEndorsers) > 0 {
   234  		layout := []*layout{{required: map[string]int{"g1": 1}}} // single layout, one group, one endorsement
   235  		groupEndorsers := map[string][]*endorser{"g1": allEndorsers}
   236  		return newPlan(layout, groupEndorsers), nil
   237  	}
   238  	return nil, fmt.Errorf("no peers available to evaluate chaincode %s in channel %s", chaincode, channel)
   239  }
   240  
   241  func sorter(e []*endorserState, host string) func(i, j int) bool {
   242  	return func(i, j int) bool {
   243  		if e[i].height == e[j].height {
   244  			// prefer host peer
   245  			return e[i].endorser.address == host
   246  		}
   247  		return e[i].height > e[j].height
   248  	}
   249  }
   250  
   251  // Returns a set of broadcastClients that can order a transaction for the given channel.
   252  func (reg *registry) orderers(channel string) ([]*orderer, error) {
   253  	var orderers []*orderer
   254  	var ordererEndpoints []*endpointConfig
   255  	addr, exists := reg.channelOrderers.Load(channel)
   256  	// if it doesn't exist, get the orderers config for this channel
   257  	if exists {
   258  		ordererEndpoints = addr.([]*endpointConfig)
   259  	} else {
   260  		// no entry in the map - get the orderer config from discovery
   261  		channelOrderers, err := reg.config(channel)
   262  		if err != nil {
   263  			return nil, err
   264  		}
   265  		// A config update may have saved this first, in which case don't overwrite it.
   266  		addr, _ = reg.channelOrderers.LoadOrStore(channel, channelOrderers)
   267  		ordererEndpoints = addr.([]*endpointConfig)
   268  	}
   269  	for _, ep := range ordererEndpoints {
   270  		entry, exists := reg.broadcastClients.Load(ep.address)
   271  		if !exists {
   272  			// this orderer is new - connect to it and add to the broadcastClients registry
   273  			client, err := reg.endpointFactory.newOrderer(ep.address, ep.mspid, ep.tlsRootCerts)
   274  			if err != nil {
   275  				// Failed to connect to this orderer for some reason.  Log the problem and skip to the next one.
   276  				reg.logger.Warnw("Failed to connect to orderer", "address", ep.address, "err", err)
   277  				continue
   278  			}
   279  			var loaded bool
   280  			entry, loaded = reg.broadcastClients.LoadOrStore(ep.address, client)
   281  			if loaded {
   282  				// another goroutine got there first, close this new connection
   283  				err = client.closeConnection()
   284  				if err != nil {
   285  					// Failed to close this new connection.  Log the problem.
   286  					reg.logger.Warnw("Failed to close connection to orderer", "address", ep.address, "err", err)
   287  				}
   288  			} else {
   289  				reg.logger.Infow("Added orderer to registry", "address", ep.address)
   290  			}
   291  		}
   292  		orderers = append(orderers, entry.(*orderer))
   293  	}
   294  
   295  	return orderers, nil
   296  }
   297  
   298  func (reg *registry) lookupEndorser(endpoint string, pkiid gossipcommon.PKIidType, channel string) *endorser {
   299  	lookup := func() (*endorser, bool) {
   300  		reg.configLock.RLock()
   301  		defer reg.configLock.RUnlock()
   302  
   303  		// find the endorser in the registry for this endpoint
   304  		if bytes.Equal(pkiid, reg.localEndorser.pkiid) {
   305  			logger.Debugw("Found local endorser", "pkiid", pkiid)
   306  			return reg.localEndorser, false
   307  		}
   308  		if endpoint == "" {
   309  			reg.logger.Warnw("No endpoint for endorser with PKI ID %s", "pkiid", pkiid.String())
   310  			return nil, false
   311  		}
   312  		if e, ok := reg.remoteEndorsers[endpoint]; ok {
   313  			logger.Debugw("Found remote endorser", "endpoint", endpoint)
   314  			return e, false
   315  		}
   316  		// not found - try to connect
   317  		return nil, true
   318  	}
   319  	endorser, connect := lookup()
   320  	if connect {
   321  		reg.logger.Infow("Attempting to connect to endorser", "endpoint", endpoint)
   322  		// for efficiency, try to connect to all peers in the channel in one pass
   323  		err := reg.connectChannelPeers(channel, true)
   324  		if err != nil {
   325  			logger.Errorw("Failed to reconnect", "endpoint", endpoint, "err", err)
   326  		}
   327  		endorser, _ = lookup() // if it failed to reconnect, this will be nil
   328  	}
   329  	return endorser
   330  }
   331  
   332  // Connect to peers in channel, and add to the registry.  If a reconnection is required, then it must be removed
   333  // from the registry first, using removeEndorser().
   334  func (reg *registry) connectChannelPeers(channel string, force bool) error {
   335  	reg.configLock.Lock() // take a write lock to populate the registry maps
   336  	defer reg.configLock.Unlock()
   337  
   338  	if !force && reg.channelInitialized[channel] {
   339  		return nil
   340  	}
   341  
   342  	// get the remoteEndorsers for the channel
   343  	peers := map[string]string{}
   344  	members := reg.discovery.PeersOfChannel(gossipcommon.ChannelID(channel))
   345  	for _, member := range members {
   346  		id := member.PKIid.String()
   347  		peers[id] = member.PreferredEndpoint()
   348  	}
   349  	config, err := reg.discovery.Config(channel)
   350  	if err != nil {
   351  		return fmt.Errorf("failed to get config for channel [%s]: %w", channel, err)
   352  	}
   353  	for mspid, infoset := range reg.discovery.IdentityInfo().ByOrg() {
   354  		var tlsRootCerts [][]byte
   355  		if mspInfo, ok := config.GetMsps()[mspid]; ok {
   356  			tlsRootCerts = append(tlsRootCerts, mspInfo.GetTlsRootCerts()...)
   357  			tlsRootCerts = append(tlsRootCerts, mspInfo.GetTlsIntermediateCerts()...)
   358  		}
   359  		for _, info := range infoset {
   360  			pkiid := info.PKIId
   361  			if address, ok := peers[pkiid.String()]; ok {
   362  				// add the peer to the peer map - except the local peer, which seems to have an empty address
   363  				if _, ok := reg.remoteEndorsers[address]; !ok && len(address) > 0 {
   364  					// this peer is new - connect to it and add to the remoteEndorsers registry
   365  					endorser, err := reg.endpointFactory.newEndorser(pkiid, address, mspid, tlsRootCerts)
   366  					if err != nil {
   367  						return err
   368  					}
   369  					reg.remoteEndorsers[address] = endorser
   370  					reg.logger.Infof("Added peer to registry: %s", address)
   371  				}
   372  			}
   373  		}
   374  	}
   375  	reg.channelInitialized[channel] = true
   376  	return nil
   377  }
   378  
   379  // removeEndorser closes the connection and removes from the registry, but if the next call to discovery returns that
   380  // peer in its plan, then it will attempt to reconnect and add it back. This could happen if the peer had gone down,
   381  // but the gossip has not notified the discovery service yet.
   382  func (reg *registry) removeEndorser(endorser *endorser) {
   383  	if endorser == reg.localEndorser {
   384  		// nothing to close
   385  		return
   386  	}
   387  	reg.configLock.Lock()
   388  	defer reg.configLock.Unlock()
   389  
   390  	err := endorser.closeConnection()
   391  	if err != nil {
   392  		reg.logger.Errorw("Failed to close connection to endorser", "address", endorser.address, "mspid", endorser.mspid, "err", err)
   393  	}
   394  	delete(reg.remoteEndorsers, endorser.address)
   395  }
   396  
   397  func (reg *registry) config(channel string) ([]*endpointConfig, error) {
   398  	config, err := reg.discovery.Config(channel)
   399  	if err != nil {
   400  		return nil, fmt.Errorf("failed to get config for channel [%s]: %w", channel, err)
   401  	}
   402  	var channelOrderers []*endpointConfig
   403  	for mspid, eps := range config.GetOrderers() {
   404  		var tlsRootCerts [][]byte
   405  		if mspInfo, ok := config.GetMsps()[mspid]; ok {
   406  			tlsRootCerts = append(tlsRootCerts, mspInfo.GetTlsRootCerts()...)
   407  			tlsRootCerts = append(tlsRootCerts, mspInfo.GetTlsIntermediateCerts()...)
   408  		}
   409  		for _, ep := range eps.Endpoint {
   410  			address := fmt.Sprintf("%s:%d", ep.Host, ep.Port)
   411  			channelOrderers = append(channelOrderers, &endpointConfig{address: address, mspid: mspid, tlsRootCerts: tlsRootCerts})
   412  		}
   413  	}
   414  	return channelOrderers, nil
   415  }
   416  
   417  func (reg *registry) configUpdate(bundle *channelconfig.Bundle) {
   418  	if ordererConfig, ok := bundle.OrdererConfig(); ok {
   419  		// orderer config has changed - process the bundle
   420  		channel := bundle.ConfigtxValidator().ChannelID()
   421  		reg.logger.Infow("Updating orderer config", "channel", channel)
   422  		var channelOrderers []*endpointConfig
   423  		for _, org := range ordererConfig.Organizations() {
   424  			mspid := org.MSPID()
   425  			msp := org.MSP()
   426  			tlsRootCerts := append([][]byte{}, msp.GetTLSRootCerts()...)
   427  			tlsRootCerts = append(tlsRootCerts, msp.GetTLSIntermediateCerts()...)
   428  			for _, address := range org.Endpoints() {
   429  				channelOrderers = append(channelOrderers, &endpointConfig{address: address, mspid: mspid, tlsRootCerts: tlsRootCerts})
   430  				reg.logger.Debugw("Channel orderer", "address", address, "mspid", mspid)
   431  			}
   432  		}
   433  		if len(channelOrderers) > 0 {
   434  			reg.closeStaleOrdererConnections(channel, channelOrderers)
   435  			reg.channelOrderers.Store(channel, channelOrderers)
   436  		}
   437  	}
   438  }
   439  
   440  func (reg *registry) closeStaleOrdererConnections(channel string, channelOrderers []*endpointConfig) {
   441  	// Load the list of orderers that is about to be overwritten, if loaded is false, then another goroutine got there first
   442  	oldList, loaded := reg.channelOrderers.LoadAndDelete(channel)
   443  	if loaded {
   444  		currentEndpoints := map[string]struct{}{}
   445  		reg.channelOrderers.Range(func(key, value interface{}) bool {
   446  			for _, ep := range value.([]*endpointConfig) {
   447  				currentEndpoints[ep.address] = struct{}{}
   448  			}
   449  			return true
   450  		})
   451  		for _, ep := range channelOrderers {
   452  			currentEndpoints[ep.address] = struct{}{}
   453  		}
   454  		// if there are any in the oldEndpoints that are not in the currentEndpoints, then remove from registry and close connection
   455  		for _, ep := range oldList.([]*endpointConfig) {
   456  			if _, exists := currentEndpoints[ep.address]; !exists {
   457  				client, found := reg.broadcastClients.LoadAndDelete(ep.address)
   458  				if found {
   459  					err := client.(*orderer).closeConnection()
   460  					if err != nil {
   461  						reg.logger.Errorw("Failed to close connection to orderer", "address", ep.address, "mspid", ep.mspid, "err", err)
   462  					}
   463  				}
   464  			}
   465  		}
   466  	}
   467  }