github.com/lzy4123/fabric@v2.1.1+incompatible/internal/pkg/peer/blocksprovider/blocksprovider.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package blocksprovider 8 9 import ( 10 "context" 11 "crypto/x509" 12 "math" 13 "time" 14 15 "github.com/hyperledger/fabric-protos-go/common" 16 "github.com/hyperledger/fabric-protos-go/gossip" 17 "github.com/hyperledger/fabric-protos-go/orderer" 18 "github.com/hyperledger/fabric/common/flogging" 19 gossipcommon "github.com/hyperledger/fabric/gossip/common" 20 "github.com/hyperledger/fabric/internal/pkg/identity" 21 "github.com/hyperledger/fabric/internal/pkg/peer/orderers" 22 "github.com/hyperledger/fabric/protoutil" 23 24 "github.com/golang/protobuf/proto" 25 "github.com/pkg/errors" 26 "google.golang.org/grpc" 27 ) 28 29 type sleeper struct { 30 sleep func(time.Duration) 31 } 32 33 func (s sleeper) Sleep(d time.Duration, doneC chan struct{}) { 34 if s.sleep == nil { 35 timer := time.NewTimer(d) 36 select { 37 case <-timer.C: 38 case <-doneC: 39 timer.Stop() 40 } 41 return 42 } 43 s.sleep(d) 44 } 45 46 // LedgerInfo an adapter to provide the interface to query 47 // the ledger committer for current ledger height 48 //go:generate counterfeiter -o fake/ledger_info.go --fake-name LedgerInfo . LedgerInfo 49 type LedgerInfo interface { 50 // LedgerHeight returns current local ledger height 51 LedgerHeight() (uint64, error) 52 } 53 54 // GossipServiceAdapter serves to provide basic functionality 55 // required from gossip service by delivery service 56 //go:generate counterfeiter -o fake/gossip_service_adapter.go --fake-name GossipServiceAdapter . GossipServiceAdapter 57 type GossipServiceAdapter interface { 58 // AddPayload adds payload to the local state sync buffer 59 AddPayload(chainID string, payload *gossip.Payload) error 60 61 // Gossip the message across the peers 62 Gossip(msg *gossip.GossipMessage) 63 } 64 65 //go:generate counterfeiter -o fake/block_verifier.go --fake-name BlockVerifier . BlockVerifier 66 type BlockVerifier interface { 67 VerifyBlock(channelID gossipcommon.ChannelID, blockNum uint64, block *common.Block) error 68 } 69 70 //go:generate counterfeiter -o fake/orderer_connection_source.go --fake-name OrdererConnectionSource . OrdererConnectionSource 71 type OrdererConnectionSource interface { 72 RandomEndpoint() (*orderers.Endpoint, error) 73 } 74 75 //go:generate counterfeiter -o fake/dialer.go --fake-name Dialer . Dialer 76 type Dialer interface { 77 Dial(address string, certPool *x509.CertPool) (*grpc.ClientConn, error) 78 } 79 80 //go:generate counterfeiter -o fake/deliver_streamer.go --fake-name DeliverStreamer . DeliverStreamer 81 type DeliverStreamer interface { 82 Deliver(context.Context, *grpc.ClientConn) (orderer.AtomicBroadcast_DeliverClient, error) 83 } 84 85 // Deliverer the actual implementation for BlocksProvider interface 86 type Deliverer struct { 87 ChannelID string 88 Gossip GossipServiceAdapter 89 Ledger LedgerInfo 90 BlockVerifier BlockVerifier 91 Dialer Dialer 92 Orderers OrdererConnectionSource 93 DoneC chan struct{} 94 Signer identity.SignerSerializer 95 DeliverStreamer DeliverStreamer 96 Logger *flogging.FabricLogger 97 YieldLeadership bool 98 99 MaxRetryDelay time.Duration 100 InitialRetryDelay time.Duration 101 MaxRetryDuration time.Duration 102 103 // TLSCertHash should be nil when TLS is not enabled 104 TLSCertHash []byte // util.ComputeSHA256(b.credSupport.GetClientCertificate().Certificate[0]) 105 106 sleeper sleeper 107 } 108 109 const backoffExponentBase = 1.2 110 111 // DeliverBlocks used to pull out blocks from the ordering service to 112 // distributed them across peers 113 func (d *Deliverer) DeliverBlocks() { 114 failureCounter := 0 115 totalDuration := time.Duration(0) 116 117 // InitialRetryDelay * backoffExponentBase^n > MaxRetryDelay 118 // backoffExponentBase^n > MaxRetryDelay / InitialRetryDelay 119 // n * log(backoffExponentBase) > log(MaxRetryDelay / InitialRetryDelay) 120 // n > log(MaxRetryDelay / InitialRetryDelay) / log(backoffExponentBase) 121 maxFailures := int(math.Log(float64(d.MaxRetryDelay)/float64(d.InitialRetryDelay)) / math.Log(backoffExponentBase)) 122 for { 123 select { 124 case <-d.DoneC: 125 return 126 default: 127 } 128 129 if failureCounter > 0 { 130 var sleepDuration time.Duration 131 if failureCounter-1 > maxFailures { 132 sleepDuration = d.MaxRetryDelay 133 } else { 134 sleepDuration = time.Duration(math.Pow(1.2, float64(failureCounter-1))*100) * time.Millisecond 135 } 136 totalDuration += sleepDuration 137 if totalDuration > d.MaxRetryDuration { 138 if d.YieldLeadership { 139 d.Logger.Warningf("attempted to retry block delivery for more than %v, giving up", d.MaxRetryDuration) 140 return 141 } 142 d.Logger.Warningf("peer is a static leader, ignoring peer.deliveryclient.reconnectTotalTimeThreshold") 143 } 144 d.sleeper.Sleep(sleepDuration, d.DoneC) 145 } 146 147 ledgerHeight, err := d.Ledger.LedgerHeight() 148 if err != nil { 149 d.Logger.Error("Did not return ledger height, something is critically wrong", err) 150 return 151 } 152 153 seekInfoEnv, err := d.createSeekInfo(ledgerHeight) 154 if err != nil { 155 d.Logger.Error("Could not create a signed Deliver SeekInfo message, something is critically wrong", err) 156 return 157 } 158 159 deliverClient, endpoint, cancel, err := d.connect(seekInfoEnv) 160 if err != nil { 161 d.Logger.Warningf("Could not connect to ordering service: %s", err) 162 failureCounter++ 163 continue 164 } 165 166 connLogger := d.Logger.With("orderer-address", endpoint.Address) 167 168 recv := make(chan *orderer.DeliverResponse) 169 go func() { 170 for { 171 resp, err := deliverClient.Recv() 172 if err != nil { 173 connLogger.Warningf("Encountered an error reading from deliver stream: %s", err) 174 close(recv) 175 return 176 } 177 select { 178 case recv <- resp: 179 case <-d.DoneC: 180 close(recv) 181 return 182 } 183 } 184 }() 185 186 RecvLoop: // Loop until the endpoint is refreshed, or there is an error on the connection 187 for { 188 select { 189 case <-endpoint.Refreshed: 190 connLogger.Infof("Ordering endpoints have been refreshed, disconnecting from deliver to reconnect using updated endpoints") 191 break RecvLoop 192 case response, ok := <-recv: 193 if !ok { 194 connLogger.Warningf("Orderer hung up without sending status") 195 failureCounter++ 196 break RecvLoop 197 } 198 err = d.processMsg(response) 199 if err != nil { 200 connLogger.Warningf("Got error while attempting to receive blocks: %v", err) 201 failureCounter++ 202 break RecvLoop 203 } 204 failureCounter = 0 205 case <-d.DoneC: 206 break RecvLoop 207 } 208 } 209 210 // cancel and wait for our spawned go routine to exit 211 cancel() 212 <-recv 213 } 214 } 215 216 func (d *Deliverer) processMsg(msg *orderer.DeliverResponse) error { 217 switch t := msg.Type.(type) { 218 case *orderer.DeliverResponse_Status: 219 if t.Status == common.Status_SUCCESS { 220 return errors.Errorf("received success for a seek that should never complete") 221 } 222 223 return errors.Errorf("received bad status %v from orderer", t.Status) 224 case *orderer.DeliverResponse_Block: 225 blockNum := t.Block.Header.Number 226 if err := d.BlockVerifier.VerifyBlock(gossipcommon.ChannelID(d.ChannelID), blockNum, t.Block); err != nil { 227 return errors.WithMessage(err, "block from orderer could not be verified") 228 } 229 230 marshaledBlock, err := proto.Marshal(t.Block) 231 if err != nil { 232 return errors.WithMessage(err, "block from orderer could not be re-marshaled") 233 } 234 235 // Create payload with a block received 236 payload := &gossip.Payload{ 237 Data: marshaledBlock, 238 SeqNum: blockNum, 239 } 240 241 // Use payload to create gossip message 242 gossipMsg := &gossip.GossipMessage{ 243 Nonce: 0, 244 Tag: gossip.GossipMessage_CHAN_AND_ORG, 245 Channel: []byte(d.ChannelID), 246 Content: &gossip.GossipMessage_DataMsg{ 247 DataMsg: &gossip.DataMessage{ 248 Payload: payload, 249 }, 250 }, 251 } 252 253 d.Logger.Debugf("Adding payload to local buffer, blockNum = [%d]", blockNum) 254 // Add payload to local state payloads buffer 255 if err := d.Gossip.AddPayload(d.ChannelID, payload); err != nil { 256 d.Logger.Warningf("Block [%d] received from ordering service wasn't added to payload buffer: %v", blockNum, err) 257 return errors.WithMessage(err, "could not add block as payload") 258 } 259 260 // Gossip messages with other nodes 261 d.Logger.Debugf("Gossiping block [%d]", blockNum) 262 d.Gossip.Gossip(gossipMsg) 263 return nil 264 default: 265 d.Logger.Warningf("Received unknown: %v", t) 266 return errors.Errorf("unknown message type '%T'", msg.Type) 267 } 268 } 269 270 // Stop stops blocks delivery provider 271 func (d *Deliverer) Stop() { 272 // this select is not race-safe, but it prevents a panic 273 // for careless callers multiply invoking stop 274 select { 275 case <-d.DoneC: 276 default: 277 close(d.DoneC) 278 } 279 } 280 281 func (d *Deliverer) connect(seekInfoEnv *common.Envelope) (orderer.AtomicBroadcast_DeliverClient, *orderers.Endpoint, func(), error) { 282 endpoint, err := d.Orderers.RandomEndpoint() 283 if err != nil { 284 return nil, nil, nil, errors.WithMessage(err, "could not get orderer endpoints") 285 } 286 287 conn, err := d.Dialer.Dial(endpoint.Address, endpoint.CertPool) 288 if err != nil { 289 return nil, nil, nil, errors.WithMessagef(err, "could not dial endpoint '%s'", endpoint.Address) 290 } 291 292 ctx, ctxCancel := context.WithCancel(context.Background()) 293 294 deliverClient, err := d.DeliverStreamer.Deliver(ctx, conn) 295 if err != nil { 296 conn.Close() 297 ctxCancel() 298 return nil, nil, nil, errors.WithMessagef(err, "could not create deliver client to endpoints '%s'", endpoint.Address) 299 } 300 301 err = deliverClient.Send(seekInfoEnv) 302 if err != nil { 303 deliverClient.CloseSend() 304 conn.Close() 305 ctxCancel() 306 return nil, nil, nil, errors.WithMessagef(err, "could not send deliver seek info handshake to '%s'", endpoint.Address) 307 } 308 309 return deliverClient, endpoint, func() { 310 deliverClient.CloseSend() 311 ctxCancel() 312 conn.Close() 313 }, nil 314 } 315 316 func (d *Deliverer) createSeekInfo(ledgerHeight uint64) (*common.Envelope, error) { 317 return protoutil.CreateSignedEnvelopeWithTLSBinding( 318 common.HeaderType_DELIVER_SEEK_INFO, 319 d.ChannelID, 320 d.Signer, 321 &orderer.SeekInfo{ 322 Start: &orderer.SeekPosition{ 323 Type: &orderer.SeekPosition_Specified{ 324 Specified: &orderer.SeekSpecified{ 325 Number: ledgerHeight, 326 }, 327 }, 328 }, 329 Stop: &orderer.SeekPosition{ 330 Type: &orderer.SeekPosition_Specified{ 331 Specified: &orderer.SeekSpecified{ 332 Number: math.MaxUint64, 333 }, 334 }, 335 }, 336 Behavior: orderer.SeekInfo_BLOCK_UNTIL_READY, 337 }, 338 int32(0), 339 uint64(0), 340 d.TLSCertHash, 341 ) 342 }