Пример #1
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=chain_state,
            payment_network_id=registry_address,
            token_address=token_address,
            partner_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)
        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_address=token_network_proxy.address,
            channel_id=channel_state.identifier,
        )

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

        if self.raiden.config['environment_type'] == Environment.PRODUCTION:
            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(
                raiden=self.raiden,
                payment_network_id=registry_address,
                token_address=token_address,
                partner_address=partner_address,
                target_address=target_address,
                target_balance=total_deposit,
                retry_timeout=retry_timeout,
            )
Пример #2
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 deposit was 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,
                )
Пример #3
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,
            )
Пример #4
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_identifiers(
            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
Пример #5
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,
                )
Пример #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_identifiers(
            chain_state=views.state_from_raiden(self.raiden),
            payment_network_id=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=chain_state,
            payment_network_id=registry_address,
            token_address=token_address,
            partner_addresses=partner_addresses,
        )
        token_network_identifier = views.get_token_network_identifier_by_token_address(
            chain_state=views.state_from_raiden(self.raiden),
            payment_network_id=registry_address,
            token_address=token_address,
        )

        greenlets: typing.List[Greenlet] = list()
        for channel_state in channels_to_close:
            channel_close = ActionChannelClose(
                token_network_identifier=token_network_identifier,
                channel_identifier=channel_state.identifier,
            )

            greenlets.extend(self.raiden.handle_state_change(channel_close), )

        gevent.joinall(greenlets, raise_error=True)

        channel_ids = [
            channel_state.identifier for channel_state in channels_to_close
        ]

        waiting.wait_for_close(
            raiden=self.raiden,
            payment_network_id=registry_address,
            token_address=token_address,
            channel_ids=channel_ids,
            retry_timeout=retry_timeout,
        )
Пример #7
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
Пример #8
0
    def set_total_channel_deposit(
        self,
        registry_address: PaymentNetworkID,
        token_address: TokenAddress,
        partner_address: Address,
        total_deposit: TokenAmount,
        retry_timeout: 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_addresses = views.get_token_identifiers(chain_state, registry_address)
        channel_state = views.get_channelstate_for(
            chain_state=chain_state,
            payment_network_id=registry_address,
            token_address=token_address,
            partner_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_addresses:
            raise UnknownTokenAddress("Unknown token address")

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

        if self.raiden.config["environment_type"] == Environment.PRODUCTION:
            per_token_network_deposit_limit = RED_EYES_PER_TOKEN_NETWORK_LIMIT
        else:
            per_token_network_deposit_limit = UINT256_MAX

        token = self.raiden.chain.token(token_address)
        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(
            canonical_identifier=channel_state.canonical_identifier
        )

        if total_deposit == 0:
            raise DepositMismatch("Attempted to deposit with total deposit being 0")

        addendum = total_deposit - channel_state.our_state.contract_balance

        total_network_balance = token.balance_of(registry_address)

        if total_network_balance + addendum > per_token_network_deposit_limit:
            raise DepositOverLimit(
                f"The deposit of {addendum} will exceed the "
                f"token network limit of {per_token_network_deposit_limit}"
            )

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

        functions = token_network_proxy.proxy.contract.functions
        deposit_limit = functions.channel_participant_deposit_limit().call()

        if total_deposit > deposit_limit:
            raise DepositOverLimit(
                f"The additional deposit of {addendum} will exceed the "
                f"channel participant limit of {deposit_limit}"
            )

        # 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)

        # set_total_deposit calls approve
        # token.approve(netcontract_address, addendum)
        channel_proxy.set_total_deposit(
            total_deposit=total_deposit,
            block_identifier=views.state_from_raiden(self.raiden).block_hash,
        )

        target_address = self.raiden.address
        waiting.wait_for_participant_newbalance(
            raiden=self.raiden,
            payment_network_id=registry_address,
            token_address=token_address,
            partner_address=partner_address,
            target_address=target_address,
            target_balance=total_deposit,
            retry_timeout=retry_timeout,
        )
Пример #9
0
    def transfer_async(
        self,
        registry_address: PaymentNetworkID,
        token_address: TokenAddress,
        amount: TokenAmount,
        target: Address,
        identifier: PaymentID = None,
        secret: Secret = None,
        secrethash: SecretHash = None,
    ):
        current_state = views.state_from_raiden(self.raiden)
        payment_network_identifier = self.raiden.default_registry.address

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

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

        if amount > UINT256_MAX:
            raise InvalidAmount("Amount too large")

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

        if token_address not in views.get_token_identifiers(current_state, registry_address):
            raise UnknownTokenAddress("Token address is not known.")

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

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

        if secret is not None and not isinstance(secret, typing.T_Secret):
            raise InvalidSecret("secret is not valid.")

        if secrethash is not None and not isinstance(secrethash, typing.T_SecretHash):
            raise InvalidSecretHash("secrethash is not valid.")

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

        token_network_identifier = views.get_token_network_identifier_by_token_address(
            chain_state=current_state,
            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,
            secrethash=secrethash,
        )
        return payment_status
Пример #10
0
    def set_total_channel_deposit(
        self,
        registry_address: TokenNetworkRegistryAddress,
        token_address: TokenAddress,
        partner_address: Address,
        total_deposit: TokenAmount,
        retry_timeout: NetworkTimeout = DEFAULT_RETRY_TIMEOUT,
    ) -> None:
        """ 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:
            InvalidBinaryAddress: If either token_address or partner_address is not
                20 bytes long.
            RaidenRecoverableError: 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.
            UnexpectedChannelState: The channel is no longer in an open state.
        """
        chain_state = views.state_from_raiden(self.raiden)

        token_addresses = views.get_token_identifiers(chain_state,
                                                      registry_address)
        channel_state = views.get_channelstate_for(
            chain_state=chain_state,
            token_network_registry_address=registry_address,
            token_address=token_address,
            partner_address=partner_address,
        )

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

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

        if token_address not in token_addresses:
            raise UnknownTokenAddress("Unknown token address")

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

        confirmed_block_identifier = chain_state.block_hash
        token = self.raiden.proxy_manager.token(
            token_address, block_identifier=confirmed_block_identifier)
        token_network_registry = self.raiden.proxy_manager.token_network_registry(
            registry_address, block_identifier=confirmed_block_identifier)
        token_network_address = token_network_registry.get_token_network(
            token_address=token_address,
            block_identifier=confirmed_block_identifier)

        if token_network_address is None:
            raise UnknownTokenAddress(
                f"Token {to_checksum_address(token_address)} is not registered "
                f"with the network {to_checksum_address(registry_address)}.")

        token_network_proxy = self.raiden.proxy_manager.token_network(
            address=token_network_address,
            block_identifier=confirmed_block_identifier)
        channel_proxy = self.raiden.proxy_manager.payment_channel(
            channel_state=channel_state,
            block_identifier=confirmed_block_identifier)

        blockhash = chain_state.block_hash
        token_network_proxy = channel_proxy.token_network

        safety_deprecation_switch = token_network_proxy.safety_deprecation_switch(
            block_identifier=blockhash)

        balance = token.balance_of(self.raiden.address,
                                   block_identifier=blockhash)

        network_balance = token.balance_of(
            address=Address(token_network_address), block_identifier=blockhash)
        token_network_deposit_limit = token_network_proxy.token_network_deposit_limit(
            block_identifier=blockhash)

        addendum = total_deposit - channel_state.our_state.contract_balance

        channel_participant_deposit_limit = token_network_proxy.channel_participant_deposit_limit(
            block_identifier=blockhash)
        total_channel_deposit = total_deposit + channel_state.partner_state.contract_balance

        is_channel_open = channel.get_status(
            channel_state) == ChannelState.STATE_OPENED

        if not is_channel_open:
            raise UnexpectedChannelState("Channel is not in an open state.")

        if safety_deprecation_switch:
            msg = ("This token_network has been deprecated. "
                   "All channels in this network should be closed and "
                   "the usage of the newly deployed token network contract "
                   "is highly encouraged.")
            raise TokenNetworkDeprecated(msg)

        if total_deposit <= channel_state.our_state.contract_balance:
            raise DepositMismatch("Total deposit did not increase.")

        # If this check succeeds it does not imply 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(
                to_checksum_address(token_address), balance, addendum)
            raise InsufficientFunds(msg)

        if network_balance + addendum > token_network_deposit_limit:
            msg = f"Deposit of {addendum} would have exceeded the token network deposit limit."
            raise DepositOverLimit(msg)

        if total_deposit > channel_participant_deposit_limit:
            msg = (f"Deposit of {total_deposit} is larger than the "
                   f"channel participant deposit limit")
            raise DepositOverLimit(msg)

        if total_channel_deposit >= UINT256_MAX:
            raise DepositOverLimit("Deposit overflow")

        try:
            channel_proxy.approve_and_set_total_deposit(
                total_deposit=total_deposit, block_identifier=blockhash)
        except RaidenRecoverableError as e:
            log.info(f"Deposit failed. {str(e)}")

        target_address = self.raiden.address
        waiting.wait_for_participant_deposit(
            raiden=self.raiden,
            token_network_registry_address=registry_address,
            token_address=token_address,
            partner_address=partner_address,
            target_address=target_address,
            target_balance=total_deposit,
            retry_timeout=retry_timeout,
        )
Пример #11
0
    def set_total_channel_withdraw(
        self,
        registry_address: TokenNetworkRegistryAddress,
        token_address: TokenAddress,
        partner_address: Address,
        total_withdraw: WithdrawAmount,
        retry_timeout: NetworkTimeout = DEFAULT_RETRY_TIMEOUT,
    ) -> None:
        """ Set the `total_withdraw` in the channel with the peer at `partner_address` and the
        given `token_address`.

        Raises:
            InvalidBinaryAddress: If either token_address or partner_address is not
                20 bytes long.
            RaidenUnrecoverableError: May happen for multiple reasons:
                - During preconditions checks, if the channel was not open
                  at the time of the approve_and_set_total_deposit call.
                - If the transaction fails during gas estimation or
                  if a previous withdraw transaction with the same value
                   was already mined.
            DepositMismatch: The total withdraw amount did not increase.
        """
        chain_state = views.state_from_raiden(self.raiden)

        token_addresses = views.get_token_identifiers(chain_state,
                                                      registry_address)
        channel_state = views.get_channelstate_for(
            chain_state=chain_state,
            token_network_registry_address=registry_address,
            token_address=token_address,
            partner_address=partner_address,
        )

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

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

        if token_address not in token_addresses:
            raise UnknownTokenAddress("Unknown token address")

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

        if total_withdraw <= channel_state.our_total_withdraw:
            raise WithdrawMismatch(
                f"Total withdraw {total_withdraw} did not increase")

        current_balance = channel.get_balance(
            sender=channel_state.our_state,
            receiver=channel_state.partner_state)
        amount_to_withdraw = total_withdraw - channel_state.our_total_withdraw
        if amount_to_withdraw > current_balance:
            raise InsufficientFunds(
                "The withdraw of {} is bigger than the current balance of {}".
                format(amount_to_withdraw, current_balance))

        self.raiden.withdraw(
            canonical_identifier=channel_state.canonical_identifier,
            total_withdraw=total_withdraw)

        waiting.wait_for_withdraw_complete(
            raiden=self.raiden,
            canonical_identifier=channel_state.canonical_identifier,
            total_withdraw=total_withdraw,
            retry_timeout=retry_timeout,
        )
Пример #12
0
    def transfer_async(
        self,
        registry_address: TokenNetworkRegistryAddress,
        token_address: TokenAddress,
        amount: PaymentAmount,
        target: TargetAddress,
        identifier: PaymentID = None,
        secret: Secret = None,
        secrethash: SecretHash = None,
        lock_timeout: BlockTimeout = None,
    ) -> "PaymentStatus":
        current_state = views.state_from_raiden(self.raiden)
        token_network_registry_address = self.raiden.default_registry.address

        if not isinstance(amount, int):  # pragma: no unittest
            raise InvalidAmount("Amount not a number")

        if Address(target) == self.address:
            raise SamePeerAddress("Address must be different than ours")

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

        if amount > UINT256_MAX:
            raise InvalidAmount("Amount too large")

        if not is_binary_address(token_address):
            raise InvalidBinaryAddress("token address is not valid.")

        if token_address not in views.get_token_identifiers(
                current_state, registry_address):
            raise UnknownTokenAddress("Token address is not known.")

        if not is_binary_address(target):
            raise InvalidBinaryAddress("target address is not valid.")

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

        if secret is not None and not isinstance(secret, T_Secret):
            raise InvalidSecret("secret is not valid.")

        if secrethash is not None and not isinstance(secrethash, T_SecretHash):
            raise InvalidSecretHash("secrethash is not valid.")

        if identifier is None:
            identifier = create_default_identifier()

        if identifier <= 0:
            raise InvalidPaymentIdentifier(
                "Payment identifier cannot be 0 or negative")

        if identifier > UINT64_MAX:
            raise InvalidPaymentIdentifier("Payment identifier is too large")

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

        token_network_address = views.get_token_network_address_by_token_address(
            chain_state=current_state,
            token_network_registry_address=token_network_registry_address,
            token_address=token_address,
        )

        if token_network_address is None:
            raise UnknownTokenAddress(
                f"Token {to_checksum_address(token_address)} is not registered "
                f"with the network {to_checksum_address(registry_address)}.")

        payment_status = self.raiden.mediated_transfer_async(
            token_network_address=token_network_address,
            amount=amount,
            target=target,
            identifier=identifier,
            secret=secret,
            secrethash=secrethash,
            lock_timeout=lock_timeout,
        )
        return payment_status
Пример #13
0
    def transfer_async(
        self,
        registry_address: PaymentNetworkID,
        token_address: TokenAddress,
        amount: TokenAmount,
        target: Address,
        identifier: PaymentID = None,
        secret: Secret = None,
        secrethash: 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 secrethash is not None:
            if len(secrethash) != SECRETHASH_HEXSTRING_LENGTH:
                raise InvalidSecretOrSecretHash(
                    "secret_hash length should be " + str(SECRETHASH_HEXSTRING_LENGTH) + "."
                )
            if not is_hex(secrethash):
                raise InvalidSecretOrSecretHash("secret_hash is not an hexadecimal string.")
            secrethash = to_bytes(hexstr=secrethash)

        # if both secret and secrethash were provided we check that sha3(secret)
        # matches the secerthash. Note that it is valid to provide a secert_hash
        # without providing a secret
        if secret is not None and secrethash is not None and secrethash != sha3(secret):
            raise InvalidSecretOrSecretHash("provided secret and secret_hash do not match.")

        valid_tokens = views.get_token_identifiers(
            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,
            secrethash=secrethash,
        )
        return payment_status
Пример #14
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,
                )