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