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  }