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  }