github.com/yous1230/fabric@v2.0.0-beta.0.20191224111736-74345bee6ac2+incompatible/gossip/comm/comm_impl.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package comm
     8  
     9  import (
    10  	"bytes"
    11  	"context"
    12  	"crypto/tls"
    13  	"encoding/hex"
    14  	"fmt"
    15  	"reflect"
    16  	"sync"
    17  	"sync/atomic"
    18  	"time"
    19  
    20  	proto "github.com/hyperledger/fabric-protos-go/gossip"
    21  	"github.com/hyperledger/fabric/gossip/api"
    22  	"github.com/hyperledger/fabric/gossip/common"
    23  	"github.com/hyperledger/fabric/gossip/identity"
    24  	"github.com/hyperledger/fabric/gossip/metrics"
    25  	"github.com/hyperledger/fabric/gossip/protoext"
    26  	"github.com/hyperledger/fabric/gossip/util"
    27  	"github.com/pkg/errors"
    28  	"google.golang.org/grpc"
    29  	"google.golang.org/grpc/peer"
    30  )
    31  
    32  const (
    33  	handshakeTimeout = time.Second * 10
    34  	DefDialTimeout   = time.Second * 3
    35  	DefConnTimeout   = time.Second * 2
    36  	DefRecvBuffSize  = 20
    37  	DefSendBuffSize  = 20
    38  )
    39  
    40  // SecurityAdvisor defines an external auxiliary object
    41  // that provides security and identity related capabilities
    42  type SecurityAdvisor interface {
    43  	// OrgByPeerIdentity returns the organization identity of the given PeerIdentityType
    44  	OrgByPeerIdentity(api.PeerIdentityType) api.OrgIdentityType
    45  }
    46  
    47  func (c *commImpl) SetDialOpts(opts ...grpc.DialOption) {
    48  	if len(opts) == 0 {
    49  		c.logger.Warning("Given an empty set of grpc.DialOption, aborting")
    50  		return
    51  	}
    52  	c.opts = opts
    53  }
    54  
    55  // NewCommInstance creates a new comm instance that binds itself to the given gRPC server
    56  func NewCommInstance(s *grpc.Server, certs *common.TLSCertificates, idStore identity.Mapper,
    57  	peerIdentity api.PeerIdentityType, secureDialOpts api.PeerSecureDialOpts, sa api.SecurityAdvisor,
    58  	commMetrics *metrics.CommMetrics, config CommConfig, dialOpts ...grpc.DialOption) (Comm, error) {
    59  
    60  	commInst := &commImpl{
    61  		sa:              sa,
    62  		pubSub:          util.NewPubSub(),
    63  		PKIID:           idStore.GetPKIidOfCert(peerIdentity),
    64  		idMapper:        idStore,
    65  		logger:          util.GetLogger(util.CommLogger, ""),
    66  		peerIdentity:    peerIdentity,
    67  		opts:            dialOpts,
    68  		secureDialOpts:  secureDialOpts,
    69  		msgPublisher:    NewChannelDemultiplexer(),
    70  		lock:            &sync.Mutex{},
    71  		deadEndpoints:   make(chan common.PKIidType, 100),
    72  		identityChanges: make(chan common.PKIidType, 1),
    73  		stopping:        int32(0),
    74  		exitChan:        make(chan struct{}),
    75  		subscriptions:   make([]chan protoext.ReceivedMessage, 0),
    76  		tlsCerts:        certs,
    77  		metrics:         commMetrics,
    78  		dialTimeout:     config.DialTimeout,
    79  		connTimeout:     config.ConnTimeout,
    80  		recvBuffSize:    config.RecvBuffSize,
    81  		sendBuffSize:    config.SendBuffSize,
    82  	}
    83  
    84  	connConfig := ConnConfig{
    85  		RecvBuffSize: config.RecvBuffSize,
    86  		SendBuffSize: config.SendBuffSize,
    87  	}
    88  
    89  	commInst.connStore = newConnStore(commInst, commInst.logger, connConfig)
    90  
    91  	proto.RegisterGossipServer(s, commInst)
    92  
    93  	return commInst, nil
    94  }
    95  
    96  // CommConfig is the configuration required to initialize a new comm
    97  type CommConfig struct {
    98  	DialTimeout  time.Duration // Dial timeout
    99  	ConnTimeout  time.Duration // Connection timeout
   100  	RecvBuffSize int           // Buffer size of received messages
   101  	SendBuffSize int           // Buffer size of sending messages
   102  }
   103  
   104  type commImpl struct {
   105  	sa              api.SecurityAdvisor
   106  	tlsCerts        *common.TLSCertificates
   107  	pubSub          *util.PubSub
   108  	peerIdentity    api.PeerIdentityType
   109  	idMapper        identity.Mapper
   110  	logger          util.Logger
   111  	opts            []grpc.DialOption
   112  	secureDialOpts  func() []grpc.DialOption
   113  	connStore       *connectionStore
   114  	PKIID           []byte
   115  	deadEndpoints   chan common.PKIidType
   116  	identityChanges chan common.PKIidType
   117  	msgPublisher    *ChannelDeMultiplexer
   118  	lock            *sync.Mutex
   119  	exitChan        chan struct{}
   120  	stopWG          sync.WaitGroup
   121  	subscriptions   []chan protoext.ReceivedMessage
   122  	stopping        int32
   123  	metrics         *metrics.CommMetrics
   124  	dialTimeout     time.Duration
   125  	connTimeout     time.Duration
   126  	recvBuffSize    int
   127  	sendBuffSize    int
   128  }
   129  
   130  func (c *commImpl) createConnection(endpoint string, expectedPKIID common.PKIidType) (*connection, error) {
   131  	var err error
   132  	var cc *grpc.ClientConn
   133  	var stream proto.Gossip_GossipStreamClient
   134  	var pkiID common.PKIidType
   135  	var connInfo *protoext.ConnectionInfo
   136  	var dialOpts []grpc.DialOption
   137  
   138  	c.logger.Debug("Entering", endpoint, expectedPKIID)
   139  	defer c.logger.Debug("Exiting")
   140  
   141  	if c.isStopping() {
   142  		return nil, errors.New("Stopping")
   143  	}
   144  	dialOpts = append(dialOpts, c.secureDialOpts()...)
   145  	dialOpts = append(dialOpts, grpc.WithBlock())
   146  	dialOpts = append(dialOpts, c.opts...)
   147  	ctx := context.Background()
   148  	ctx, cancel := context.WithTimeout(ctx, c.dialTimeout)
   149  	defer cancel()
   150  	cc, err = grpc.DialContext(ctx, endpoint, dialOpts...)
   151  	if err != nil {
   152  		return nil, errors.WithStack(err)
   153  	}
   154  
   155  	cl := proto.NewGossipClient(cc)
   156  
   157  	ctx, cancel = context.WithTimeout(context.Background(), DefConnTimeout)
   158  	defer cancel()
   159  	if _, err = cl.Ping(ctx, &proto.Empty{}); err != nil {
   160  		cc.Close()
   161  		return nil, errors.WithStack(err)
   162  	}
   163  
   164  	ctx, cancel = context.WithCancel(context.Background())
   165  	if stream, err = cl.GossipStream(ctx); err == nil {
   166  		connInfo, err = c.authenticateRemotePeer(stream, true)
   167  		if err == nil {
   168  			pkiID = connInfo.ID
   169  			// PKIID is nil when we don't know the remote PKI id's
   170  			if expectedPKIID != nil && !bytes.Equal(pkiID, expectedPKIID) {
   171  				actualOrg := c.sa.OrgByPeerIdentity(connInfo.Identity)
   172  				// If the identity isn't present, it's nil - therefore OrgByPeerIdentity would
   173  				// return nil too and thus would be different than the actual organization
   174  				identity, _ := c.idMapper.Get(expectedPKIID)
   175  				oldOrg := c.sa.OrgByPeerIdentity(identity)
   176  				if !bytes.Equal(actualOrg, oldOrg) {
   177  					c.logger.Warning("Remote endpoint claims to be a different peer, expected", expectedPKIID, "but got", pkiID)
   178  					cc.Close()
   179  					cancel()
   180  					return nil, errors.New("authentication failure")
   181  				} else {
   182  					c.logger.Infof("Peer %s changed its PKI-ID from %s to %s", endpoint, expectedPKIID, pkiID)
   183  					c.identityChanges <- expectedPKIID
   184  				}
   185  			}
   186  			connConfig := ConnConfig{
   187  				RecvBuffSize: c.recvBuffSize,
   188  				SendBuffSize: c.sendBuffSize,
   189  			}
   190  			conn := newConnection(cl, cc, stream, c.metrics, connConfig)
   191  			conn.pkiID = pkiID
   192  			conn.info = connInfo
   193  			conn.logger = c.logger
   194  			conn.cancel = cancel
   195  
   196  			h := func(m *protoext.SignedGossipMessage) {
   197  				c.logger.Debug("Got message:", m)
   198  				c.msgPublisher.DeMultiplex(&ReceivedMessageImpl{
   199  					conn:                conn,
   200  					SignedGossipMessage: m,
   201  					connInfo:            connInfo,
   202  				})
   203  			}
   204  			conn.handler = interceptAcks(h, connInfo.ID, c.pubSub)
   205  			return conn, nil
   206  		}
   207  		c.logger.Warningf("Authentication failed: %+v", err)
   208  	}
   209  	cc.Close()
   210  	cancel()
   211  	return nil, errors.WithStack(err)
   212  }
   213  
   214  func (c *commImpl) Send(msg *protoext.SignedGossipMessage, peers ...*RemotePeer) {
   215  	if c.isStopping() || len(peers) == 0 {
   216  		return
   217  	}
   218  	c.logger.Debug("Entering, sending", msg, "to ", len(peers), "peers")
   219  
   220  	for _, peer := range peers {
   221  		go func(peer *RemotePeer, msg *protoext.SignedGossipMessage) {
   222  			c.sendToEndpoint(peer, msg, nonBlockingSend)
   223  		}(peer, msg)
   224  	}
   225  }
   226  
   227  func (c *commImpl) sendToEndpoint(peer *RemotePeer, msg *protoext.SignedGossipMessage, shouldBlock blockingBehavior) {
   228  	if c.isStopping() {
   229  		return
   230  	}
   231  	c.logger.Debug("Entering, Sending to", peer.Endpoint, ", msg:", msg)
   232  	defer c.logger.Debug("Exiting")
   233  	var err error
   234  
   235  	conn, err := c.connStore.getConnection(peer)
   236  	if err == nil {
   237  		disConnectOnErr := func(err error) {
   238  			c.logger.Warningf("%v isn't responsive: %v", peer, err)
   239  			c.disconnect(peer.PKIID)
   240  			conn.close()
   241  		}
   242  		conn.send(msg, disConnectOnErr, shouldBlock)
   243  		return
   244  	}
   245  	c.logger.Warningf("Failed obtaining connection for %v reason: %v", peer, err)
   246  	c.disconnect(peer.PKIID)
   247  }
   248  
   249  func (c *commImpl) isStopping() bool {
   250  	return atomic.LoadInt32(&c.stopping) == int32(1)
   251  }
   252  
   253  func (c *commImpl) Probe(remotePeer *RemotePeer) error {
   254  	var dialOpts []grpc.DialOption
   255  	endpoint := remotePeer.Endpoint
   256  	pkiID := remotePeer.PKIID
   257  	if c.isStopping() {
   258  		return fmt.Errorf("Stopping")
   259  	}
   260  	c.logger.Debug("Entering, endpoint:", endpoint, "PKIID:", pkiID)
   261  	dialOpts = append(dialOpts, c.secureDialOpts()...)
   262  	dialOpts = append(dialOpts, grpc.WithBlock())
   263  	dialOpts = append(dialOpts, c.opts...)
   264  	ctx := context.Background()
   265  	ctx, cancel := context.WithTimeout(ctx, c.dialTimeout)
   266  	defer cancel()
   267  	cc, err := grpc.DialContext(ctx, remotePeer.Endpoint, dialOpts...)
   268  	if err != nil {
   269  		c.logger.Debugf("Returning %v", err)
   270  		return err
   271  	}
   272  	defer cc.Close()
   273  	cl := proto.NewGossipClient(cc)
   274  	ctx, cancel = context.WithTimeout(context.Background(), DefConnTimeout)
   275  	defer cancel()
   276  	_, err = cl.Ping(ctx, &proto.Empty{})
   277  	c.logger.Debugf("Returning %v", err)
   278  	return err
   279  }
   280  
   281  func (c *commImpl) Handshake(remotePeer *RemotePeer) (api.PeerIdentityType, error) {
   282  	var dialOpts []grpc.DialOption
   283  	dialOpts = append(dialOpts, c.secureDialOpts()...)
   284  	dialOpts = append(dialOpts, grpc.WithBlock())
   285  	dialOpts = append(dialOpts, c.opts...)
   286  	ctx := context.Background()
   287  	ctx, cancel := context.WithTimeout(ctx, c.dialTimeout)
   288  	defer cancel()
   289  	cc, err := grpc.DialContext(ctx, remotePeer.Endpoint, dialOpts...)
   290  	if err != nil {
   291  		return nil, err
   292  	}
   293  	defer cc.Close()
   294  
   295  	cl := proto.NewGossipClient(cc)
   296  	ctx, cancel = context.WithTimeout(context.Background(), DefConnTimeout)
   297  	defer cancel()
   298  	if _, err = cl.Ping(ctx, &proto.Empty{}); err != nil {
   299  		return nil, err
   300  	}
   301  
   302  	ctx, cancel = context.WithTimeout(context.Background(), handshakeTimeout)
   303  	defer cancel()
   304  	stream, err := cl.GossipStream(ctx)
   305  	if err != nil {
   306  		return nil, err
   307  	}
   308  	connInfo, err := c.authenticateRemotePeer(stream, true)
   309  	if err != nil {
   310  		c.logger.Warningf("Authentication failed: %v", err)
   311  		return nil, err
   312  	}
   313  	if len(remotePeer.PKIID) > 0 && !bytes.Equal(connInfo.ID, remotePeer.PKIID) {
   314  		return nil, fmt.Errorf("PKI-ID of remote peer doesn't match expected PKI-ID")
   315  	}
   316  	return connInfo.Identity, nil
   317  }
   318  
   319  func (c *commImpl) Accept(acceptor common.MessageAcceptor) <-chan protoext.ReceivedMessage {
   320  	genericChan := c.msgPublisher.AddChannel(acceptor)
   321  	specificChan := make(chan protoext.ReceivedMessage, 10)
   322  
   323  	if c.isStopping() {
   324  		c.logger.Warning("Accept() called but comm module is stopping, returning empty channel")
   325  		return specificChan
   326  	}
   327  
   328  	c.lock.Lock()
   329  	c.subscriptions = append(c.subscriptions, specificChan)
   330  	c.lock.Unlock()
   331  
   332  	c.stopWG.Add(1)
   333  	go func() {
   334  		defer c.logger.Debug("Exiting Accept() loop")
   335  
   336  		defer c.stopWG.Done()
   337  
   338  		for {
   339  			select {
   340  			case msg, channelOpen := <-genericChan:
   341  				if !channelOpen {
   342  					return
   343  				}
   344  				select {
   345  				case specificChan <- msg.(*ReceivedMessageImpl):
   346  				case <-c.exitChan:
   347  					return
   348  				}
   349  			case <-c.exitChan:
   350  				return
   351  			}
   352  		}
   353  	}()
   354  	return specificChan
   355  }
   356  
   357  func (c *commImpl) PresumedDead() <-chan common.PKIidType {
   358  	return c.deadEndpoints
   359  }
   360  
   361  func (c *commImpl) IdentitySwitch() <-chan common.PKIidType {
   362  	return c.identityChanges
   363  }
   364  
   365  func (c *commImpl) CloseConn(peer *RemotePeer) {
   366  	c.logger.Debug("Closing connection for", peer)
   367  	c.connStore.closeConnByPKIid(peer.PKIID)
   368  }
   369  
   370  func (c *commImpl) closeSubscriptions() {
   371  	c.lock.Lock()
   372  	defer c.lock.Unlock()
   373  	for _, ch := range c.subscriptions {
   374  		close(ch)
   375  	}
   376  }
   377  
   378  func (c *commImpl) Stop() {
   379  	if !atomic.CompareAndSwapInt32(&c.stopping, 0, int32(1)) {
   380  		return
   381  	}
   382  	c.logger.Info("Stopping")
   383  	defer c.logger.Info("Stopped")
   384  	c.connStore.shutdown()
   385  	c.logger.Debug("Shut down connection store, connection count:", c.connStore.connNum())
   386  	c.msgPublisher.Close()
   387  	close(c.exitChan)
   388  	c.stopWG.Wait()
   389  	c.closeSubscriptions()
   390  }
   391  
   392  func (c *commImpl) GetPKIid() common.PKIidType {
   393  	return c.PKIID
   394  }
   395  
   396  func extractRemoteAddress(stream stream) string {
   397  	var remoteAddress string
   398  	p, ok := peer.FromContext(stream.Context())
   399  	if ok {
   400  		if address := p.Addr; address != nil {
   401  			remoteAddress = address.String()
   402  		}
   403  	}
   404  	return remoteAddress
   405  }
   406  
   407  func (c *commImpl) authenticateRemotePeer(stream stream, initiator bool) (*protoext.ConnectionInfo, error) {
   408  	ctx := stream.Context()
   409  	remoteAddress := extractRemoteAddress(stream)
   410  	remoteCertHash := extractCertificateHashFromContext(ctx)
   411  	var err error
   412  	var cMsg *protoext.SignedGossipMessage
   413  	useTLS := c.tlsCerts != nil
   414  	var selfCertHash []byte
   415  
   416  	if useTLS {
   417  		certReference := c.tlsCerts.TLSServerCert
   418  		if initiator {
   419  			certReference = c.tlsCerts.TLSClientCert
   420  		}
   421  		selfCertHash = certHashFromRawCert(certReference.Load().(*tls.Certificate).Certificate[0])
   422  	}
   423  
   424  	signer := func(msg []byte) ([]byte, error) {
   425  		return c.idMapper.Sign(msg)
   426  	}
   427  
   428  	// TLS enabled but not detected on other side
   429  	if useTLS && len(remoteCertHash) == 0 {
   430  		c.logger.Warningf("%s didn't send TLS certificate", remoteAddress)
   431  		return nil, fmt.Errorf("No TLS certificate")
   432  	}
   433  
   434  	cMsg, err = c.createConnectionMsg(c.PKIID, selfCertHash, c.peerIdentity, signer)
   435  	if err != nil {
   436  		return nil, err
   437  	}
   438  
   439  	c.logger.Debug("Sending", cMsg, "to", remoteAddress)
   440  	stream.Send(cMsg.Envelope)
   441  	m, err := readWithTimeout(stream, c.connTimeout, remoteAddress)
   442  	if err != nil {
   443  		c.logger.Warningf("Failed reading messge from %s, reason: %v", remoteAddress, err)
   444  		return nil, err
   445  	}
   446  	receivedMsg := m.GetConn()
   447  	if receivedMsg == nil {
   448  		c.logger.Warning("Expected connection message from", remoteAddress, "but got", receivedMsg)
   449  		return nil, fmt.Errorf("Wrong type")
   450  	}
   451  
   452  	if receivedMsg.PkiId == nil {
   453  		c.logger.Warningf("%s didn't send a pkiID", remoteAddress)
   454  		return nil, fmt.Errorf("No PKI-ID")
   455  	}
   456  
   457  	c.logger.Debug("Received", receivedMsg, "from", remoteAddress)
   458  	err = c.idMapper.Put(receivedMsg.PkiId, receivedMsg.Identity)
   459  	if err != nil {
   460  		c.logger.Warningf("Identity store rejected %s : %v", remoteAddress, err)
   461  		return nil, err
   462  	}
   463  
   464  	connInfo := &protoext.ConnectionInfo{
   465  		ID:       receivedMsg.PkiId,
   466  		Identity: receivedMsg.Identity,
   467  		Endpoint: remoteAddress,
   468  		Auth: &protoext.AuthInfo{
   469  			Signature:  m.Signature,
   470  			SignedData: m.Payload,
   471  		},
   472  	}
   473  
   474  	// if TLS is enabled and detected, verify remote peer
   475  	if useTLS {
   476  		// If the remote peer sent its TLS certificate, make sure it actually matches the TLS cert
   477  		// that the peer used.
   478  		if !bytes.Equal(remoteCertHash, receivedMsg.TlsCertHash) {
   479  			return nil, errors.Errorf("Expected %v in remote hash of TLS cert, but got %v", remoteCertHash, receivedMsg.TlsCertHash)
   480  		}
   481  	}
   482  	// Final step - verify the signature on the connection message itself
   483  	verifier := func(peerIdentity []byte, signature, message []byte) error {
   484  		pkiID := c.idMapper.GetPKIidOfCert(api.PeerIdentityType(peerIdentity))
   485  		return c.idMapper.Verify(pkiID, signature, message)
   486  	}
   487  	err = m.Verify(receivedMsg.Identity, verifier)
   488  	if err != nil {
   489  		c.logger.Errorf("Failed verifying signature from %s : %v", remoteAddress, err)
   490  		return nil, err
   491  	}
   492  
   493  	c.logger.Debug("Authenticated", remoteAddress)
   494  
   495  	return connInfo, nil
   496  }
   497  
   498  // SendWithAck sends a message to remote peers, waiting for acknowledgement from minAck of them, or until a certain timeout expires
   499  func (c *commImpl) SendWithAck(msg *protoext.SignedGossipMessage, timeout time.Duration, minAck int, peers ...*RemotePeer) AggregatedSendResult {
   500  	if len(peers) == 0 {
   501  		return nil
   502  	}
   503  	var err error
   504  
   505  	// Roll a random NONCE to be used as a send ID to differentiate
   506  	// between different invocations
   507  	msg.Nonce = util.RandomUInt64()
   508  	// Replace the envelope in the message to update the NONCE
   509  	msg, err = protoext.NoopSign(msg.GossipMessage)
   510  
   511  	if c.isStopping() || err != nil {
   512  		if err == nil {
   513  			err = errors.New("comm is stopping")
   514  		}
   515  		results := []SendResult{}
   516  		for _, p := range peers {
   517  			results = append(results, SendResult{
   518  				error:      err,
   519  				RemotePeer: *p,
   520  			})
   521  		}
   522  		return results
   523  	}
   524  	c.logger.Debug("Entering, sending", msg, "to ", len(peers), "peers")
   525  	sndFunc := func(peer *RemotePeer, msg *protoext.SignedGossipMessage) {
   526  		c.sendToEndpoint(peer, msg, blockingSend)
   527  	}
   528  	// Subscribe to acks
   529  	subscriptions := make(map[string]func() error)
   530  	for _, p := range peers {
   531  		topic := topicForAck(msg.Nonce, p.PKIID)
   532  		sub := c.pubSub.Subscribe(topic, timeout)
   533  		subscriptions[string(p.PKIID)] = func() error {
   534  			msg, err := sub.Listen()
   535  			if err != nil {
   536  				return err
   537  			}
   538  			if msg, isAck := msg.(*proto.Acknowledgement); !isAck {
   539  				return fmt.Errorf("Received a message of type %s, expected *proto.Acknowledgement", reflect.TypeOf(msg))
   540  			} else {
   541  				if msg.Error != "" {
   542  					return errors.New(msg.Error)
   543  				}
   544  			}
   545  			return nil
   546  		}
   547  	}
   548  	waitForAck := func(p *RemotePeer) error {
   549  		return subscriptions[string(p.PKIID)]()
   550  	}
   551  	ackOperation := newAckSendOperation(sndFunc, waitForAck)
   552  	return ackOperation.send(msg, minAck, peers...)
   553  }
   554  
   555  func (c *commImpl) GossipStream(stream proto.Gossip_GossipStreamServer) error {
   556  	if c.isStopping() {
   557  		return fmt.Errorf("Shutting down")
   558  	}
   559  	connInfo, err := c.authenticateRemotePeer(stream, false)
   560  	if err != nil {
   561  		c.logger.Errorf("Authentication failed: %v", err)
   562  		return err
   563  	}
   564  	c.logger.Debug("Servicing", extractRemoteAddress(stream))
   565  
   566  	conn := c.connStore.onConnected(stream, connInfo, c.metrics)
   567  
   568  	h := func(m *protoext.SignedGossipMessage) {
   569  		c.msgPublisher.DeMultiplex(&ReceivedMessageImpl{
   570  			conn:                conn,
   571  			SignedGossipMessage: m,
   572  			connInfo:            connInfo,
   573  		})
   574  	}
   575  
   576  	conn.handler = interceptAcks(h, connInfo.ID, c.pubSub)
   577  
   578  	defer func() {
   579  		c.logger.Debug("Client", extractRemoteAddress(stream), " disconnected")
   580  		c.connStore.closeConnByPKIid(connInfo.ID)
   581  	}()
   582  
   583  	return conn.serviceConnection()
   584  }
   585  
   586  func (c *commImpl) Ping(context.Context, *proto.Empty) (*proto.Empty, error) {
   587  	return &proto.Empty{}, nil
   588  }
   589  
   590  func (c *commImpl) disconnect(pkiID common.PKIidType) {
   591  	if c.isStopping() {
   592  		return
   593  	}
   594  	c.deadEndpoints <- pkiID
   595  	c.connStore.closeConnByPKIid(pkiID)
   596  }
   597  
   598  func readWithTimeout(stream stream, timeout time.Duration, address string) (*protoext.SignedGossipMessage, error) {
   599  	incChan := make(chan *protoext.SignedGossipMessage, 1)
   600  	errChan := make(chan error, 1)
   601  	go func() {
   602  		if m, err := stream.Recv(); err == nil {
   603  			msg, err := protoext.EnvelopeToGossipMessage(m)
   604  			if err != nil {
   605  				errChan <- err
   606  				return
   607  			}
   608  			incChan <- msg
   609  		}
   610  	}()
   611  	select {
   612  	case <-time.After(timeout):
   613  		return nil, errors.Errorf("timed out waiting for connection message from %s", address)
   614  	case m := <-incChan:
   615  		return m, nil
   616  	case err := <-errChan:
   617  		return nil, errors.WithStack(err)
   618  	}
   619  }
   620  
   621  func (c *commImpl) createConnectionMsg(pkiID common.PKIidType, certHash []byte, cert api.PeerIdentityType, signer protoext.Signer) (*protoext.SignedGossipMessage, error) {
   622  	m := &proto.GossipMessage{
   623  		Tag:   proto.GossipMessage_EMPTY,
   624  		Nonce: 0,
   625  		Content: &proto.GossipMessage_Conn{
   626  			Conn: &proto.ConnEstablish{
   627  				TlsCertHash: certHash,
   628  				Identity:    cert,
   629  				PkiId:       pkiID,
   630  			},
   631  		},
   632  	}
   633  	sMsg := &protoext.SignedGossipMessage{
   634  		GossipMessage: m,
   635  	}
   636  	_, err := sMsg.Sign(signer)
   637  	return sMsg, errors.WithStack(err)
   638  }
   639  
   640  type stream interface {
   641  	Send(envelope *proto.Envelope) error
   642  	Recv() (*proto.Envelope, error)
   643  	Context() context.Context
   644  }
   645  
   646  func topicForAck(nonce uint64, pkiID common.PKIidType) string {
   647  	return fmt.Sprintf("%d %s", nonce, hex.EncodeToString(pkiID))
   648  }