github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/core/deliverservice/blocksprovider/blocksprovider.go (about)

     1  /*
     2  Copyright IBM Corp. 2017 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8                   http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package blocksprovider
    18  
    19  import (
    20  	"math"
    21  	"time"
    22  
    23  	"sync/atomic"
    24  
    25  	"github.com/golang/protobuf/proto"
    26  	gossipcommon "github.com/hyperledger/fabric/gossip/common"
    27  	"github.com/hyperledger/fabric/gossip/discovery"
    28  
    29  	"github.com/hyperledger/fabric/common/flogging"
    30  	"github.com/hyperledger/fabric/gossip/api"
    31  	"github.com/hyperledger/fabric/protos/common"
    32  	gossip_proto "github.com/hyperledger/fabric/protos/gossip"
    33  	"github.com/hyperledger/fabric/protos/orderer"
    34  	"github.com/op/go-logging"
    35  )
    36  
    37  // LedgerInfo an adapter to provide the interface to query
    38  // the ledger committer for current ledger height
    39  type LedgerInfo interface {
    40  	// LedgerHeight returns current local ledger height
    41  	LedgerHeight() (uint64, error)
    42  }
    43  
    44  // GossipServiceAdapter serves to provide basic functionality
    45  // required from gossip service by delivery service
    46  type GossipServiceAdapter interface {
    47  	// PeersOfChannel returns slice with members of specified channel
    48  	PeersOfChannel(gossipcommon.ChainID) []discovery.NetworkMember
    49  
    50  	// AddPayload adds payload to the local state sync buffer
    51  	AddPayload(chainID string, payload *gossip_proto.Payload) error
    52  
    53  	// Gossip the message across the peers
    54  	Gossip(msg *gossip_proto.GossipMessage)
    55  }
    56  
    57  // BlocksProvider used to read blocks from the ordering service
    58  // for specified chain it subscribed to
    59  type BlocksProvider interface {
    60  	// DeliverBlocks starts delivering and disseminating blocks
    61  	DeliverBlocks()
    62  
    63  	// Stop shutdowns blocks provider and stops delivering new blocks
    64  	Stop()
    65  }
    66  
    67  // BlocksDeliverer defines interface which actually helps
    68  // to abstract the AtomicBroadcast_DeliverClient with only
    69  // required method for blocks provider.
    70  // This also decouples the production implementation of the gRPC stream
    71  // from the code in order for the code to be more modular and testable.
    72  type BlocksDeliverer interface {
    73  	// Recv retrieves a response from the ordering service
    74  	Recv() (*orderer.DeliverResponse, error)
    75  
    76  	// Send sends an envelope to the ordering service
    77  	Send(*common.Envelope) error
    78  }
    79  
    80  type streamClient interface {
    81  	BlocksDeliverer
    82  
    83  	// Close closes the stream and its underlying connection
    84  	Close()
    85  
    86  	// Disconnect disconnects from the remote node
    87  	Disconnect()
    88  }
    89  
    90  // blocksProviderImpl the actual implementation for BlocksProvider interface
    91  type blocksProviderImpl struct {
    92  	chainID string
    93  
    94  	client streamClient
    95  
    96  	gossip GossipServiceAdapter
    97  
    98  	mcs api.MessageCryptoService
    99  
   100  	done int32
   101  
   102  	wrongStatusThreshold int
   103  }
   104  
   105  const wrongStatusThreshold = 10
   106  
   107  var MaxRetryDelay = time.Second * 10
   108  
   109  var logger *logging.Logger // package-level logger
   110  
   111  func init() {
   112  	logger = flogging.MustGetLogger("blocksProvider")
   113  }
   114  
   115  // NewBlocksProvider constructor function to create blocks deliverer instance
   116  func NewBlocksProvider(chainID string, client streamClient, gossip GossipServiceAdapter, mcs api.MessageCryptoService) BlocksProvider {
   117  	return &blocksProviderImpl{
   118  		chainID:              chainID,
   119  		client:               client,
   120  		gossip:               gossip,
   121  		mcs:                  mcs,
   122  		wrongStatusThreshold: wrongStatusThreshold,
   123  	}
   124  }
   125  
   126  // DeliverBlocks used to pull out blocks from the ordering service to
   127  // distributed them across peers
   128  func (b *blocksProviderImpl) DeliverBlocks() {
   129  	errorStatusCounter := 0
   130  	statusCounter := 0
   131  	defer b.client.Close()
   132  	for !b.isDone() {
   133  		msg, err := b.client.Recv()
   134  		if err != nil {
   135  			logger.Warningf("[%s] Receive error: %s", b.chainID, err.Error())
   136  			return
   137  		}
   138  		switch t := msg.Type.(type) {
   139  		case *orderer.DeliverResponse_Status:
   140  			if t.Status == common.Status_SUCCESS {
   141  				logger.Warningf("[%s] ERROR! Received success for a seek that should never complete", b.chainID)
   142  				return
   143  			}
   144  			if t.Status == common.Status_BAD_REQUEST || t.Status == common.Status_FORBIDDEN {
   145  				logger.Errorf("[%s] Got error %v", b.chainID, t)
   146  				errorStatusCounter++
   147  				if errorStatusCounter > b.wrongStatusThreshold {
   148  					logger.Criticalf("[%s] Wrong statuses threshold passed, stopping block provider", b.chainID)
   149  					return
   150  				}
   151  			} else {
   152  				errorStatusCounter = 0
   153  				logger.Warningf("[%s] Got error %v", b.chainID, t)
   154  			}
   155  			maxDelay := float64(MaxRetryDelay)
   156  			currDelay := float64(time.Duration(math.Pow(2, float64(statusCounter))) * 100 * time.Millisecond)
   157  			time.Sleep(time.Duration(math.Min(maxDelay, currDelay)))
   158  			if currDelay < maxDelay {
   159  				statusCounter++
   160  			}
   161  			b.client.Disconnect()
   162  			continue
   163  		case *orderer.DeliverResponse_Block:
   164  			errorStatusCounter = 0
   165  			statusCounter = 0
   166  			seqNum := t.Block.Header.Number
   167  
   168  			marshaledBlock, err := proto.Marshal(t.Block)
   169  			if err != nil {
   170  				logger.Errorf("[%s] Error serializing block with sequence number %d, due to %s", b.chainID, seqNum, err)
   171  				continue
   172  			}
   173  			if err := b.mcs.VerifyBlock(gossipcommon.ChainID(b.chainID), seqNum, marshaledBlock); err != nil {
   174  				logger.Errorf("[%s] Error verifying block with sequnce number %d, due to %s", b.chainID, seqNum, err)
   175  				continue
   176  			}
   177  
   178  			numberOfPeers := len(b.gossip.PeersOfChannel(gossipcommon.ChainID(b.chainID)))
   179  			// Create payload with a block received
   180  			payload := createPayload(seqNum, marshaledBlock)
   181  			// Use payload to create gossip message
   182  			gossipMsg := createGossipMsg(b.chainID, payload)
   183  
   184  			logger.Debugf("[%s] Adding payload locally, buffer seqNum = [%d], peers number [%d]", b.chainID, seqNum, numberOfPeers)
   185  			// Add payload to local state payloads buffer
   186  			if err := b.gossip.AddPayload(b.chainID, payload); err != nil {
   187  				logger.Warning("Failed adding payload of", seqNum, "because:", err)
   188  			}
   189  
   190  			// Gossip messages with other nodes
   191  			logger.Debugf("[%s] Gossiping block [%d], peers number [%d]", b.chainID, seqNum, numberOfPeers)
   192  			b.gossip.Gossip(gossipMsg)
   193  		default:
   194  			logger.Warningf("[%s] Received unknown: ", b.chainID, t)
   195  			return
   196  		}
   197  	}
   198  }
   199  
   200  // Stop stops blocks delivery provider
   201  func (b *blocksProviderImpl) Stop() {
   202  	atomic.StoreInt32(&b.done, 1)
   203  	b.client.Close()
   204  }
   205  
   206  // Check whenever provider is stopped
   207  func (b *blocksProviderImpl) isDone() bool {
   208  	return atomic.LoadInt32(&b.done) == 1
   209  }
   210  
   211  func createGossipMsg(chainID string, payload *gossip_proto.Payload) *gossip_proto.GossipMessage {
   212  	gossipMsg := &gossip_proto.GossipMessage{
   213  		Nonce:   0,
   214  		Tag:     gossip_proto.GossipMessage_CHAN_AND_ORG,
   215  		Channel: []byte(chainID),
   216  		Content: &gossip_proto.GossipMessage_DataMsg{
   217  			DataMsg: &gossip_proto.DataMessage{
   218  				Payload: payload,
   219  			},
   220  		},
   221  	}
   222  	return gossipMsg
   223  }
   224  
   225  func createPayload(seqNum uint64, marshaledBlock []byte) *gossip_proto.Payload {
   226  	return &gossip_proto.Payload{
   227  		Data:   marshaledBlock,
   228  		SeqNum: seqNum,
   229  	}
   230  }