github.com/kchristidis/fabric@v1.0.4-0.20171028114726-837acd08cde1/bddtests/steps/orderer_util.py (about) 1 2 # Copyright IBM Corp. 2016 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 import time 18 import datetime 19 import Queue 20 from concurrent.futures import ThreadPoolExecutor 21 from orderer import ab_pb2, ab_pb2_grpc 22 from common import common_pb2 23 24 import bdd_test_util 25 import bootstrap_util 26 import bdd_grpc_util 27 28 29 from grpc.beta import implementations 30 from grpc.framework.interfaces.face.face import AbortionError 31 from grpc.beta.interfaces import StatusCode 32 33 # The default chain ID when the system is statically bootstrapped for testing 34 TEST_CHAIN_ID = "testchainid" 35 36 def _defaultDataFunction(index): 37 payload = common_pb2.Payload( 38 header = common_pb2.Header( 39 chainHeader = common_pb2.ChainHeader( 40 chainID = TEST_CHAIN_ID, 41 type = common_pb2.ENDORSER_TRANSACTION, 42 ), 43 signatureHeader = common_pb2.SignatureHeader(), 44 ), 45 data = str("BDD test: {0}".format(datetime.datetime.utcnow())), 46 ) 47 envelope = common_pb2.Envelope( 48 payload = payload.SerializeToString() 49 ) 50 return envelope 51 52 53 class StreamHelper: 54 55 def __init__(self): 56 self.streamClosed = False 57 self.sendQueue = Queue.Queue() 58 self.receiveQueue = Queue.Queue() 59 self.receivedMessages = [] 60 self.replyGenerator = None 61 62 def setReplyGenerator(self, replyGenerator): 63 assert self.replyGenerator == None, "reply generator already set!!" 64 self.replyGenerator = replyGenerator 65 66 def createSendGenerator(self, timeout = 2): 67 while True: 68 try: 69 nextMsg = self.sendQueue.get(True, timeout) 70 if nextMsg: 71 yield nextMsg 72 else: 73 #None indicates desire to close send 74 return 75 except Queue.Empty: 76 return 77 78 def readMessage(self): 79 for reply in self.readMessages(1): 80 return reply 81 assert False, "Received no messages" 82 83 def readMessages(self, expectedCount): 84 msgsReceived = [] 85 counter = 0 86 try: 87 for reply in self.replyGenerator: 88 counter += 1 89 #print("received reply: {0}, counter = {1}".format(reply, counter)) 90 msgsReceived.append(reply) 91 if counter == int(expectedCount): 92 break 93 except AbortionError as networkError: 94 self.handleNetworkError(networkError) 95 return msgsReceived 96 97 def _start_receive(self): 98 counter = 0 99 try: 100 for reply in self.replyGenerator: 101 counter += 1 102 #print("received reply: {0}, counter = {1}".format(reply, counter)) 103 self.receiveQueue.put(reply) 104 except AbortionError as networkError: 105 self.handleNetworkError(networkError) 106 finally: 107 self.streamClosed = True 108 return 109 110 def handleNetworkError(self, networkError): 111 if networkError.code == StatusCode.OUT_OF_RANGE and networkError.details == "EOF": 112 print("Error received and ignored: {0}".format(networkError)) 113 print() 114 self.streamClosed = True 115 else: 116 raise Exception("Unexpected NetworkError: {0}".format(networkError)) 117 118 def send(self, msg): 119 if msg: 120 if self.streamClosed: 121 raise Exception("Stream is closed.") 122 self.sendQueue.put(msg) 123 124 125 class DeliverStreamHelper(StreamHelper): 126 127 def __init__(self, ordererStub, entity, directory, nodeAdminTuple, timeout = 600): 128 pool = ThreadPoolExecutor(1) 129 StreamHelper.__init__(self) 130 self.nodeAdminTuple = nodeAdminTuple 131 self.directory = directory 132 self.entity = entity 133 # Set the UpdateMessage and start the stream 134 sendGenerator = self.createSendGenerator(timeout) 135 self.replyGenerator = ordererStub.Deliver(sendGenerator, timeout + 1) 136 pool.submit(self._start_receive) 137 138 def createSeekInfo(self, chainID, start = 'Oldest', end = 'Newest', behavior = 'FAIL_IF_NOT_READY'): 139 seekInfo = ab_pb2.SeekInfo( 140 start = seekPosition(start), 141 stop = seekPosition(end), 142 behavior = ab_pb2.SeekInfo.SeekBehavior.Value(behavior), 143 ) 144 return seekInfo 145 146 def seekToRange(self, chainID = TEST_CHAIN_ID, start = 'Oldest', end = 'Newest'): 147 seekInfo = self.createSeekInfo(start = start, end = end, chainID = chainID) 148 envelope = bootstrap_util.createEnvelopeForMsg(directory=self.directory, chainId=chainID, msg=seekInfo, typeAsString="DELIVER_SEEK_INFO", nodeAdminTuple=self.nodeAdminTuple) 149 self.send(envelope) 150 151 def getBlocks(self, timeout=.5, max_wait_seconds=.5): 152 blocks = [] 153 error_reply = None 154 max_wait_time = datetime.datetime.now() + datetime.timedelta(seconds=max_wait_seconds) 155 try: 156 while datetime.datetime.now() < max_wait_time: 157 try: 158 reply = self.receiveQueue.get(True, timeout) 159 if reply.HasField("block"): 160 blocks.append(reply.block) 161 #print("received reply: {0}, len(blocks) = {1}".format(reply, len(blocks))) 162 else: 163 if reply.status != common_pb2.SUCCESS: 164 print("Got error: {0}".format(reply.status)) 165 error_reply = reply 166 break 167 except Queue.Empty: 168 pass 169 except Exception as e: 170 print("getBlocks got error: {0}".format(e) ) 171 return blocks 172 173 174 class UserRegistration: 175 176 def __init__(self, userName, directory): 177 self.userName= userName 178 self.directory = directory 179 self.tags = {} 180 # Dictionary of composeService->atomic broadcast grpc Stub 181 self.atomicBroadcastStubsDict = {} 182 # composeService->StreamHelper 183 self.abDeliversStreamHelperDict = {} 184 185 def getUserName(self): 186 return self.userName 187 188 def closeStreams(self): 189 for compose_service, deliverStreamHelper in self.abDeliversStreamHelperDict.iteritems(): 190 deliverStreamHelper.send(None) 191 self.abDeliversStreamHelperDict.clear() 192 193 def connectToDeliverFunction(self, context, composeService, nodeAdminTuple, timeout=1): 194 'Connect to the deliver function and drain messages to associated orderer queue' 195 assert not composeService in self.abDeliversStreamHelperDict, "Already connected to deliver stream on {0}".format(composeService) 196 streamHelper = DeliverStreamHelper(directory=self.directory, 197 ordererStub=self.getABStubForComposeService(context=context, 198 composeService=composeService), 199 entity=self, nodeAdminTuple=nodeAdminTuple) 200 self.abDeliversStreamHelperDict[composeService] = streamHelper 201 return streamHelper 202 203 def getDelivererStreamHelper(self, context, composeService): 204 assert composeService in self.abDeliversStreamHelperDict, "NOT connected to deliver stream on {0}".format(composeService) 205 return self.abDeliversStreamHelperDict[composeService] 206 207 def broadcastMessages(self, context, numMsgsToBroadcast, composeService, dataFunc=_defaultDataFunction): 208 abStub = self.getABStubForComposeService(context, composeService) 209 replyGenerator = abStub.Broadcast(generateBroadcastMessages(numToGenerate = int(numMsgsToBroadcast), dataFunc=dataFunc), 2) 210 counter = 0 211 try: 212 for reply in replyGenerator: 213 counter += 1 214 print("{0} received reply: {1}, counter = {2}".format(self.getUserName(), reply, counter)) 215 if counter == int(numMsgsToBroadcast): 216 break 217 except Exception as e: 218 print("Got error: {0}".format(e) ) 219 print("Got error") 220 print("Done") 221 assert counter == int(numMsgsToBroadcast), "counter = {0}, expected {1}".format(counter, numMsgsToBroadcast) 222 223 def getABStubForComposeService(self, context, composeService): 224 'Return a Stub for the supplied composeService, will cache' 225 if composeService in self.atomicBroadcastStubsDict: 226 return self.atomicBroadcastStubsDict[composeService] 227 # Get the IP address of the server that the user registered on 228 root_certificates = self.directory.getTrustedRootsForOrdererNetworkAsPEM() 229 ipAddress, port = bdd_test_util.getPortHostMapping(context.compose_containers, composeService, 7050) 230 # print("ipAddress in getABStubForComposeService == {0}:{1}".format(ipAddress, port)) 231 channel = bdd_grpc_util.getGRPCChannel(ipAddress=ipAddress, port=port, root_certificates=root_certificates, ssl_target_name_override=composeService) 232 newABStub = ab_pb2_grpc.AtomicBroadcastStub(channel) 233 self.atomicBroadcastStubsDict[composeService] = newABStub 234 return newABStub 235 236 237 # Registerses a user on a specific composeService 238 def registerUser(context, secretMsg, composeService): 239 userName = secretMsg['enrollId'] 240 if 'ordererUsers' in context: 241 pass 242 else: 243 context.ordererUsers = {} 244 if userName in context.ordererUsers: 245 raise Exception("Orderer user already registered: {0}".format(userName)) 246 userRegistration = UserRegistration(secretMsg) 247 context.ordererUsers[userName] = userRegistration 248 return userRegistration 249 250 def getUserRegistration(context, enrollId): 251 userRegistration = None 252 if 'ordererUsers' in context: 253 pass 254 else: 255 ordererContext.ordererUsers = {} 256 if enrollId in context.ordererUsers: 257 userRegistration = context.ordererUsers[enrollId] 258 else: 259 raise Exception("Orderer user has not been registered: {0}".format(enrollId)) 260 return userRegistration 261 262 def seekPosition(position): 263 if position == 'Oldest': 264 return ab_pb2.SeekPosition(oldest = ab_pb2.SeekOldest()) 265 elif position == 'Newest': 266 return ab_pb2.SeekPosition(newest = ab_pb2.SeekNewest()) 267 else: 268 return ab_pb2.SeekPosition(specified = ab_pb2.SeekSpecified(number = position)) 269 270 def convertSeek(utfString): 271 try: 272 return int(utfString) 273 except ValueError: 274 return str(utfString) 275 276 def createSeekInfo(chainID = TEST_CHAIN_ID, start = 'Oldest', end = 'Newest', behavior = 'FAIL_IF_NOT_READY'): 277 return common_pb2.Envelope( 278 payload = common_pb2.Payload( 279 header = common_pb2.Header( 280 channel_header = common_pb2.ChannelHeader( channel_id = chainID ).SerializeToString(), 281 signature_header = common_pb2.SignatureHeader().SerializeToString(), 282 ), 283 data = ab_pb2.SeekInfo( 284 start = seekPosition(start), 285 stop = seekPosition(end), 286 behavior = ab_pb2.SeekInfo.SeekBehavior.Value(behavior), 287 ).SerializeToString(), 288 ).SerializeToString(), 289 ) 290 291 292 def generateBroadcastMessages(numToGenerate = 3, timeToHoldOpen = 1, dataFunc =_defaultDataFunction): 293 messages = [] 294 for i in range(0, numToGenerate): 295 messages.append(dataFunc(i)) 296 for msg in messages: 297 yield msg 298 time.sleep(timeToHoldOpen)