github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/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  	"sync/atomic"
    21  
    22  	"github.com/golang/protobuf/proto"
    23  	gossipcommon "github.com/hyperledger/fabric/gossip/common"
    24  	"github.com/hyperledger/fabric/gossip/discovery"
    25  
    26  	"github.com/hyperledger/fabric/common/flogging"
    27  	"github.com/hyperledger/fabric/gossip/api"
    28  	"github.com/hyperledger/fabric/protos/common"
    29  	gossip_proto "github.com/hyperledger/fabric/protos/gossip"
    30  	"github.com/hyperledger/fabric/protos/orderer"
    31  	"github.com/op/go-logging"
    32  )
    33  
    34  // LedgerInfo an adapter to provide the interface to query
    35  // the ledger committer for current ledger height
    36  type LedgerInfo interface {
    37  	// LedgerHeight returns current local ledger height
    38  	LedgerHeight() (uint64, error)
    39  }
    40  
    41  // GossipServiceAdapter serves to provide basic functionality
    42  // required from gossip service by delivery service
    43  type GossipServiceAdapter interface {
    44  	// PeersOfChannel returns slice with members of specified channel
    45  	PeersOfChannel(gossipcommon.ChainID) []discovery.NetworkMember
    46  
    47  	// AddPayload adds payload to the local state sync buffer
    48  	AddPayload(chainID string, payload *gossip_proto.Payload) error
    49  
    50  	// Gossip the message across the peers
    51  	Gossip(msg *gossip_proto.GossipMessage)
    52  }
    53  
    54  // BlocksProvider used to read blocks from the ordering service
    55  // for specified chain it subscribed to
    56  type BlocksProvider interface {
    57  	// DeliverBlocks starts delivering and disseminating blocks
    58  	DeliverBlocks()
    59  
    60  	// Stop shutdowns blocks provider and stops delivering new blocks
    61  	Stop()
    62  }
    63  
    64  // BlocksDeliverer defines interface which actually helps
    65  // to abstract the AtomicBroadcast_DeliverClient with only
    66  // required method for blocks provider.
    67  // This also decouples the production implementation of the gRPC stream
    68  // from the code in order for the code to be more modular and testable.
    69  type BlocksDeliverer interface {
    70  	// Recv retrieves a response from the ordering service
    71  	Recv() (*orderer.DeliverResponse, error)
    72  
    73  	// Send sends an envelope to the ordering service
    74  	Send(*common.Envelope) error
    75  }
    76  
    77  type streamClient interface {
    78  	BlocksDeliverer
    79  
    80  	// Close closes the stream and its underlying connection
    81  	Close()
    82  }
    83  
    84  // blocksProviderImpl the actual implementation for BlocksProvider interface
    85  type blocksProviderImpl struct {
    86  	chainID string
    87  
    88  	client streamClient
    89  
    90  	gossip GossipServiceAdapter
    91  
    92  	mcs api.MessageCryptoService
    93  
    94  	done int32
    95  }
    96  
    97  var logger *logging.Logger // package-level logger
    98  
    99  func init() {
   100  	logger = flogging.MustGetLogger("blocksProvider")
   101  }
   102  
   103  // NewBlocksProvider constructor function to create blocks deliverer instance
   104  func NewBlocksProvider(chainID string, client streamClient, gossip GossipServiceAdapter, mcs api.MessageCryptoService) BlocksProvider {
   105  	return &blocksProviderImpl{
   106  		chainID: chainID,
   107  		client:  client,
   108  		gossip:  gossip,
   109  		mcs:     mcs,
   110  	}
   111  }
   112  
   113  // DeliverBlocks used to pull out blocks from the ordering service to
   114  // distributed them across peers
   115  func (b *blocksProviderImpl) DeliverBlocks() {
   116  	defer b.client.Close()
   117  	for !b.isDone() {
   118  		msg, err := b.client.Recv()
   119  		if err != nil {
   120  			logger.Warningf("Receive error: %s", err.Error())
   121  			return
   122  		}
   123  		switch t := msg.Type.(type) {
   124  		case *orderer.DeliverResponse_Status:
   125  			if t.Status == common.Status_SUCCESS {
   126  				logger.Warning("ERROR! Received success for a seek that should never complete")
   127  				return
   128  			}
   129  			logger.Warning("Got error ", t)
   130  		case *orderer.DeliverResponse_Block:
   131  			seqNum := t.Block.Header.Number
   132  
   133  			marshaledBlock, err := proto.Marshal(t.Block)
   134  			if err != nil {
   135  				logger.Errorf("Error serializing block with sequence number %d, due to %s", seqNum, err)
   136  				continue
   137  			}
   138  			if err := b.mcs.VerifyBlock(gossipcommon.ChainID(b.chainID), marshaledBlock); err != nil {
   139  				logger.Errorf("Error verifying block with sequnce number %d, due to %s", seqNum, err)
   140  				continue
   141  			}
   142  
   143  			numberOfPeers := len(b.gossip.PeersOfChannel(gossipcommon.ChainID(b.chainID)))
   144  			// Create payload with a block received
   145  			payload := createPayload(seqNum, marshaledBlock)
   146  			// Use payload to create gossip message
   147  			gossipMsg := createGossipMsg(b.chainID, payload)
   148  
   149  			logger.Debugf("Adding payload locally, buffer seqNum = [%d], peers number [%d]", seqNum, numberOfPeers)
   150  			// Add payload to local state payloads buffer
   151  			b.gossip.AddPayload(b.chainID, payload)
   152  
   153  			// Gossip messages with other nodes
   154  			logger.Debugf("Gossiping block [%d], peers number [%d]", seqNum, numberOfPeers)
   155  			b.gossip.Gossip(gossipMsg)
   156  		default:
   157  			logger.Warning("Received unknown: ", t)
   158  			return
   159  		}
   160  	}
   161  }
   162  
   163  // Stop stops blocks delivery provider
   164  func (b *blocksProviderImpl) Stop() {
   165  	atomic.StoreInt32(&b.done, 1)
   166  	b.client.Close()
   167  }
   168  
   169  // Check whenever provider is stopped
   170  func (b *blocksProviderImpl) isDone() bool {
   171  	return atomic.LoadInt32(&b.done) == 1
   172  }
   173  
   174  func createGossipMsg(chainID string, payload *gossip_proto.Payload) *gossip_proto.GossipMessage {
   175  	gossipMsg := &gossip_proto.GossipMessage{
   176  		Nonce:   0,
   177  		Tag:     gossip_proto.GossipMessage_CHAN_AND_ORG,
   178  		Channel: []byte(chainID),
   179  		Content: &gossip_proto.GossipMessage_DataMsg{
   180  			DataMsg: &gossip_proto.DataMessage{
   181  				Payload: payload,
   182  			},
   183  		},
   184  	}
   185  	return gossipMsg
   186  }
   187  
   188  func createPayload(seqNum uint64, marshaledBlock []byte) *gossip_proto.Payload {
   189  	return &gossip_proto.Payload{
   190  		Data:   marshaledBlock,
   191  		SeqNum: seqNum,
   192  	}
   193  }