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