github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/validator/sawtooth_validator/journal/publisher.py (about)

     1  # Copyright 2018 Intel Corporation
     2  #
     3  # Licensed under the Apache License, Version 2.0 (the "License");
     4  # you may not use this file except in compliance with the License.
     5  # You may obtain a copy of the License at
     6  #
     7  #     http://www.apache.org/licenses/LICENSE-2.0
     8  #
     9  # Unless required by applicable law or agreed to in writing, software
    10  # distributed under the License is distributed on an "AS IS" BASIS,
    11  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  # See the License for the specific language governing permissions and
    13  # limitations under the License.
    14  # ------------------------------------------------------------------------------
    15  
    16  # pylint: disable=inconsistent-return-statements
    17  
    18  import abc
    19  import ctypes
    20  import logging
    21  
    22  from enum import IntEnum
    23  
    24  from sawtooth_validator.ffi import PY_LIBRARY, LIBRARY
    25  from sawtooth_validator.ffi import OwnedPointer
    26  
    27  LOGGER = logging.getLogger(__name__)
    28  
    29  
    30  class PendingBatchObserver(metaclass=abc.ABCMeta):
    31      """An interface class for components wishing to be notified when a Batch
    32      has begun being processed.
    33      """
    34  
    35      @abc.abstractmethod
    36      def notify_batch_pending(self, batch):
    37          """This method will be called when a Batch has passed initial
    38          validation and is queued to be processed by the Publisher.
    39  
    40          Args:
    41              batch (Batch): The Batch that has been added to the Publisher
    42          """
    43          raise NotImplementedError('PendingBatchObservers must have a '
    44                                    '"notify_batch_pending" method')
    45  
    46  
    47  class IncomingBatchSenderErrorCode(IntEnum):
    48      Success = 0
    49      NullPointerProvided = 0x01
    50      InvalidInput = 0x02
    51      Disconnected = 0x03
    52  
    53  
    54  class Disconnected(Exception):
    55      """The receiving end of the IncomingBatchQueue hung up."""
    56  
    57  
    58  class IncomingBatchSender(OwnedPointer):
    59      def __init__(self, sender_ptr):
    60          super().__init__("incoming_batch_sender_drop")
    61          self._ptr = sender_ptr
    62  
    63      def send(self, item):
    64          res = PY_LIBRARY.call(
    65              "incoming_batch_sender_send",
    66              self._ptr,
    67              ctypes.py_object(item))
    68  
    69          if res == IncomingBatchSenderErrorCode.Success:
    70              return
    71          elif res == IncomingBatchSenderErrorCode.NullPointerProvided:
    72              raise TypeError("Provided null pointer(s)")
    73          elif res == IncomingBatchSenderErrorCode.InvalidInput:
    74              raise ValueError("Input was not valid ")
    75          elif res == IncomingBatchSenderErrorCode.Disconnected:
    76              raise Disconnected()
    77          else:
    78              raise ValueError("An unknown error occurred: {}".format(res))
    79  
    80  
    81  class ChainHeadLockErrorCode(IntEnum):
    82      Success = 0
    83      NullPointerProvided = 0x01
    84  
    85  
    86  class ChainHeadLock(OwnedPointer):
    87      def __init__(self, chain_head_lock_ptr):
    88          super().__init__("chain_head_lock_drop")
    89          self._ptr = chain_head_lock_ptr
    90          self._guard = None
    91  
    92      def acquire(self):
    93          guard_ptr = ctypes.c_void_p()
    94          res = LIBRARY.call(
    95              "chain_head_lock_acquire",
    96              self._ptr,
    97              ctypes.byref(guard_ptr))
    98  
    99          if res == ChainHeadLockErrorCode.Success:
   100              self._guard = ChainHeadGuard(guard_ptr)
   101              return self._guard
   102          elif res == ChainHeadLockErrorCode.NullPointerProvided:
   103              raise TypeError("Provided null pointer(s)")
   104          else:
   105              raise ValueError("An unknown error occurred: {}".format(res))
   106  
   107      def release(self):
   108          res = LIBRARY.call(
   109              "chain_head_guard_drop",
   110              self._guard.pointer)
   111  
   112          if res == ChainHeadLockErrorCode.Success:
   113              return
   114          elif res == ChainHeadLockErrorCode.NullPointerProvided:
   115              raise TypeError("Provided null pointer(s)")
   116          else:
   117              raise ValueError("An unknown error occurred: {}".format(res))
   118  
   119  
   120  class ChainHeadGuard:
   121      def __init__(self, guard_ptr):
   122          self.pointer = guard_ptr
   123  
   124      def notify_on_chain_updated(self,
   125                                  chain_head,
   126                                  committed_batches=None,
   127                                  uncommitted_batches=None):
   128          res = LIBRARY.call(
   129              "chain_head_guard_on_chain_updated",
   130              self.pointer,
   131              ctypes.py_object(chain_head),
   132              ctypes.py_object(committed_batches),
   133              ctypes.py_object(uncommitted_batches))
   134  
   135          if res == ChainHeadLockErrorCode.Success:
   136              return
   137          elif res == ChainHeadLockErrorCode.NullPointerProvided:
   138              raise TypeError("Provided null pointer(s)")
   139          else:
   140              raise ValueError("An unknown error occurred: {}".format(res))
   141  
   142  
   143  class BlockPublisherErrorCode(IntEnum):
   144      Success = 0
   145      NullPointerProvided = 0x01
   146      InvalidInput = 0x02
   147  
   148  
   149  class BlockPublisher(OwnedPointer):
   150      """
   151      Responsible for generating new blocks and publishing them when the
   152      Consensus deems it appropriate.
   153      """
   154  
   155      def __init__(self,
   156                   transaction_executor,
   157                   block_cache,
   158                   state_view_factory,
   159                   settings_cache,
   160                   block_sender,
   161                   batch_sender,
   162                   chain_head,
   163                   identity_signer,
   164                   data_dir,
   165                   config_dir,
   166                   permission_verifier,
   167                   check_publish_block_frequency,
   168                   batch_observers,
   169                   batch_injector_factory=None):
   170          """
   171          Initialize the BlockPublisher object
   172  
   173          Args:
   174              transaction_executor (:obj:`TransactionExecutor`): A
   175                  TransactionExecutor instance.
   176              block_cache (:obj:`BlockCache`): A BlockCache instance.
   177              state_view_factory (:obj:`StateViewFactory`): StateViewFactory for
   178                  read-only state views.
   179              block_sender (:obj:`BlockSender`): The BlockSender instance.
   180              batch_sender (:obj:`BatchSender`): The BatchSender instance.
   181              chain_head (:obj:`BlockWrapper`): The initial chain head.
   182              chain_head_lock (:obj:`RLock`): The chain head lock.
   183              identity_signer (:obj:`Signer`): Cryptographic signer for signing
   184                  blocks
   185              data_dir (str): path to location where persistent data for the
   186                  consensus module can be stored.
   187              config_dir (str): path to location where configuration can be
   188                  found.
   189              batch_injector_factory (:obj:`BatchInjectorFatctory`): A factory
   190                  for creating BatchInjectors.
   191          """
   192          super(BlockPublisher, self).__init__('block_publisher_drop')
   193  
   194          self._to_exception(PY_LIBRARY.call(
   195              'block_publisher_new',
   196              ctypes.py_object(transaction_executor),
   197              ctypes.py_object(block_cache),
   198              ctypes.py_object(state_view_factory),
   199              ctypes.py_object(settings_cache),
   200              ctypes.py_object(block_sender),
   201              ctypes.py_object(batch_sender),
   202              ctypes.py_object(chain_head),
   203              ctypes.py_object(identity_signer),
   204              ctypes.py_object(data_dir),
   205              ctypes.py_object(config_dir),
   206              ctypes.py_object(permission_verifier),
   207              ctypes.py_object(check_publish_block_frequency * 1000),
   208              ctypes.py_object(batch_observers),
   209              ctypes.py_object(batch_injector_factory),
   210              ctypes.byref(self.pointer)))
   211  
   212      def _call(self, method, *args, library=LIBRARY):
   213          self._to_exception(library.call(
   214              'block_publisher_' + method,
   215              self.pointer,
   216              *args))
   217  
   218      def _py_call(self, method, *args):
   219          self._call(method, *args, library=PY_LIBRARY)
   220  
   221      @staticmethod
   222      def _to_exception(res):
   223          if res == BlockPublisherErrorCode.Success:
   224              return
   225          elif res == BlockPublisherErrorCode.NullPointerProvided:
   226              raise TypeError("Provided null pointer(s)")
   227          elif res == BlockPublisherErrorCode.InvalidInput:
   228              raise ValueError("Input was not valid ")
   229  
   230      def batch_sender(self):
   231          sender_ptr = ctypes.c_void_p()
   232          self._call(
   233              'batch_sender',
   234              ctypes.byref(sender_ptr))
   235          return IncomingBatchSender(sender_ptr)
   236  
   237      def start(self):
   238          self._call('start')
   239  
   240      def stop(self):
   241          self._call('stop')
   242  
   243      def pending_batch_info(self):
   244          """Returns a tuple of the current size of the pending batch queue
   245          and the current queue limit.
   246          """
   247          c_length = ctypes.c_int(0)
   248          c_limit = ctypes.c_int(0)
   249          self._call(
   250              'pending_batch_info',
   251              ctypes.byref(c_length),
   252              ctypes.byref(c_limit))
   253  
   254          return (c_length.value, c_limit.value)
   255  
   256      def on_check_publish_block(self, force=False):
   257  
   258          self._py_call(
   259              'on_check_publish_block',
   260              ctypes.c_bool(force))
   261  
   262      def on_batch_received(self, batch):
   263          self._py_call(
   264              'on_batch_received',
   265              ctypes.py_object(batch))
   266  
   267      @property
   268      def chain_head_lock(self):
   269          chain_head_lock_ptr = ctypes.c_void_p()
   270          self._call('chain_head_lock', ctypes.byref(chain_head_lock_ptr))
   271          return ChainHeadLock(chain_head_lock_ptr)
   272  
   273      def on_chain_updated(self, chain_head,
   274                           committed_batches=None,
   275                           uncommitted_batches=None):
   276          """
   277          The existing chain has been updated, the current head block has
   278          changed.
   279  
   280          :param chain_head: the new head of block_chain, can be None if
   281          no block publishing is desired.
   282          :param committed_batches: the set of batches that were committed
   283           as part of the new chain.
   284          :param uncommitted_batches: the list of transactions if any that are
   285          now de-committed when the new chain was selected.
   286          :return: None
   287          """
   288          try:
   289              self._py_call(
   290                  'on_chain_updated',
   291                  ctypes.py_object(chain_head),
   292                  ctypes.py_object(committed_batches),
   293                  ctypes.py_object(uncommitted_batches))
   294  
   295          # pylint: disable=broad-except
   296          except Exception:
   297              LOGGER.exception(
   298                  "Unhandled exception in BlockPublisher.on_chain_updated")
   299  
   300      def has_batch(self, batch_id):
   301          has = ctypes.c_bool(False)
   302          c_batch_id = ctypes.c_char_p(batch_id.encode())
   303  
   304          self._call(
   305              'has_batch',
   306              c_batch_id,
   307              ctypes.byref(has))
   308  
   309          return has