github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/deliverservice/deliveryclient.go (about)

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