def find_new_partners(self, number: int):
        """Search the token network for potential channel partners.

        Args:
            number: number of partners to return
        """
        open_channels = views.get_channelstate_open(
            chain_state=views.state_from_raiden(self.raiden),
            payment_network_id=self.registry_address,
            token_address=self.token_address,
        )
        known = set(channel_state.partner_state.address for channel_state in open_channels)
        known.add(self.BOOTSTRAP_ADDR)
        known.add(self.raiden.address)

        participants_addresses = views.get_participants_addresses(
            views.state_from_raiden(self.raiden),
            self.registry_address,
            self.token_address,
        )

        available = participants_addresses - known
        new_partners = list(available)[:number]

        log.debug('found {} partners'.format(len(available)))

        return new_partners
 def __repr__(self) -> str:
     open_channels = views.get_channelstate_open(
         chain_state=views.state_from_raiden(self.raiden),
         payment_network_id=self.registry_address,
         token_address=self.token_address,
     )
     return f'{self.__class__.__name__}(target={self.initial_channel_target} ' +\
         f'channels={len(open_channels)}:{open_channels!r})'
    def leave(self, registry_address, only_receiving=True):
        """ Leave the token network.

        This implies closing all channels and waiting for all channels to be
        settled.

        Note: By default we're just discarding all channels for which we haven't
        received anything.  This potentially leaves deposits locked in channels after
        `closing`. This is "safe" from an accounting point of view (deposits
        can not be lost), but may still be undesirable from a liquidity point
        of view (deposits will only be freed after manually closing or after
        the partner closed the channel).

        If only_receiving is False then we close and settle all channels
        irrespective of them having received transfers or not.
        """
        with self.lock:
            self.initial_channel_target = 0

            if only_receiving:
                channels_to_close = views.get_channelstate_for_receiving(
                    views.state_from_raiden(self.raiden),
                    registry_address,
                    self.token_address,
                )
            else:
                channels_to_close = views.get_channelstate_open(
                    chain_state=views.state_from_raiden(self.raiden),
                    payment_network_id=registry_address,
                    token_address=self.token_address,
                )

            partner_addresses = [
                channel_state.partner_state.address
                for channel_state in channels_to_close
            ]
            self.api.channel_batch_close(
                registry_address,
                self.token_address,
                partner_addresses,
            )

            channel_ids = [
                channel_state.identifier
                for channel_state in channels_to_close
            ]

            waiting.wait_for_settle(
                self.raiden,
                registry_address,
                self.token_address,
                channel_ids,
                self.raiden.alarm.sleep_time,
            )

        return channels_to_close
    def retry_connect(self):
        """Will be called when new channels in the token network are detected.
        If the minimum number of channels was not yet established, it will try
        to open new channels.

        If the connection manager has no funds, this is a noop.
        """
        with self.lock:
            if self._funds_remaining <= 0 or self._leaving_state:
                return

            open_channels = views.get_channelstate_open(
                chain_state=views.state_from_raiden(self.raiden),
                payment_network_id=self.registry_address,
                token_address=self.token_address,
            )
            if len(open_channels) >= self.initial_channel_target:
                return

            self._open_channels()
    def _open_channels(self):
        """ Open channels until there are `self.initial_channel_target`
        channels open. Do nothing if there are enough channels open already.

        Note:
            - This method must be called with the lock held.
        """
        open_channels = views.get_channelstate_open(
            chain_state=views.state_from_raiden(self.raiden),
            payment_network_id=self.registry_address,
            token_address=self.token_address,
        )

        qty_channels_to_open = self.initial_channel_target - len(open_channels)
        if qty_channels_to_open <= 0:
            return

        for partner in self.find_new_partners(qty_channels_to_open):
            try:
                self.api.channel_open(
                    self.registry_address,
                    self.token_address,
                    partner,
                )
            except DuplicatedChannelError:
                # This can fail because of a race condition, where the channel
                # partner opens first.
                log.info('partner opened channel first')

            try:
                self.api.set_total_channel_deposit(
                    self.registry_address,
                    self.token_address,
                    partner,
                    self._initial_funding_per_partner,
                )
            except AddressWithoutCode:
                log.warn('connection manager: channel closed just after it was created')
            except TransactionThrew:
                log.exception('connection manager: deposit failed')
def log_open_channels(raiden, registry_address, token_address, funds):
    chain_state = views.state_from_raiden(raiden)
    open_channels = views.get_channelstate_open(
        chain_state=chain_state,
        payment_network_id=registry_address,
        token_address=token_address,
    )

    if open_channels:
        sum_deposits = views.get_our_capacity_for_token_network(
            views.state_from_raiden(raiden),
            registry_address,
            token_address,
        )
        log.debug(
            'connect() called on an already joined token network',
            registry_address=pex(registry_address),
            token_address=pex(token_address),
            open_channels=len(open_channels),
            sum_deposits=sum_deposits,
            funds=funds,
        )
Beispiel #7
0
    def get_connection_managers_info(self, registry_address):
        """Get a dict whose keys are token addresses and whose values are
        open channels, funds of last request, sum of deposits and number of channels"""
        connection_managers = dict()

        for token in self.raiden_api.get_tokens_list(registry_address):
            token_network_identifier = views.get_token_network_identifier_by_token_address(
                views.state_from_raiden(self.raiden_api.raiden),
                payment_network_id=registry_address,
                token_address=token,
            )

            try:
                connection_manager = self.raiden_api.raiden.connection_manager_for_token_network(
                    token_network_identifier,
                )
            except InvalidAddress:
                connection_manager = None

            open_channels = views.get_channelstate_open(
                chain_state=views.state_from_raiden(self.raiden_api.raiden),
                payment_network_id=registry_address,
                token_address=token,
            )
            if connection_manager is not None and open_channels:
                connection_managers[to_checksum_address(connection_manager.token_address)] = {
                    'funds': connection_manager.funds,
                    'sum_deposits': views.get_our_capacity_for_token_network(
                        views.state_from_raiden(self.raiden_api.raiden),
                        registry_address,
                        token,
                    ),
                    'channels': len(open_channels),
                }

        return connection_managers
Beispiel #8
0
    def _open_channels(self) -> bool:
        """ Open channels until there are `self.initial_channel_target`
        channels open. Do nothing if there are enough channels open already.

        Note:
            - This method must be called with the lock held.
        Return:
            - False if no channels could be opened
        """

        open_channels = views.get_channelstate_open(
            chain_state=views.state_from_raiden(self.raiden),
            payment_network_id=self.registry_address,
            token_address=self.token_address,
        )
        open_channels = [
            channel_state for channel_state in open_channels
            if channel_state.partner_state.address != self.BOOTSTRAP_ADDR
        ]
        funded_channels = [
            channel_state for channel_state in open_channels
            if channel_state.our_state.contract_balance >=
            self._initial_funding_per_partner
        ]
        nonfunded_channels = [
            channel_state for channel_state in open_channels
            if channel_state not in funded_channels
        ]
        possible_new_partners = self._find_new_partners()
        if possible_new_partners == 0:
            return False

        # if we already met our target, break
        if len(funded_channels) >= self.initial_channel_target:
            return False
        # if we didn't, but there's no nonfunded channels and no available partners
        # it means the network is smaller than our target, so we should also break
        if not nonfunded_channels and possible_new_partners == 0:
            return False

        n_to_join = self.initial_channel_target - len(funded_channels)
        nonfunded_partners = [
            channel_state.partner_state.address
            for channel_state in nonfunded_channels
        ]
        # first, fund nonfunded channels, then open and fund with possible_new_partners,
        # until initial_channel_target of funded channels is met
        join_partners = (nonfunded_partners +
                         possible_new_partners)[:n_to_join]

        log.debug(
            "Spawning greenlets to join partners",
            node=pex(self.raiden.address),
            num_greenlets=len(join_partners),
        )

        greenlets = set(
            gevent.spawn(self._join_partner, partner)
            for partner in join_partners)
        gevent.joinall(greenlets, raise_error=True)
        return True
Beispiel #9
0
def test_channelstate_filters():
    test_state = factories.make_chain_state(number_of_channels=5)
    chain_state = test_state.chain_state
    token_network_registry_address = test_state.token_network_registry_address
    token_address = test_state.token_address

    channel_open, channel_closing, channel_closed, channel_settling, channel_settled = (
        test_state.channels)
    in_progress = TransactionExecutionStatus(
        started_block_number=chain_state.block_number)
    done = TransactionExecutionStatus(
        started_block_number=chain_state.block_number,
        finished_block_number=chain_state.block_number,
        result=TransactionExecutionStatus.SUCCESS,
    )
    channel_closing.close_transaction = in_progress
    channel_closed.close_transaction = done
    channel_settling.close_transaction = done
    channel_settling.settle_transaction = in_progress
    channel_settled.close_transaction = done
    channel_settled.settle_transaction = done

    unknown_token = factories.make_address()
    assert (views.get_channelstate_open(
        chain_state=chain_state,
        token_network_registry_address=token_network_registry_address,
        token_address=unknown_token,
    ) == [])

    opened = views.get_channelstate_open(
        chain_state=chain_state,
        token_network_registry_address=token_network_registry_address,
        token_address=token_address,
    )
    assert opened == [channel_open]

    closing = views.get_channelstate_closing(
        chain_state=chain_state,
        token_network_registry_address=token_network_registry_address,
        token_address=token_address,
    )
    assert closing == [channel_closing]

    closed = views.get_channelstate_closed(
        chain_state=chain_state,
        token_network_registry_address=token_network_registry_address,
        token_address=token_address,
    )
    assert closed == [channel_closed]

    settling = views.get_channelstate_settling(
        chain_state=chain_state,
        token_network_registry_address=token_network_registry_address,
        token_address=token_address,
    )
    assert settling == [channel_settling]

    settled = views.get_channelstate_settled(
        chain_state=chain_state,
        token_network_registry_address=token_network_registry_address,
        token_address=token_address,
    )
    assert settled == [channel_settled]
Beispiel #10
0
    def _open_channels(self) -> bool:
        """ Open channels until there are `self.initial_channel_target`
        channels open. Do nothing if there are enough channels open already.

        Note:
            - This method must be called with the lock held.
        Return:
            - False if no channels could be opened
        """

        open_channels = views.get_channelstate_open(
            chain_state=views.state_from_raiden(self.raiden),
            token_network_registry_address=self.registry_address,
            token_address=self.token_address,
        )
        open_channels = [
            channel_state for channel_state in open_channels
            if channel_state.partner_state.address != self.BOOTSTRAP_ADDR
        ]
        funded_channels = [
            channel_state for channel_state in open_channels
            if channel_state.our_state.contract_balance >=
            self._initial_funding_per_partner
        ]
        nonfunded_channels = [
            channel_state for channel_state in open_channels
            if channel_state not in funded_channels
        ]
        possible_new_partners = self._find_new_partners()

        # if we already met our target, break
        if len(funded_channels) >= self.initial_channel_target:
            return False

        # if we didn't, but there's no nonfunded channels and no available partners
        # it means the network is smaller than our target, so we should also break
        if len(nonfunded_channels) == 0 and len(possible_new_partners) == 0:
            return False

        n_to_join = self.initial_channel_target - len(funded_channels)
        nonfunded_partners = [
            channel_state.partner_state.address
            for channel_state in nonfunded_channels
        ]
        # first, fund nonfunded channels, then open and fund with possible_new_partners,
        # until initial_channel_target of funded channels is met
        possible_partners = nonfunded_partners + possible_new_partners
        join_partners: List[Address] = []
        # Also filter the possible partners by excluding offline addresses
        for possible_partner in possible_partners:
            if len(join_partners) == n_to_join:
                break

            reachability = self.raiden.transport.force_check_address_reachability(
                possible_partner)
            if reachability == AddressReachability.REACHABLE:
                join_partners.append(possible_partner)

        log.debug(
            "Spawning greenlets to join partners",
            node=to_checksum_address(self.raiden.address),
            num_greenlets=len(join_partners),
        )

        greenlets = set(
            spawn_named(f"cm-join_partner-{to_checksum_address(partner)}",
                        self._join_partner, partner)
            for partner in join_partners)
        gevent.joinall(greenlets, raise_error=True)
        return True