github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/core/deliverservice/deliveryclient.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package deliverservice
     8  
     9  import (
    10  	"context"
    11  	"crypto/x509"
    12  	"errors"
    13  	"fmt"
    14  	"sync"
    15  	"time"
    16  
    17  	"github.com/hyperledger/fabric-protos-go/orderer"
    18  	"github.com/hyperledger/fabric/common/flogging"
    19  	"github.com/hyperledger/fabric/common/util"
    20  	"github.com/hyperledger/fabric/core/comm"
    21  	"github.com/hyperledger/fabric/internal/pkg/identity"
    22  	"github.com/hyperledger/fabric/internal/pkg/peer/blocksprovider"
    23  	"github.com/hyperledger/fabric/internal/pkg/peer/orderers"
    24  	"google.golang.org/grpc"
    25  )
    26  
    27  var logger = flogging.MustGetLogger("deliveryClient")
    28  
    29  // DeliverService used to communicate with orderers to obtain
    30  // new blocks and send them to the committer service
    31  type DeliverService interface {
    32  	// StartDeliverForChannel dynamically starts delivery of new blocks from ordering service
    33  	// to channel peers.
    34  	// When the delivery finishes, the finalizer func is called
    35  	StartDeliverForChannel(chainID string, ledgerInfo blocksprovider.LedgerInfo, finalizer func()) error
    36  
    37  	// StopDeliverForChannel dynamically stops delivery of new blocks from ordering service
    38  	// to channel peers.
    39  	StopDeliverForChannel(chainID string) error
    40  
    41  	// Stop terminates delivery service and closes the connection
    42  	Stop()
    43  }
    44  
    45  // deliverServiceImpl the implementation of the delivery service
    46  // maintains connection to the ordering service and maps of
    47  // blocks providers
    48  type deliverServiceImpl struct {
    49  	conf           *Config
    50  	blockProviders map[string]*blocksprovider.Deliverer
    51  	lock           sync.RWMutex
    52  	stopping       bool
    53  }
    54  
    55  // Config dictates the DeliveryService's properties,
    56  // namely how it connects to an ordering service endpoint,
    57  // how it verifies messages received from it,
    58  // and how it disseminates the messages to other peers
    59  type Config struct {
    60  	// CryptoSvc performs cryptographic actions like message verification and signing
    61  	// and identity validation.
    62  	CryptoSvc blocksprovider.BlockVerifier
    63  	// Gossip enables to enumerate peers in the channel, send a message to peers,
    64  	// and add a block to the gossip state transfer layer.
    65  	Gossip blocksprovider.GossipServiceAdapter
    66  	// OrdererSource provides orderer endpoints, complete with TLS cert pools.
    67  	OrdererSource *orderers.ConnectionSource
    68  	// Signer is the identity used to sign requests.
    69  	Signer identity.SignerSerializer
    70  	// GRPC Client
    71  	DeliverGRPCClient *comm.GRPCClient
    72  	// Configuration values for deliver service.
    73  	// TODO: merge 2 Config struct
    74  	DeliverServiceConfig *DeliverServiceConfig
    75  }
    76  
    77  // NewDeliverService construction function to create and initialize
    78  // delivery service instance. It tries to establish connection to
    79  // the specified in the configuration ordering service, in case it
    80  // fails to dial to it, return nil
    81  func NewDeliverService(conf *Config) DeliverService {
    82  	ds := &deliverServiceImpl{
    83  		conf:           conf,
    84  		blockProviders: make(map[string]*blocksprovider.Deliverer),
    85  	}
    86  	return ds
    87  }
    88  
    89  type DialerAdapter struct {
    90  	Client *comm.GRPCClient
    91  }
    92  
    93  func (da DialerAdapter) Dial(address string, certPool *x509.CertPool) (*grpc.ClientConn, error) {
    94  	return da.Client.NewConnection(address, comm.CertPoolOverride(certPool))
    95  }
    96  
    97  type DeliverAdapter struct{}
    98  
    99  func (DeliverAdapter) Deliver(ctx context.Context, clientConn *grpc.ClientConn) (orderer.AtomicBroadcast_DeliverClient, error) {
   100  	return orderer.NewAtomicBroadcastClient(clientConn).Deliver(ctx)
   101  }
   102  
   103  // StartDeliverForChannel starts blocks delivery for channel
   104  // initializes the grpc stream for given chainID, creates blocks provider instance
   105  // that spawns in go routine to read new blocks starting from the position provided by ledger
   106  // info instance.
   107  func (d *deliverServiceImpl) StartDeliverForChannel(chainID string, ledgerInfo blocksprovider.LedgerInfo, finalizer func()) error {
   108  	d.lock.Lock()
   109  	defer d.lock.Unlock()
   110  	if d.stopping {
   111  		errMsg := fmt.Sprintf("Delivery service is stopping cannot join a new channel %s", chainID)
   112  		logger.Errorf(errMsg)
   113  		return errors.New(errMsg)
   114  	}
   115  	if _, exist := d.blockProviders[chainID]; exist {
   116  		errMsg := fmt.Sprintf("Delivery service - block provider already exists for %s found, can't start delivery", chainID)
   117  		logger.Errorf(errMsg)
   118  		return errors.New(errMsg)
   119  	}
   120  	logger.Info("This peer will retrieve blocks from ordering service and disseminate to other peers in the organization for channel", chainID)
   121  	dc := &blocksprovider.Deliverer{
   122  		ChannelID:     chainID,
   123  		Gossip:        d.conf.Gossip,
   124  		Ledger:        ledgerInfo,
   125  		BlockVerifier: d.conf.CryptoSvc,
   126  		Dialer: DialerAdapter{
   127  			Client: d.conf.DeliverGRPCClient,
   128  		},
   129  		Orderers:          d.conf.OrdererSource,
   130  		DoneC:             make(chan struct{}),
   131  		Signer:            d.conf.Signer,
   132  		DeliverStreamer:   DeliverAdapter{},
   133  		Logger:            flogging.MustGetLogger("peer.blocksprovider").With("channel", chainID),
   134  		MaxRetryDelay:     time.Duration(d.conf.DeliverServiceConfig.ReConnectBackoffThreshold),
   135  		MaxRetryDuration:  d.conf.DeliverServiceConfig.ReconnectTotalTimeThreshold,
   136  		InitialRetryDelay: 100 * time.Millisecond,
   137  	}
   138  
   139  	if d.conf.DeliverGRPCClient.MutualTLSRequired() {
   140  		dc.TLSCertHash = util.ComputeSHA256(d.conf.DeliverGRPCClient.Certificate().Certificate[0])
   141  	}
   142  
   143  	d.blockProviders[chainID] = dc
   144  	go func() {
   145  		dc.DeliverBlocks()
   146  		finalizer()
   147  	}()
   148  	return nil
   149  }
   150  
   151  // StopDeliverForChannel stops blocks delivery for channel by stopping channel block provider
   152  func (d *deliverServiceImpl) StopDeliverForChannel(chainID string) error {
   153  	d.lock.Lock()
   154  	defer d.lock.Unlock()
   155  	if d.stopping {
   156  		errMsg := fmt.Sprintf("Delivery service is stopping, cannot stop delivery for channel %s", chainID)
   157  		logger.Errorf(errMsg)
   158  		return errors.New(errMsg)
   159  	}
   160  	client, exist := d.blockProviders[chainID]
   161  	if !exist {
   162  		errMsg := fmt.Sprintf("Delivery service - no block provider for %s found, can't stop delivery", chainID)
   163  		logger.Errorf(errMsg)
   164  		return errors.New(errMsg)
   165  	}
   166  	client.Stop()
   167  	delete(d.blockProviders, chainID)
   168  	logger.Debug("This peer will stop pass blocks from orderer service to other peers")
   169  	return nil
   170  }
   171  
   172  // Stop all service and release resources
   173  func (d *deliverServiceImpl) Stop() {
   174  	d.lock.Lock()
   175  	defer d.lock.Unlock()
   176  	// Marking flag to indicate the shutdown of the delivery service
   177  	d.stopping = true
   178  
   179  	for _, client := range d.blockProviders {
   180  		client.Stop()
   181  	}
   182  }