github.com/true-sqn/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  }