github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/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 deliverclient
     8  
     9  import (
    10  	"errors"
    11  	"fmt"
    12  	"math"
    13  	"sync"
    14  	"time"
    15  
    16  	"github.com/hyperledger/fabric/common/flogging"
    17  	"github.com/hyperledger/fabric/core/comm"
    18  	"github.com/hyperledger/fabric/core/deliverservice/blocksprovider"
    19  	"github.com/hyperledger/fabric/gossip/api"
    20  	"github.com/hyperledger/fabric/protos/orderer"
    21  	"github.com/op/go-logging"
    22  	"google.golang.org/grpc"
    23  )
    24  
    25  var logger *logging.Logger // package-level logger
    26  
    27  func init() {
    28  	logger = flogging.MustGetLogger("deliveryClient")
    29  }
    30  
    31  var (
    32  	reConnectTotalTimeThreshold = time.Second * 60 * 5
    33  	connTimeout                 = time.Second * 3
    34  	reConnectBackoffThreshold   = float64(time.Hour)
    35  )
    36  
    37  // SetReconnectTotalTimeThreshold sets the total time the delivery service
    38  // may spend in reconnection attempts until its retry logic gives up
    39  // and returns an error
    40  func SetReconnectTotalTimeThreshold(duration time.Duration) {
    41  	reConnectTotalTimeThreshold = duration
    42  }
    43  
    44  // DeliverService used to communicate with orderers to obtain
    45  // new blocks and send them to the committer service
    46  type DeliverService interface {
    47  	// StartDeliverForChannel dynamically starts delivery of new blocks from ordering service
    48  	// to channel peers.
    49  	// When the delivery finishes, the finalizer func is called
    50  	StartDeliverForChannel(chainID string, ledgerInfo blocksprovider.LedgerInfo, finalizer func()) error
    51  
    52  	// StopDeliverForChannel dynamically stops delivery of new blocks from ordering service
    53  	// to channel peers.
    54  	StopDeliverForChannel(chainID string) error
    55  
    56  	// Stop terminates delivery service and closes the connection
    57  	Stop()
    58  }
    59  
    60  // deliverServiceImpl the implementation of the delivery service
    61  // maintains connection to the ordering service and maps of
    62  // blocks providers
    63  type deliverServiceImpl struct {
    64  	conf           *Config
    65  	blockProviders map[string]blocksprovider.BlocksProvider
    66  	lock           sync.RWMutex
    67  	stopping       bool
    68  }
    69  
    70  // Config dictates the DeliveryService's properties,
    71  // namely how it connects to an ordering service endpoint,
    72  // how it verifies messages received from it,
    73  // and how it disseminates the messages to other peers
    74  type Config struct {
    75  	// ConnFactory returns a function that creates a connection to an endpoint
    76  	ConnFactory func(channelID string) func(endpoint string) (*grpc.ClientConn, error)
    77  	// ABCFactory creates an AtomicBroadcastClient out of a connection
    78  	ABCFactory func(*grpc.ClientConn) orderer.AtomicBroadcastClient
    79  	// CryptoSvc performs cryptographic actions like message verification and signing
    80  	// and identity validation
    81  	CryptoSvc api.MessageCryptoService
    82  	// Gossip enables to enumerate peers in the channel, send a message to peers,
    83  	// and add a block to the gossip state transfer layer
    84  	Gossip blocksprovider.GossipServiceAdapter
    85  	// Endpoints specifies the endpoints of the ordering service
    86  	Endpoints []string
    87  }
    88  
    89  // NewDeliverService construction function to create and initialize
    90  // delivery service instance. It tries to establish connection to
    91  // the specified in the configuration ordering service, in case it
    92  // fails to dial to it, return nil
    93  func NewDeliverService(conf *Config) (DeliverService, error) {
    94  	ds := &deliverServiceImpl{
    95  		conf:           conf,
    96  		blockProviders: make(map[string]blocksprovider.BlocksProvider),
    97  	}
    98  	if err := ds.validateConfiguration(); err != nil {
    99  		return nil, err
   100  	}
   101  	return ds, nil
   102  }
   103  
   104  func (d *deliverServiceImpl) validateConfiguration() error {
   105  	conf := d.conf
   106  	if len(conf.Endpoints) == 0 {
   107  		return errors.New("No endpoints specified")
   108  	}
   109  	if conf.Gossip == nil {
   110  		return errors.New("No gossip provider specified")
   111  	}
   112  	if conf.ABCFactory == nil {
   113  		return errors.New("No AtomicBroadcast factory specified")
   114  	}
   115  	if conf.ConnFactory == nil {
   116  		return errors.New("No connection factory specified")
   117  	}
   118  	if conf.CryptoSvc == nil {
   119  		return errors.New("No crypto service specified")
   120  	}
   121  	return nil
   122  }
   123  
   124  // StartDeliverForChannel starts blocks delivery for channel
   125  // initializes the grpc stream for given chainID, creates blocks provider instance
   126  // that spawns in go routine to read new blocks starting from the position provided by ledger
   127  // info instance.
   128  func (d *deliverServiceImpl) StartDeliverForChannel(chainID string, ledgerInfo blocksprovider.LedgerInfo, finalizer func()) error {
   129  	d.lock.Lock()
   130  	defer d.lock.Unlock()
   131  	if d.stopping {
   132  		errMsg := fmt.Sprintf("Delivery service is stopping cannot join a new channel %s", chainID)
   133  		logger.Errorf(errMsg)
   134  		return errors.New(errMsg)
   135  	}
   136  	if _, exist := d.blockProviders[chainID]; exist {
   137  		errMsg := fmt.Sprintf("Delivery service - block provider already exists for %s found, can't start delivery", chainID)
   138  		logger.Errorf(errMsg)
   139  		return errors.New(errMsg)
   140  	} else {
   141  		client := d.newClient(chainID, ledgerInfo)
   142  		logger.Debug("This peer will pass blocks from orderer service to other peers for channel", chainID)
   143  		d.blockProviders[chainID] = blocksprovider.NewBlocksProvider(chainID, client, d.conf.Gossip, d.conf.CryptoSvc)
   144  		go func() {
   145  			d.blockProviders[chainID].DeliverBlocks()
   146  			finalizer()
   147  		}()
   148  	}
   149  	return nil
   150  }
   151  
   152  // StopDeliverForChannel stops blocks delivery for channel by stopping channel block provider
   153  func (d *deliverServiceImpl) StopDeliverForChannel(chainID string) error {
   154  	d.lock.Lock()
   155  	defer d.lock.Unlock()
   156  	if d.stopping {
   157  		errMsg := fmt.Sprintf("Delivery service is stopping, cannot stop delivery for channel %s", chainID)
   158  		logger.Errorf(errMsg)
   159  		return errors.New(errMsg)
   160  	}
   161  	if client, exist := d.blockProviders[chainID]; exist {
   162  		client.Stop()
   163  		delete(d.blockProviders, chainID)
   164  		logger.Debug("This peer will stop pass blocks from orderer service to other peers")
   165  	} else {
   166  		errMsg := fmt.Sprintf("Delivery service - no block provider for %s found, can't stop delivery", chainID)
   167  		logger.Errorf(errMsg)
   168  		return errors.New(errMsg)
   169  	}
   170  	return nil
   171  }
   172  
   173  // Stop all service and release resources
   174  func (d *deliverServiceImpl) Stop() {
   175  	d.lock.Lock()
   176  	defer d.lock.Unlock()
   177  	// Marking flag to indicate the shutdown of the delivery service
   178  	d.stopping = true
   179  
   180  	for _, client := range d.blockProviders {
   181  		client.Stop()
   182  	}
   183  }
   184  
   185  func (d *deliverServiceImpl) newClient(chainID string, ledgerInfoProvider blocksprovider.LedgerInfo) *broadcastClient {
   186  	requester := &blocksRequester{
   187  		chainID: chainID,
   188  	}
   189  	broadcastSetup := func(bd blocksprovider.BlocksDeliverer) error {
   190  		return requester.RequestBlocks(ledgerInfoProvider)
   191  	}
   192  	backoffPolicy := func(attemptNum int, elapsedTime time.Duration) (time.Duration, bool) {
   193  		if elapsedTime.Nanoseconds() > reConnectTotalTimeThreshold.Nanoseconds() {
   194  			return 0, false
   195  		}
   196  		sleepIncrement := float64(time.Millisecond * 500)
   197  		attempt := float64(attemptNum)
   198  		return time.Duration(math.Min(math.Pow(2, attempt)*sleepIncrement, reConnectBackoffThreshold)), true
   199  	}
   200  	connProd := comm.NewConnectionProducer(d.conf.ConnFactory(chainID), d.conf.Endpoints)
   201  	bClient := NewBroadcastClient(connProd, d.conf.ABCFactory, broadcastSetup, backoffPolicy)
   202  	requester.client = bClient
   203  	return bClient
   204  }
   205  
   206  func DefaultConnectionFactory(channelID string) func(endpoint string) (*grpc.ClientConn, error) {
   207  	return func(endpoint string) (*grpc.ClientConn, error) {
   208  		dialOpts := []grpc.DialOption{grpc.WithTimeout(connTimeout), grpc.WithBlock()}
   209  		// set max send/recv msg sizes
   210  		dialOpts = append(dialOpts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(comm.MaxRecvMsgSize()),
   211  			grpc.MaxCallSendMsgSize(comm.MaxSendMsgSize())))
   212  		// set the keepalive options
   213  		dialOpts = append(dialOpts, comm.ClientKeepaliveOptions()...)
   214  
   215  		if comm.TLSEnabled() {
   216  			creds, err := comm.GetCASupport().GetDeliverServiceCredentials(channelID)
   217  			if err != nil {
   218  				return nil, fmt.Errorf("Failed obtaining credentials for channel %s: %v", channelID, err)
   219  			}
   220  			dialOpts = append(dialOpts, grpc.WithTransportCredentials(creds))
   221  		} else {
   222  			dialOpts = append(dialOpts, grpc.WithInsecure())
   223  		}
   224  		grpc.EnableTracing = true
   225  		return grpc.Dial(endpoint, dialOpts...)
   226  	}
   227  }
   228  
   229  func DefaultABCFactory(conn *grpc.ClientConn) orderer.AtomicBroadcastClient {
   230  	return orderer.NewAtomicBroadcastClient(conn)
   231  }