Esempio n. 1
0
def wait_for_usable_channel(
    raiden: RaidenService,
    partner_address: Address,
    token_network_registry_address: TokenNetworkRegistryAddress,
    token_address: TokenAddress,
    our_deposit: TokenAmount,
    partner_deposit: TokenAmount,
    retry_timeout: float = DEFAULT_RETRY_TIMEOUT,
) -> None:
    """ Wait until the channel from app0 to app1 is usable.

    The channel and the deposits are registered, and the partner network state
    is reachable.
    """
    waiting.wait_for_newchannel(
        raiden=raiden,
        token_network_registry_address=token_network_registry_address,
        token_address=token_address,
        partner_address=partner_address,
        retry_timeout=retry_timeout,
    )

    # wait for our deposit
    waiting.wait_for_participant_deposit(
        raiden=raiden,
        token_network_registry_address=token_network_registry_address,
        token_address=token_address,
        partner_address=partner_address,
        target_address=raiden.address,
        target_balance=our_deposit,
        retry_timeout=retry_timeout,
    )

    # wait for the partner deposit
    waiting.wait_for_participant_deposit(
        raiden=raiden,
        token_network_registry_address=token_network_registry_address,
        token_address=token_address,
        partner_address=partner_address,
        target_address=partner_address,
        target_balance=partner_deposit,
        retry_timeout=retry_timeout,
    )

    waiting.wait_for_healthy(raiden=raiden,
                             node_address=partner_address,
                             retry_timeout=retry_timeout)
Esempio n. 2
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,
        )
Esempio n. 3
0
def test_api_channel_open_and_deposit_race(
    api_server_test_instance: APIServer,
    raiden_network,
    token_addresses,
    reveal_timeout,
    token_network_registry_address,
    retry_timeout,
):
    """Tests that a race for the same deposit from the API is handled properly

    The proxy's approve_and_set_total_deposit is raising a
    RaidenRecoverableError in case of races. That needs to be properly handled
    and not allowed to bubble out of the greenlet.

    Regression test for https://github.com/raiden-network/raiden/issues/4937
    """
    app0 = raiden_network[0]
    # let's create a new channel
    first_partner_address = "0x61C808D82A3Ac53231750daDc13c777b59310bD9"
    token_address = token_addresses[0]
    settle_timeout = 1650
    channel_data_obj = {
        "partner_address": first_partner_address,
        "token_address": to_checksum_address(token_address),
        "settle_timeout": str(settle_timeout),
        "reveal_timeout": str(reveal_timeout),
    }

    request = grequests.put(api_url_for(api_server_test_instance,
                                        "channelsresource"),
                            json=channel_data_obj)
    response = request.send().response

    assert_proper_response(response, HTTPStatus.CREATED)
    json_response = get_json_response(response)
    expected_response = channel_data_obj.copy()
    expected_response.update({
        "balance": "0",
        "state": ChannelState.STATE_OPENED.value,
        "channel_identifier": "1",
        "total_deposit": "0",
    })
    assert check_dict_nested_attrs(json_response, expected_response)

    # Prepare the deposit api call
    deposit_amount = TokenAmount(99)
    request = grequests.patch(
        api_url_for(
            api_server_test_instance,
            "channelsresourcebytokenandpartneraddress",
            token_address=token_address,
            partner_address=first_partner_address,
        ),
        json={"total_deposit": str(deposit_amount)},
    )

    # Spawn two greenlets doing the same deposit request
    greenlets = [gevent.spawn(request.send), gevent.spawn(request.send)]
    gevent.joinall(set(greenlets), raise_error=True)
    # Make sure that both responses are fine
    g1_response = greenlets[0].get().response
    assert_proper_response(g1_response, HTTPStatus.OK)
    json_response = get_json_response(g1_response)
    expected_response.update({
        "total_deposit": str(deposit_amount),
        "balance": str(deposit_amount)
    })
    assert check_dict_nested_attrs(json_response, expected_response)
    g2_response = greenlets[0].get().response
    assert_proper_response(g2_response, HTTPStatus.OK)
    json_response = get_json_response(g2_response)
    assert check_dict_nested_attrs(json_response, expected_response)

    # Wait for the deposit to be seen
    timeout_seconds = 20
    exception = Exception(
        f"Expected deposit not seen within {timeout_seconds}")
    with gevent.Timeout(seconds=timeout_seconds, exception=exception):
        wait_for_participant_deposit(
            raiden=app0.raiden,
            token_network_registry_address=token_network_registry_address,
            token_address=token_address,
            partner_address=to_canonical_address(first_partner_address),
            target_address=app0.raiden.address,
            target_balance=deposit_amount,
            retry_timeout=retry_timeout,
        )

    request = grequests.get(
        api_url_for(api_server_test_instance, "channelsresource"))
    response = request.send().response
    assert_proper_response(response, HTTPStatus.OK)
    json_response = get_json_response(response)
    channel_info = json_response[0]
    assert channel_info["token_address"] == to_checksum_address(token_address)
    assert channel_info["total_deposit"] == str(deposit_amount)