github.com/true-sqn/fabric@v2.1.1+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/internal/pkg/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  	IsStaticLeader bool
    61  	// CryptoSvc performs cryptographic actions like message verification and signing
    62  	// and identity validation.
    63  	CryptoSvc blocksprovider.BlockVerifier
    64  	// Gossip enables to enumerate peers in the channel, send a message to peers,
    65  	// and add a block to the gossip state transfer layer.
    66  	Gossip blocksprovider.GossipServiceAdapter
    67  	// OrdererSource provides orderer endpoints, complete with TLS cert pools.
    68  	OrdererSource *orderers.ConnectionSource
    69  	// Signer is the identity used to sign requests.
    70  	Signer identity.SignerSerializer
    71  	// GRPC Client
    72  	DeliverGRPCClient *comm.GRPCClient
    73  	// Configuration values for deliver service.
    74  	// TODO: merge 2 Config struct
    75  	DeliverServiceConfig *DeliverServiceConfig
    76  }
    77  
    78  // NewDeliverService construction function to create and initialize
    79  // delivery service instance. It tries to establish connection to
    80  // the specified in the configuration ordering service, in case it
    81  // fails to dial to it, return nil
    82  func NewDeliverService(conf *Config) DeliverService {
    83  	ds := &deliverServiceImpl{
    84  		conf:           conf,
    85  		blockProviders: make(map[string]*blocksprovider.Deliverer),
    86  	}
    87  	return ds
    88  }
    89  
    90  type DialerAdapter struct {
    91  	Client *comm.GRPCClient
    92  }
    93  
    94  func (da DialerAdapter) Dial(address string, certPool *x509.CertPool) (*grpc.ClientConn, error) {
    95  	return da.Client.NewConnection(address, comm.CertPoolOverride(certPool))
    96  }
    97  
    98  type DeliverAdapter struct{}
    99  
   100  func (DeliverAdapter) Deliver(ctx context.Context, clientConn *grpc.ClientConn) (orderer.AtomicBroadcast_DeliverClient, error) {
   101  	return orderer.NewAtomicBroadcastClient(clientConn).Deliver(ctx)
   102  }
   103  
   104  // StartDeliverForChannel starts blocks delivery for channel
   105  // initializes the grpc stream for given chainID, creates blocks provider instance
   106  // that spawns in go routine to read new blocks starting from the position provided by ledger
   107  // info instance.
   108  func (d *deliverServiceImpl) StartDeliverForChannel(chainID string, ledgerInfo blocksprovider.LedgerInfo, finalizer func()) error {
   109  	d.lock.Lock()
   110  	defer d.lock.Unlock()
   111  	if d.stopping {
   112  		errMsg := fmt.Sprintf("Delivery service is stopping cannot join a new channel %s", chainID)
   113  		logger.Errorf(errMsg)
   114  		return errors.New(errMsg)
   115  	}
   116  	if _, exist := d.blockProviders[chainID]; exist {
   117  		errMsg := fmt.Sprintf("Delivery service - block provider already exists for %s found, can't start delivery", chainID)
   118  		logger.Errorf(errMsg)
   119  		return errors.New(errMsg)
   120  	}
   121  	logger.Info("This peer will retrieve blocks from ordering service and disseminate to other peers in the organization for channel", chainID)
   122  
   123  	dc := &blocksprovider.Deliverer{
   124  		ChannelID:     chainID,
   125  		Gossip:        d.conf.Gossip,
   126  		Ledger:        ledgerInfo,
   127  		BlockVerifier: d.conf.CryptoSvc,
   128  		Dialer: DialerAdapter{
   129  			Client: d.conf.DeliverGRPCClient,
   130  		},
   131  		Orderers:          d.conf.OrdererSource,
   132  		DoneC:             make(chan struct{}),
   133  		Signer:            d.conf.Signer,
   134  		DeliverStreamer:   DeliverAdapter{},
   135  		Logger:            flogging.MustGetLogger("peer.blocksprovider").With("channel", chainID),
   136  		MaxRetryDelay:     d.conf.DeliverServiceConfig.ReConnectBackoffThreshold,
   137  		MaxRetryDuration:  d.conf.DeliverServiceConfig.ReconnectTotalTimeThreshold,
   138  		InitialRetryDelay: 100 * time.Millisecond,
   139  		YieldLeadership:   !d.conf.IsStaticLeader,
   140  	}
   141  
   142  	if d.conf.DeliverGRPCClient.MutualTLSRequired() {
   143  		dc.TLSCertHash = util.ComputeSHA256(d.conf.DeliverGRPCClient.Certificate().Certificate[0])
   144  	}
   145  
   146  	d.blockProviders[chainID] = dc
   147  	go func() {
   148  		dc.DeliverBlocks()
   149  		finalizer()
   150  	}()
   151  	return nil
   152  }
   153  
   154  // StopDeliverForChannel stops blocks delivery for channel by stopping channel block provider
   155  func (d *deliverServiceImpl) StopDeliverForChannel(chainID string) error {
   156  	d.lock.Lock()
   157  	defer d.lock.Unlock()
   158  	if d.stopping {
   159  		errMsg := fmt.Sprintf("Delivery service is stopping, cannot stop delivery for channel %s", chainID)
   160  		logger.Errorf(errMsg)
   161  		return errors.New(errMsg)
   162  	}
   163  	client, exist := d.blockProviders[chainID]
   164  	if !exist {
   165  		errMsg := fmt.Sprintf("Delivery service - no block provider for %s found, can't stop delivery", chainID)
   166  		logger.Errorf(errMsg)
   167  		return errors.New(errMsg)
   168  	}
   169  	client.Stop()
   170  	delete(d.blockProviders, chainID)
   171  	logger.Debug("This peer will stop pass blocks from orderer service to other peers")
   172  	return nil
   173  }
   174  
   175  // Stop all service and release resources
   176  func (d *deliverServiceImpl) Stop() {
   177  	d.lock.Lock()
   178  	defer d.lock.Unlock()
   179  	// Marking flag to indicate the shutdown of the delivery service
   180  	d.stopping = true
   181  
   182  	for _, client := range d.blockProviders {
   183  		client.Stop()
   184  	}
   185  }