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