Exemple #1
0
def next_channel_from_routes(
        available_routes: typing.List[RouteState],
        channelidentifiers_to_channels: ChannelMap,
        transfer_amount: typing.TokenAmount,
) -> typing.Optional[NettingChannelState]:
    """ Returns the first channel that can be used to start the transfer.
    The routing service can race with local changes, so the recommended routes
    must be validated.
    """
    for route in available_routes:
        channel_identifier = route.channel_identifier
        channel_state = channelidentifiers_to_channels.get(channel_identifier)

        if not channel_state:
            continue

        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            continue

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )
        if transfer_amount > distributable:
            continue

        if channel.is_valid_amount(channel_state.our_state, transfer_amount):
            return channel_state

    return None
Exemple #2
0
def next_channel_from_routes(
    available_routes: typing.List[RouteState],
    channelidentifiers_to_channels: typing.ChannelMap,
    transfer_amount: typing.TokenAmount,
) -> typing.Optional[NettingChannelState]:
    """ Returns the first channel that can be used to start the transfer.
    The routing service can race with local changes, so the recommended routes
    must be validated.
    """
    for route in available_routes:
        channel_identifier = route.channel_identifier
        channel_state = channelidentifiers_to_channels.get(channel_identifier)

        if not channel_state:
            continue

        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            continue

        pending_transfers = channel.get_number_of_pending_transfers(
            channel_state.our_state)
        if pending_transfers >= MAXIMUM_PENDING_TRANSFERS:
            continue

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )
        if transfer_amount > distributable:
            continue

        if channel.is_valid_amount(channel_state.our_state, transfer_amount):
            return channel_state

    return None
Exemple #3
0
def assert_balance(from_channel: NettingChannelState, balance: Balance,
                   locked: LockedAmount) -> None:
    """ Assert the from_channel overall token values. """
    assert balance >= 0
    assert locked >= 0

    distributable = balance - locked
    channel_distributable = channel.get_distributable(
        from_channel.our_state, from_channel.partner_state)
    channel_balance = channel.get_balance(from_channel.our_state,
                                          from_channel.partner_state)
    channel_locked_amount = channel.get_amount_locked(from_channel.our_state)

    msg = f"channel balance does not match. Expected: {balance} got: {channel_balance}"
    assert channel_balance == balance, msg

    msg = (f"channel distributable amount does not match. "
           f"Expected: {distributable} got: {channel_distributable}")
    assert channel_distributable == distributable, msg

    msg = f"channel locked amount does not match. Expected: {locked} got: {channel_locked_amount}"
    assert channel_locked_amount == locked, msg

    msg = (f"locked_amount ({locked}) + distributable ({distributable}) "
           f"did not equal the balance ({balance})")
    assert balance == locked + distributable, msg
def test_channelstate_directtransfer_overspent():
    """Receiving a direct transfer with an amount large than distributable must
    be ignored.
    """
    our_model1, _ = create_model(70)
    partner_model1, privkey2 = create_model(100)
    channel_state = create_channel_from_models(our_model1, partner_model1)

    distributable = channel.get_distributable(channel_state.partner_state, channel_state.our_state)

    nonce = 1
    transferred_amount = distributable + 1
    receive_lockedtransfer = make_receive_transfer_direct(
        channel_state,
        privkey2,
        nonce,
        transferred_amount,
    )

    is_valid, _ = channel.is_valid_directtransfer(
        receive_lockedtransfer,
        channel_state,
        channel_state.partner_state,
        channel_state.our_state,
    )
    assert not is_valid, 'message is invalid because it is spending more than the distributable'

    iteration = channel.handle_receive_directtransfer(
        channel_state,
        receive_lockedtransfer,
    )

    assert must_contain_entry(iteration.events, EventTransferReceivedInvalidDirectTransfer, {})
    assert_partner_state(channel_state.our_state, channel_state.partner_state, our_model1)
    assert_partner_state(channel_state.partner_state, channel_state.our_state, partner_model1)
Exemple #5
0
def run_smoketests(
    raiden_service: RaidenService,
    transport: str,
    token_addresses,
    discovery_address,
    debug: bool = False,
):
    """ Test that the assembled raiden_service correctly reflects the configuration from the
    smoketest_genesis. """
    try:
        chain = raiden_service.chain
        token_network_added_events = raiden_service.default_registry.filter_token_added_events(
        )
        events_token_addresses = [
            event['args']['token_address']
            for event in token_network_added_events
        ]

        assert events_token_addresses == token_addresses

        if transport == 'udp':
            discovery_addresses = list(chain.address_to_discovery.keys())
            assert len(discovery_addresses) == 1, repr(
                chain.address_to_discovery)
            assert discovery_addresses[0] == discovery_address
            discovery = chain.address_to_discovery[discovery_addresses[0]]
            assert discovery.endpoint_by_address(
                raiden_service.address) != TEST_ENDPOINT

        token_networks = views.get_token_network_addresses_for(
            views.state_from_raiden(raiden_service),
            raiden_service.default_registry.address,
        )
        assert len(token_networks) == 1

        channel_state = views.get_channelstate_for(
            views.state_from_raiden(raiden_service),
            raiden_service.default_registry.address,
            token_networks[0],
            decode_hex(TEST_PARTNER_ADDRESS),
        )

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )
        assert distributable == TEST_DEPOSIT_AMOUNT
        assert distributable == channel_state.our_state.contract_balance
        assert channel.get_status(channel_state) == CHANNEL_STATE_OPENED

        # Run API test
        run_restapi_smoketests()
    except:  # NOQA pylint: disable=bare-except
        error = traceback.format_exc()
        if debug:
            import pdb
            pdb.post_mortem()  # pylint: disable=no-member
        return error

    return None
Exemple #6
0
def assert_mirror(original, mirror):
    """ Assert that `mirror` has a correct `partner_state` to represent `original`."""
    original_locked_amount = channel.get_amount_locked(original.our_state)
    mirror_locked_amount = channel.get_amount_locked(mirror.partner_state)
    assert original_locked_amount == mirror_locked_amount

    balance0 = channel.get_balance(original.our_state, original.partner_state)
    balance1 = channel.get_balance(mirror.partner_state, mirror.our_state)
    assert balance0 == balance1

    balanceproof0 = channel.get_current_balanceproof(original.our_state)
    balanceproof1 = channel.get_current_balanceproof(mirror.partner_state)
    assert balanceproof0 == balanceproof1

    distributable0 = channel.get_distributable(original.our_state, original.partner_state)
    distributable1 = channel.get_distributable(mirror.partner_state, mirror.our_state)
    assert distributable0 == distributable1
Exemple #7
0
def assert_mirror(original, mirror):
    """ Assert that `mirror` has a correct `partner_state` to represent `original`."""
    original_locked_amount = channel.get_amount_locked(original.our_state)
    mirror_locked_amount = channel.get_amount_locked(mirror.partner_state)
    assert original_locked_amount == mirror_locked_amount

    balance0 = channel.get_balance(original.our_state, original.partner_state)
    balance1 = channel.get_balance(mirror.partner_state, mirror.our_state)
    assert balance0 == balance1

    balanceproof0 = channel.get_current_balanceproof(original.our_state)
    balanceproof1 = channel.get_current_balanceproof(mirror.partner_state)
    assert balanceproof0 == balanceproof1

    distributable0 = channel.get_distributable(original.our_state, original.partner_state)
    distributable1 = channel.get_distributable(mirror.partner_state, mirror.our_state)
    assert distributable0 == distributable1
Exemple #8
0
 def from_channel_state(cls, channel_state: NettingChannelState) -> "PFSCapacityUpdate":
     # pylint: disable=unexpected-keyword-arg
     return cls(
         canonical_identifier=channel_state.canonical_identifier,
         updating_participant=channel_state.our_state.address,
         other_participant=channel_state.partner_state.address,
         updating_nonce=channel.get_current_nonce(channel_state.our_state),
         other_nonce=channel.get_current_nonce(channel_state.partner_state),
         updating_capacity=channel.get_distributable(
             sender=channel_state.our_state, receiver=channel_state.partner_state
         ),
         other_capacity=channel.get_distributable(
             sender=channel_state.partner_state, receiver=channel_state.our_state
         ),
         reveal_timeout=channel_state.reveal_timeout,
         signature=EMPTY_SIGNATURE,
     )
Exemple #9
0
def events_for_refund_transfer(
        refund_channel,
        refund_transfer,
        pseudo_random_generator,
        timeout_blocks,
        block_number,
):
    """ Refund the transfer.
    Args:
        refund_route (RouteState): The original route that sent the mediated
            transfer to this node.
        refund_transfer (LockedTransferSignedState): The original mediated transfer
            from the refund_route.
        timeout_blocks (int): The number of blocks available from the /latest
            transfer/ received by this node, this transfer might be the
            original mediated transfer (if no route was available) or a refund
            transfer from a down stream node.
        block_number (int): The current block number.
    Returns:
        An empty list if there are not enough blocks to safely create a refund,
        or a list with a refund event."""
    # A refund transfer works like a special SendLockedTransfer, so it must
    # follow the same rules and decrement reveal_timeout from the
    # payee_transfer.
    new_lock_timeout = timeout_blocks - refund_channel.reveal_timeout

    distributable = channel.get_distributable(
        refund_channel.our_state,
        refund_channel.partner_state,
    )

    is_valid = (
        new_lock_timeout > 0 and
        refund_transfer.lock.amount <= distributable and
        channel.is_valid_amount(refund_channel.our_state, refund_transfer.lock.amount)
    )

    if is_valid:
        new_lock_expiration = new_lock_timeout + block_number

        message_identifier = message_identifier_from_prng(pseudo_random_generator)
        refund_transfer = channel.send_refundtransfer(
            refund_channel,
            refund_transfer.initiator,
            refund_transfer.target,
            refund_transfer.lock.amount,
            message_identifier,
            refund_transfer.payment_identifier,
            new_lock_expiration,
            refund_transfer.lock.secrethash,
        )

        return [refund_transfer]

    # Can not create a refund lock with a safe expiration, so don't do anything
    # and wait for the received lock to expire.
    return list()
Exemple #10
0
def run_smoketests(raiden_service: RaidenService, test_config: Dict, debug: bool = False):
    """ Test that the assembled raiden_service correctly reflects the configuration from the
    smoketest_genesis. """
    try:
        chain = raiden_service.chain
        assert (
            raiden_service.default_registry.address ==
            to_canonical_address(test_config['contracts']['registry_address'])
        )
        assert (
            raiden_service.default_secret_registry.address ==
            to_canonical_address(test_config['contracts']['secret_registry_address'])
        )

        token_network_added_events = raiden_service.default_registry.filter_token_added_events()
        token_addresses = [event['args']['token_address'] for event in token_network_added_events]

        assert token_addresses == [test_config['contracts']['token_address']]

        if test_config.get('transport') == 'udp':
            assert len(chain.address_to_discovery.keys()) == 1, repr(chain.address_to_discovery)
            assert (
                list(chain.address_to_discovery.keys())[0] ==
                to_canonical_address(test_config['contracts']['discovery_address'])
            )
            discovery = list(chain.address_to_discovery.values())[0]
            assert discovery.endpoint_by_address(raiden_service.address) != TEST_ENDPOINT

        token_networks = views.get_token_network_addresses_for(
            views.state_from_raiden(raiden_service),
            raiden_service.default_registry.address,
        )
        assert len(token_networks) == 1

        channel_state = views.get_channelstate_for(
            views.state_from_raiden(raiden_service),
            raiden_service.default_registry.address,
            token_networks[0],
            unhexlify(TEST_PARTNER_ADDRESS),
        )

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )
        assert distributable == TEST_DEPOSIT_AMOUNT
        assert distributable == channel_state.our_state.contract_balance
        assert channel.get_status(channel_state) == CHANNEL_STATE_OPENED

        # Run API test
        run_restapi_smoketests()
    except Exception:
        error = traceback.format_exc()
        if debug:
            import pdb
            pdb.post_mortem()
        return error
Exemple #11
0
def run_smoketests(raiden_service: RaidenService, test_config: Dict, debug: bool = False):
    """ Test that the assembled raiden_service correctly reflects the configuration from the
    smoketest_genesis. """
    try:
        chain = raiden_service.chain
        assert (
            raiden_service.default_registry.address ==
            to_canonical_address(test_config['contracts']['registry_address'])
        )
        assert (
            raiden_service.default_secret_registry.address ==
            to_canonical_address(test_config['contracts']['secret_registry_address'])
        )

        token_network_added_events = raiden_service.default_registry.filter_token_added_events()
        token_addresses = [event['args']['token_address'] for event in token_network_added_events]

        assert token_addresses == [test_config['contracts']['token_address']]

        if test_config.get('transport') == 'udp':
            assert len(chain.address_to_discovery.keys()) == 1, repr(chain.address_to_discovery)
            assert (
                list(chain.address_to_discovery.keys())[0] ==
                to_canonical_address(test_config['contracts']['discovery_address'])
            )
            discovery = list(chain.address_to_discovery.values())[0]
            assert discovery.endpoint_by_address(raiden_service.address) != TEST_ENDPOINT

        token_networks = views.get_token_network_addresses_for(
            views.state_from_raiden(raiden_service),
            raiden_service.default_registry.address,
        )
        assert len(token_networks) == 1

        channel_state = views.get_channelstate_for(
            views.state_from_raiden(raiden_service),
            raiden_service.default_registry.address,
            token_networks[0],
            unhexlify(TEST_PARTNER_ADDRESS),
        )

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )
        assert distributable == TEST_DEPOSIT_AMOUNT
        assert distributable == channel_state.our_state.contract_balance
        assert channel.get_status(channel_state) == CHANNEL_STATE_OPENED

        # Run API test
        run_restapi_smoketests()
    except Exception:
        error = traceback.format_exc()
        if debug:
            import pdb
            pdb.post_mortem()
        return error
Exemple #12
0
def events_for_refund_transfer(
    refund_channel,
    refund_transfer,
    pseudo_random_generator,
    timeout_blocks,
    block_number,
):
    """ Refund the transfer.
    Args:
        refund_route (RouteState): The original route that sent the mediated
            transfer to this node.
        refund_transfer (LockedTransferSignedState): The original mediated transfer
            from the refund_route.
        timeout_blocks (int): The number of blocks available from the /latest
            transfer/ received by this node, this transfer might be the
            original mediated transfer (if no route was available) or a refund
            transfer from a down stream node.
        block_number (int): The current block number.
    Returns:
        An empty list if there are not enough blocks to safely create a refund,
        or a list with a refund event."""
    # A refund transfer works like a special SendLockedTransfer, so it must
    # follow the same rules and decrement reveal_timeout from the
    # payee_transfer.
    new_lock_timeout = timeout_blocks - refund_channel.reveal_timeout

    distributable = channel.get_distributable(
        refund_channel.our_state,
        refund_channel.partner_state,
    )

    is_valid = (new_lock_timeout > 0
                and refund_transfer.lock.amount <= distributable
                and channel.is_valid_amount(refund_channel.our_state,
                                            refund_transfer.lock.amount))

    if is_valid:
        new_lock_expiration = new_lock_timeout + block_number

        message_identifier = message_identifier_from_prng(
            pseudo_random_generator)
        refund_transfer = channel.send_refundtransfer(
            refund_channel,
            refund_transfer.initiator,
            refund_transfer.target,
            refund_transfer.lock.amount,
            message_identifier,
            refund_transfer.payment_identifier,
            new_lock_expiration,
            refund_transfer.lock.secrethash,
        )

        return [refund_transfer]

    # Can not create a refund lock with a safe expiration, so don't do anything
    # and wait for the received lock to expire.
    return list()
Exemple #13
0
def assert_partner_state(end_state, partner_state, model):
    """Checks that the stored data for both ends correspond to the model."""
    assert end_state.address == model.participant_address
    assert channel.get_amount_locked(end_state) == model.amount_locked
    assert channel.get_balance(end_state, partner_state) == model.balance
    assert channel.get_distributable(end_state, partner_state) == model.distributable
    assert channel.get_next_nonce(end_state) == model.next_nonce
    assert set(end_state.merkletree.layers[LEAVES]) == set(model.merkletree_leaves)
    assert end_state.contract_balance == model.contract_balance
    def channel_state_invariants(self):
        """ Check the invariants for the channel state given in the Raiden specification """

        for netting_channel in self.address_to_channel.values():
            our_state = netting_channel.our_state
            partner_state = netting_channel.partner_state

            our_transferred_amount = 0
            if our_state.balance_proof:
                our_transferred_amount = our_state.balance_proof.transferred_amount
                assert our_transferred_amount >= 0

            partner_transferred_amount = 0
            if partner_state.balance_proof:
                partner_transferred_amount = partner_state.balance_proof.transferred_amount
                assert partner_transferred_amount >= 0

            assert channel.get_distributable(our_state, partner_state) >= 0
            assert channel.get_distributable(partner_state, our_state) >= 0

            our_deposit = netting_channel.our_total_deposit
            partner_deposit = netting_channel.partner_total_deposit
            total_deposit = our_deposit + partner_deposit

            our_amount_locked = channel.get_amount_locked(our_state)
            our_balance = channel.get_balance(our_state, partner_state)
            partner_amount_locked = channel.get_amount_locked(partner_state)
            partner_balance = channel.get_balance(partner_state, our_state)

            # invariant (5.1R), add withdrawn amounts when implemented
            assert 0 <= our_amount_locked <= our_balance
            assert 0 <= partner_amount_locked <= partner_balance
            assert our_amount_locked <= total_deposit
            assert partner_amount_locked <= total_deposit

            our_transferred = partner_transferred_amount - our_transferred_amount
            netted_transferred = our_transferred + partner_amount_locked - our_amount_locked

            # invariant (6R), add withdrawn amounts when implemented
            assert 0 <= our_deposit + our_transferred - our_amount_locked <= total_deposit
            assert 0 <= partner_deposit - our_transferred - partner_amount_locked <= total_deposit

            # invariant (7R), add withdrawn amounts when implemented
            assert - our_deposit <= netted_transferred <= partner_deposit
    def channel_state_invariants(self):
        """ Check the invariants for the channel state given in the Raiden specification """

        for netting_channel in self.address_to_channel.values():
            our_state = netting_channel.our_state
            partner_state = netting_channel.partner_state

            our_transferred_amount = 0
            if our_state.balance_proof:
                our_transferred_amount = our_state.balance_proof.transferred_amount
                assert our_transferred_amount >= 0

            partner_transferred_amount = 0
            if partner_state.balance_proof:
                partner_transferred_amount = partner_state.balance_proof.transferred_amount
                assert partner_transferred_amount >= 0

            assert channel.get_distributable(our_state, partner_state) >= 0
            assert channel.get_distributable(partner_state, our_state) >= 0

            our_deposit = netting_channel.our_total_deposit
            partner_deposit = netting_channel.partner_total_deposit
            total_deposit = our_deposit + partner_deposit

            our_amount_locked = channel.get_amount_locked(our_state)
            our_balance = channel.get_balance(our_state, partner_state)
            partner_amount_locked = channel.get_amount_locked(partner_state)
            partner_balance = channel.get_balance(partner_state, our_state)

            # invariant (5.1R), add withdrawn amounts when implemented
            assert 0 <= our_amount_locked <= our_balance
            assert 0 <= partner_amount_locked <= partner_balance
            assert our_amount_locked <= total_deposit
            assert partner_amount_locked <= total_deposit

            our_transferred = partner_transferred_amount - our_transferred_amount
            netted_transferred = our_transferred + partner_amount_locked - our_amount_locked

            # invariant (6R), add withdrawn amounts when implemented
            assert 0 <= our_deposit + our_transferred - our_amount_locked <= total_deposit
            assert 0 <= partner_deposit - our_transferred - partner_amount_locked <= total_deposit

            # invariant (7R), add withdrawn amounts when implemented
            assert -our_deposit <= netted_transferred <= partner_deposit
Exemple #16
0
def smoketest_perform_tests(
    raiden_service: RaidenService,
    transport: str,
    token_addresses,
    discovery_address,
):
    """ Perform high level tests designed to quickly discover broken functionality. """
    try:
        chain = raiden_service.chain
        token_network_added_events = raiden_service.default_registry.filter_token_added_events(
        )
        events_token_addresses = [
            event['args']['token_address']
            for event in token_network_added_events
        ]

        assert events_token_addresses == token_addresses

        if transport == 'udp':
            discovery_addresses = list(chain.address_to_discovery.keys())
            assert len(discovery_addresses) == 1, repr(
                chain.address_to_discovery)
            assert discovery_addresses[0] == discovery_address
            discovery = chain.address_to_discovery[discovery_addresses[0]]
            assert discovery.endpoint_by_address(
                raiden_service.address) != TEST_ENDPOINT

        token_networks = views.get_token_identifiers(
            views.state_from_raiden(raiden_service),
            raiden_service.default_registry.address,
        )
        assert len(token_networks) == 1

        channel_state = views.get_channelstate_for(
            views.state_from_raiden(raiden_service),
            raiden_service.default_registry.address,
            token_networks[0],
            decode_hex(TEST_PARTNER_ADDRESS),
        )

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )
        assert distributable == TEST_DEPOSIT_AMOUNT
        assert distributable == channel_state.our_state.contract_balance
        assert channel.get_status(channel_state) == CHANNEL_STATE_OPENED

        # Run API test
        run_restapi_smoketests()
    except:  # NOQA pylint: disable=bare-except
        error = traceback.format_exc()
        return error

    return None
Exemple #17
0
def next_channel_from_routes(
    available_routes: List['RouteState'],
    channelidentifiers_to_channels: Dict,
    transfer_amount: int,
    timeout_blocks: int,
) -> NettingChannelState:
    """ Returns the first route that may be used to mediated the transfer.
    The routing service can race with local changes, so the recommended routes
    must be validated.
    Args:
        available_routes: Current available routes that may be used, it's
            assumed that the available_routes list is ordered from best to
            worst.
        channelidentifiers_to_channels: Mapping from channel identifier
            to NettingChannelState.
        transfer_amount: The amount of tokens that will be transferred
            through the given route.
        timeout_blocks: Base number of available blocks used to compute
            the lock timeout.
    Returns:
        The next route.
    """
    for route in available_routes:
        channel_identifier = route.channel_identifier
        channel_state = channelidentifiers_to_channels.get(channel_identifier)

        if not channel_state:
            continue

        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            continue

        pending_transfers = channel.get_number_of_pending_transfers(
            channel_state.our_state)
        if pending_transfers >= MAXIMUM_PENDING_TRANSFERS:
            continue

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )
        if transfer_amount > distributable:
            continue

        lock_timeout = timeout_blocks - channel_state.reveal_timeout
        if lock_timeout <= 0:
            continue

        if channel.is_valid_amount(channel_state.our_state, transfer_amount):
            return channel_state

    return None
Exemple #18
0
def increase_transferred_amount(
    payment_network_identifier,
    from_channel,
    partner_channel,
    amount,
    pkey,
):
    # increasing the transferred amount by a value larger than distributable
    # would put one end of the channel in a negative balance, which is forbidden
    distributable_from_to = channel.get_distributable(
        from_channel.our_state,
        from_channel.partner_state,
    )
    assert distributable_from_to >= amount, 'operation would end up in a incosistent state'

    message_identifier = random.randint(0, UINT64_MAX)
    payment_identifier = 1
    registry_address = make_address()
    event = channel.send_directtransfer(
        registry_address,
        from_channel,
        amount,
        payment_identifier,
        message_identifier,
    )

    direct_transfer_message = DirectTransfer.from_event(event)
    address = privatekey_to_address(pkey)
    sign_key = PrivateKey(pkey)
    direct_transfer_message.sign(sign_key, address)

    # if this fails it's not the right key for the current `from_channel`
    assert direct_transfer_message.sender == from_channel.our_state.address

    balance_proof = balanceproof_from_envelope(direct_transfer_message)
    receive_direct = ReceiveTransferDirect(
        payment_network_identifier,
        from_channel.token_address,
        message_identifier,
        payment_identifier,
        balance_proof,
    )

    channel.handle_receive_directtransfer(
        partner_channel,
        receive_direct,
    )

    return direct_transfer_message
Exemple #19
0
def is_channel_usable(candidate_channel_state, transfer_amount, lock_timeout):
    pending_transfers = channel.get_number_of_pending_transfers(
        candidate_channel_state.our_state)
    distributable = channel.get_distributable(
        candidate_channel_state.our_state,
        candidate_channel_state.partner_state,
    )

    return (lock_timeout > 0 and channel.get_status(candidate_channel_state)
            == CHANNEL_STATE_OPENED
            and candidate_channel_state.settle_timeout >= lock_timeout
            and candidate_channel_state.reveal_timeout < lock_timeout
            and pending_transfers < MAXIMUM_PENDING_TRANSFERS
            and transfer_amount <= distributable and channel.is_valid_amount(
                candidate_channel_state.our_state, transfer_amount))
Exemple #20
0
def check_channel_constraints(
    channel_state: NettingChannelState,
    from_address: typing.InitiatorAddress,
    partner_address: typing.Address,
    amount: int,
    network_statuses: Dict[typing.Address, str],
    routing_module: str,
) -> bool:
    # check channel state
    if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
        log.info(
            'Channel is not opened, ignoring',
            from_address=pex(from_address),
            partner_address=pex(partner_address),
            routing_source=routing_module,
        )
        return False

    # check channel distributable
    distributable = channel.get_distributable(
        channel_state.our_state,
        channel_state.partner_state,
    )

    if amount > distributable:
        log.info(
            'Channel doesnt have enough funds, ignoring',
            from_address=pex(from_address),
            partner_address=pex(partner_address),
            amount=amount,
            distributable=distributable,
            routing_source=routing_module,
        )
        return False

    # check channel partner reachability
    network_state = network_statuses.get(partner_address, NODE_NETWORK_UNKNOWN)
    if network_state != NODE_NETWORK_REACHABLE:
        log.info(
            'Partner for channel isn\'t reachable, ignoring',
            from_address=pex(from_address),
            partner_address=pex(partner_address),
            status=network_state,
            routing_source=routing_module,
        )
        return False

    return True
Exemple #21
0
def check_channel_constraints(
        channel_state: NettingChannelState,
        from_address: typing.InitiatorAddress,
        partner_address: typing.Address,
        amount: int,
        network_statuses: Dict[typing.Address, str],
        routing_module: str,
) -> bool:
    # check channel state
    if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
        log.info(
            'Channel is not opened, ignoring',
            from_address=pex(from_address),
            partner_address=pex(partner_address),
            routing_source=routing_module,
        )
        return False

    # check channel distributable
    distributable = channel.get_distributable(
        channel_state.our_state,
        channel_state.partner_state,
    )

    if amount > distributable:
        log.info(
            'Channel doesnt have enough funds, ignoring',
            from_address=pex(from_address),
            partner_address=pex(partner_address),
            amount=amount,
            distributable=distributable,
            routing_source=routing_module,
        )
        return False

    # check channel partner reachability
    network_state = network_statuses.get(partner_address, NODE_NETWORK_UNKNOWN)
    if network_state != NODE_NETWORK_REACHABLE:
        log.info(
            'Partner for channel isn\'t reachable, ignoring',
            from_address=pex(from_address),
            partner_address=pex(partner_address),
            status=network_state,
            routing_source=routing_module,
        )
        return False

    return True
Exemple #22
0
def next_channel_from_routes(
        available_routes: List['RouteState'],
        channelidentifiers_to_channels: Dict,
        transfer_amount: int,
        timeout_blocks: int,
) -> NettingChannelState:
    """ Returns the first route that may be used to mediated the transfer.
    The routing service can race with local changes, so the recommended routes
    must be validated.
    Args:
        available_routes: Current available routes that may be used, it's
            assumed that the available_routes list is ordered from best to
            worst.
        channelidentifiers_to_channels: Mapping from channel identifier
            to NettingChannelState.
        transfer_amount: The amount of tokens that will be transferred
            through the given route.
        timeout_blocks: Base number of available blocks used to compute
            the lock timeout.
    Returns:
        The next route.
    """
    for route in available_routes:
        channel_identifier = route.channel_identifier
        channel_state = channelidentifiers_to_channels.get(channel_identifier)

        if not channel_state:
            continue

        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            continue

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )
        if transfer_amount > distributable:
            continue

        lock_timeout = timeout_blocks - channel_state.reveal_timeout
        if lock_timeout <= 0:
            continue

        if channel.is_valid_amount(channel_state.our_state, transfer_amount):
            return channel_state

    return None
Exemple #23
0
def assert_balance(from_channel, balance, locked):
    """ Assert the from_channel overall token values. """
    assert balance >= 0
    assert locked >= 0

    distributable = balance - locked
    channel_distributable = channel.get_distributable(
        from_channel.our_state,
        from_channel.partner_state,
    )

    assert channel.get_balance(from_channel.our_state, from_channel.partner_state) == balance
    assert channel_distributable == distributable
    assert channel.get_amount_locked(from_channel.our_state) == locked

    amount_locked = channel.get_amount_locked(from_channel.our_state)
    assert balance == amount_locked + distributable
Exemple #24
0
def assert_balance(from_channel, balance, locked):
    """ Assert the from_channel overall token values. """
    assert balance >= 0
    assert locked >= 0

    distributable = balance - locked
    channel_distributable = channel.get_distributable(
        from_channel.our_state,
        from_channel.partner_state,
    )

    assert channel.get_balance(from_channel.our_state, from_channel.partner_state) == balance
    assert channel_distributable == distributable
    assert channel.get_amount_locked(from_channel.our_state) == locked

    amount_locked = channel.get_amount_locked(from_channel.our_state)
    assert balance == amount_locked + distributable
Exemple #25
0
def run_smoketests(raiden_service, test_config, debug=False):
    """ Test that the assembled raiden_service correctly reflects the configuration from the
    smoketest_genesis. """
    try:
        chain = raiden_service.chain
        assert (
            raiden_service.default_registry.address == to_canonical_address(
                test_config['contracts']['registry_address']))
        assert (raiden_service.default_registry.token_addresses() == [
            to_canonical_address(test_config['contracts']['token_address'])
        ])
        assert len(chain.address_to_discovery.keys()) == 1
        assert (list(
            chain.address_to_discovery.keys())[0] == to_canonical_address(
                test_config['contracts']['discovery_address']))
        discovery = list(chain.address_to_discovery.values())[0]
        assert discovery.endpoint_by_address(
            raiden_service.address) != TEST_ENDPOINT

        token_networks = views.get_token_network_addresses_for(
            views.state_from_raiden(raiden_service),
            raiden_service.default_registry.address,
        )
        assert len(token_networks) == 1

        channel_state = views.get_channelstate_for(
            views.state_from_raiden(raiden_service),
            raiden_service.default_registry.address,
            token_networks[0],
            unhexlify(TEST_PARTNER_ADDRESS),
        )

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )
        assert distributable == TEST_DEPOSIT_AMOUNT
        assert distributable == channel_state.our_state.contract_balance
        assert channel.get_status(channel_state) == CHANNEL_STATE_OPENED
        run_restapi_smoketests(raiden_service, test_config)
    except Exception:
        error = traceback.format_exc()
        if debug:
            pdb.post_mortem()
        return error
Exemple #26
0
def channelstate_to_api_dict(channel_state):
    """Takes in a Channel Object and turns it into a dictionary for
    usage in the REST API. Decoding from binary to hex happens through
    the marshmallow AddressField in encoding.py.
    """
    from raiden.transfer import channel
    balance = channel.get_distributable(
        channel_state.our_state,
        channel_state.partner_state,
    )
    return {
        'channel_address': channel_state.identifier,
        'token_address': channel_state.token_address,
        'partner_address': channel_state.partner_state.address,
        'settle_timeout': channel_state.settle_timeout,
        'reveal_timeout': channel_state.reveal_timeout,
        'balance': balance,
        'state': channel.get_status(channel_state),
    }
def test_channelstate_lockedtransfer_invalid_chainid():
    """Receiving a locked transfer with chain_id different from the channel's
    chain_id should be ignored
    """
    our_model1, _ = create_model(70)
    partner_model1, privkey2 = create_model(100)
    channel_state = create_channel_from_models(our_model1, partner_model1)

    distributable = channel.get_distributable(channel_state.partner_state, channel_state.our_state)

    lock_amount = distributable - 1
    lock_expiration = 10
    lock_secrethash = sha3(b'test_channelstate_lockedtransfer_overspent')
    lock = HashTimeLockState(
        lock_amount,
        lock_expiration,
        lock_secrethash,
    )

    nonce = 1
    transferred_amount = 0
    receive_lockedtransfer = make_receive_transfer_mediated(
        channel_state,
        privkey2,
        nonce,
        transferred_amount,
        lock,
        chain_id=UNIT_CHAIN_ID + 1,
    )

    is_valid, _, _ = channel.handle_receive_lockedtransfer(
        channel_state,
        receive_lockedtransfer,
    )
    assert not is_valid, (
        'message is invalid because it uses different chain_id than the channel'
    )

    assert_partner_state(channel_state.our_state, channel_state.partner_state, our_model1)
    assert_partner_state(channel_state.partner_state, channel_state.our_state, partner_model1)
def test_channelstate_lockedtransfer_overspent():
    """Receiving a lock with an amount large than distributable must be
    ignored.
    """
    our_model1, _ = create_model(70)
    partner_model1, privkey2 = create_model(100)
    channel_state = create_channel_from_models(our_model1, partner_model1)

    distributable = channel.get_distributable(channel_state.partner_state,
                                              channel_state.our_state)

    lock_amount = distributable + 1
    lock_expiration = 10
    lock_secrethash = sha3(b'test_channelstate_lockedtransfer_overspent')
    lock = HashTimeLockState(
        lock_amount,
        lock_expiration,
        lock_secrethash,
    )

    nonce = 1
    transferred_amount = 0
    receive_lockedtransfer = make_receive_transfer_mediated(
        channel_state,
        privkey2,
        nonce,
        transferred_amount,
        lock,
    )

    is_valid, _, _ = channel.handle_receive_lockedtransfer(
        channel_state,
        receive_lockedtransfer,
    )
    assert not is_valid, 'message is invalid because it is spending more than the distributable'

    assert_partner_state(channel_state.our_state, channel_state.partner_state,
                         our_model1)
    assert_partner_state(channel_state.partner_state, channel_state.our_state,
                         partner_model1)
def test_channelstate_directtransfer_invalid_chainid():
    """Receiving a direct transfer with a chain_id different than the channel's
    chain_id should be ignored
    """
    our_model1, _ = create_model(70)
    partner_model1, privkey2 = create_model(100)
    channel_state = create_channel_from_models(our_model1, partner_model1)

    distributable = channel.get_distributable(channel_state.partner_state, channel_state.our_state)

    nonce = 1
    transferred_amount = distributable - 1
    receive_lockedtransfer = make_receive_transfer_direct(
        channel_state=channel_state,
        privkey=privkey2,
        nonce=nonce,
        transferred_amount=transferred_amount,
        chain_id=UNIT_CHAIN_ID + 2,
    )

    is_valid, _ = channel.is_valid_directtransfer(
        receive_lockedtransfer,
        channel_state,
        channel_state.partner_state,
        channel_state.our_state,
    )
    assert not is_valid, (
        'message is invalid because it contains different chain id than the channel'
    )

    iteration = channel.handle_receive_directtransfer(
        channel_state,
        receive_lockedtransfer,
    )

    assert must_contain_entry(iteration.events, EventTransferReceivedInvalidDirectTransfer, {})
    assert_partner_state(channel_state.our_state, channel_state.partner_state, our_model1)
    assert_partner_state(channel_state.partner_state, channel_state.our_state, partner_model1)
Exemple #30
0
def next_channel_from_routes(available_routes, channelidentifiers_to_channels, transfer_amount):
    """ Returns the first channel that can be used to start the transfer.
    The routing service can race with local changes, so the recommended routes
    must be validated.
    """
    for route in available_routes:
        channel_identifier = route.channel_identifier
        channel_state = channelidentifiers_to_channels.get(channel_identifier)

        if not channel_state:
            continue

        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            continue

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )
        if transfer_amount > distributable:
            continue

        return channel_state
Exemple #31
0
def get_best_routes(
    chain_state: ChainState,
    token_network_id: TokenNetworkID,
    one_to_n_address: Optional[Address],
    from_address: InitiatorAddress,
    to_address: TargetAddress,
    amount: PaymentAmount,
    previous_address: Optional[Address],
    config: Dict[str, Any],
    privkey: bytes,
) -> Tuple[List[RouteState], Optional[UUID]]:
    services_config = config.get("services", None)

    # the pfs should not be requested when the target is linked via a direct channel
    if to_address in views.all_neighbour_nodes(chain_state):
        neighbours = get_best_routes_internal(
            chain_state=chain_state,
            token_network_id=token_network_id,
            from_address=from_address,
            to_address=to_address,
            amount=amount,
            previous_address=previous_address,
        )
        channel_state = views.get_channelstate_by_token_network_and_partner(
            chain_state=chain_state,
            token_network_id=token_network_id,
            partner_address=Address(to_address),
        )

        for route_state in neighbours:
            if to_address == route_state.node_address and (
                    channel_state
                    # other conditions about e.g. channel state are checked in best routes internal
                    and channel.get_distributable(
                        sender=channel_state.our_state,
                        receiver=channel_state.partner_state) >= amount):
                return [route_state], None

    if (services_config
            and services_config["pathfinding_service_address"] is not None
            and one_to_n_address is not None):
        pfs_answer_ok, pfs_routes, pfs_feedback_token = get_best_routes_pfs(
            chain_state=chain_state,
            token_network_id=token_network_id,
            one_to_n_address=one_to_n_address,
            from_address=from_address,
            to_address=to_address,
            amount=amount,
            previous_address=previous_address,
            config=services_config,
            privkey=privkey,
        )

        if pfs_answer_ok:
            log.info("Received route(s) from PFS",
                     routes=pfs_routes,
                     feedback_token=pfs_feedback_token)
            return pfs_routes, pfs_feedback_token
        else:
            log.warning("Request to Pathfinding Service was not successful, "
                        "falling back to internal routing.")

    return (
        get_best_routes_internal(
            chain_state=chain_state,
            token_network_id=token_network_id,
            from_address=from_address,
            to_address=to_address,
            amount=amount,
            previous_address=previous_address,
        ),
        None,
    )
 def _available_amount(self, partner_address):
     netting_channel = self.address_to_channel[partner_address]
     return channel.get_distributable(netting_channel.our_state, netting_channel.partner_state)
Exemple #33
0
 def get_balance(channel_state):
     return channel.get_distributable(channel_state.our_state,
                                      channel_state.partner_state)
Exemple #34
0
def get_best_routes(
    chain_state: ChainState,
    token_network_address: TokenNetworkAddress,
    one_to_n_address: Optional[OneToNAddress],
    from_address: InitiatorAddress,
    to_address: TargetAddress,
    amount: PaymentAmount,
    previous_address: Optional[Address],
    pfs_config: Optional[PFSConfig],
    privkey: bytes,
) -> Tuple[Optional[str], List[RouteState], Optional[UUID]]:

    token_network = views.get_token_network_by_address(chain_state,
                                                       token_network_address)
    assert token_network, "The token network must be validated and exist."

    try:
        # networkx returns a generator, consume the result since it will be
        # iterated over multiple times.
        all_neighbors = list(
            networkx.all_neighbors(token_network.network_graph.network,
                                   from_address))
    except networkx.NetworkXError:
        # If `our_address` is not in the graph, no channels opened with the
        # address.
        log.debug(
            "Node does not have a channel in the requested token network.",
            source=to_checksum_address(from_address),
            target=to_checksum_address(to_address),
            amount=amount,
        )
        return ("Node does not have a channel in the requested token network.",
                list(), None)

    error_closed = 0
    error_no_route = 0
    error_no_capacity = 0
    error_not_online = 0
    error_direct = None
    shortest_routes: List[Neighbour] = list()

    # Always use a direct channel if available:
    # - There are no race conditions and the capacity is guaranteed to be
    #   available.
    # - There will be no mediation fees
    # - The transfer will be faster
    if to_address in all_neighbors:
        for channel_id in token_network.partneraddresses_to_channelidentifiers[
                Address(to_address)]:
            channel_state = token_network.channelidentifiers_to_channels[
                channel_id]

            # direct channels don't have fees
            payment_with_fee_amount = PaymentWithFeeAmount(amount)
            is_usable = channel.is_channel_usable_for_new_transfer(
                channel_state, payment_with_fee_amount, None)

            if is_usable is channel.ChannelUsability.USABLE:
                direct_route = RouteState(
                    route=[Address(from_address),
                           Address(to_address)],
                    forward_channel_id=channel_state.canonical_identifier.
                    channel_identifier,
                    estimated_fee=FeeAmount(0),
                )
                return (None, [direct_route], None)

            error_direct = is_usable

    latest_channel_opened_at = BlockNumber(0)
    for partner_address in all_neighbors:
        for channel_id in token_network.partneraddresses_to_channelidentifiers[
                partner_address]:
            channel_state = token_network.channelidentifiers_to_channels[
                channel_id]

            if channel.get_status(channel_state) != ChannelState.STATE_OPENED:
                error_closed += 1
                continue

            latest_channel_opened_at = max(
                latest_channel_opened_at,
                channel_state.open_transaction.finished_block_number)

            try:
                route = networkx.shortest_path(
                    token_network.network_graph.network, partner_address,
                    to_address)
            except (networkx.NetworkXNoPath, networkx.NodeNotFound):
                error_no_route += 1
            else:
                distributable = channel.get_distributable(
                    channel_state.our_state, channel_state.partner_state)

                network_status = views.get_node_network_status(
                    chain_state, channel_state.partner_state.address)

                if distributable < amount:
                    error_no_capacity += 1
                elif network_status != NetworkState.REACHABLE:
                    error_not_online += 1
                else:
                    nonrefundable = amount > channel.get_distributable(
                        channel_state.partner_state, channel_state.our_state)

                    # The complete route includes the initiator, add it to the beginning
                    complete_route = [Address(from_address)] + route
                    neighbour = Neighbour(
                        length=len(route),
                        nonrefundable=nonrefundable,
                        partner_address=partner_address,
                        channelid=channel_state.identifier,
                        route=complete_route,
                    )
                    heappush(shortest_routes, neighbour)

    if not shortest_routes:
        qty_channels = sum(
            len(token_network.
                partneraddresses_to_channelidentifiers[partner_address])
            for partner_address in all_neighbors)
        error_msg = (
            f"None of the existing channels could be used to complete the "
            f"transfer. From the {qty_channels} existing channels. "
            f"{error_closed} are closed. {error_not_online} are not online. "
            f"{error_no_route} don't have a route to the target in the given "
            f"token network. {error_no_capacity} don't have enough capacity for "
            f"the requested transfer.")
        if error_direct is not None:
            error_msg += f"direct channel {error_direct}."

        log.warning(
            "None of the existing channels could be used to complete the transfer",
            from_address=to_checksum_address(from_address),
            to_address=to_checksum_address(to_address),
            error_closed=error_closed,
            error_no_route=error_no_route,
            error_no_capacity=error_no_capacity,
            error_direct=error_direct,
            error_not_online=error_not_online,
        )
        return (error_msg, list(), None)

    if pfs_config is not None and one_to_n_address is not None:
        pfs_error_msg, pfs_routes, pfs_feedback_token = get_best_routes_pfs(
            chain_state=chain_state,
            token_network_address=token_network_address,
            one_to_n_address=one_to_n_address,
            from_address=from_address,
            to_address=to_address,
            amount=amount,
            previous_address=previous_address,
            pfs_config=pfs_config,
            privkey=privkey,
            pfs_wait_for_block=latest_channel_opened_at,
        )

        if not pfs_error_msg:
            # As of version 0.5 it is possible for the PFS to return an empty
            # list of routes without an error message.
            if not pfs_routes:
                return ("PFS could not find any routes", list(), None)

            log.info("Received route(s) from PFS",
                     routes=pfs_routes,
                     feedback_token=pfs_feedback_token)
            return (pfs_error_msg, pfs_routes, pfs_feedback_token)

        log.warning(
            "Request to Pathfinding Service was not successful. "
            "No routes to the target are found.",
            pfs_message=pfs_error_msg,
        )
        return (pfs_error_msg, list(), None)

    else:
        available_routes = list()

        while shortest_routes:
            neighbour = heappop(shortest_routes)

            # https://github.com/raiden-network/raiden/issues/4751
            # Internal routing doesn't know how much fees the initiator will be charged,
            # so it should set a percentage on top of the original amount
            # for the whole route.
            estimated_fee = FeeAmount(
                round(INTERNAL_ROUTING_DEFAULT_FEE_PERC * amount))
            if neighbour.length == 1:  # Target is our direct neighbour, pay no fees.
                estimated_fee = FeeAmount(0)

            available_routes.append(
                RouteState(
                    route=neighbour.route,
                    forward_channel_id=neighbour.channelid,
                    estimated_fee=estimated_fee,
                ))

        return (None, available_routes, None)
 def _available_amount(self, partner_address):
     netting_channel = self.address_to_channel[partner_address]
     return channel.get_distributable(netting_channel.our_state,
                                      netting_channel.partner_state)
Exemple #36
0
def get_best_routes_internal(
        chain_state: ChainState,
        token_network_id: typing.TokenNetworkID,
        from_address: typing.InitiatorAddress,
        to_address: typing.TargetAddress,
        amount: int,
        previous_address: typing.Optional[typing.Address],
) -> List[RouteState]:
    """ Returns a list of channels that can be used to make a transfer.

    This will filter out channels that are not open and don't have enough
    capacity.
    """
    # TODO: Route ranking.
    # Rate each route to optimize the fee price/quality of each route and add a
    # rate from in the range [0.0,1.0].

    available_routes = list()

    token_network = views.get_token_network_by_identifier(
        chain_state,
        token_network_id,
    )

    network_statuses = views.get_networkstatuses(chain_state)

    neighbors_heap = list()
    try:
        all_neighbors = networkx.all_neighbors(token_network.network_graph.network, from_address)
    except networkx.NetworkXError:
        # If `our_address` is not in the graph, no channels opened with the
        # address
        return list()

    for partner_address in all_neighbors:
        # don't send the message backwards
        if partner_address == previous_address:
            continue

        channel_state = views.get_channelstate_by_token_network_and_partner(
            chain_state,
            token_network_id,
            partner_address,
        )

        channel_constraints_fulfilled = check_channel_constraints(
            channel_state=channel_state,
            from_address=from_address,
            partner_address=partner_address,
            amount=amount,
            network_statuses=network_statuses,
            routing_module='Internal Routing',
        )
        if not channel_constraints_fulfilled:
            continue

        nonrefundable = amount > channel.get_distributable(
            channel_state.partner_state,
            channel_state.our_state,
        )

        try:
            length = networkx.shortest_path_length(
                token_network.network_graph.network,
                partner_address,
                to_address,
            )
            heappush(
                neighbors_heap,
                (length, nonrefundable, partner_address, channel_state.identifier),
            )
        except (networkx.NetworkXNoPath, networkx.NodeNotFound):
            pass

    if not neighbors_heap:
        log.warning(
            'No routes available',
            from_address=pex(from_address),
            to_address=pex(to_address),
        )
        return list()

    while neighbors_heap:
        *_, partner_address, channel_state_id = heappop(neighbors_heap)
        route_state = RouteState(partner_address, channel_state_id)
        available_routes.append(route_state)
    return available_routes
Exemple #37
0
 def get_balance(self, channel_state):  # pylint: disable=no-self-use
     return channel.get_distributable(
         channel_state.our_state,
         channel_state.partner_state,
     )
Exemple #38
0
def get_best_routes_internal(
        chain_state: ChainState,
        token_network_id: typing.TokenNetworkID,
        from_address: typing.InitiatorAddress,
        to_address: typing.TargetAddress,
        amount: int,
        previous_address: typing.Optional[typing.Address],
) -> List[RouteState]:
    """ Returns a list of channels that can be used to make a transfer.

    This will filter out channels that are not open and don't have enough
    capacity.
    """
    # TODO: Route ranking.
    # Rate each route to optimize the fee price/quality of each route and add a
    # rate from in the range [0.0,1.0].

    available_routes = list()

    token_network = views.get_token_network_by_identifier(
        chain_state,
        token_network_id,
    )

    neighbors_heap = list()
    try:
        all_neighbors = networkx.all_neighbors(token_network.network_graph.network, from_address)
    except networkx.NetworkXError:
        # If `our_address` is not in the graph, no channels opened with the
        # address
        return list()

    for partner_address in all_neighbors:
        # don't send the message backwards
        if partner_address == previous_address:
            continue

        channel_state = views.get_channelstate_by_token_network_and_partner(
            chain_state,
            token_network_id,
            partner_address,
        )

        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            log.info(
                'Channel is not opened, ignoring',
                from_address=pex(from_address),
                partner_address=pex(partner_address),
                routing_source='Internal Routing',
            )
            continue

        nonrefundable = amount > channel.get_distributable(
            channel_state.partner_state,
            channel_state.our_state,
        )

        try:
            length = networkx.shortest_path_length(
                token_network.network_graph.network,
                partner_address,
                to_address,
            )
            heappush(
                neighbors_heap,
                (length, nonrefundable, partner_address, channel_state.identifier),
            )
        except (networkx.NetworkXNoPath, networkx.NodeNotFound):
            pass

    if not neighbors_heap:
        log.warning(
            'No routes available',
            from_address=pex(from_address),
            to_address=pex(to_address),
        )
        return list()

    while neighbors_heap:
        *_, partner_address, channel_state_id = heappop(neighbors_heap)
        route_state = RouteState(partner_address, channel_state_id)
        available_routes.append(route_state)
    return available_routes
Exemple #39
0
def test_channelstate_lockedtransfer_overspend_with_multiple_pending_transfers():
    """Receiving a concurrent lock with an amount large than distributable
    must be ignored.
    """
    our_model1, _ = create_model(70)
    partner_model1, privkey2 = create_model(100)
    channel_state = create_channel_from_models(our_model1, partner_model1)

    # Step 1: Create a lock with an amount of 1
    # - this wont be unlocked
    lock1_amount = 1
    lock1_expiration = 1 + channel_state.settle_timeout
    lock1_secrethash = sha3(b'test_receive_cannot_overspend_with_multiple_pending_transfers1')
    lock1 = HashTimeLockState(
        lock1_amount,
        lock1_expiration,
        lock1_secrethash,
    )

    nonce1 = 1
    transferred_amount = 0
    receive_lockedtransfer1 = make_receive_transfer_mediated(
        channel_state,
        privkey2,
        nonce1,
        transferred_amount,
        lock1,
    )

    is_valid, msg = channel.handle_receive_lockedtransfer(
        channel_state,
        receive_lockedtransfer1,
    )
    assert is_valid, msg

    our_model2 = our_model1
    partner_model2 = partner_model1._replace(
        distributable=partner_model1.distributable - lock1.amount,
        amount_locked=lock1.amount,
        next_nonce=2,
        merkletree_leaves=[lock1.lockhash],
    )

    # The valid transfer is handled normally
    assert_partner_state(channel_state.our_state, channel_state.partner_state, our_model2)
    assert_partner_state(channel_state.partner_state, channel_state.our_state, partner_model2)

    # Step 2: Create a lock with the current *distributable + 1*
    # - This must be ignored
    distributable = channel.get_distributable(channel_state.partner_state, channel_state.our_state)
    lock2_amount = distributable + 1
    lock2_expiration = channel_state.settle_timeout
    lock2_secrethash = sha3(b'test_receive_cannot_overspend_with_multiple_pending_transfers2')
    lock2 = HashTimeLockState(
        lock2_amount,
        lock2_expiration,
        lock2_secrethash,
    )
    leaves = [lock1.lockhash, lock2.lockhash]

    nonce2 = 2
    receive_lockedtransfer2 = make_receive_transfer_mediated(
        channel_state,
        privkey2,
        nonce2,
        transferred_amount,
        lock2,
        merkletree_leaves=leaves,
    )

    is_valid, msg = channel.handle_receive_lockedtransfer(
        channel_state,
        receive_lockedtransfer2,
    )
    assert not is_valid, 'message is invalid because its expending more than the distributable'

    # The overspending transfer must be ignored
    assert_partner_state(channel_state.our_state, channel_state.partner_state, our_model2)
    assert_partner_state(channel_state.partner_state, channel_state.our_state, partner_model2)
Exemple #40
0
 def get_balance(self, channel_state):  # pylint: disable=no-self-use
     return channel.get_distributable(
         channel_state.our_state,
         channel_state.partner_state,
     )
Exemple #41
0
def get_best_routes(
        node_state: NodeState,
        token_network_id: typing.Address,
        from_address: typing.Address,
        to_address: typing.Address,
        amount: int,
        previous_address: typing.Address,
) -> List[RouteState]:
    """ Returns a list of channels that can be used to make a transfer.

    This will filter out channels that are not open and don't have enough
    capacity.
    """
    # TODO: Route ranking.
    # Rate each route to optimize the fee price/quality of each route and add a
    # rate from in the range [0.0,1.0].

    available_routes = list()

    token_network = views.get_token_network_by_identifier(
        node_state,
        token_network_id,
    )

    network_statuses = views.get_networkstatuses(node_state)

    neighbors_heap = get_ordered_partners(
        token_network.network_graph.network,
        from_address,
        to_address,
    )

    if not neighbors_heap:
        log.warning(
            'No routes available from %s to %s' % (pex(from_address), pex(to_address)),
        )

    while neighbors_heap:
        _, partner_address = heappop(neighbors_heap)

        channel_state = views.get_channelstate_by_token_network_and_partner(
            node_state,
            token_network_id,
            partner_address,
        )

        # don't send the message backwards
        if partner_address == previous_address:
            continue

        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            log.info(
                'channel %s - %s is not opened, ignoring' %
                (pex(from_address), pex(partner_address)),
            )
            continue

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )

        if amount > distributable:
            log.info(
                'channel %s - %s doesnt have enough funds [%s], ignoring' %
                (pex(from_address), pex(partner_address), amount),
            )
            continue

        network_state = network_statuses.get(partner_address, NODE_NETWORK_UNKNOWN)
        if network_state != NODE_NETWORK_REACHABLE:
            log.info(
                'partner for channel %s - %s is not %s, ignoring' %
                (pex(from_address), pex(partner_address), NODE_NETWORK_REACHABLE),
            )
            continue

        route_state = RouteState(partner_address, channel_state.identifier)
        available_routes.append(route_state)

    return available_routes
Exemple #42
0
def get_best_routes(
        chain_state: ChainState,
        token_network_id: typing.Address,
        from_address: typing.Address,
        to_address: typing.Address,
        amount: int,
        previous_address: typing.Address,
) -> List[RouteState]:
    """ Returns a list of channels that can be used to make a transfer.

    This will filter out channels that are not open and don't have enough
    capacity.
    """
    # TODO: Route ranking.
    # Rate each route to optimize the fee price/quality of each route and add a
    # rate from in the range [0.0,1.0].

    available_routes = list()

    token_network = views.get_token_network_by_identifier(
        chain_state,
        token_network_id,
    )

    network_statuses = views.get_networkstatuses(chain_state)

    neighbors_heap = get_ordered_partners(
        token_network.network_graph.network,
        from_address,
        to_address,
    )

    if not neighbors_heap:
        log.warning(
            'No routes available from %s to %s' % (pex(from_address), pex(to_address)),
        )

    while neighbors_heap:
        _, partner_address = heappop(neighbors_heap)

        channel_state = views.get_channelstate_by_token_network_and_partner(
            chain_state,
            token_network_id,
            partner_address,
        )

        # don't send the message backwards
        if partner_address == previous_address:
            continue

        if channel.get_status(channel_state) != CHANNEL_STATE_OPENED:
            log.info(
                'channel %s - %s is not opened, ignoring' %
                (pex(from_address), pex(partner_address)),
            )
            continue

        distributable = channel.get_distributable(
            channel_state.our_state,
            channel_state.partner_state,
        )

        if amount > distributable:
            log.info(
                'channel %s - %s doesnt have enough funds [%s], ignoring' %
                (pex(from_address), pex(partner_address), amount),
            )
            continue

        network_state = network_statuses.get(partner_address, NODE_NETWORK_UNKNOWN)
        if network_state != NODE_NETWORK_REACHABLE:
            log.info(
                'partner for channel %s - %s is not %s, ignoring' %
                (pex(from_address), pex(partner_address), NODE_NETWORK_REACHABLE),
            )
            continue

        route_state = RouteState(partner_address, channel_state.identifier)
        available_routes.append(route_state)

    return available_routes