Exemple #1
0
    def _funds_remaining(self) -> TokenAmount:
        """The remaining funds after subtracting the already deposited amounts.

        Note:
            - This attribute must be accessed with the lock held.
        """
        if self.funds > 0:
            confirmed_block_identifier = views.get_confirmed_blockhash(
                self.raiden)
            token = self.raiden.proxy_manager.token(
                self.token_address, confirmed_block_identifier)
            token_balance = token.balance_of(self.raiden.address)
            sum_deposits = views.get_our_deposits_for_token_network(
                views.state_from_raiden(self.raiden), self.registry_address,
                self.token_address)

            return TokenAmount(min(self.funds - sum_deposits, token_balance))

        return TokenAmount(0)
Exemple #2
0
    def join_channel(self, partner_address: Address,
                     partner_deposit: TokenAmount) -> None:
        """Will be called, when we were selected as channel partner by another
        node. It will fund the channel with up to the partners deposit, but
        not more than remaining funds or the initial funding per channel.

        If the connection manager has no funds, this is a noop.
        """

        # Consider this race condition:
        #
        # - Partner opens the channel and starts the deposit.
        # - This nodes learns about the new channel, starts ConnectionManager's
        #   retry_connect, which will start a deposit for this half of the
        #   channel.
        # - This node learns about the partner's deposit before its own.
        #   join_channel is called which will try to deposit again.
        #
        # To fix this race, first the node must wait for the pending operations
        # to finish, because in them could be a deposit, and then deposit must
        # be called only if the channel is still not funded.
        confirmed_block_identifier = views.get_confirmed_blockhash(self.raiden)
        token_network_proxy = self.raiden.proxy_manager.token_network(
            self.token_network_address,
            block_identifier=confirmed_block_identifier)

        # Wait for any pending operation in the channel to complete, before
        # deciding on the deposit
        with self.lock, token_network_proxy.channel_operations_lock[
                partner_address]:
            channel_state = views.get_channelstate_for(
                chain_state=views.state_from_raiden(self.raiden),
                token_network_registry_address=self.registry_address,
                token_address=self.token_address,
                partner_address=partner_address,
            )

            if not channel_state:
                return

            joining_funds = min(partner_deposit, self._funds_remaining,
                                self._initial_funding_per_partner)
            if joining_funds <= 0 or self._leaving_state:
                return

            if joining_funds <= channel_state.our_state.contract_balance:
                return

            try:
                self.api.set_total_channel_deposit(self.registry_address,
                                                   self.token_address,
                                                   partner_address,
                                                   joining_funds)
            except RaidenRecoverableError:
                log.info("Channel not in opened state",
                         node=to_checksum_address(self.raiden.address))
            except InvalidDBData:
                raise
            except RaidenUnrecoverableError as e:
                should_crash = (
                    self.raiden.config.environment_type !=
                    Environment.PRODUCTION
                    or self.raiden.config.unrecoverable_error_should_crash)
                if should_crash:
                    raise

                log.critical(str(e),
                             node=to_checksum_address(self.raiden.address))
            else:
                log.info(
                    "Joined a channel",
                    node=to_checksum_address(self.raiden.address),
                    partner=to_checksum_address(partner_address),
                    funds=joining_funds,
                )
Exemple #3
0
def payment_channel_open_and_deposit(
    app0: App,
    app1: App,
    token_address: TokenAddress,
    deposit: TokenAmount,
    settle_timeout: BlockTimeout,
) -> None:
    """ Open a new channel with app0 and app1 as participants """
    assert token_address

    block_identifier: BlockIdentifier
    if app0.raiden.wal:
        block_identifier = views.get_confirmed_blockhash(app0.raiden)
    else:
        block_identifier = BLOCK_ID_LATEST
    token_network_address = app0.raiden.default_registry.get_token_network(
        token_address=token_address, block_identifier=block_identifier)
    assert token_network_address, "request a channel for an unregistered token"
    token_network_proxy = app0.raiden.proxy_manager.token_network(
        token_network_address, block_identifier=BLOCK_ID_LATEST)

    channel_identifier, _, _ = token_network_proxy.new_netting_channel(
        partner=app1.raiden.address,
        settle_timeout=settle_timeout,
        given_block_identifier=block_identifier,
    )
    assert channel_identifier

    if deposit != 0:
        for app, partner in [(app0, app1), (app1, app0)]:
            waiting.wait_for_newchannel(
                raiden=app.raiden,
                token_network_registry_address=app.raiden.default_registry.
                address,
                token_address=token_address,
                partner_address=partner.raiden.address,
                retry_timeout=0.5,
            )

            chain_state = state_from_raiden(app.raiden)
            canonical_identifier = CanonicalIdentifier(
                chain_identifier=chain_state.chain_id,
                token_network_address=token_network_proxy.address,
                channel_identifier=channel_identifier,
            )
            channel_state = get_channelstate_by_canonical_identifier(
                chain_state=chain_state,
                canonical_identifier=canonical_identifier)
            assert channel_state, "nodes dont share a channel"

            # Use each app's own chain because of the private key / local signing
            token = app.raiden.proxy_manager.token(token_address,
                                                   BLOCK_ID_LATEST)
            payment_channel_proxy = app.raiden.proxy_manager.payment_channel(
                channel_state=channel_state, block_identifier=BLOCK_ID_LATEST)

            # This check can succeed and the deposit still fail, if channels are
            # openned in parallel
            previous_balance = token.balance_of(app.raiden.address)
            assert previous_balance >= deposit

            # the payment channel proxy will call approve
            # token.approve(token_network_proxy.address, deposit)
            payment_channel_proxy.approve_and_set_total_deposit(
                total_deposit=deposit, block_identifier=BLOCK_ID_LATEST)

            # Balance must decrease by at least but not exactly `deposit` amount,
            # because channels can be openned in parallel
            new_balance = token.balance_of(app.raiden.address)
            assert new_balance <= previous_balance - deposit

        check_channel(app0, app1, token_network_proxy.address, settle_timeout,
                      deposit)
Exemple #4
0
    def connect(
        self,
        funds: typing.TokenAmount,
        initial_channel_target: int = 3,
        joinable_funds_target: float = 0.4,
    ) -> None:
        """Connect to the network.

        Subsequent calls to `connect` are allowed, but will only affect the spendable
        funds and the connection strategy parameters for the future. `connect` will not
        close any channels.

        Note: the ConnectionManager does not discriminate manually opened channels from
        automatically opened ones. If the user manually opened channels, those deposit
        amounts will affect the funding per channel and the number of new channels opened.

        Args:
            funds: Target amount of tokens spendable to join the network.
            initial_channel_target: Target number of channels to open.
            joinable_funds_target: Amount of funds not initially assigned.
        """
        confirmed_block_identifier = views.get_confirmed_blockhash(self.raiden)
        token = self.raiden.proxy_manager.token(self.token_address,
                                                confirmed_block_identifier)
        token_balance = token.balance_of(self.raiden.address)

        if token_balance < funds:
            raise InvalidAmount(
                f"Insufficient balance for token {to_checksum_address(self.token_address)}"
            )

        if funds <= 0:
            raise InvalidAmount(
                "The funds to use in the connection need to be a positive integer"
            )

        if joinable_funds_target < 0 or joinable_funds_target > 1:
            raise InvalidAmount(
                f"joinable_funds_target should be between 0 and 1. Given: {joinable_funds_target}"
            )

        with self.lock:
            self.funds = funds
            self.initial_channel_target = initial_channel_target
            self.joinable_funds_target = joinable_funds_target

            log_open_channels(self.raiden, self.registry_address,
                              self.token_address, funds)
            have_online_partners, potential_partners = self._have_online_channels_to_connect_to(
            )
            if not have_online_partners:
                bootstrap_address = (self.BOOTSTRAP_ADDR
                                     if len(potential_partners) == 0 else
                                     random.choice(potential_partners))
                log.info(
                    "Bootstrapping token network.",
                    node=to_checksum_address(self.raiden.address),
                    network_id=to_checksum_address(self.registry_address),
                    token_id=to_checksum_address(self.token_address),
                    bootstrap_address=to_checksum_address(bootstrap_address),
                )
                try:
                    self.api.channel_open(
                        registry_address=self.registry_address,
                        token_address=self.token_address,
                        partner_address=bootstrap_address,
                    )
                except DuplicatedChannelError:
                    # If we have none else to connect to and connect got called twice
                    # then it's possible to already have channel with the bootstrap node.
                    # In that case do nothing
                    pass
            else:
                self._open_channels()
Exemple #5
0
    def channel_open(
        self,
        registry_address: TokenNetworkRegistryAddress,
        token_address: TokenAddress,
        partner_address: Address,
        settle_timeout: BlockTimeout = None,
        reveal_timeout: BlockTimeout = None,
        retry_timeout: NetworkTimeout = DEFAULT_RETRY_TIMEOUT,
    ) -> 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 reveal_timeout is None:
            reveal_timeout = self.raiden.config.reveal_timeout

        if reveal_timeout <= 0:
            raise InvalidRevealTimeout(
                "reveal_timeout should be larger than zero")

        if settle_timeout < reveal_timeout * 2:
            raise InvalidSettleTimeout(
                "`settle_timeout` can not be smaller than double the "
                "`reveal_timeout`.\n "
                "\n "
                "The setting `reveal_timeout` determines the maximum number of "
                "blocks it should take a transaction to be mined when the "
                "blockchain is under congestion. This setting determines the "
                "when a node must go on-chain to register a secret, and it is "
                "therefore the lower bound of the lock expiration. The "
                "`settle_timeout` determines when a channel can be settled "
                "on-chain, for this operation to be safe all locks must have "
                "been resolved, for this reason the `settle_timeout` has to be "
                "larger than `reveal_timeout`.")

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

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

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

        confirmed_block_identifier = views.get_confirmed_blockhash(self.raiden)
        registry = self.raiden.proxy_manager.token_network_registry(
            registry_address, block_identifier=confirmed_block_identifier)

        settlement_timeout_min = registry.settlement_timeout_min(
            block_identifier=confirmed_block_identifier)
        settlement_timeout_max = registry.settlement_timeout_max(
            block_identifier=confirmed_block_identifier)

        if settle_timeout < settlement_timeout_min:
            raise InvalidSettleTimeout(
                f"Settlement timeout should be at least {settlement_timeout_min}"
            )

        if settle_timeout > settlement_timeout_max:
            raise InvalidSettleTimeout(
                f"Settlement timeout exceeds max of {settlement_timeout_max}")

        token_network_address = registry.get_token_network(
            token_address=token_address,
            block_identifier=confirmed_block_identifier)
        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.proxy_manager.token_network(
            address=token_network_address,
            block_identifier=confirmed_block_identifier)

        safety_deprecation_switch = token_network.safety_deprecation_switch(
            block_identifier=confirmed_block_identifier)

        if safety_deprecation_switch:
            msg = (
                "This token_network has been deprecated. New channels cannot be "
                "open for this network, usage of the newly deployed token "
                "network contract is highly encouraged.")
            raise TokenNetworkDeprecated(msg)

        duplicated_channel = self.is_already_existing_channel(
            token_network_address=token_network_address,
            partner_address=partner_address,
            block_identifier=confirmed_block_identifier,
        )
        if duplicated_channel:
            raise DuplicatedChannelError(
                f"A channel with {to_checksum_address(partner_address)} for token "
                f"{to_checksum_address(token_address)} already exists. "
                f"(At blockhash: {confirmed_block_identifier.hex()})")

        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=partner_address,
                settle_timeout=settle_timeout,
                given_block_identifier=confirmed_block_identifier,
            )
        except DuplicatedChannelError:
            log.info("partner opened channel first")
        except RaidenRecoverableError:
            # The channel may have been created in the pending block.
            duplicated_channel = self.is_already_existing_channel(
                token_network_address=token_network_address,
                partner_address=partner_address)
            if duplicated_channel:
                log.info("Channel has already been opened")
            else:
                raise

        waiting.wait_for_newchannel(
            raiden=self.raiden,
            token_network_registry_address=registry_address,
            token_address=token_address,
            partner_address=partner_address,
            retry_timeout=retry_timeout,
        )

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

        assert channel_state, f"channel {channel_state} is gone"

        self.raiden.set_channel_reveal_timeout(
            canonical_identifier=channel_state.canonical_identifier,
            reveal_timeout=reveal_timeout)

        return channel_state.identifier