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 }