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)