github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+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 98 MaxRetryDelay time.Duration 99 InitialRetryDelay time.Duration 100 MaxRetryDuration time.Duration 101 102 // TLSCertHash should be nil when TLS is not enabled 103 TLSCertHash []byte // util.ComputeSHA256(b.credSupport.GetClientCertificate().Certificate[0]) 104 105 sleeper sleeper 106 } 107 108 const backoffExponentBase = 1.2 109 110 // DeliverBlocks used to pull out blocks from the ordering service to 111 // distributed them across peers 112 func (d *Deliverer) DeliverBlocks() { 113 failureCounter := 0 114 totalDuration := time.Duration(0) 115 116 // InitialRetryDelay * backoffExponentBase^n > MaxRetryDelay 117 // backoffExponentBase^n > MaxRetryDelay / InitialRetryDelay 118 // n * log(backoffExponentBase) > log(MaxRetryDelay / InitialRetryDelay) 119 // n > log(MaxRetryDelay / InitialRetryDelay) / log(backoffExponentBase) 120 maxFailures := int(math.Log(float64(d.MaxRetryDelay)/float64(d.InitialRetryDelay)) / math.Log(backoffExponentBase)) 121 for { 122 select { 123 case <-d.DoneC: 124 return 125 default: 126 } 127 128 if failureCounter > 0 { 129 var sleepDuration time.Duration 130 if failureCounter-1 > maxFailures { 131 sleepDuration = d.MaxRetryDelay 132 } else { 133 sleepDuration = time.Duration(math.Pow(1.2, float64(failureCounter-1))*100) * time.Millisecond 134 } 135 totalDuration += sleepDuration 136 if totalDuration > d.MaxRetryDuration { 137 d.Logger.Warningf("attempted to retry block delivery for more than %v, giving up", d.MaxRetryDuration) 138 return 139 } 140 d.sleeper.Sleep(sleepDuration, d.DoneC) 141 } 142 143 ledgerHeight, err := d.Ledger.LedgerHeight() 144 if err != nil { 145 d.Logger.Error("Did not return ledger height, something is critically wrong", err) 146 return 147 } 148 149 seekInfoEnv, err := d.createSeekInfo(ledgerHeight) 150 if err != nil { 151 d.Logger.Error("Could not create a signed Deliver SeekInfo message, something is critically wrong", err) 152 return 153 } 154 155 deliverClient, endpoint, cancel, err := d.connect(seekInfoEnv) 156 if err != nil { 157 d.Logger.Warningf("Could not connect to ordering service: %s", err) 158 failureCounter++ 159 continue 160 } 161 162 connLogger := d.Logger.With("orderer-address", endpoint.Address) 163 164 recv := make(chan *orderer.DeliverResponse) 165 go func() { 166 for { 167 resp, err := deliverClient.Recv() 168 if err != nil { 169 connLogger.Warningf("Encountered an error reading from deliver stream: %s", err) 170 close(recv) 171 return 172 } 173 select { 174 case recv <- resp: 175 case <-d.DoneC: 176 close(recv) 177 return 178 } 179 } 180 }() 181 182 RecvLoop: // Loop until the endpoint is refreshed, or there is an error on the connection 183 for { 184 select { 185 case <-endpoint.Refreshed: 186 connLogger.Infof("Ordering endpoints have been refreshed, disconnecting from deliver to reconnect using updated endpoints") 187 break RecvLoop 188 case response, ok := <-recv: 189 if !ok { 190 connLogger.Warningf("Orderer hung up without sending status") 191 failureCounter++ 192 break RecvLoop 193 } 194 err = d.processMsg(response) 195 if err != nil { 196 connLogger.Warningf("Got error while attempting to receive blocks: %v", err) 197 failureCounter++ 198 break RecvLoop 199 } 200 failureCounter = 0 201 case <-d.DoneC: 202 break RecvLoop 203 } 204 } 205 206 // cancel and wait for our spawned go routine to exit 207 cancel() 208 <-recv 209 } 210 } 211 212 func (d *Deliverer) processMsg(msg *orderer.DeliverResponse) error { 213 switch t := msg.Type.(type) { 214 case *orderer.DeliverResponse_Status: 215 if t.Status == common.Status_SUCCESS { 216 return errors.Errorf("received success for a seek that should never complete") 217 } 218 219 return errors.Errorf("received bad status %v from orderer", t.Status) 220 case *orderer.DeliverResponse_Block: 221 blockNum := t.Block.Header.Number 222 if err := d.BlockVerifier.VerifyBlock(gossipcommon.ChannelID(d.ChannelID), blockNum, t.Block); err != nil { 223 return errors.WithMessage(err, "block from orderer could not be verified") 224 } 225 226 marshaledBlock, err := proto.Marshal(t.Block) 227 if err != nil { 228 return errors.WithMessage(err, "block from orderer could not be re-marshaled") 229 } 230 231 // Create payload with a block received 232 payload := &gossip.Payload{ 233 Data: marshaledBlock, 234 SeqNum: blockNum, 235 } 236 237 // Use payload to create gossip message 238 gossipMsg := &gossip.GossipMessage{ 239 Nonce: 0, 240 Tag: gossip.GossipMessage_CHAN_AND_ORG, 241 Channel: []byte(d.ChannelID), 242 Content: &gossip.GossipMessage_DataMsg{ 243 DataMsg: &gossip.DataMessage{ 244 Payload: payload, 245 }, 246 }, 247 } 248 249 d.Logger.Debugf("Adding payload to local buffer, blockNum = [%d]", blockNum) 250 // Add payload to local state payloads buffer 251 if err := d.Gossip.AddPayload(d.ChannelID, payload); err != nil { 252 d.Logger.Warningf("Block [%d] received from ordering service wasn't added to payload buffer: %v", blockNum, err) 253 return errors.WithMessage(err, "could not add block as payload") 254 } 255 256 // Gossip messages with other nodes 257 d.Logger.Debugf("Gossiping block [%d]", blockNum) 258 d.Gossip.Gossip(gossipMsg) 259 return nil 260 default: 261 d.Logger.Warningf("Received unknown: %v", t) 262 return errors.Errorf("unknown message type '%T'", msg.Type) 263 } 264 } 265 266 // Stop stops blocks delivery provider 267 func (d *Deliverer) Stop() { 268 // this select is not race-safe, but it prevents a panic 269 // for careless callers multiply invoking stop 270 select { 271 case <-d.DoneC: 272 default: 273 close(d.DoneC) 274 } 275 } 276 277 func (d *Deliverer) connect(seekInfoEnv *common.Envelope) (orderer.AtomicBroadcast_DeliverClient, *orderers.Endpoint, func(), error) { 278 endpoint, err := d.Orderers.RandomEndpoint() 279 if err != nil { 280 return nil, nil, nil, errors.WithMessage(err, "could not get orderer endpoints") 281 } 282 283 conn, err := d.Dialer.Dial(endpoint.Address, endpoint.CertPool) 284 if err != nil { 285 return nil, nil, nil, errors.WithMessagef(err, "could not dial endpoint '%s'", endpoint.Address) 286 } 287 288 ctx, ctxCancel := context.WithCancel(context.Background()) 289 290 deliverClient, err := d.DeliverStreamer.Deliver(ctx, conn) 291 if err != nil { 292 conn.Close() 293 ctxCancel() 294 return nil, nil, nil, errors.WithMessagef(err, "could not create deliver client to endpoints '%s'", endpoint.Address) 295 } 296 297 err = deliverClient.Send(seekInfoEnv) 298 if err != nil { 299 deliverClient.CloseSend() 300 conn.Close() 301 ctxCancel() 302 return nil, nil, nil, errors.WithMessagef(err, "could not send deliver seek info handshake to '%s'", endpoint.Address) 303 } 304 305 return deliverClient, endpoint, func() { 306 deliverClient.CloseSend() 307 ctxCancel() 308 conn.Close() 309 }, nil 310 } 311 312 func (d *Deliverer) createSeekInfo(ledgerHeight uint64) (*common.Envelope, error) { 313 return protoutil.CreateSignedEnvelopeWithTLSBinding( 314 common.HeaderType_DELIVER_SEEK_INFO, 315 d.ChannelID, 316 d.Signer, 317 &orderer.SeekInfo{ 318 Start: &orderer.SeekPosition{ 319 Type: &orderer.SeekPosition_Specified{ 320 Specified: &orderer.SeekSpecified{ 321 Number: ledgerHeight, 322 }, 323 }, 324 }, 325 Stop: &orderer.SeekPosition{ 326 Type: &orderer.SeekPosition_Specified{ 327 Specified: &orderer.SeekSpecified{ 328 Number: math.MaxUint64, 329 }, 330 }, 331 }, 332 Behavior: orderer.SeekInfo_BLOCK_UNTIL_READY, 333 }, 334 int32(0), 335 uint64(0), 336 d.TLSCertHash, 337 ) 338 }