Ejemplo n.º 1
0
def test_channel_deposit(raiden_chain, deposit, retry_timeout,
                         token_addresses):
    app0, app1 = raiden_chain
    token_address = token_addresses[0]

    registry_address = app0.raiden.default_registry.address
    token_network_address = views.get_token_network_address_by_token_address(
        views.state_from_app(app0), app0.raiden.default_registry.address,
        token_address)
    assert token_network_address

    channel0 = views.get_channelstate_by_token_network_and_partner(
        views.state_from_app(app0), token_network_address, app1.raiden.address)
    channel1 = views.get_channelstate_by_token_network_and_partner(
        views.state_from_app(app0), token_network_address, app1.raiden.address)
    assert channel0 is None
    assert channel1 is None

    RaidenAPI(app0.raiden).channel_open(registry_address, token_address,
                                        app1.raiden.address)

    timeout_seconds = 15
    exception = RuntimeError(
        f"Did not see the channels open within {timeout_seconds} seconds")
    with gevent.Timeout(seconds=timeout_seconds, exception=exception):
        wait_both_channel_open(app0, app1, registry_address, token_address,
                               retry_timeout)

    assert_synced_channel_state(token_network_address, app0, Balance(0), [],
                                app1, Balance(0), [])

    RaidenAPI(app0.raiden).set_total_channel_deposit(registry_address,
                                                     token_address,
                                                     app1.raiden.address,
                                                     deposit)

    exception = RuntimeError(
        f"Did not see the channel deposit within {timeout_seconds} seconds")
    with gevent.Timeout(seconds=timeout_seconds, exception=exception):
        waiting.wait_single_channel_deposit(app0, app1, registry_address,
                                            token_address, deposit,
                                            retry_timeout)

    assert_synced_channel_state(token_network_address, app0, deposit, [], app1,
                                Balance(0), [])

    RaidenAPI(app1.raiden).set_total_channel_deposit(registry_address,
                                                     token_address,
                                                     app0.raiden.address,
                                                     deposit)

    with gevent.Timeout(seconds=timeout_seconds, exception=exception):
        waiting.wait_single_channel_deposit(app1, app0, registry_address,
                                            token_address, deposit,
                                            retry_timeout)

    assert_synced_channel_state(token_network_address, app0, deposit, [], app1,
                                deposit, [])
Ejemplo n.º 2
0
def check_channel(
    app1: App,
    app2: App,
    token_network_address: TokenNetworkAddress,
    settle_timeout: BlockTimeout,
    deposit_amount: TokenAmount,
) -> None:
    channel_state1 = get_channelstate_by_token_network_and_partner(
        chain_state=state_from_raiden(app1.raiden),
        token_network_address=token_network_address,
        partner_address=app2.raiden.address,
    )
    assert channel_state1, "app1 does not have a channel with app2."
    netcontract1 = app1.raiden.proxy_manager.payment_channel(
        channel_state=channel_state1, block_identifier=BLOCK_ID_LATEST)

    channel_state2 = get_channelstate_by_token_network_and_partner(
        chain_state=state_from_raiden(app2.raiden),
        token_network_address=token_network_address,
        partner_address=app1.raiden.address,
    )
    assert channel_state2, "app2 does not have a channel with app1."
    netcontract2 = app2.raiden.proxy_manager.payment_channel(
        channel_state=channel_state2, block_identifier=BLOCK_ID_LATEST)

    # Check a valid settle timeout was used, the netting contract has an
    # enforced minimum and maximum
    assert settle_timeout == netcontract1.settle_timeout()
    assert settle_timeout == netcontract2.settle_timeout()

    if deposit_amount > 0:
        assert netcontract1.can_transfer(BLOCK_ID_LATEST)
        assert netcontract2.can_transfer(BLOCK_ID_LATEST)

    app1_details = netcontract1.detail(BLOCK_ID_LATEST)
    app2_details = netcontract2.detail(BLOCK_ID_LATEST)

    assert (app1_details.participants_data.our_details.address ==
            app2_details.participants_data.partner_details.address)
    assert (app1_details.participants_data.partner_details.address ==
            app2_details.participants_data.our_details.address)

    assert (app1_details.participants_data.our_details.deposit ==
            app2_details.participants_data.partner_details.deposit)
    assert (app1_details.participants_data.partner_details.deposit ==
            app2_details.participants_data.our_details.deposit)
    assert app1_details.chain_id == app2_details.chain_id

    assert app1_details.participants_data.our_details.deposit == deposit_amount
    assert app1_details.participants_data.partner_details.deposit == deposit_amount
    assert app2_details.participants_data.our_details.deposit == deposit_amount
    assert app2_details.participants_data.partner_details.deposit == deposit_amount
    assert app2_details.chain_id == UNIT_CHAIN_ID
Ejemplo n.º 3
0
def handle_processed(
        chain_state: ChainState,
        state_change: ReceiveProcessed,
) -> TransitionResult:
    events = list()

    for queue in chain_state.queueids_to_queues.values():
        for message in queue:
            message_found = (
                message.message_identifier == state_change.message_identifier and
                message.recipient == state_change.sender
            )
            if message_found:
                if type(message) == SendDirectTransfer:
                    channel_state = views.get_channelstate_by_token_network_and_partner(
                        chain_state,
                        message.balance_proof.token_network_identifier,
                        message.recipient,
                    )
                    events.append(EventPaymentSentSuccess(
                        channel_state.payment_network_identifier,
                        channel_state.token_network_identifier,
                        message.payment_identifier,
                        message.balance_proof.transferred_amount,
                        message.recipient,
                    ))

    # Clean up message queue
    for queueid in list(chain_state.queueids_to_queues.keys()):
        inplace_delete_message_queue(chain_state, state_change, queueid)

    return TransitionResult(chain_state, events)
Ejemplo n.º 4
0
def replay_wal(storage, token_network_identifier, partner_address):
    all_state_changes = storage.get_statechanges_by_identifier(
        from_identifier=0,
        to_identifier='latest',
    )

    state_manager = StateManager(state_transition=node.state_transition,
                                 current_state=None)
    wal = WriteAheadLog(state_manager, storage)

    for _, state_change in enumerate(all_state_changes):
        events = wal.state_manager.dispatch(state_change)

        chain_state = wal.state_manager.current_state

        channel_state = views.get_channelstate_by_token_network_and_partner(
            chain_state,
            to_canonical_address(token_network_identifier),
            to_canonical_address(partner_address),
        )

        if not channel_state:
            continue

        ###
        # Customize this to filter things further somewhere around here.
        # An example would be to add `import pdb; pdb.set_trace()`
        # and inspect the state.
        ###

        print_state_change(state_change)
        print_events(events)
Ejemplo n.º 5
0
def run_test_create_monitoring_request(raiden_network, token_addresses):
    app0, app1 = raiden_network
    token_address = token_addresses[0]
    chain_state = views.state_from_app(app0)
    payment_network_id = app0.raiden.default_registry.address
    token_network_identifier = views.get_token_network_identifier_by_token_address(
        chain_state=chain_state,
        payment_network_id=payment_network_id,
        token_address=token_address,
    )

    payment_identifier = create_default_identifier()
    transfer(
        initiator_app=app1,
        target_app=app0,
        token_address=token_address,
        amount=1,
        identifier=payment_identifier,
    )
    chain_state = views.state_from_raiden(app0.raiden)
    channel_state = views.get_channelstate_by_token_network_and_partner(
        chain_state,
        token_network_identifier,
        app1.raiden.address,
    )
    balance_proof = channel_state.partner_state.balance_proof
    api = RaidenAPI(app0.raiden)
    request = api.create_monitoring_request(
        balance_proof=balance_proof,
        reward_amount=1,
    )
    assert request
    as_dict = request.to_dict()
    from_dict = RequestMonitoring.from_dict(as_dict)
    assert from_dict.to_dict() == as_dict
Ejemplo n.º 6
0
def direct_transfer(
        initiator_app,
        target_app,
        token_network_identifier,
        amount,
        identifier=None,
        timeout=5,
):
    """ Nice to read shortcut to make a DirectTransfer. """

    channel_state = views.get_channelstate_by_token_network_and_partner(
        views.state_from_app(initiator_app),
        token_network_identifier,
        target_app.raiden.address,
    )
    assert channel_state, 'there is not a direct channel'

    initiator_app.raiden.direct_transfer_async(
        token_network_identifier,
        amount,
        target_app.raiden.address,
        identifier,
    )
    # direct transfers don't have confirmation
    gevent.sleep(timeout)
Ejemplo n.º 7
0
def get_channelstate(app0, app1, token_network_identifier) -> NettingChannelState:
    channel_state = views.get_channelstate_by_token_network_and_partner(
        views.state_from_app(app0),
        token_network_identifier,
        app1.raiden.address,
    )
    return channel_state
Ejemplo n.º 8
0
def replay_wal(storage, token_network_identifier, partner_address, translator=None):
    all_state_changes = storage.get_statechanges_by_identifier(
        from_identifier=0,
        to_identifier='latest',
    )

    state_manager = StateManager(state_transition=node.state_transition, current_state=None)
    wal = WriteAheadLog(state_manager, storage)

    for _, state_change in enumerate(all_state_changes):
        events = wal.state_manager.dispatch(state_change)

        chain_state = wal.state_manager.current_state

        channel_state = views.get_channelstate_by_token_network_and_partner(
            chain_state,
            to_canonical_address(token_network_identifier),
            to_canonical_address(partner_address),
        )

        if not channel_state:
            continue

        ###
        # Customize this to filter things further somewhere around here.
        # An example would be to add `import pdb; pdb.set_trace()`
        # and inspect the state.
        ###

        print_state_change(state_change, translator=translator)
        print_events(events, translator=translator)
Ejemplo n.º 9
0
def direct_transfer(
    initiator_app,
    target_app,
    token_network_identifier,
    amount,
    identifier=None,
    timeout=5,
):
    """ Nice to read shortcut to make a DirectTransfer. """

    channel_state = views.get_channelstate_by_token_network_and_partner(
        views.state_from_app(initiator_app),
        token_network_identifier,
        target_app.raiden.address,
    )
    assert channel_state, 'there is not a direct channel'

    initiator_app.raiden.direct_transfer_async(
        token_network_identifier,
        amount,
        target_app.raiden.address,
        identifier,
    )

    # direct transfers don't have confirmation
    gevent.sleep(timeout)
Ejemplo n.º 10
0
def resolve_routes(
    routes: List[RouteMetadata],
    token_network_address: TokenNetworkAddress,
    chain_state: ChainState,
) -> List[RouteState]:
    """resolve the forward_channel_id for a given route

    TODO: We don't have ``forward_channel_id``, anymore. Does this function still make sense?
    """

    resolvable = []
    for route_metadata in routes:
        if len(route_metadata.route) < 2:
            continue

        channel_state = views.get_channelstate_by_token_network_and_partner(
            chain_state=chain_state,
            token_network_address=token_network_address,
            partner_address=route_metadata.route[1],
        )

        if channel_state is not None:
            resolvable.append(
                RouteState(
                    route=route_metadata.route,
                    # This is only used in the mediator, so fees are set to 0
                    estimated_fee=FeeAmount(0),
                ))
    return resolvable
Ejemplo n.º 11
0
def pending_mediated_transfer(app_chain, token_network_identifier, amount, identifier):
    """ Nice to read shortcut to make a LockedTransfer where the secret is _not_ revealed.

    While the secret is not revealed all apps will be synchronized, meaning
    they are all going to receive the LockedTransfer message.
    Returns:
        The secret used to generate the LockedTransfer
    """
    # pylint: disable=too-many-locals

    if len(app_chain) < 2:
        raise ValueError('Cannot make a LockedTransfer with less than two apps')

    target = app_chain[-1].raiden.address

    # Generate a secret
    initiator_channel = views.get_channelstate_by_token_network_and_partner(
        views.state_from_app(app_chain[0]),
        token_network_identifier,
        app_chain[1].raiden.address,
    )
    address = initiator_channel.identifier
    nonce_int = channel.get_next_nonce(initiator_channel.our_state)
    nonce_bytes = nonce_int.to_bytes(2, 'big')
    secret = sha3(address + nonce_bytes)

    initiator_app = app_chain[0]
    init_initiator_statechange = initiator_init(
        initiator_app.raiden,
        identifier,
        amount,
        secret,
        token_network_identifier,
        target,
    )
    events = initiator_app.raiden.wal.log_and_dispatch(
        init_initiator_statechange,
        initiator_app.raiden.get_block_number(),
    )
    send_transfermessage = must_contain_entry(events, SendLockedTransfer, {})
    transfermessage = LockedTransfer.from_event(send_transfermessage)
    initiator_app.raiden.sign(transfermessage)

    for mediator_app in app_chain[1:-1]:
        mediator_init_statechange = mediator_init(mediator_app.raiden, transfermessage)
        events = mediator_app.raiden.wal.log_and_dispatch(
            mediator_init_statechange,
            mediator_app.raiden.get_block_number(),
        )
        send_transfermessage = must_contain_entry(events, SendLockedTransfer, {})
        transfermessage = LockedTransfer.from_event(send_transfermessage)
        mediator_app.raiden.sign(transfermessage)

    target_app = app_chain[-1]
    mediator_init_statechange = target_init(transfermessage)
    events = target_app.raiden.wal.log_and_dispatch(
        mediator_init_statechange,
        target_app.raiden.get_block_number(),
    )
    return secret
Ejemplo n.º 12
0
def get_channelstate(app0, app1, token_network_identifier) -> NettingChannelState:
    channel_state = views.get_channelstate_by_token_network_and_partner(
        views.state_from_app(app0),
        token_network_identifier,
        app1.raiden.address,
    )
    return channel_state
Ejemplo n.º 13
0
def resolve_routes(
    routes: List[RouteMetadata],
    token_network_address: TokenNetworkAddress,
    chain_state: ChainState,
) -> List[RouteState]:
    """ resolve the forward_channel_id for a given route """

    resolvable = []
    for route_metadata in routes:
        if len(route_metadata.route) < 2:
            continue

        channel_state = views.get_channelstate_by_token_network_and_partner(
            chain_state=chain_state,
            token_network_address=token_network_address,
            partner_address=route_metadata.route[1],
        )

        if channel_state is not None:
            resolvable.append(
                RouteState(
                    route=route_metadata.route,
                    forward_channel_id=channel_state.canonical_identifier.
                    channel_identifier,
                    # This is only used in the mediator, so fees are set to 0
                    estimated_fee=FeeAmount(0),
                ))
    return resolvable
Ejemplo n.º 14
0
def handle_processed(
    chain_state: ChainState,
    state_change: ReceiveProcessed,
) -> TransitionResult:
    # TODO: improve the complexity of this algorithm
    events = list()
    for queue in chain_state.queueids_to_queues.values():
        remove = []

        # TODO: ensure Processed message came from the correct peer
        for pos, message in enumerate(queue):
            if message.message_identifier == state_change.message_identifier:
                if type(message) == SendDirectTransfer:
                    channel_state = views.get_channelstate_by_token_network_and_partner(
                        chain_state,
                        message.balance_proof.token_network_identifier,
                        message.recipient,
                    )
                    events.append(
                        EventPaymentSentSuccess(
                            channel_state.payment_network_identifier,
                            channel_state.token_network_identifier,
                            message.payment_identifier,
                            message.balance_proof.transferred_amount,
                            message.recipient,
                        ))
                remove.append(pos)

        for removepos in reversed(remove):
            queue.pop(removepos)

    return TransitionResult(chain_state, events)
Ejemplo n.º 15
0
def get_channelstate(
        app0: App, app1: App,
        token_network_address: TokenNetworkAddress) -> NettingChannelState:
    channel_state = views.get_channelstate_by_token_network_and_partner(
        views.state_from_app(app0), token_network_address, app1.raiden.address)
    assert channel_state
    return channel_state
Ejemplo n.º 16
0
def pending_mediated_transfer(app_chain, token_network_identifier, amount, identifier):
    """ Nice to read shortcut to make a LockedTransfer where the secret is _not_ revealed.

    While the secret is not revealed all apps will be synchronized, meaning
    they are all going to receive the LockedTransfer message.
    Returns:
        The secret used to generate the LockedTransfer
    """
    # pylint: disable=too-many-locals

    if len(app_chain) < 2:
        raise ValueError('Cannot make a LockedTransfer with less than two apps')

    target = app_chain[-1].raiden.address

    # Generate a secret
    initiator_channel = views.get_channelstate_by_token_network_and_partner(
        views.state_from_app(app_chain[0]),
        token_network_identifier,
        app_chain[1].raiden.address,
    )
    address = initiator_channel.identifier
    nonce_int = channel.get_next_nonce(initiator_channel.our_state)
    nonce_bytes = nonce_int.to_bytes(2, 'big')
    secret = sha3(address + nonce_bytes)

    initiator_app = app_chain[0]
    init_initiator_statechange = initiator_init(
        initiator_app.raiden,
        identifier,
        amount,
        secret,
        token_network_identifier,
        target,
    )
    events = initiator_app.raiden.wal.log_and_dispatch(
        init_initiator_statechange,
        initiator_app.raiden.get_block_number(),
    )
    send_transfermessage = must_contain_entry(events, SendLockedTransfer, {})
    transfermessage = LockedTransfer.from_event(send_transfermessage)
    initiator_app.raiden.sign(transfermessage)

    for mediator_app in app_chain[1:-1]:
        mediator_init_statechange = mediator_init(mediator_app.raiden, transfermessage)
        events = mediator_app.raiden.wal.log_and_dispatch(
            mediator_init_statechange,
            mediator_app.raiden.get_block_number(),
        )
        send_transfermessage = must_contain_entry(events, SendLockedTransfer, {})
        transfermessage = LockedTransfer.from_event(send_transfermessage)
        mediator_app.raiden.sign(transfermessage)

    target_app = app_chain[-1]
    mediator_init_statechange = target_init(transfermessage)
    events = target_app.raiden.wal.log_and_dispatch(
        mediator_init_statechange,
        target_app.raiden.get_block_number(),
    )
    return secret
Ejemplo n.º 17
0
def test_create_monitoring_request(raiden_network, token_addresses):
    app0, app1 = raiden_network
    token_address = token_addresses[0]
    chain_state = views.state_from_app(app0)
    token_network_registry_address = app0.raiden.default_registry.address
    token_network_address = views.get_token_network_address_by_token_address(
        chain_state=chain_state,
        token_network_registry_address=token_network_registry_address,
        token_address=token_address,
    )
    assert token_network_address

    payment_identifier = create_default_identifier()
    transfer(
        initiator_app=app1,
        target_app=app0,
        token_address=token_address,
        amount=PaymentAmount(1),
        identifier=payment_identifier,
    )
    chain_state = views.state_from_raiden(app0.raiden)
    channel_state = views.get_channelstate_by_token_network_and_partner(
        chain_state, token_network_address, app1.raiden.address)
    assert channel_state
    balance_proof = cast(BalanceProofSignedState,
                         channel_state.partner_state.balance_proof)
    api = RaidenAPI(app0.raiden)
    request = api.create_monitoring_request(balance_proof=balance_proof,
                                            reward_amount=TokenAmount(1))
    assert request
    as_dict = DictSerializer.serialize(request)
    from_dict = DictSerializer.deserialize(as_dict)
    assert DictSerializer.serialize(from_dict) == as_dict
Ejemplo n.º 18
0
def assert_synced_channel_state(
    token_network_address: TokenNetworkAddress,
    app0: App,
    balance0: Balance,
    pending_locks0: List[HashTimeLockState],
    app1: App,
    balance1: Balance,
    pending_locks1: List[HashTimeLockState],
) -> None:
    """Compare channel's state from both nodes.

    Note:
        This assert does not work for an intermediate state, where one message
        hasn't been delivered yet or has been completely lost.
    """
    assert app0.raiden.wal
    assert app1.raiden.wal

    saved_state0 = app0.raiden.wal.saved_state
    saved_state1 = app1.raiden.wal.saved_state

    assert_deposit(token_network_address, app0, app1, saved_state0,
                   saved_state1)
    assert_deposit(token_network_address, app1, app0, saved_state1,
                   saved_state0)

    assert_balance_proof(token_network_address, app1, app0, saved_state1,
                         saved_state0)
    assert_balance_proof(token_network_address, app0, app1, saved_state0,
                         saved_state1)

    channel0 = views.get_channelstate_by_token_network_and_partner(
        saved_state0.state, token_network_address, app1.raiden.address)
    channel1 = views.get_channelstate_by_token_network_and_partner(
        saved_state1.state, token_network_address, app0.raiden.address)

    assert channel0
    assert channel1

    assert_channel_values(
        channel0=channel0,
        balance0=balance0,
        pending_locks0=pending_locks0,
        channel1=channel1,
        balance1=balance1,
        pending_locks1=pending_locks1,
    )
Ejemplo n.º 19
0
def test_api_channel_set_reveal_timeout(
    api_server_test_instance: APIServer,
    raiden_network: List[RaidenService],
    token_addresses,
    settle_timeout,
):
    app0, app1 = raiden_network
    token_address = token_addresses[0]
    partner_address = app1.address

    request = grequests.patch(
        api_url_for(
            api_server_test_instance,
            "channelsresourcebytokenandpartneraddress",
            token_address=token_address,
            partner_address=partner_address,
        ),
        json=dict(reveal_timeout=0),
    )
    response = request.send().response
    assert_response_with_error(response, HTTPStatus.CONFLICT)

    request = grequests.patch(
        api_url_for(
            api_server_test_instance,
            "channelsresourcebytokenandpartneraddress",
            token_address=token_address,
            partner_address=partner_address,
        ),
        json=dict(reveal_timeout=settle_timeout + 1),
    )
    response = request.send().response
    assert_response_with_error(response, HTTPStatus.CONFLICT)

    reveal_timeout = int(settle_timeout / 2)
    request = grequests.patch(
        api_url_for(
            api_server_test_instance,
            "channelsresourcebytokenandpartneraddress",
            token_address=token_address,
            partner_address=partner_address,
        ),
        json=dict(reveal_timeout=reveal_timeout),
    )
    response = request.send().response
    assert_response_with_code(response, HTTPStatus.OK)

    token_network_address = views.get_token_network_address_by_token_address(
        views.state_from_raiden(app0), app0.default_registry.address,
        token_address)
    assert token_network_address
    channel_state = views.get_channelstate_by_token_network_and_partner(
        chain_state=views.state_from_raiden(app0),
        token_network_address=token_network_address,
        partner_address=app1.address,
    )
    assert channel_state

    assert channel_state.reveal_timeout == reveal_timeout
Ejemplo n.º 20
0
def test_get_channelstate_by_token_network_and_partner():
    test_state = factories.make_chain_state(number_of_channels=3)
    partner_address = test_state.channels[1].partner_state.address
    assert (views.get_channelstate_by_token_network_and_partner(
        chain_state=test_state.chain_state,
        token_network_address=test_state.token_network_address,
        partner_address=partner_address,
    ) == test_state.channels[1])
Ejemplo n.º 21
0
 def set_fee_schedule(app: App, other_app: App,
                      fee_schedule: FeeScheduleState):
     channel_state = views.get_channelstate_by_token_network_and_partner(  # type: ignore
         chain_state=views.state_from_raiden(app.raiden),
         token_network_address=token_network_address,
         partner_address=other_app.raiden.address,
     )
     assert channel_state
     channel_state.fee_schedule = fee_schedule
Ejemplo n.º 22
0
    def get_channel_state(app) -> NettingChannelState:
        chain_state = views.state_from_app(app)
        token_network_registry_address = app.raiden.default_registry.address
        token_network_address = views.get_token_network_address_by_token_address(
            chain_state, token_network_registry_address, token_address)
        assert token_network_address
        channel_state = views.get_channelstate_by_token_network_and_partner(
            chain_state, token_network_address, app1.raiden.address)
        assert channel_state

        return channel_state
Ejemplo n.º 23
0
def replay_wal(
    storage: SerializedSQLiteStorage,
    token_network_address: TokenNetworkAddress,
    partner_address: Address,
    translator: Optional[Translator] = None,
) -> None:
    snapshot = storage.get_snapshot_before_state_change(
        state_change_identifier=LOW_STATECHANGE_ULID)
    assert snapshot is not None, "No snapshot found"

    wal = WriteAheadLog(snapshot.data, storage, node.state_transition)
    state = wal.get_current_state()

    all_state_changes = storage.get_statechanges_by_range(
        RANGE_ALL_STATE_CHANGES)
    for state_change in all_state_changes:
        # Dispatching the state changes one-by-one to easy debugging
        state, events = dispatch(
            state=state,
            state_change=state_change,
            state_transition=wal.state_transition,
        )

        msg = "Chain state must never be cleared up."
        assert state, msg

        channel_state = views.get_channelstate_by_token_network_and_partner(
            state,
            to_canonical_address(token_network_address),
            to_canonical_address(partner_address),
        )

        if channel_state is None:
            continue

        ###
        # Customize this to filter things further somewhere around here.
        # An example would be to add `breakpoint()`
        # and inspect the state.
        ###
        print_state_change(state_change, translator=translator)
        print_events(events, translator=translator)

        # Enable to print color coded presence state of channel partners
        # print_presence_view(chain_state, translator)

        # Enable to print balances & balance sum with all channel partners
        # print_node_balances(chain_state, token_network_address, translator)

        print_nl()
Ejemplo n.º 24
0
def replay_wal(
    storage: SerializedSQLiteStorage,
    token_network_address: TokenNetworkAddress,
    partner_address: Address,
    translator: Optional[Translator] = None,
) -> None:
    all_state_changes = storage.get_statechanges_by_range(
        RANGE_ALL_STATE_CHANGES)

    state_manager = StateManager(state_transition=node.state_transition,
                                 current_state=None)
    wal = WriteAheadLog(state_manager, storage)

    for _, state_change in enumerate(all_state_changes):
        # Dispatching the state changes one-by-one to easy debugging
        _, events = wal.state_manager.dispatch([state_change])

        chain_state = wal.state_manager.current_state
        msg = "Chain state must never be cleared up."
        assert chain_state, msg

        channel_state = views.get_channelstate_by_token_network_and_partner(
            chain_state,
            to_canonical_address(token_network_address),
            to_canonical_address(partner_address),
        )

        if channel_state is None:
            continue

        ###
        # Customize this to filter things further somewhere around here.
        # An example would be to add `breakpoint()`
        # and inspect the state.
        ###
        print_state_change(state_change, translator=translator)
        print_events(chain.from_iterable(events), translator=translator)

        # Enable to print color coded presence state of channel partners
        # print_presence_view(chain_state, translator)

        # Enable to print balances & balance sum with all channel partners
        # print_node_balances(chain_state, token_network_address, translator)

        print_nl()
Ejemplo n.º 25
0
    def update_pfs(raiden: RaidenService, event: Event):
        channel_state = get_channelstate_by_token_network_and_partner(
            chain_state=state_from_raiden(raiden),
            token_network_id=to_canonical_address(
                event.balance_proof.token_network_identifier, ),
            partner_address=to_canonical_address(event.recipient),
        )
        error_msg = 'tried to send a balance proof in non-existant channel '
        f'token_network_address: {pex(event.balance_proof.token_network_identifier)} '
        f'recipient: {pex(event.recipient)}'
        assert channel_state is not None, error_msg

        msg = UpdatePFS.from_balance_proof(
            balance_proof=event.balance_proof,
            reveal_timeout=channel_state.reveal_timeout,
        )
        msg.sign(raiden.signer)
        raiden.transport.send_global(PATH_FINDING_BROADCASTING_ROOM, msg)
        log.debug(
            'sent a PFS Update',
            balance_proof=event.balance_proof,
            recipient=event.recipient,
        )
Ejemplo n.º 26
0
def get_best_routes_pfs(
    chain_state: ChainState,
    token_network_address: TokenNetworkAddress,
    one_to_n_address: OneToNAddress,
    from_address: InitiatorAddress,
    to_address: TargetAddress,
    amount: PaymentAmount,
    previous_address: Optional[Address],
    pfs_config: PFSConfig,
    privkey: bytes,
    pfs_wait_for_block: BlockNumber,
) -> Tuple[Optional[str], List[RouteState], Optional[UUID]]:
    try:
        pfs_routes, feedback_token = query_paths(
            pfs_config=pfs_config,
            our_address=chain_state.our_address,
            privkey=privkey,
            current_block_number=chain_state.block_number,
            token_network_address=token_network_address,
            one_to_n_address=one_to_n_address,
            chain_id=chain_state.chain_id,
            route_from=from_address,
            route_to=to_address,
            value=amount,
            pfs_wait_for_block=pfs_wait_for_block,
        )
    except ServiceRequestFailed as e:
        log_message = ("PFS: " + e.args[0]) if e.args[0] else None
        log_info = e.args[1] if len(e.args) > 1 else {}
        log.warning("An error with the path request occurred",
                    log_message=log_message,
                    **log_info)
        return log_message, [], None

    paths = []
    for path_object in pfs_routes:
        path = path_object["path"]
        estimated_fee = path_object["estimated_fee"]
        canonical_path = [to_canonical_address(node) for node in path]

        # get the second entry, as the first one is the node itself
        # also needs to be converted to canonical representation
        partner_address = canonical_path[1]

        # don't route back
        if partner_address == previous_address:
            continue

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

        if not channel_state:
            continue

        # check channel state
        if channel.get_status(channel_state) != ChannelState.STATE_OPENED:
            log.info(
                "Channel is not opened, ignoring",
                from_address=to_checksum_address(from_address),
                partner_address=to_checksum_address(partner_address),
                routing_source="Pathfinding Service",
            )
            continue

        paths.append(
            RouteState(
                route=canonical_path,
                forward_channel_id=channel_state.identifier,
                estimated_fee=estimated_fee,
            ))

    return None, paths, feedback_token
Ejemplo n.º 27
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
Ejemplo n.º 28
0
    def handle_contract_send_channelunlock(
        raiden: "RaidenService",
        chain_state: ChainState,
        channel_unlock_event: ContractSendChannelBatchUnlock,
    ):
        assert raiden.wal, "The Raiden Service must be initialize to handle events"

        canonical_identifier = channel_unlock_event.canonical_identifier
        token_network_identifier = canonical_identifier.token_network_address
        channel_identifier = canonical_identifier.channel_identifier
        participant = channel_unlock_event.participant

        payment_channel: PaymentChannel = raiden.chain.payment_channel(
            canonical_identifier=canonical_identifier
        )

        channel_state = get_channelstate_by_token_network_and_partner(
            chain_state=chain_state,
            token_network_id=TokenNetworkID(token_network_identifier),
            partner_address=participant,
        )

        if not channel_state:
            # channel was cleaned up already due to an unlock
            raise RaidenUnrecoverableError(
                f"Failed to find channel state with partner:"
                f"{to_checksum_address(participant)}, token_network:pex(token_network_identifier)"
            )

        our_address = channel_state.our_state.address
        our_locksroot = channel_state.our_state.onchain_locksroot

        partner_address = channel_state.partner_state.address
        partner_locksroot = channel_state.partner_state.onchain_locksroot

        # we want to unlock because there are on-chain unlocked locks
        search_events = our_locksroot != EMPTY_HASH
        # we want to unlock, because there are unlocked/unclaimed locks
        search_state_changes = partner_locksroot != EMPTY_HASH

        if not search_events and not search_state_changes:
            # In the case that someone else sent the unlock we do nothing
            # Check https://github.com/raiden-network/raiden/issues/3152
            # for more details
            log.warning(
                "Onchain unlock already mined",
                canonical_identifier=canonical_identifier,
                channel_identifier=canonical_identifier.channel_identifier,
                participant=to_checksum_address(participant),
            )
            return

        if search_state_changes:
            state_change_record = get_state_change_with_balance_proof_by_locksroot(
                storage=raiden.wal.storage,
                canonical_identifier=canonical_identifier,
                locksroot=partner_locksroot,
                sender=partner_address,
            )
            state_change_identifier = state_change_record.state_change_identifier

            if not state_change_identifier:
                raise RaidenUnrecoverableError(
                    f"Failed to find state that matches the current channel locksroots. "
                    f"chain_id:{raiden.chain.network_id} "
                    f"token_network:{to_checksum_address(token_network_identifier)} "
                    f"channel:{channel_identifier} "
                    f"participant:{to_checksum_address(participant)} "
                    f"our_locksroot:{to_hex(our_locksroot)} "
                    f"partner_locksroot:{to_hex(partner_locksroot)} "
                )

            restored_channel_state = channel_state_until_state_change(
                raiden=raiden,
                canonical_identifier=canonical_identifier,
                state_change_identifier=state_change_identifier,
            )
            assert restored_channel_state is not None

            gain = get_batch_unlock_gain(restored_channel_state)

            skip_unlock = (
                restored_channel_state.partner_state.address == participant
                and gain.from_partner_locks == 0
            )
            if not skip_unlock:
                unlock(
                    raiden=raiden,
                    payment_channel=payment_channel,
                    end_state=restored_channel_state.partner_state,
                    participant=our_address,
                    partner=partner_address,
                )

        if search_events:
            event_record = get_event_with_balance_proof_by_locksroot(
                storage=raiden.wal.storage,
                canonical_identifier=canonical_identifier,
                locksroot=our_locksroot,
                recipient=partner_address,
            )
            state_change_identifier = event_record.state_change_identifier

            if not state_change_identifier:
                raise RaidenUnrecoverableError(
                    f"Failed to find event that match current channel locksroots. "
                    f"chain_id:{raiden.chain.network_id} "
                    f"token_network:{to_checksum_address(token_network_identifier)} "
                    f"channel:{channel_identifier} "
                    f"participant:{to_checksum_address(participant)} "
                    f"our_locksroot:{to_hex(our_locksroot)} "
                    f"partner_locksroot:{to_hex(partner_locksroot)} "
                )

            restored_channel_state = channel_state_until_state_change(
                raiden=raiden,
                canonical_identifier=canonical_identifier,
                state_change_identifier=state_change_identifier,
            )
            assert restored_channel_state is not None

            gain = get_batch_unlock_gain(restored_channel_state)

            skip_unlock = (
                restored_channel_state.our_state.address == participant
                and gain.from_our_locks == 0
            )
            if not skip_unlock:
                unlock(
                    raiden=raiden,
                    payment_channel=payment_channel,
                    end_state=restored_channel_state.our_state,
                    participant=partner_address,
                    partner=our_address,
                )
def test_handle_contract_send_channelunlock_already_unlocked():
    """This is a test for the scenario where the onchain unlock has
    already happened when we get to handle our own send unlock
    transaction.

    Regression test for https://github.com/raiden-network/raiden/issues/3152
    """
    channel_identifier = 1
    payment_network_identifier = make_address()
    token_network_identifier = make_address()
    participant = make_address()
    raiden = make_raiden_service_mock(
        payment_network_identifier=payment_network_identifier,
        token_network_identifier=token_network_identifier,
        channel_identifier=channel_identifier,
        partner=participant,
    )

    channel_state = get_channelstate_by_token_network_and_partner(
        chain_state=state_from_raiden(raiden),
        token_network_id=token_network_identifier,
        partner_address=participant,
    )

    channel_state.our_state.onchain_locksroot = EMPTY_MERKLE_ROOT
    channel_state.partner_state.onchain_locksroot = EMPTY_MERKLE_ROOT

    def detail_participants(  # pylint: disable=unused-argument
            participant1, participant2, block_identifier, channel_identifier):
        transferred_amount = 1
        locked_amount = 1
        locksroot = make_32bytes()
        balance_hash = hash_balance_data(transferred_amount, locked_amount,
                                         locksroot)
        our_details = ParticipantDetails(
            address=raiden.address,
            deposit=5,
            withdrawn=0,
            is_closer=False,
            balance_hash=balance_hash,
            nonce=1,
            locksroot=locksroot,
            locked_amount=locked_amount,
        )

        transferred_amount = 1
        locked_amount = 1
        # Let's mock here that partner locksroot is 0x0
        balance_hash = hash_balance_data(transferred_amount, locked_amount,
                                         locksroot)
        partner_details = ParticipantDetails(
            address=participant,
            deposit=5,
            withdrawn=0,
            is_closer=True,
            balance_hash=balance_hash,
            nonce=1,
            locksroot=EMPTY_HASH,
            locked_amount=locked_amount,
        )
        return ParticipantsDetails(our_details, partner_details)

    # make sure detail_participants returns partner data with a locksroot of 0x0
    raiden.chain.token_network.detail_participants = detail_participants

    event = ContractSendChannelBatchUnlock(
        canonical_identifier=make_canonical_identifier(
            token_network_address=token_network_identifier,
            channel_identifier=channel_identifier),
        participant=participant,
        triggered_by_block_hash=make_block_hash(),
    )

    # This should not throw an unrecoverable error
    RaidenEventHandler().on_raiden_event(
        raiden=raiden,
        chain_state=raiden.wal.state_manager.current_state,
        event=event)
Ejemplo n.º 30
0
def get_best_routes_pfs(
        chain_state: ChainState,
        token_network_id: typing.TokenNetworkID,
        from_address: typing.InitiatorAddress,
        to_address: typing.TargetAddress,
        amount: int,
        previous_address: typing.Optional[typing.Address],
        config: Dict[str, Any],
) -> Tuple[bool, List[RouteState]]:
    pfs_path = '{}/api/v1/{}/paths'.format(
        config['pathfinding_service_address'],
        to_checksum_address(token_network_id),
    )
    payload = {
        'from': to_checksum_address(from_address),
        'to': to_checksum_address(to_address),
        'value': amount,
        'max_paths': config['pathfinding_max_paths'],
    }

    # check that the response is successful
    try:
        response = requests.get(pfs_path, params=payload, timeout=DEFAULT_HTTP_REQUEST_TIMEOUT)
    except requests.RequestException:
        log.warning(
            'Could not connect to Pathfinding Service',
            request=pfs_path,
            parameters=payload,
            exc_info=True,
        )
        return False, []

    # check that the response contains valid json
    try:
        response_json = response.json()
    except ValueError:
        log.warning(
            'Pathfinding Service returned invalid JSON',
            response_text=response.text,
            exc_info=True,
        )
        return False, []

    if response.status_code != 200:
        log_info = {
            'error_code': response.status_code,
        }

        error = response_json.get('errors')
        if error is not None:
            log_info['pfs_error'] = error

        log.info(
            'Pathfinding Service returned error code',
            **log_info,
        )
        return False, []

    if response_json.get('result') is None:
        log.info(
            'Pathfinding Service returned unexpected result',
            result=response_json,
        )
        return False, []

    paths = []
    network_statuses = views.get_networkstatuses(chain_state)
    for path_object in response_json['result']:
        path = path_object['path']

        # get the second entry, as the first one is the node itself
        # also needs to be converted to canonical representation
        partner_address = to_canonical_address(path[1])

        # don't route back
        if partner_address == previous_address:
            continue

        channel_state = views.get_channelstate_by_token_network_and_partner(
            chain_state=chain_state,
            token_network_id=token_network_id,
            partner_address=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='Pathfinding Service',
        )
        if not channel_constraints_fulfilled:
            continue

        paths.append(RouteState(
            node_address=partner_address,
            channel_identifier=channel_state.identifier,
        ))

    return True, paths
Ejemplo n.º 31
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
Ejemplo n.º 32
0
def get_best_routes_pfs(
        chain_state: ChainState,
        token_network_id: typing.TokenNetworkID,
        from_address: typing.InitiatorAddress,
        to_address: typing.TargetAddress,
        amount: int,
        previous_address: typing.Optional[typing.Address],
        config: Dict[str, Any],
) -> Tuple[bool, List[RouteState]]:
    pfs_path = '{}/api/v1/{}/paths'.format(
        config['pathfinding_service_address'],
        to_checksum_address(token_network_id),
    )
    payload = {
        'from': to_checksum_address(from_address),
        'to': to_checksum_address(to_address),
        'value': amount,
        'max_paths': config['pathfinding_max_paths'],
    }

    # check that the response is successful
    try:
        response = requests.get(pfs_path, params=payload, timeout=DEFAULT_HTTP_REQUEST_TIMEOUT)
    except requests.RequestException:
        log.warning(
            'Could not connect to Pathfinding Service',
            request=pfs_path,
            parameters=payload,
            exc_info=True,
        )
        return False, []

    # check that the response contains valid json
    try:
        response_json = response.json()
    except ValueError:
        log.warning(
            'Pathfinding Service returned invalid JSON',
            response_text=response.text,
            exc_info=True,
        )
        return False, []

    if response.status_code != 200:
        log_info = {
            'error_code': response.status_code,
        }

        error = response_json.get('errors')
        if error is not None:
            log_info['pfs_error'] = error

        log.info(
            'Pathfinding Service returned error code',
            **log_info,
        )
        return False, []

    if response_json.get('result') is None:
        log.info(
            'Pathfinding Service returned unexpected result',
            result=response_json,
        )
        return False, []

    paths = []
    for path_object in response_json['result']:
        path = path_object['path']

        # get the second entry, as the first one is the node itself
        # also needs to be converted to canonical representation
        partner_address = to_canonical_address(path[1])

        # don't route back
        if partner_address == previous_address:
            continue

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

        # 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='Pathfinding Service',
            )
            continue

        paths.append(RouteState(
            node_address=partner_address,
            channel_identifier=channel_state.identifier,
        ))

    return True, paths
Ejemplo n.º 33
0
def is_transaction_effect_satisfied(
        chain_state: ChainState,
        transaction: ContractSendEvent,
        state_change: StateChange,
) -> bool:
    """ True if the side-effect of `transaction` is satisfied by
    `state_change`.

    This predicate is used to clear the transaction queue. This should only be
    done once the expected side effect of a transaction is achieved. This
    doesn't necessarily mean that the transaction sent by *this* node was
    mined, but only that *some* transaction which achieves the same side-effect
    was successfully executed and mined. This distinction is important for
    restarts and to reduce the number of state changes.

    On restarts: The state of the on-chain channel could have changed while the
    node was offline. Once the node learns about the change (e.g. the channel
    was settled), new transactions can be dispatched by Raiden as a side effect for the
    on-chain *event* (e.g. do the batch unlock with the latest merkle tree),
    but the dispatched transaction could have been completed by another agent (e.g.
    the partner node). For these cases, the transaction from a different
    address which achieves the same side-effect is sufficient, otherwise
    unnecessary transactions would be sent by the node.

    NOTE: The above is not important for transactions sent as a side-effect for
    a new *block*. On restart the node first synchronizes its state by querying
    for new events, only after the off-chain state is up-to-date, a Block state
    change is dispatched. At this point some transactions are not required
    anymore and therefore are not dispatched.

    On the number of state changes: Accepting a transaction from another
    address removes the need for clearing state changes, e.g. when our the
    node's close transaction fails but its partner's close transaction
    succeeds.
    """
    # These transactions are not made atomic through the WAL, they are sent
    # exclusively through the external APIs.
    #
    #  - ContractReceiveChannelNew
    #  - ContractReceiveChannelNewBalance
    #  - ContractReceiveNewPaymentNetwork
    #  - ContractReceiveNewTokenNetwork
    #  - ContractReceiveRouteNew
    #
    # Note: Deposits and Withdraws must consider a transaction with a higher
    # value as sufficient, because the values are monotonically increasing and
    # the transaction with a lower value will never be executed.

    # Transactions are used to change the on-chain state of a channel. It
    # doesn't matter if the sender of the transaction is the local node or
    # another node authorized to perform the operation. So, for the following
    # transactions, as long as the side-effects are the same, the local
    # transaction can be removed from the queue.
    #
    # - An update transfer can be done by a trusted third party (i.e. monitoring service)
    # - A close transaction can be sent by our partner
    # - A settle transaction can be sent by anyone
    # - A secret reveal can be done by anyone

    # - A lower nonce is not a valid replacement, since that is an older balance
    #   proof
    # - A larger raiden state change nonce is impossible.
    #   That would require the partner node to produce an invalid balance proof,
    # and this node to accept the invalid balance proof and sign it
    is_valid_update_transfer = (
        isinstance(state_change, ContractReceiveUpdateTransfer) and
        isinstance(transaction, ContractSendChannelUpdateTransfer) and
        state_change.token_network_identifier == transaction.token_network_identifier and
        state_change.channel_identifier == transaction.channel_identifier and
        state_change.nonce == transaction.balance_proof.nonce
    )
    if is_valid_update_transfer:
        return True

    # The balance proof data cannot be verified, the local close could have
    # lost a race against a remote close, and the balance proof data would be
    # the one provided by this node's partner
    is_valid_close = (
        isinstance(state_change, ContractReceiveChannelClosed) and
        isinstance(transaction, ContractSendChannelClose) and
        state_change.token_network_identifier == transaction.token_network_identifier and
        state_change.channel_identifier == transaction.channel_identifier
    )
    if is_valid_close:
        return True

    is_valid_settle = (
        isinstance(state_change, ContractReceiveChannelSettled) and
        isinstance(transaction, ContractSendChannelSettle) and
        state_change.token_network_identifier == transaction.token_network_identifier and
        state_change.channel_identifier == transaction.channel_identifier
    )
    if is_valid_settle:
        return True

    is_valid_secret_reveal = (
        isinstance(state_change, ContractReceiveSecretReveal) and
        isinstance(transaction, ContractSendSecretReveal) and
        state_change.secret == transaction.secret
    )
    if is_valid_secret_reveal:
        return True

    is_batch_unlock = (
        isinstance(state_change, ContractReceiveChannelBatchUnlock) and
        isinstance(transaction, ContractSendChannelBatchUnlock)
    )
    if is_batch_unlock:
        our_address = chain_state.our_address

        # Don't assume that because we sent the transaction, we are a
        # participant
        partner_address = None
        if state_change.participant == our_address:
            partner_address = state_change.partner
        elif state_change.partner == our_address:
            partner_address = state_change.participant

        # Use the second address as the partner address, but check that a
        # channel exists for our_address and partner_address
        if partner_address:
            channel_state = views.get_channelstate_by_token_network_and_partner(
                chain_state,
                state_change.token_network_identifier,
                partner_address,
            )

            if channel_state:
                is_our_batch_unlock = (
                    state_change.participant == our_address and
                    state_change.token_network_identifier == transaction.token_network_identifier
                )
                if is_our_batch_unlock:
                    return True

    return False
Ejemplo n.º 34
0
def is_transaction_effect_satisfied(
        chain_state: ChainState,
        transaction: ContractSendEvent,
        state_change: StateChange,
) -> bool:
    """ True if the side-effect of `transaction` is satisfied by
    `state_change`.

    This predicate is used to clear the transaction queue. This should only be
    done once the expected side effect of a transaction is achieved. This
    doesn't necessarily mean that the transaction sent by *this* node was
    mined, but only that *some* transaction which achieves the same side-effect
    was successfully executed and mined. This distinction is important for
    restarts and to reduce the number of state changes.

    On restarts: The state of the on-chain channel could have changed while the
    node was offline. Once the node learns about the change (e.g. the channel
    was settled), new transactions can be dispatched by Raiden as a side effect for the
    on-chain *event* (e.g. do the batch unlock with the latest merkle tree),
    but the dispatched transaction could have been completed by another agent (e.g.
    the partner node). For these cases, the transaction from a different
    address which achieves the same side-effect is sufficient, otherwise
    unnecessary transactions would be sent by the node.

    NOTE: The above is not important for transactions sent as a side-effect for
    a new *block*. On restart the node first synchronizes its state by querying
    for new events, only after the off-chain state is up-to-date, a Block state
    change is dispatched. At this point some transactions are not required
    anymore and therefore are not dispatched.

    On the number of state changes: Accepting a transaction from another
    address removes the need for clearing state changes, e.g. when our the
    node's close transaction fails but its partner's close transaction
    succeeds.
    """
    # These transactions are not made atomic through the WAL, they are sent
    # exclusively through the external APIs.
    #
    #  - ContractReceiveChannelNew
    #  - ContractReceiveChannelNewBalance
    #  - ContractReceiveNewPaymentNetwork
    #  - ContractReceiveNewTokenNetwork
    #  - ContractReceiveRouteNew
    #
    # Note: Deposits and Withdraws must consider a transaction with a higher
    # value as sufficient, because the values are monotonically increasing and
    # the transaction with a lower value will never be executed.

    # Transactions are used to change the on-chain state of a channel. It
    # doesn't matter if the sender of the transaction is the local node or
    # another node authorized to perform the operation. So, for the following
    # transactions, as long as the side-effects are the same, the local
    # transaction can be removed from the queue.
    #
    # - An update transfer can be done by a trusted third party (i.e. monitoring service)
    # - A close transaction can be sent by our partner
    # - A settle transaction can be sent by anyone
    # - A secret reveal can be done by anyone

    # - A lower nonce is not a valid replacement, since that is an older balance
    #   proof
    # - A larger raiden state change nonce is impossible.
    #   That would require the partner node to produce an invalid balance proof,
    # and this node to accept the invalid balance proof and sign it
    is_valid_update_transfer = (
        isinstance(state_change, ContractReceiveUpdateTransfer) and
        isinstance(transaction, ContractSendChannelUpdateTransfer) and
        state_change.token_network_identifier == transaction.token_network_identifier and
        state_change.channel_identifier == transaction.channel_identifier and
        state_change.nonce == transaction.balance_proof.nonce
    )
    if is_valid_update_transfer:
        return True

    # The balance proof data cannot be verified, the local close could have
    # lost a race against a remote close, and the balance proof data would be
    # the one provided by this node's partner
    is_valid_close = (
        isinstance(state_change, ContractReceiveChannelClosed) and
        isinstance(transaction, ContractSendChannelClose) and
        state_change.token_network_identifier == transaction.token_network_identifier and
        state_change.channel_identifier == transaction.channel_identifier
    )
    if is_valid_close:
        return True

    is_valid_settle = (
        isinstance(state_change, ContractReceiveChannelSettled) and
        isinstance(transaction, ContractSendChannelSettle) and
        state_change.token_network_identifier == transaction.token_network_identifier and
        state_change.channel_identifier == transaction.channel_identifier
    )
    if is_valid_settle:
        return True

    is_valid_secret_reveal = (
        isinstance(state_change, ContractReceiveSecretReveal) and
        isinstance(transaction, ContractSendSecretReveal) and
        state_change.secret == transaction.secret
    )
    if is_valid_secret_reveal:
        return True

    is_batch_unlock = (
        isinstance(state_change, ContractReceiveChannelBatchUnlock) and
        isinstance(transaction, ContractSendChannelBatchUnlock)
    )
    if is_batch_unlock:
        our_address = chain_state.our_address

        # Don't assume that because we sent the transaction, we are a
        # participant
        partner_address = None
        if state_change.participant == our_address:
            partner_address = state_change.partner
        elif state_change.partner == our_address:
            partner_address = state_change.participant

        # Use the second address as the partner address, but check that a
        # channel exists for our_address and partner_address
        if partner_address:
            channel_state = views.get_channelstate_by_token_network_and_partner(
                chain_state,
                state_change.token_network_identifier,
                partner_address,
            )

            if channel_state:
                is_our_batch_unlock = (
                    state_change.participant == our_address and
                    state_change.token_network_identifier == transaction.token_network_identifier
                )
                if is_our_batch_unlock:
                    return True

    return False
Ejemplo n.º 35
0
def test_mediated_transfer_with_node_consuming_more_than_allocated_fee(
        raiden_network, number_of_nodes, deposit, token_addresses,
        network_wait):
    """
    Tests a mediator node consuming more fees than allocated.
    Which means that the initiator will not reveal the secret
    to the target.
    """
    app0, app1, app2 = raiden_network
    token_address = token_addresses[0]
    chain_state = views.state_from_app(app0)
    token_network_registry_address = app0.raiden.default_registry.address
    token_network_address = views.get_token_network_address_by_token_address(
        chain_state, token_network_registry_address, token_address)
    assert token_network_address
    amount = PaymentAmount(100)
    fee = FeeAmount(5)
    fee_margin = calculate_fee_margin(amount, fee)

    app1_app2_channel_state = views.get_channelstate_by_token_network_and_partner(
        chain_state=views.state_from_raiden(app1.raiden),
        token_network_address=token_network_address,
        partner_address=app2.raiden.address,
    )
    assert app1_app2_channel_state

    # Let app1 consume all of the allocated mediation fee
    app1_app2_channel_state.fee_schedule = FeeScheduleState(
        flat=FeeAmount(fee * 2))

    secret = factories.make_secret(0)
    secrethash = sha256_secrethash(secret)

    wait_message_handler = WaitForMessage()
    app0.raiden.message_handler = wait_message_handler
    secret_request_received = wait_message_handler.wait_for_message(
        SecretRequest, {"secrethash": secrethash})

    def get_best_routes_with_fees(*args, **kwargs):
        routes = get_best_routes_internal(*args, **kwargs)
        for r in routes:
            r.estimated_fee = fee
        return routes

    with patch("raiden.routing.get_best_routes_internal",
               get_best_routes_with_fees):
        app0.raiden.start_mediated_transfer_with_secret(
            token_network_address=token_network_address,
            amount=amount,
            target=app2.raiden.address,
            identifier=1,
            secret=secret,
        )

    app0_app1_channel_state = views.get_channelstate_by_token_network_and_partner(
        chain_state=views.state_from_raiden(app0.raiden),
        token_network_address=token_network_address,
        partner_address=app1.raiden.address,
    )
    assert app0_app1_channel_state

    msg = "App0 should have the transfer in secrethashes_to_lockedlocks"
    assert secrethash in app0_app1_channel_state.our_state.secrethashes_to_lockedlocks, msg

    msg = "App0 should have locked the amount + fee"
    lock_amount = app0_app1_channel_state.our_state.secrethashes_to_lockedlocks[
        secrethash].amount
    assert lock_amount == amount + fee + fee_margin, msg

    secret_request_received.wait()

    app0_chain_state = views.state_from_app(app0)
    initiator_task = cast(
        InitiatorTask,
        app0_chain_state.payment_mapping.secrethashes_to_task[secrethash])

    msg = "App0 should have never revealed the secret"
    transfer_state = initiator_task.manager_state.initiator_transfers[
        secrethash].transfer_state
    assert transfer_state != "transfer_secret_revealed", msg
Ejemplo n.º 36
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,
    )
Ejemplo n.º 37
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
Ejemplo n.º 38
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