github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/gossip/comm/comm_impl.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package comm
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/tls"
    22  	"errors"
    23  	"fmt"
    24  	"math/rand"
    25  	"net"
    26  	"os"
    27  	"reflect"
    28  	"sync"
    29  	"sync/atomic"
    30  	"time"
    31  
    32  	"github.com/hyperledger/fabric/gossip/api"
    33  	"github.com/hyperledger/fabric/gossip/common"
    34  	"github.com/hyperledger/fabric/gossip/identity"
    35  	"github.com/hyperledger/fabric/gossip/util"
    36  	proto "github.com/hyperledger/fabric/protos/gossip"
    37  	"github.com/op/go-logging"
    38  	"github.com/spf13/viper"
    39  	"golang.org/x/net/context"
    40  	"google.golang.org/grpc"
    41  	"google.golang.org/grpc/credentials"
    42  	"google.golang.org/grpc/peer"
    43  )
    44  
    45  const (
    46  	defDialTimeout  = time.Second * time.Duration(3)
    47  	defConnTimeout  = time.Second * time.Duration(2)
    48  	defRecvBuffSize = 20
    49  	defSendBuffSize = 20
    50  	sendOverflowErr = "Send buffer overflow"
    51  )
    52  
    53  var errSendOverflow = errors.New(sendOverflowErr)
    54  
    55  func init() {
    56  	rand.Seed(42)
    57  }
    58  
    59  // SetDialTimeout sets the dial timeout
    60  func SetDialTimeout(timeout time.Duration) {
    61  	viper.Set("peer.gossip.dialTimeout", timeout)
    62  }
    63  
    64  func (c *commImpl) SetDialOpts(opts ...grpc.DialOption) {
    65  	if len(opts) == 0 {
    66  		c.logger.Warning("Given an empty set of grpc.DialOption, aborting")
    67  		return
    68  	}
    69  	c.opts = opts
    70  }
    71  
    72  // NewCommInstanceWithServer creates a comm instance that creates an underlying gRPC server
    73  func NewCommInstanceWithServer(port int, idMapper identity.Mapper, peerIdentity api.PeerIdentityType, dialOpts ...grpc.DialOption) (Comm, error) {
    74  	var ll net.Listener
    75  	var s *grpc.Server
    76  	var secOpt grpc.DialOption
    77  	var certHash []byte
    78  
    79  	if len(dialOpts) == 0 {
    80  		dialOpts = []grpc.DialOption{grpc.WithTimeout(util.GetDurationOrDefault("peer.gossip.dialTimeout", defDialTimeout))}
    81  	}
    82  
    83  	if port > 0 {
    84  		s, ll, secOpt, certHash = createGRPCLayer(port)
    85  		dialOpts = append(dialOpts, secOpt)
    86  	}
    87  
    88  	commInst := &commImpl{
    89  		selfCertHash:      certHash,
    90  		PKIID:             idMapper.GetPKIidOfCert(peerIdentity),
    91  		idMapper:          idMapper,
    92  		logger:            util.GetLogger(util.LoggingCommModule, fmt.Sprintf("%d", port)),
    93  		peerIdentity:      peerIdentity,
    94  		opts:              dialOpts,
    95  		port:              port,
    96  		lsnr:              ll,
    97  		gSrv:              s,
    98  		msgPublisher:      NewChannelDemultiplexer(),
    99  		lock:              &sync.RWMutex{},
   100  		deadEndpoints:     make(chan common.PKIidType, 100),
   101  		stopping:          int32(0),
   102  		exitChan:          make(chan struct{}, 1),
   103  		subscriptions:     make([]chan proto.ReceivedMessage, 0),
   104  		blackListedPKIIDs: make([]common.PKIidType, 0),
   105  	}
   106  	commInst.connStore = newConnStore(commInst, commInst.logger)
   107  	commInst.idMapper.Put(idMapper.GetPKIidOfCert(peerIdentity), peerIdentity)
   108  
   109  	if port > 0 {
   110  		go func() {
   111  			commInst.stopWG.Add(1)
   112  			defer commInst.stopWG.Done()
   113  			s.Serve(ll)
   114  		}()
   115  		proto.RegisterGossipServer(s, commInst)
   116  	}
   117  
   118  	return commInst, nil
   119  }
   120  
   121  // NewCommInstance creates a new comm instance that binds itself to the given gRPC server
   122  func NewCommInstance(s *grpc.Server, cert *tls.Certificate, idStore identity.Mapper, peerIdentity api.PeerIdentityType, dialOpts ...grpc.DialOption) (Comm, error) {
   123  	commInst, err := NewCommInstanceWithServer(-1, idStore, peerIdentity, dialOpts...)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  
   128  	if cert != nil {
   129  		inst := commInst.(*commImpl)
   130  		if len(cert.Certificate) == 0 {
   131  			inst.logger.Panic("Certificate supplied but certificate chain is empty")
   132  		} else {
   133  			inst.selfCertHash = certHashFromRawCert(cert.Certificate[0])
   134  		}
   135  	}
   136  
   137  	proto.RegisterGossipServer(s, commInst.(*commImpl))
   138  
   139  	return commInst, nil
   140  }
   141  
   142  type commImpl struct {
   143  	selfCertHash      []byte
   144  	peerIdentity      api.PeerIdentityType
   145  	idMapper          identity.Mapper
   146  	logger            *logging.Logger
   147  	opts              []grpc.DialOption
   148  	connStore         *connectionStore
   149  	PKIID             []byte
   150  	port              int
   151  	deadEndpoints     chan common.PKIidType
   152  	msgPublisher      *ChannelDeMultiplexer
   153  	lock              *sync.RWMutex
   154  	lsnr              net.Listener
   155  	gSrv              *grpc.Server
   156  	exitChan          chan struct{}
   157  	stopping          int32
   158  	stopWG            sync.WaitGroup
   159  	subscriptions     []chan proto.ReceivedMessage
   160  	blackListedPKIIDs []common.PKIidType
   161  }
   162  
   163  func (c *commImpl) createConnection(endpoint string, expectedPKIID common.PKIidType) (*connection, error) {
   164  	var err error
   165  	var cc *grpc.ClientConn
   166  	var stream proto.Gossip_GossipStreamClient
   167  	var pkiID common.PKIidType
   168  
   169  	c.logger.Debug("Entering", endpoint, expectedPKIID)
   170  	defer c.logger.Debug("Exiting")
   171  
   172  	if c.isStopping() {
   173  		return nil, errors.New("Stopping")
   174  	}
   175  	cc, err = grpc.Dial(endpoint, append(c.opts, grpc.WithBlock())...)
   176  	if err != nil {
   177  		return nil, err
   178  	}
   179  
   180  	cl := proto.NewGossipClient(cc)
   181  
   182  	if _, err = cl.Ping(context.Background(), &proto.Empty{}); err != nil {
   183  		cc.Close()
   184  		return nil, err
   185  	}
   186  
   187  	if stream, err = cl.GossipStream(context.Background()); err == nil {
   188  		pkiID, err = c.authenticateRemotePeer(stream)
   189  		if err == nil {
   190  			if expectedPKIID != nil && !bytes.Equal(pkiID, expectedPKIID) {
   191  				// PKIID is nil when we don't know the remote PKI id's
   192  				c.logger.Warning("Remote endpoint claims to be a different peer, expected", expectedPKIID, "but got", pkiID)
   193  				return nil, errors.New("Authentication failure")
   194  			}
   195  			conn := newConnection(cl, cc, stream, nil)
   196  			conn.pkiID = pkiID
   197  			conn.logger = c.logger
   198  
   199  			h := func(m *proto.SignedGossipMessage) {
   200  				c.logger.Debug("Got message:", m)
   201  				c.msgPublisher.DeMultiplex(&ReceivedMessageImpl{
   202  					conn:                conn,
   203  					lock:                conn,
   204  					SignedGossipMessage: m,
   205  				})
   206  			}
   207  			conn.handler = h
   208  			return conn, nil
   209  		}
   210  	}
   211  	cc.Close()
   212  	return nil, err
   213  }
   214  
   215  func (c *commImpl) Send(msg *proto.SignedGossipMessage, peers ...*RemotePeer) {
   216  	if c.isStopping() || len(peers) == 0 {
   217  		return
   218  	}
   219  
   220  	c.logger.Debug("Entering, sending", msg, "to ", len(peers), "peers")
   221  
   222  	for _, peer := range peers {
   223  		go func(peer *RemotePeer, msg *proto.SignedGossipMessage) {
   224  			c.sendToEndpoint(peer, msg)
   225  		}(peer, msg)
   226  	}
   227  }
   228  
   229  func (c *commImpl) BlackListPKIid(PKIID common.PKIidType) {
   230  	c.logger.Info("Entering", PKIID)
   231  	defer c.logger.Info("Exiting")
   232  	c.lock.Lock()
   233  	defer c.lock.Unlock()
   234  	c.connStore.closeByPKIid(PKIID)
   235  	c.blackListedPKIIDs = append(c.blackListedPKIIDs, PKIID)
   236  }
   237  
   238  func (c *commImpl) isPKIblackListed(p common.PKIidType) bool {
   239  	c.lock.RLock()
   240  	defer c.lock.RUnlock()
   241  	for _, pki := range c.blackListedPKIIDs {
   242  		if bytes.Equal(pki, p) {
   243  			c.logger.Debug(p, ":", true)
   244  			return true
   245  		}
   246  	}
   247  	c.logger.Debug(p, ":", false)
   248  	return false
   249  }
   250  
   251  func (c *commImpl) sendToEndpoint(peer *RemotePeer, msg *proto.SignedGossipMessage) {
   252  	if c.isStopping() {
   253  		return
   254  	}
   255  	c.logger.Debug("Entering, Sending to", peer.Endpoint, ", msg:", msg)
   256  	defer c.logger.Debug("Exiting")
   257  	var err error
   258  
   259  	conn, err := c.connStore.getConnection(peer)
   260  	if err == nil {
   261  		disConnectOnErr := func(err error) {
   262  			c.logger.Warning(peer, "isn't responsive:", err)
   263  			c.disconnect(peer.PKIID)
   264  		}
   265  		conn.send(msg, disConnectOnErr)
   266  		return
   267  	}
   268  	c.logger.Warning("Failed obtaining connection for", peer, "reason:", err)
   269  	c.disconnect(peer.PKIID)
   270  }
   271  
   272  func (c *commImpl) isStopping() bool {
   273  	return atomic.LoadInt32(&c.stopping) == int32(1)
   274  }
   275  
   276  func (c *commImpl) Probe(remotePeer *RemotePeer) error {
   277  	endpoint := remotePeer.Endpoint
   278  	pkiID := remotePeer.PKIID
   279  	if c.isStopping() {
   280  		return errors.New("Stopping")
   281  	}
   282  	c.logger.Debug("Entering, endpoint:", endpoint, "PKIID:", pkiID)
   283  	cc, err := grpc.Dial(remotePeer.Endpoint, append(c.opts, grpc.WithBlock())...)
   284  	if err != nil {
   285  		c.logger.Debug("Returning", err)
   286  		return err
   287  	}
   288  	defer cc.Close()
   289  	cl := proto.NewGossipClient(cc)
   290  	_, err = cl.Ping(context.Background(), &proto.Empty{})
   291  	c.logger.Debug("Returning", err)
   292  	return err
   293  }
   294  
   295  func (c *commImpl) Accept(acceptor common.MessageAcceptor) <-chan proto.ReceivedMessage {
   296  	genericChan := c.msgPublisher.AddChannel(acceptor)
   297  	specificChan := make(chan proto.ReceivedMessage, 10)
   298  
   299  	if c.isStopping() {
   300  		c.logger.Warning("Accept() called but comm module is stopping, returning empty channel")
   301  		return specificChan
   302  	}
   303  
   304  	c.lock.Lock()
   305  	c.subscriptions = append(c.subscriptions, specificChan)
   306  	c.lock.Unlock()
   307  
   308  	go func() {
   309  		defer c.logger.Debug("Exiting Accept() loop")
   310  		defer func() {
   311  			c.logger.Warning("Recovered")
   312  			recover()
   313  		}()
   314  
   315  		c.stopWG.Add(1)
   316  		defer c.stopWG.Done()
   317  
   318  		for {
   319  			select {
   320  			case msg := <-genericChan:
   321  				specificChan <- msg.(*ReceivedMessageImpl)
   322  				break
   323  			case s := <-c.exitChan:
   324  				c.exitChan <- s
   325  				return
   326  			}
   327  		}
   328  	}()
   329  	return specificChan
   330  }
   331  
   332  func (c *commImpl) PresumedDead() <-chan common.PKIidType {
   333  	return c.deadEndpoints
   334  }
   335  
   336  func (c *commImpl) CloseConn(peer *RemotePeer) {
   337  	c.logger.Debug("Closing connection for", peer)
   338  	c.connStore.closeConn(peer)
   339  }
   340  
   341  func (c *commImpl) emptySubscriptions() {
   342  	c.lock.Lock()
   343  	defer c.lock.Unlock()
   344  	for _, ch := range c.subscriptions {
   345  		close(ch)
   346  	}
   347  }
   348  
   349  func (c *commImpl) Stop() {
   350  	if c.isStopping() {
   351  		return
   352  	}
   353  	atomic.StoreInt32(&c.stopping, int32(1))
   354  	c.logger.Info("Stopping")
   355  	defer c.logger.Info("Stopped")
   356  	if c.gSrv != nil {
   357  		c.gSrv.Stop()
   358  	}
   359  	if c.lsnr != nil {
   360  		c.lsnr.Close()
   361  	}
   362  	c.connStore.shutdown()
   363  	c.logger.Debug("Shut down connection store, connection count:", c.connStore.connNum())
   364  	c.exitChan <- struct{}{}
   365  	c.msgPublisher.Close()
   366  	c.logger.Debug("Shut down publisher")
   367  	c.emptySubscriptions()
   368  	c.logger.Debug("Closed subscriptions, waiting for goroutines to stop...")
   369  	c.stopWG.Wait()
   370  }
   371  
   372  func (c *commImpl) GetPKIid() common.PKIidType {
   373  	return c.PKIID
   374  }
   375  
   376  func extractRemoteAddress(stream stream) string {
   377  	var remoteAddress string
   378  	p, ok := peer.FromContext(stream.Context())
   379  	if ok {
   380  		if address := p.Addr; address != nil {
   381  			remoteAddress = address.String()
   382  		}
   383  	}
   384  	return remoteAddress
   385  }
   386  
   387  func (c *commImpl) authenticateRemotePeer(stream stream) (common.PKIidType, error) {
   388  	ctx := stream.Context()
   389  	remoteAddress := extractRemoteAddress(stream)
   390  	remoteCertHash := extractCertificateHashFromContext(ctx)
   391  	var err error
   392  	var cMsg *proto.SignedGossipMessage
   393  	var signer proto.Signer
   394  
   395  	// If TLS is detected, sign the hash of our cert to bind our TLS cert
   396  	// to the gRPC session
   397  	if remoteCertHash != nil && c.selfCertHash != nil {
   398  		signer = func(msg []byte) ([]byte, error) {
   399  			return c.idMapper.Sign(msg)
   400  		}
   401  	} else { // If we don't use TLS, we have no unique text to sign,
   402  		//  so don't sign anything
   403  		signer = func(msg []byte) ([]byte, error) {
   404  			return msg, nil
   405  		}
   406  	}
   407  
   408  	cMsg = c.createConnectionMsg(c.PKIID, c.selfCertHash, c.peerIdentity, signer)
   409  
   410  	c.logger.Debug("Sending", cMsg, "to", remoteAddress)
   411  	stream.Send(cMsg.Envelope)
   412  	m, err := readWithTimeout(stream, util.GetDurationOrDefault("peer.gossip.connTimeout", defConnTimeout), remoteAddress)
   413  	if err != nil {
   414  		err := fmt.Errorf("Failed reading messge from %s, reason: %v", remoteAddress, err)
   415  		c.logger.Warning(err)
   416  		return nil, err
   417  	}
   418  	receivedMsg := m.GetConn()
   419  	if receivedMsg == nil {
   420  		c.logger.Warning("Expected connection message but got", receivedMsg)
   421  		return nil, errors.New("Wrong type")
   422  	}
   423  
   424  	if receivedMsg.PkiID == nil {
   425  		c.logger.Warning("%s didn't send a pkiID")
   426  		return nil, fmt.Errorf("%s didn't send a pkiID", remoteAddress)
   427  	}
   428  
   429  	if c.isPKIblackListed(receivedMsg.PkiID) {
   430  		c.logger.Warning("Connection attempt from", remoteAddress, "but it is black-listed")
   431  		return nil, errors.New("Black-listed")
   432  	}
   433  	c.logger.Debug("Received", receivedMsg, "from", remoteAddress)
   434  	err = c.idMapper.Put(receivedMsg.PkiID, receivedMsg.Cert)
   435  	if err != nil {
   436  		c.logger.Warning("Identity store rejected", remoteAddress, ":", err)
   437  		return nil, err
   438  	}
   439  
   440  	// if TLS is detected, verify remote peer
   441  	if remoteCertHash != nil && c.selfCertHash != nil {
   442  		if !bytes.Equal(remoteCertHash, receivedMsg.Hash) {
   443  			return nil, fmt.Errorf("Expected %v in remote hash, but got %v", remoteCertHash, receivedMsg.Hash)
   444  		}
   445  		verifier := func(peerIdentity []byte, signature, message []byte) error {
   446  			pkiID := c.idMapper.GetPKIidOfCert(api.PeerIdentityType(peerIdentity))
   447  			return c.idMapper.Verify(pkiID, signature, message)
   448  		}
   449  		err = m.Verify(receivedMsg.Cert, verifier)
   450  		if err != nil {
   451  			c.logger.Error("Failed verifying signature from", remoteAddress, ":", err)
   452  			return nil, err
   453  		}
   454  	}
   455  
   456  	c.logger.Debug("Authenticated", remoteAddress)
   457  	return receivedMsg.PkiID, nil
   458  }
   459  
   460  func (c *commImpl) GossipStream(stream proto.Gossip_GossipStreamServer) error {
   461  	if c.isStopping() {
   462  		return errors.New("Shutting down")
   463  	}
   464  	PKIID, err := c.authenticateRemotePeer(stream)
   465  	if err != nil {
   466  		c.logger.Error("Authentication failed")
   467  		return err
   468  	}
   469  	c.logger.Debug("Servicing", extractRemoteAddress(stream))
   470  
   471  	conn := c.connStore.onConnected(stream, PKIID)
   472  
   473  	// if connStore denied the connection, it means we already have a connection to that peer
   474  	// so close this stream
   475  	if conn == nil {
   476  		return nil
   477  	}
   478  
   479  	h := func(m *proto.SignedGossipMessage) {
   480  		c.msgPublisher.DeMultiplex(&ReceivedMessageImpl{
   481  			conn:                conn,
   482  			lock:                conn,
   483  			SignedGossipMessage: m,
   484  		})
   485  	}
   486  
   487  	conn.handler = h
   488  
   489  	defer func() {
   490  		c.logger.Debug("Client", extractRemoteAddress(stream), " disconnected")
   491  		c.connStore.closeByPKIid(PKIID)
   492  		conn.close()
   493  	}()
   494  
   495  	return conn.serviceConnection()
   496  }
   497  
   498  func (c *commImpl) Ping(context.Context, *proto.Empty) (*proto.Empty, error) {
   499  	return &proto.Empty{}, nil
   500  }
   501  
   502  func (c *commImpl) disconnect(pkiID common.PKIidType) {
   503  	if c.isStopping() {
   504  		return
   505  	}
   506  	c.deadEndpoints <- pkiID
   507  	c.connStore.closeByPKIid(pkiID)
   508  }
   509  
   510  func readWithTimeout(stream interface{}, timeout time.Duration, address string) (*proto.SignedGossipMessage, error) {
   511  	incChan := make(chan *proto.SignedGossipMessage, 1)
   512  	errChan := make(chan error, 1)
   513  	go func() {
   514  		if srvStr, isServerStr := stream.(proto.Gossip_GossipStreamServer); isServerStr {
   515  			if m, err := srvStr.Recv(); err == nil {
   516  				msg, err := m.ToGossipMessage()
   517  				if err != nil {
   518  					errChan <- err
   519  					return
   520  				}
   521  				incChan <- msg
   522  			}
   523  		} else if clStr, isClientStr := stream.(proto.Gossip_GossipStreamClient); isClientStr {
   524  			if m, err := clStr.Recv(); err == nil {
   525  				msg, err := m.ToGossipMessage()
   526  				if err != nil {
   527  					errChan <- err
   528  					return
   529  				}
   530  				incChan <- msg
   531  			}
   532  		} else {
   533  			panic(fmt.Errorf("Stream isn't a GossipStreamServer or a GossipStreamClient, but %v. Aborting", reflect.TypeOf(stream)))
   534  		}
   535  	}()
   536  	select {
   537  	case <-time.NewTicker(timeout).C:
   538  		return nil, fmt.Errorf("Timed out waiting for connection message from %s", address)
   539  	case m := <-incChan:
   540  		return m, nil
   541  	case err := <-errChan:
   542  		return nil, err
   543  	}
   544  }
   545  
   546  func (c *commImpl) createConnectionMsg(pkiID common.PKIidType, hash []byte, cert api.PeerIdentityType, signer proto.Signer) *proto.SignedGossipMessage {
   547  	m := &proto.GossipMessage{
   548  		Tag:   proto.GossipMessage_EMPTY,
   549  		Nonce: 0,
   550  		Content: &proto.GossipMessage_Conn{
   551  			Conn: &proto.ConnEstablish{
   552  				Hash:  hash,
   553  				Cert:  cert,
   554  				PkiID: pkiID,
   555  			},
   556  		},
   557  	}
   558  	sMsg := &proto.SignedGossipMessage{
   559  		GossipMessage: m,
   560  	}
   561  	sMsg.Sign(signer)
   562  	return sMsg
   563  }
   564  
   565  type stream interface {
   566  	Send(envelope *proto.Envelope) error
   567  	Recv() (*proto.Envelope, error)
   568  	grpc.Stream
   569  }
   570  
   571  func createGRPCLayer(port int) (*grpc.Server, net.Listener, grpc.DialOption, []byte) {
   572  	var returnedCertHash []byte
   573  	var s *grpc.Server
   574  	var ll net.Listener
   575  	var err error
   576  	var serverOpts []grpc.ServerOption
   577  	var dialOpts grpc.DialOption
   578  
   579  	keyFileName := fmt.Sprintf("key.%d.pem", rand.Int63())
   580  	certFileName := fmt.Sprintf("cert.%d.pem", rand.Int63())
   581  
   582  	defer os.Remove(keyFileName)
   583  	defer os.Remove(certFileName)
   584  
   585  	err = generateCertificates(keyFileName, certFileName)
   586  	if err == nil {
   587  		cert, err := tls.LoadX509KeyPair(certFileName, keyFileName)
   588  		if err != nil {
   589  			panic(err)
   590  		}
   591  
   592  		if len(cert.Certificate) == 0 {
   593  			panic(errors.New("Certificate chain is nil"))
   594  		}
   595  
   596  		returnedCertHash = certHashFromRawCert(cert.Certificate[0])
   597  
   598  		tlsConf := &tls.Config{
   599  			Certificates:       []tls.Certificate{cert},
   600  			ClientAuth:         tls.RequestClientCert,
   601  			InsecureSkipVerify: true,
   602  		}
   603  		serverOpts = append(serverOpts, grpc.Creds(credentials.NewTLS(tlsConf)))
   604  		ta := credentials.NewTLS(&tls.Config{
   605  			Certificates:       []tls.Certificate{cert},
   606  			InsecureSkipVerify: true,
   607  		})
   608  		dialOpts = grpc.WithTransportCredentials(&authCreds{tlsCreds: ta})
   609  	} else {
   610  		dialOpts = grpc.WithInsecure()
   611  	}
   612  
   613  	listenAddress := fmt.Sprintf("%s:%d", "", port)
   614  	ll, err = net.Listen("tcp", listenAddress)
   615  	if err != nil {
   616  		panic(err)
   617  	}
   618  
   619  	s = grpc.NewServer(serverOpts...)
   620  	return s, ll, dialOpts, returnedCertHash
   621  }