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 }