Exemple #1
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 timeout 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.')

        log_details = {
            'peer1': pex(self.node_address),
            'peer2': pex(partner),
        }
        log.debug('new_netting_channel called', **log_details)

        # 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 = AsyncResult()
            self.open_channel_transactions[
                partner] = new_open_channel_transaction

            try:
                transaction_hash = self._new_netting_channel(
                    partner, settle_timeout)
            except Exception as e:
                log.critical('new_netting_channel failed', **log_details)
                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.critical('new_netting_channel failed', **log_details)
            raise RaidenUnrecoverableError('creating new channel failed')

        channel_identifier = self.detail_channel(self.node_address,
                                                 partner).channel_identifier
        log_details['channel_identifier'] = channel_identifier
        log.info('new_netting_channel successful', **log_details)

        return channel_identifier
Exemple #2
0
    def expect_token_swap(
            self,
            identifier,
            maker_token,
            maker_amount,
            maker_address,
            taker_token,
            taker_amount,
            taker_address):
        """ Register an expected transfer for this node.

        If a MediatedMessage is received for the `maker_asset` with
        `maker_amount` then proceed to send a MediatedTransfer to
        `maker_address` for `taker_asset` with `taker_amout`.
        """

        if not isaddress(maker_token):
            raise InvalidAddress(
                'Address for maker token is not in expected binary format in expect_token_swap'
            )
        if not isaddress(maker_address):
            raise InvalidAddress(
                'Address for maker is not in expected binary format in expect_token_swap'
            )

        if not isaddress(taker_token):
            raise InvalidAddress(
                'Address for taker token is not in expected binary format in expect_token_swap'
            )
        if not isaddress(taker_address):
            raise InvalidAddress(
                'Address for taker is not in expected binary format in expect_token_swap'
            )

        channelgraphs = self.raiden.channelgraphs

        if taker_token not in channelgraphs:
            log.error('Unknown token {}'.format(pex(taker_token)))
            return

        if maker_token not in channelgraphs:
            log.error('Unknown token {}'.format(pex(maker_token)))
            return

        # the taker is expecting the maker transfer
        key = SwapKey(
            identifier,
            maker_token,
            maker_amount,
        )

        token_swap = TokenSwap(
            identifier,
            maker_token,
            maker_amount,
            maker_address,
            taker_token,
            taker_amount,
            taker_address,
        )

        self.raiden.swapkeys_tokenswaps[key] = token_swap
Exemple #3
0
    def token_swap_async(self, identifier, maker_token, maker_amount,
                         maker_address, taker_token, taker_amount,
                         taker_address):
        """ Start a token swap operation by sending a MediatedTransfer with
        `maker_amount` of `maker_token` to `taker_address`. Only proceed when a
        new valid MediatedTransfer is received with `taker_amount` of
        `taker_token`.
        """
        if not isaddress(maker_token):
            raise InvalidAddress(
                'Address for maker token is not in expected binary format in token swap'
            )
        if not isaddress(maker_address):
            raise InvalidAddress(
                'Address for maker is not in expected binary format in token swap'
            )

        if not isaddress(taker_token):
            raise InvalidAddress(
                'Address for taker token is not in expected binary format in token swap'
            )
        if not isaddress(taker_address):
            raise InvalidAddress(
                'Address for taker is not in expected binary format in token swap'
            )

        channelgraphs = self.raiden.channelgraphs

        if taker_token not in channelgraphs:
            log.error('Unknown token {}'.format(pex(taker_token)))
            return

        if maker_token not in channelgraphs:
            log.error('Unknown token {}'.format(pex(maker_token)))
            return

        token_swap = TokenSwap(
            identifier,
            maker_token,
            maker_amount,
            maker_address,
            taker_token,
            taker_amount,
            taker_address,
        )

        async_result = AsyncResult()
        task = MakerTokenSwapTask(
            self.raiden,
            token_swap,
            async_result,
        )
        task.start()

        # the maker is expecting the taker transfer
        key = SwapKey(
            identifier,
            taker_token,
            taker_amount,
        )
        self.raiden.swapkeys_greenlettasks[key] = task
        self.raiden.swapkeys_tokenswaps[key] = token_swap

        return async_result
Exemple #4
0
    def channel_batch_close(
        self,
        registry_address,
        token_address,
        partner_addresses,
        poll_timeout=DEFAULT_POLL_TIMEOUT,
    ):
        """Close a channel opened with `partner_address` for the given
        `token_address`.

        Race condition, this can fail if channel was closed externally.
        """

        if not is_binary_address(token_address):
            raise InvalidAddress(
                'Expected binary address format for token in channel close')

        if not all(map(is_binary_address, partner_addresses)):
            raise InvalidAddress(
                'Expected binary address format for partner in channel close')

        valid_tokens = views.get_token_network_addresses_for(
            views.state_from_raiden(self.raiden),
            registry_address,
        )
        if token_address not in valid_tokens:
            raise UnknownTokenAddress('Token address is not known.')

        node_state = views.state_from_raiden(self.raiden)
        channels_to_close = views.filter_channels_by_partneraddress(
            node_state,
            registry_address,
            token_address,
            partner_addresses,
        )
        token_network_identifier = views.get_token_network_identifier_by_token_address(
            views.state_from_raiden(self.raiden),
            registry_address,
            token_address,
        )

        # If concurrent operations are happening on one of the channels, fail entire
        # request.
        with ExitStack() as stack:
            # Put all the locks in this outer context so that the netting channel functions
            # don't release the locks when their context goes out of scope
            for channel_state in channels_to_close:
                channel = self.raiden.chain.netting_channel(
                    channel_state.identifier)

                # Check if we can acquire the lock. If we can't raise an exception, which
                # will cause the ExitStack to exit, releasing all locks acquired so far
                if not channel.channel_operations_lock.acquire(blocking=False):
                    raise ChannelBusyError(
                        f'Channel with id {channel_state.identifier} is '
                        f'busy with another ongoing operation.', )

                stack.push(channel.channel_operations_lock)

            for channel_state in channels_to_close:
                channel_close = ActionChannelClose(
                    token_network_identifier,
                    channel_state.identifier,
                )

                self.raiden.handle_state_change(channel_close)

            msg = 'After {} seconds the closing transactions were not properly processed.'.format(
                poll_timeout, )

            channel_ids = [
                channel_state.identifier for channel_state in channels_to_close
            ]

            with gevent.Timeout(poll_timeout, EthNodeCommunicationError(msg)):
                waiting.wait_for_close(
                    self.raiden,
                    registry_address,
                    token_address,
                    channel_ids,
                    self.raiden.alarm.wait_time,
                )
Exemple #5
0
    def get_channel_list(self,
                         registry_address,
                         token_address=None,
                         partner_address=None):
        """Returns a list of channels associated with the optionally given
           `token_address` and/or `partner_address`.

        Args:
            token_address (bin): an optionally provided token address
            partner_address (bin): an optionally provided partner address

        Return:
            A list containing all channels the node participates. Optionally
            filtered by a token address and/or partner address.

        Raises:
            KeyError: An error occurred when the token address is unknown to the node.
        """

        if token_address and not is_binary_address(token_address):
            raise InvalidAddress(
                'Expected binary address format for token in get_channel_list')

        if partner_address and not is_binary_address(partner_address):
            raise InvalidAddress(
                'Expected binary address format for partner in get_channel_list'
            )

        result = list()
        if token_address and partner_address:
            channel_state = views.get_channelstate_for(
                views.state_from_raiden(self.raiden),
                registry_address,
                token_address,
                partner_address,
            )

            if channel_state:
                result = [channel_state]
            else:
                result = []

        elif token_address:
            result = views.list_channelstate_for_tokennetwork(
                views.state_from_raiden(self.raiden),
                registry_address,
                token_address,
            )

        elif partner_address:
            result = views.list_channelstate_for_tokennetwork(
                views.state_from_raiden(self.raiden),
                registry_address,
                partner_address,
            )

        else:
            result = views.list_all_channelstate(
                views.state_from_raiden(self.raiden), )

        return result
Exemple #6
0
    def channel_batch_close(
            self,
            registry_address: typing.PaymentNetworkID,
            token_address: typing.TokenAddress,
            partner_addresses: typing.List[typing.Address],
            retry_timeout: typing.NetworkTimeout = DEFAULT_RETRY_TIMEOUT,
    ):
        """Close a channel opened with `partner_address` for the given
        `token_address`.

        Race condition, this can fail if channel was closed externally.
        """

        if not is_binary_address(token_address):
            raise InvalidAddress('Expected binary address format for token in channel close')

        if not all(map(is_binary_address, partner_addresses)):
            raise InvalidAddress('Expected binary address format for partner in channel close')

        valid_tokens = views.get_token_network_addresses_for(
            views.state_from_raiden(self.raiden),
            registry_address,
        )
        if token_address not in valid_tokens:
            raise UnknownTokenAddress('Token address is not known.')

        chain_state = views.state_from_raiden(self.raiden)
        channels_to_close = views.filter_channels_by_partneraddress(
            chain_state,
            registry_address,
            token_address,
            partner_addresses,
        )
        token_network_identifier = views.get_token_network_identifier_by_token_address(
            views.state_from_raiden(self.raiden),
            registry_address,
            token_address,
        )

        # If concurrent operations are happening on one of the channels, fail entire
        # request.
        with ExitStack() as stack:
            # Put all the locks in this outer context so that the netting channel functions
            # don't release the locks when their context goes out of scope
            for channel_state in channels_to_close:
                channel = self.raiden.chain.payment_channel(
                    token_network_identifier,
                    channel_state.identifier,
                )
                stack.enter_context(channel.lock_or_raise())

            for channel_state in channels_to_close:
                channel_close = ActionChannelClose(
                    token_network_identifier,
                    channel_state.identifier,
                )

                self.raiden.handle_state_change(channel_close)

        channel_ids = [channel_state.identifier for channel_state in channels_to_close]

        waiting.wait_for_close(
            self.raiden,
            registry_address,
            token_address,
            channel_ids,
            retry_timeout,
        )
Exemple #7
0
    def channel_deposit(
        self,
        registry_address,
        token_address,
        partner_address,
        amount,
        poll_timeout=DEFAULT_POLL_TIMEOUT,
    ):
        """ Deposit `amount` in the channel with the peer at `partner_address` and the
        given `token_address` in order to be able to do transfers.

        Raises:
            InvalidAddress: If either token_address or partner_address is not
            20 bytes long.
            TransactionThrew: May happen for multiple reasons:
                - If the token approval fails, e.g. the token may validate if
                  account has enough balance for the allowance.
                - The deposit failed, e.g. the allowance did not set the token
                  aside for use and the user spent it before deposit was called.
                - The channel was closed/settled between the allowance call and
                  the deposit call.
            AddressWithoutCode: The channel was settled during the deposit
            execution.
        """
        node_state = views.state_from_raiden(self.raiden)

        token_networks = views.get_token_network_addresses_for(
            node_state,
            registry_address,
        )
        channel_state = views.get_channelstate_for(
            node_state,
            registry_address,
            token_address,
            partner_address,
        )

        if not is_binary_address(token_address):
            raise InvalidAddress(
                'Expected binary address format for token in channel deposit')

        if not is_binary_address(partner_address):
            raise InvalidAddress(
                'Expected binary address format for partner in channel deposit'
            )

        if token_address not in token_networks:
            raise UnknownTokenAddress('Unknown token address')

        if channel_state is None:
            raise InvalidAddress(
                'No channel with partner_address for the given token')

        token = self.raiden.chain.token(token_address)
        balance = token.balance_of(self.raiden.address)

        # If this check succeeds it does not imply the the `deposit` will
        # succeed, since the `deposit` transaction may race with another
        # transaction.
        if not balance >= amount:
            msg = 'Not enough balance to deposit. {} Available={} Tried={}'.format(
                pex(token_address),
                balance,
                amount,
            )
            raise InsufficientFunds(msg)

        netcontract_address = channel_state.identifier
        channel_proxy = self.raiden.chain.netting_channel(netcontract_address)

        # If concurrent operations are happening on the channel, fail the request
        if not channel_proxy.channel_operations_lock.acquire(blocking=False):
            raise ChannelBusyError(
                f'Channel with id {channel_state.identifier} is '
                f'busy with another ongoing operation', )

        with releasing(channel_proxy.channel_operations_lock):
            token.approve(netcontract_address, amount)
            channel_proxy.deposit(amount)

            old_balance = channel_state.our_state.contract_balance
            target_balance = old_balance + amount

            msg = 'After {} seconds the deposit was not properly processed.'.format(
                poll_timeout, )

            # Wait until the `ChannelNewBalance` event is processed.
            with gevent.Timeout(poll_timeout, EthNodeCommunicationError(msg)):
                target_address = self.raiden.address
                waiting.wait_for_participant_newbalance(
                    self.raiden,
                    registry_address,
                    token_address,
                    partner_address,
                    target_address,
                    target_balance,
                    self.raiden.alarm.wait_time,
                )
Exemple #8
0
    def channel_open(
            self,
            registry_address: typing.PaymentNetworkID,
            token_address: typing.TokenAddress,
            partner_address: typing.Address,
            settle_timeout: typing.BlockTimeout = None,
            retry_timeout: typing.NetworkTimeout = DEFAULT_RETRY_TIMEOUT,
    ) -> typing.ChannelID:
        """ Open a channel with the peer at `partner_address`
        with the given `token_address`.
        """
        if settle_timeout is None:
            settle_timeout = self.raiden.config['settle_timeout']

        if settle_timeout < self.raiden.config['reveal_timeout'] * 2:
            raise InvalidSettleTimeout(
                'settle_timeout can not be smaller than double the reveal_timeout',
            )

        if not is_binary_address(registry_address):
            raise InvalidAddress('Expected binary address format for registry in channel open')

        if not is_binary_address(token_address):
            raise InvalidAddress('Expected binary address format for token in channel open')

        if not is_binary_address(partner_address):
            raise InvalidAddress('Expected binary address format for partner in channel open')

        chain_state = views.state_from_raiden(self.raiden)
        channel_state = views.get_channelstate_for(
            chain_state,
            registry_address,
            token_address,
            partner_address,
        )

        if channel_state:
            raise DuplicatedChannelError('Channel with given partner address already exists')

        registry = self.raiden.chain.token_network_registry(registry_address)
        token_network_address = registry.get_token_network(token_address)

        if token_network_address is None:
            raise TokenNotRegistered(
                'Token network for token %s does not exist' % to_checksum_address(token_address),
            )

        token_network = self.raiden.chain.token_network(
            registry.get_token_network(token_address),
        )

        has_enough_reserve, estimated_required_reserve = has_enough_gas_reserve(
            self.raiden,
            channels_to_open=1,
        )

        if not has_enough_reserve:
            raise InsufficientGasReserve((
                'The account balance is below the estimated amount necessary to '
                'finish the lifecycles of all active channels. A balance of at '
                f'least {estimated_required_reserve} wei is required.'
            ))

        try:
            token_network.new_netting_channel(
                partner_address,
                settle_timeout,
            )
        except DuplicatedChannelError:
            log.info('partner opened channel first')

        waiting.wait_for_newchannel(
            self.raiden,
            registry_address,
            token_address,
            partner_address,
            retry_timeout,
        )
        chain_state = views.state_from_raiden(self.raiden)
        channel_state = views.get_channelstate_for(
            chain_state,
            registry_address,
            token_address,
            partner_address,
        )

        return channel_state.identifier
Exemple #9
0
    def set_total_channel_deposit(
            self,
            registry_address: typing.PaymentNetworkID,
            token_address: typing.TokenAddress,
            partner_address: typing.Address,
            total_deposit: typing.TokenAmount,
            retry_timeout: typing.NetworkTimeout = DEFAULT_RETRY_TIMEOUT,
    ):
        """ Set the `total_deposit` in the channel with the peer at `partner_address` and the
        given `token_address` in order to be able to do transfers.

        Raises:
            InvalidAddress: If either token_address or partner_address is not
            20 bytes long.
            TransactionThrew: May happen for multiple reasons:
                - If the token approval fails, e.g. the token may validate if
                  account has enough balance for the allowance.
                - The deposit failed, e.g. the allowance did not set the token
                  aside for use and the user spent it before deposit was called.
                - The channel was closed/settled between the allowance call and
                  the deposit call.
            AddressWithoutCode: The channel was settled during the deposit
            execution.
            DepositOverLimit: The total deposit amount is higher than the limit.
        """
        chain_state = views.state_from_raiden(self.raiden)

        token_networks = views.get_token_network_addresses_for(
            chain_state,
            registry_address,
        )
        channel_state = views.get_channelstate_for(
            chain_state,
            registry_address,
            token_address,
            partner_address,
        )

        if not is_binary_address(token_address):
            raise InvalidAddress('Expected binary address format for token in channel deposit')

        if not is_binary_address(partner_address):
            raise InvalidAddress('Expected binary address format for partner in channel deposit')

        if token_address not in token_networks:
            raise UnknownTokenAddress('Unknown token address')

        if channel_state is None:
            raise InvalidAddress('No channel with partner_address for the given token')

        token = self.raiden.chain.token(token_address)
        netcontract_address = channel_state.identifier
        token_network_registry = self.raiden.chain.token_network_registry(registry_address)
        token_network_address = token_network_registry.get_token_network(token_address)
        token_network_proxy = self.raiden.chain.token_network(token_network_address)
        channel_proxy = self.raiden.chain.payment_channel(
            token_network_proxy.address,
            netcontract_address,
        )

        balance = token.balance_of(self.raiden.address)

        if self.raiden.config['network_type'] == NetworkType.MAIN:
            deposit_limit = (
                token_network_proxy.proxy.contract.functions.
                channel_participant_deposit_limit().call()
            )
            if total_deposit > deposit_limit:
                raise DepositOverLimit(
                    'The deposit of {} is bigger than the current limit of {}'.format(
                        total_deposit,
                        deposit_limit,
                    ),
                )

        addendum = total_deposit - channel_state.our_state.contract_balance

        # If this check succeeds it does not imply the the `deposit` will
        # succeed, since the `deposit` transaction may race with another
        # transaction.
        if not balance >= addendum:
            msg = 'Not enough balance to deposit. {} Available={} Needed={}'.format(
                pex(token_address),
                balance,
                addendum,
            )
            raise InsufficientFunds(msg)

        # If concurrent operations are happening on the channel, fail the request
        with channel_proxy.lock_or_raise():
            # set_total_deposit calls approve
            # token.approve(netcontract_address, addendum)
            channel_proxy.set_total_deposit(total_deposit)

            target_address = self.raiden.address
            waiting.wait_for_participant_newbalance(
                self.raiden,
                registry_address,
                token_address,
                partner_address,
                target_address,
                total_deposit,
                retry_timeout,
            )
Exemple #10
0
 def get(self, node_address):
     try:
         return self.nodeid_to_hostport[node_address]
     except KeyError:
         raise InvalidAddress('Unknown address {}'.format(
             pex(node_address)))
Exemple #11
0
    def channel_open(
        self,
        registry_address,
        token_address,
        partner_address,
        settle_timeout=None,
        reveal_timeout=None,
        poll_timeout=DEFAULT_POLL_TIMEOUT,
        retry_timeout=DEFAULT_RETRY_TIMEOUT,
    ):
        """ Open a channel with the peer at `partner_address`
        with the given `token_address`.
        """
        if reveal_timeout is None:
            reveal_timeout = self.raiden.config['reveal_timeout']

        if settle_timeout is None:
            settle_timeout = self.raiden.config['settle_timeout']

        if settle_timeout <= reveal_timeout:
            raise InvalidSettleTimeout(
                'reveal_timeout can not be larger-or-equal to settle_timeout',
            )

        if not is_binary_address(registry_address):
            raise InvalidAddress(
                'Expected binary address format for registry in channel open')

        if not is_binary_address(token_address):
            raise InvalidAddress(
                'Expected binary address format for token in channel open')

        if not is_binary_address(partner_address):
            raise InvalidAddress(
                'Expected binary address format for partner in channel open')

        chain_state = views.state_from_raiden(self.raiden)
        channel_state = views.get_channelstate_for(
            chain_state,
            registry_address,
            token_address,
            partner_address,
        )

        if channel_state:
            raise DuplicatedChannelError(
                'Channel with given partner address already exists')

        registry = self.raiden.chain.token_network_registry(registry_address)
        token_network_address = registry.get_token_network(token_address)

        if token_network_address is None:
            raise TokenNotRegistered(
                'Token network for token %s does not exist' %
                to_checksum_address(token_address), )

        token_network = self.raiden.chain.token_network(
            registry.get_token_network(token_address), )

        try:
            token_network.new_netting_channel(
                partner_address,
                settle_timeout,
            )
        except DuplicatedChannelError:
            log.info('partner opened channel first')

        msg = 'After {} seconds the channel was not properly created.'.format(
            poll_timeout, )

        with gevent.Timeout(poll_timeout, EthNodeCommunicationError(msg)):
            waiting.wait_for_newchannel(
                self.raiden,
                registry_address,
                token_address,
                partner_address,
                retry_timeout,
            )
        chain_state = views.state_from_raiden(self.raiden)
        channel_state = views.get_channelstate_for(
            chain_state,
            registry_address,
            token_address,
            partner_address,
        )

        return channel_state.identifier
Exemple #12
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 address of the new netting channel.
        """
        if not is_binary_address(partner):
            raise InvalidAddress(
                'Expected binary address format for channel partner')

        invalid_timeout = (settle_timeout < NETTINGCHANNEL_SETTLE_TIMEOUT_MIN
                           or
                           settle_timeout > NETTINGCHANNEL_SETTLE_TIMEOUT_MAX)
        if invalid_timeout:
            raise InvalidSettleTimeout(
                'settle_timeout must be in range [{}, {}]'.format(
                    NETTINGCHANNEL_SETTLE_TIMEOUT_MIN,
                    NETTINGCHANNEL_SETTLE_TIMEOUT_MAX,
                ))

        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 = AsyncResult()
            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
            transaction_hash = self.open_channel_transactions[partner].get()

        channel_created = self.channel_exists(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(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 #13
0
    def set_total_channel_deposit(
            self,
            registry_address,
            token_address,
            partner_address,
            total_deposit,
            poll_timeout=DEFAULT_POLL_TIMEOUT,
            retry_timeout=DEFAULT_RETRY_TIMEOUT,
    ):
        """ Set the `total_deposit` in the channel with the peer at `partner_address` and the
        given `token_address` in order to be able to do transfers.

        Raises:
            InvalidAddress: If either token_address or partner_address is not
            20 bytes long.
            TransactionThrew: May happen for multiple reasons:
                - If the token approval fails, e.g. the token may validate if
                  account has enough balance for the allowance.
                - The deposit failed, e.g. the allowance did not set the token
                  aside for use and the user spent it before deposit was called.
                - The channel was closed/settled between the allowance call and
                  the deposit call.
            AddressWithoutCode: The channel was settled during the deposit
            execution.
            DepositOverLimit: The total deposit amount is higher than the limit.
        """
        chain_state = views.state_from_raiden(self.raiden)

        token_networks = views.get_token_network_addresses_for(
            chain_state,
            registry_address,
        )
        channel_state = views.get_channelstate_for(
            chain_state,
            registry_address,
            token_address,
            partner_address,
        )

        if not is_binary_address(token_address):
            raise InvalidAddress('Expected binary address format for token in channel deposit')

        if not is_binary_address(partner_address):
            raise InvalidAddress('Expected binary address format for partner in channel deposit')

        if token_address not in token_networks:
            raise UnknownTokenAddress('Unknown token address')

        if channel_state is None:
            raise InvalidAddress('No channel with partner_address for the given token')

        token = self.raiden.chain.token(token_address)
        netcontract_address = channel_state.identifier
        token_network_registry = self.raiden.chain.token_network_registry(registry_address)
        token_network_proxy = token_network_registry.token_network_by_token(token_address)
        channel_proxy = self.raiden.chain.payment_channel(
            token_network_proxy.address,
            netcontract_address,
        )

        balance = token.balance_of(self.raiden.address)

        deposit_limit = token_network_proxy.proxy.contract.functions.deposit_limit().call()
        if total_deposit > deposit_limit:
            raise DepositOverLimit(
                'The deposit of {} is bigger than the current limit of {}'.format(
                    total_deposit,
                    deposit_limit,
                ),
            )

        if total_deposit <= channel_state.our_state.contract_balance:
            # no action required
            return

        addendum = total_deposit - channel_state.our_state.contract_balance

        # If this check succeeds it does not imply the the `deposit` will
        # succeed, since the `deposit` transaction may race with another
        # transaction.
        if not balance >= addendum:
            msg = 'Not enough balance to deposit. {} Available={} Needed={}'.format(
                pex(token_address),
                balance,
                addendum,
            )
            raise InsufficientFunds(msg)

        # If concurrent operations are happening on the channel, fail the request
        if not channel_proxy.channel_operations_lock.acquire(blocking=False):
            raise ChannelBusyError(
                f'Channel with id {channel_state.identifier} is '
                f'busy with another ongoing operation',
            )

        with releasing(channel_proxy.channel_operations_lock):
            # set_total_deposit calls approve
            # token.approve(netcontract_address, addendum)
            channel_proxy.set_total_deposit(total_deposit)

            msg = 'After {} seconds the deposit was not properly processed.'.format(
                poll_timeout,
            )

            # Wait until the `ChannelNewBalance` event is processed.
            with gevent.Timeout(poll_timeout, EthNodeCommunicationError(msg)):
                target_address = self.raiden.address
                waiting.wait_for_participant_newbalance(
                    self.raiden,
                    registry_address,
                    token_address,
                    partner_address,
                    target_address,
                    total_deposit,
                    retry_timeout,
                )
Exemple #14
0
    def transfer_async(
        self,
        registry_address: typing.PaymentNetworkID,
        token_address: typing.TokenAddress,
        amount: typing.TokenAmount,
        target: typing.Address,
        identifier: typing.PaymentID = None,
        secret: typing.Secret = None,
        secret_hash: typing.SecretHash = None,
    ):

        if not isinstance(amount, int):
            raise InvalidAmount('Amount not a number')

        if amount <= 0:
            raise InvalidAmount('Amount negative')

        if not is_binary_address(token_address):
            raise InvalidAddress('token address is not valid.')

        if not is_binary_address(target):
            raise InvalidAddress('target address is not valid.')

        if secret is not None:
            if len(secret) != SECRET_HEXSTRING_LENGTH:
                raise InvalidSecretOrSecretHash(
                    'secret length should be ' + str(SECRET_HEXSTRING_LENGTH) +
                    '.', )
            if not is_hex(secret):
                raise InvalidSecretOrSecretHash(
                    'provided secret is not an hexadecimal string.')
            secret = to_bytes(hexstr=secret)

        if secret_hash is not None:
            if len(secret_hash) != SECRET_HASH_HEXSTRING_LENGTH:
                raise InvalidSecretOrSecretHash(
                    'secret_hash length should be ' +
                    str(SECRET_HASH_HEXSTRING_LENGTH) + '.', )
            if not is_hex(secret_hash):
                raise InvalidSecretOrSecretHash(
                    'secret_hash is not an hexadecimal string.')
            secret_hash = to_bytes(hexstr=secret_hash)

        if secret is None and secret_hash is not None:
            raise InvalidSecretOrSecretHash(
                'secret_hash without a secret is not supported yet.')

        if secret is not None and secret_hash is not None and secret_hash != sha3(
                secret):
            raise InvalidSecretOrSecretHash(
                'provided secret and secret_hash do not match.')

        valid_tokens = views.get_token_network_addresses_for(
            views.state_from_raiden(self.raiden),
            registry_address,
        )
        if token_address not in valid_tokens:
            raise UnknownTokenAddress('Token address is not known.')

        log.debug(
            'Initiating transfer',
            initiator=pex(self.raiden.address),
            target=pex(target),
            token=pex(token_address),
            amount=amount,
            identifier=identifier,
        )

        payment_network_identifier = self.raiden.default_registry.address
        token_network_identifier = views.get_token_network_identifier_by_token_address(
            chain_state=views.state_from_raiden(self.raiden),
            payment_network_id=payment_network_identifier,
            token_address=token_address,
        )
        payment_status = self.raiden.mediated_transfer_async(
            token_network_identifier=token_network_identifier,
            amount=amount,
            target=target,
            identifier=identifier,
            secret=secret,
            secret_hash=secret_hash,
        )
        return payment_status
Exemple #15
0
    def get_channel_list(
        self,
        registry_address: typing.PaymentNetworkID,
        token_address: typing.TokenAddress = None,
        partner_address: typing.Address = None,
    ) -> typing.List[NettingChannelState]:
        """Returns a list of channels associated with the optionally given
           `token_address` and/or `partner_address`.

        Args:
            token_address: an optionally provided token address
            partner_address: an optionally provided partner address

        Return:
            A list containing all channels the node participates. Optionally
            filtered by a token address and/or partner address.

        Raises:
            KeyError: An error occurred when the token address is unknown to the node.
        """
        if registry_address and not is_binary_address(registry_address):
            raise InvalidAddress(
                'Expected binary address format for registry in get_channel_list'
            )

        if token_address and not is_binary_address(token_address):
            raise InvalidAddress(
                'Expected binary address format for token in get_channel_list')

        if partner_address:
            if not is_binary_address(partner_address):
                raise InvalidAddress(
                    'Expected binary address format for partner in get_channel_list',
                )
            if not token_address:
                raise UnknownTokenAddress(
                    'Provided a partner address but no token address')

        if token_address and partner_address:
            channel_state = views.get_channelstate_for(
                chain_state=views.state_from_raiden(self.raiden),
                payment_network_id=registry_address,
                token_address=token_address,
                partner_address=partner_address,
            )

            if channel_state:
                result = [channel_state]
            else:
                result = []

        elif token_address:
            result = views.list_channelstate_for_tokennetwork(
                chain_state=views.state_from_raiden(self.raiden),
                payment_network_id=registry_address,
                token_address=token_address,
            )

        else:
            result = views.list_all_channelstate(
                chain_state=views.state_from_raiden(self.raiden), )

        return result
Exemple #16
0
    def deposit(self, token_address, partner_address, amount):
        """ Deposit `amount` in the channel with the peer at `partner_address` and the
        given `token_address` in order to be able to do transfers.

        Raises:
            InvalidAddress: If either token_address or partner_address is not
            20 bytes long.
            TransactionThrew: May happen for multiple reasons:
                - If the token approval fails, e.g. the token may validate if
                  account has enough balance for the allowance.
                - The deposit failed, e.g. the allowance did not set the token
                  aside for use and the user spent it before deposit was called.
                - The channel was closed/settled between the allowance call and
                  the deposit call.
            AddressWithoutCode: The channel was settled during the deposit
            execution.
        """
        if not isaddress(token_address):
            raise InvalidAddress('Expected binary address format for token in channel deposit')

        if not isaddress(partner_address):
            raise InvalidAddress('Expected binary address format for partner in channel deposit')

        graph = self.raiden.token_to_channelgraph.get(token_address)
        if graph is None:
            raise InvalidAddress('Unknown token address')

        channel = graph.partneraddress_to_channel.get(partner_address)
        if channel is None:
            raise InvalidAddress('No channel with partner_address for the given token')

        if channel.token_address != token_address:
            raise InvalidAddress('token_address does not match the netting channel attribute')

        token = self.raiden.chain.token(token_address)
        netcontract_address = channel.external_state.netting_channel.address
        old_balance = channel.contract_balance

        # Checking the balance is not helpful since this requires multiple
        # transactions that can race, e.g. the deposit check succeed but the
        # user spent his balance before deposit.
        balance = token.balance_of(self.raiden.address.encode('hex'))
        if not balance >= amount:
            msg = 'Not enough balance to deposit. {} Available={} Tried={}'.format(
                pex(token_address),
                balance,
                amount,
            )
            raise InsufficientFunds(msg)
        token.approve(netcontract_address, amount)

        channel_proxy = self.raiden.chain.netting_channel(netcontract_address)
        channel_proxy.deposit(amount)

        # Wait until the `ChannelNewBalance` event is processed.
        #
        # Usually a single sleep is sufficient, since the `deposit` waits for
        # the transaction to be polled.
        wait_timeout_secs = 10  # FIXME: hardcoded timeout
        sucess = wait_until(
            lambda: channel.contract_balance != old_balance,
            wait_timeout_secs,
            self.raiden.alarm.wait_time,
        )

        if not sucess:
            raise EthNodeCommunicationError(
                'After {} seconds the deposit was not properly processed.'.format(
                    wait_timeout_secs
                )
            )

        return channel