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 }