def send_async( self, queue_identifier: QueueIdentifier, message: Message, ): if not self._running: return receiver_address = queue_identifier.recipient self.log.info( 'SEND ASYNC', receiver_address=to_normalized_address(receiver_address), message=message, queue_identifier=queue_identifier, ) if not is_binary_address(receiver_address): raise ValueError('Invalid address {}'.format(pex(receiver_address))) # These are not protocol messages, but transport specific messages if isinstance(message, (Delivered, Ping, Pong)): raise ValueError( 'Do not use send_async for {} messages'.format(message.__class__.__name__), ) message_id = message.message_identifier async_result = RaidenAsyncResult() if isinstance(message, Processed): async_result.set(True) # processed messages shouldn't get a Delivered reply self._send_immediate(receiver_address, json.dumps(message.to_dict())) else: self._messageids_to_asyncresult[message_id] = async_result self._send_with_retry(receiver_address, async_result, json.dumps(message.to_dict()))
def __init__(self, chain): super().__init__() # TODO: Start with a larger sleep_time and decrease it as the # probability of a new block increases. sleep_time = 0.5 self.callbacks = list() self.chain = chain self.chain_id = None self.last_block_number = None self.stop_event = RaidenAsyncResult() self.sleep_time = sleep_time
def start_mediated_transfer( self, token_network_identifier, amount, target, identifier, ): self.start_health_check_for(target) if identifier is None: identifier = create_default_identifier() if identifier in self.identifier_to_results: return self.identifier_to_results[identifier] async_result = RaidenAsyncResult() self.identifier_to_results[identifier] = async_result secret = random_secret() init_initiator_statechange = initiator_init( self, identifier, amount, secret, token_network_identifier, target, ) # Dispatch the state change even if there are no routes to create the # wal entry. self.handle_state_change(init_initiator_statechange) return async_result
def send_async( self, recipient: typing.Address, queue_name: bytes, message: 'Message', ): """ Send a new ordered message to recipient. Messages that use the same `queue_name` are ordered. """ if not is_binary_address(recipient): raise ValueError('Invalid address {}'.format(pex(recipient))) # These are not protocol messages, but transport specific messages if isinstance(message, (Delivered, Ping, Pong)): raise ValueError('Do not use send for {} messages'.format( message.__class__.__name__)) messagedata = message.encode() if len(messagedata) > self.UDP_MAX_MESSAGE_SIZE: raise ValueError( 'message size exceeds the maximum {}'.format( self.UDP_MAX_MESSAGE_SIZE), ) # message identifiers must be unique message_id = message.message_identifier # ignore duplicates if message_id not in self.messageids_to_asyncresults: self.messageids_to_asyncresults[message_id] = RaidenAsyncResult() queue = self.get_queue_for(recipient, queue_name) queue.put((messagedata, message_id)) log.debug( 'MESSAGE QUEUED', node=pex(self.raiden.address), queue_name=queue_name, to=pex(recipient), message=message, )
def maybe_sendraw_with_result( self, recipient: typing.Address, messagedata: bytes, message_id: typing.MessageID, ) -> AsyncResult: """ Send message to recipient if the transport is running. Returns: An AsyncResult that will be set once the message is delivered. As long as the message has not been acknowledged with a Delivered message the function will return the same AsyncResult. """ async_result = self.messageids_to_asyncresults.get(message_id) if async_result is None: async_result = RaidenAsyncResult() self.messageids_to_asyncresults[message_id] = async_result host_port = self.get_host_port(recipient) self.maybe_sendraw(host_port, messagedata) return async_result
def register_secret_batch(self, secrets: List[typing.Secret]): secret_batch = list() secret_registry_transaction = RaidenAsyncResult() for secret in secrets: secrethash = sha3(secret) if not self.check_registered(secrethash): if secret not in self.open_secret_transactions: secret_batch.append(secret) self.open_secret_transactions[ secret] = secret_registry_transaction else: log.info( 'secret already registered', node=pex(self.node_address), contract=pex(self.address), secrethash=encode_hex(secrethash), ) if not secret_batch: return log.info( 'registerSecretBatch called', node=pex(self.node_address), contract=pex(self.address), ) try: transaction_hash = self._register_secret_batch(secret_batch) except Exception as e: secret_registry_transaction.set_exception(e) raise else: secret_registry_transaction.set(transaction_hash) finally: for secret in secret_batch: self.open_secret_transactions.pop(secret, None)
def leave_async(self): """ Async version of `leave()` """ leave_result = RaidenAsyncResult() gevent.spawn(self.leave).link_safe(leave_result) return leave_result
def new_netting_channel( self, partner: typing.Address, settle_timeout: int, ) -> typing.ChannelID: """ Creates a new channel in the TokenNetwork contract. Args: partner: The peer to open the channel with. settle_timeout: The settle timout to use for this channel. Returns: The ChannelID of the new netting channel. """ if not is_binary_address(partner): raise InvalidAddress( 'Expected binary address format for channel partner') invalid_timeout = (settle_timeout < self.settlement_timeout_min() or settle_timeout > self.settlement_timeout_max()) if invalid_timeout: raise InvalidSettleTimeout( 'settle_timeout must be in range [{}, {}], is {}'.format( self.settlement_timeout_min(), self.settlement_timeout_max(), settle_timeout, )) if self.node_address == partner: raise SamePeerAddress( 'The other peer must not have the same address as the client.') # Prevent concurrent attempts to open a channel with the same token and # partner address. if partner not in self.open_channel_transactions: new_open_channel_transaction = RaidenAsyncResult() self.open_channel_transactions[ partner] = new_open_channel_transaction try: transaction_hash = self._new_netting_channel( partner, settle_timeout) except Exception as e: new_open_channel_transaction.set_exception(e) raise else: new_open_channel_transaction.set(transaction_hash) finally: self.open_channel_transactions.pop(partner, None) else: # All other concurrent threads should block on the result of opening this channel self.open_channel_transactions[partner].get() channel_created = self.channel_exists_and_not_settled( self.node_address, partner) if channel_created is False: log.error( 'creating new channel failed', peer1=pex(self.node_address), peer2=pex(partner), ) raise RuntimeError('creating new channel failed') channel_identifier = self.detail_channel(self.node_address, partner).channel_identifier log.info( 'new_netting_channel called', peer1=pex(self.node_address), peer2=pex(partner), channel_identifier=channel_identifier, ) return channel_identifier
class AlarmTask(RaidenGreenlet): """ Task to notify when a block is mined. """ def __init__(self, chain): super().__init__() # TODO: Start with a larger sleep_time and decrease it as the # probability of a new block increases. sleep_time = 0.5 self.callbacks = list() self.chain = chain self.chain_id = None self.last_block_number = None self.stop_event = RaidenAsyncResult() self.sleep_time = sleep_time def _run(self): # pylint: disable=method-hidden try: self.loop_until_stop() except RaidenShuttingDown: pass finally: self.callbacks = list() def register_callback(self, callback): """ Register a new callback. Note: The callback will be executed in the AlarmTask context and for this reason it should not block, otherwise we can miss block changes. """ if not callable(callback): raise ValueError('callback is not a callable') self.callbacks.append(callback) def remove_callback(self, callback): """Remove callback from the list of callbacks if it exists""" if callback in self.callbacks: self.callbacks.remove(callback) def loop_until_stop(self): # The AlarmTask must have completed its first_run() before starting # the background greenlet. # # This is required because the first run will synchronize the node with # the blockchain since the last run. assert self.chain_id, 'chain_id not set' assert self.last_block_number, 'last_block_number not set' chain_id = self.chain_id sleep_time = self.sleep_time while self.stop_event.wait(sleep_time) is not True: last_block_number = self.last_block_number current_block = self.chain.block_number() if chain_id != self.chain.network_id: raise RuntimeError( 'Changing the underlying blockchain while the Raiden node is running ' 'is not supported.', ) if current_block != last_block_number: log.debug('new block', number=current_block) if current_block > last_block_number + 1: missed_blocks = current_block - last_block_number - 1 log.info( 'missed blocks', missed_blocks=missed_blocks, current_block=current_block, ) self.run_callbacks(current_block, chain_id) def first_run(self): # callbacks must be executed during the first run to update the node state assert self.callbacks, 'callbacks not set' chain_id = self.chain.network_id current_block = self.chain.block_number() log.debug('starting at block number', current_block=current_block) self.run_callbacks(current_block, chain_id) self.chain_id = chain_id def run_callbacks(self, current_block, chain_id): remove = list() for callback in self.callbacks: result = callback(current_block, chain_id) if result is REMOVE_CALLBACK: remove.append(callback) for callback in remove: self.callbacks.remove(callback) self.last_block_number = current_block def stop_async(self): self.stop_event.set(True)