Exemple #1
0
    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()))
Exemple #2
0
    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
Exemple #3
0
    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
Exemple #4
0
    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,
            )
Exemple #5
0
    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
Exemple #6
0
    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)
Exemple #7
0
 def leave_async(self):
     """ Async version of `leave()` """
     leave_result = RaidenAsyncResult()
     gevent.spawn(self.leave).link_safe(leave_result)
     return leave_result
Exemple #8
0
    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
Exemple #9
0
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)