github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/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  	"sync/atomic"
    22  
    23  	"github.com/golang/protobuf/proto"
    24  	gossipcommon "github.com/hyperledger/fabric/gossip/common"
    25  	"github.com/hyperledger/fabric/gossip/discovery"
    26  
    27  	"github.com/hyperledger/fabric/protos/common"
    28  	gossip_proto "github.com/hyperledger/fabric/protos/gossip"
    29  	"github.com/hyperledger/fabric/protos/orderer"
    30  	"github.com/hyperledger/fabric/protos/utils"
    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  	// RequestBlock acquire new blocks from ordering service based on
    58  	// information provided by ledger info instance
    59  	RequestBlocks(ledgerInfoProvider LedgerInfo) error
    60  
    61  	// DeliverBlocks starts delivering and disseminating blocks
    62  	DeliverBlocks()
    63  
    64  	// Stop shutdowns blocks provider and stops delivering new blocks
    65  	Stop()
    66  }
    67  
    68  // BlocksDeliverer defines interface which actually helps
    69  // to abstract the AtomicBroadcast_DeliverClient with only
    70  // required method for blocks provider. This also help to
    71  // build up mocking facilities for testing purposes
    72  type BlocksDeliverer interface {
    73  	// Recv capable to bring new blocks from the ordering service
    74  	Recv() (*orderer.DeliverResponse, error)
    75  
    76  	// Send used to send request to the ordering service to obtain new blocks
    77  	Send(*common.Envelope) error
    78  }
    79  
    80  // blocksProviderImpl the actual implementation for BlocksProvider interface
    81  type blocksProviderImpl struct {
    82  	chainID string
    83  
    84  	client BlocksDeliverer
    85  
    86  	gossip GossipServiceAdapter
    87  
    88  	done int32
    89  }
    90  
    91  var logger *logging.Logger // package-level logger
    92  
    93  func init() {
    94  	logger = logging.MustGetLogger("blocksProvider")
    95  }
    96  
    97  // NewBlocksProvider constructor function to creare blocks deliverer instance
    98  func NewBlocksProvider(chainID string, client BlocksDeliverer, gossip GossipServiceAdapter) BlocksProvider {
    99  	return &blocksProviderImpl{
   100  		chainID: chainID,
   101  		client:  client,
   102  		gossip:  gossip,
   103  	}
   104  }
   105  
   106  // DeliverBlocks used to pull out blocks from the ordering service to
   107  // distributed them across peers
   108  func (b *blocksProviderImpl) DeliverBlocks() {
   109  	for !b.isDone() {
   110  		msg, err := b.client.Recv()
   111  		if err != nil {
   112  			logger.Warningf("Receive error: %s", err.Error())
   113  			return
   114  		}
   115  		switch t := msg.Type.(type) {
   116  		case *orderer.DeliverResponse_Status:
   117  			if t.Status == common.Status_SUCCESS {
   118  				logger.Warning("ERROR! Received success for a seek that should never complete")
   119  				return
   120  			}
   121  			logger.Warning("Got error ", t)
   122  		case *orderer.DeliverResponse_Block:
   123  			seqNum := t.Block.Header.Number
   124  
   125  			numberOfPeers := len(b.gossip.PeersOfChannel(gossipcommon.ChainID(b.chainID)))
   126  			// Create payload with a block received
   127  			payload := createPayload(seqNum, t.Block)
   128  			// Use payload to create gossip message
   129  			gossipMsg := createGossipMsg(b.chainID, payload)
   130  
   131  			logger.Debugf("Adding payload locally, buffer seqNum = [%d], peers number [%d]", seqNum, numberOfPeers)
   132  			// Add payload to local state payloads buffer
   133  			b.gossip.AddPayload(b.chainID, payload)
   134  
   135  			// Gossip messages with other nodes
   136  			logger.Debugf("Gossiping block [%d], peers number [%d]", seqNum, numberOfPeers)
   137  			b.gossip.Gossip(gossipMsg)
   138  		default:
   139  			logger.Warning("Received unknown: ", t)
   140  			return
   141  		}
   142  	}
   143  }
   144  
   145  // Stops blocks delivery provider
   146  func (b *blocksProviderImpl) Stop() {
   147  	atomic.StoreInt32(&b.done, 1)
   148  }
   149  
   150  // Check whenever provider is stopped
   151  func (b *blocksProviderImpl) isDone() bool {
   152  	return atomic.LoadInt32(&b.done) == 1
   153  }
   154  
   155  func (b *blocksProviderImpl) RequestBlocks(ledgerInfoProvider LedgerInfo) error {
   156  	height, err := ledgerInfoProvider.LedgerHeight()
   157  	if err != nil {
   158  		logger.Errorf("Can't get legder height from committer [%s]", err)
   159  		return err
   160  	}
   161  
   162  	if height > 0 {
   163  		logger.Debugf("Starting deliver with block [%d]", height)
   164  		if err := b.seekLatestFromCommitter(height); err != nil {
   165  			return err
   166  		}
   167  	} else {
   168  		logger.Debug("Starting deliver with olders block")
   169  		if err := b.seekOldest(); err != nil {
   170  			return err
   171  		}
   172  	}
   173  
   174  	return nil
   175  }
   176  
   177  func (b *blocksProviderImpl) seekOldest() error {
   178  	return b.client.Send(&common.Envelope{
   179  		Payload: utils.MarshalOrPanic(&common.Payload{
   180  			Header: &common.Header{
   181  				ChannelHeader: utils.MarshalOrPanic(&common.ChannelHeader{
   182  					ChannelId: b.chainID,
   183  				}),
   184  				SignatureHeader: utils.MarshalOrPanic(&common.SignatureHeader{}),
   185  			},
   186  			Data: utils.MarshalOrPanic(&orderer.SeekInfo{
   187  				Start:    &orderer.SeekPosition{Type: &orderer.SeekPosition_Oldest{Oldest: &orderer.SeekOldest{}}},
   188  				Stop:     &orderer.SeekPosition{Type: &orderer.SeekPosition_Specified{Specified: &orderer.SeekSpecified{Number: math.MaxUint64}}},
   189  				Behavior: orderer.SeekInfo_BLOCK_UNTIL_READY,
   190  			}),
   191  		}),
   192  	})
   193  }
   194  
   195  func (b *blocksProviderImpl) seekLatestFromCommitter(height uint64) error {
   196  	return b.client.Send(&common.Envelope{
   197  		Payload: utils.MarshalOrPanic(&common.Payload{
   198  			Header: &common.Header{
   199  				ChannelHeader: utils.MarshalOrPanic(&common.ChannelHeader{
   200  					ChannelId: b.chainID,
   201  				}),
   202  				SignatureHeader: utils.MarshalOrPanic(&common.SignatureHeader{}),
   203  			},
   204  			Data: utils.MarshalOrPanic(&orderer.SeekInfo{
   205  				Start:    &orderer.SeekPosition{Type: &orderer.SeekPosition_Specified{Specified: &orderer.SeekSpecified{Number: height}}},
   206  				Stop:     &orderer.SeekPosition{Type: &orderer.SeekPosition_Specified{Specified: &orderer.SeekSpecified{Number: math.MaxUint64}}},
   207  				Behavior: orderer.SeekInfo_BLOCK_UNTIL_READY,
   208  			}),
   209  		}),
   210  	})
   211  }
   212  
   213  func createGossipMsg(chainID string, payload *gossip_proto.Payload) *gossip_proto.GossipMessage {
   214  	gossipMsg := &gossip_proto.GossipMessage{
   215  		Nonce:   0,
   216  		Tag:     gossip_proto.GossipMessage_CHAN_AND_ORG,
   217  		Channel: []byte(chainID),
   218  		Content: &gossip_proto.GossipMessage_DataMsg{
   219  			DataMsg: &gossip_proto.DataMessage{
   220  				Payload: payload,
   221  			},
   222  		},
   223  	}
   224  	return gossipMsg
   225  }
   226  
   227  func createPayload(seqNum uint64, block *common.Block) *gossip_proto.Payload {
   228  	marshaledBlock, _ := proto.Marshal(block)
   229  	return &gossip_proto.Payload{
   230  		Data:   marshaledBlock,
   231  		SeqNum: seqNum,
   232  	}
   233  }