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 }