示例#1
0
def try_new_route(
    old_initiator_state: typing.Optional[InitiatorTransferState],
    channelidentifiers_to_channels: typing.ChannelMap,
    available_routes: typing.List[RouteState],
    transfer_description: TransferDescriptionWithSecretState,
    pseudo_random_generator: random.Random,
    block_number: typing.BlockNumber,
) -> TransitionResult:

    channel_state = next_channel_from_routes(
        available_routes,
        channelidentifiers_to_channels,
        transfer_description.amount,
    )

    events: typing.List[Event] = list()
    if channel_state is None:
        if not available_routes:
            reason = 'there is no route available'
        else:
            reason = 'none of the available routes could be used'

        transfer_failed = EventPaymentSentFailed(
            payment_network_identifier=transfer_description.
            payment_network_identifier,
            token_network_identifier=transfer_description.
            token_network_identifier,
            identifier=transfer_description.payment_identifier,
            target=transfer_description.target,
            reason=reason,
        )
        events.append(transfer_failed)
        # Here we don't delete the initiator state, but instead let it live.
        # It will be deleted when the lock expires. We do that so that we
        # still have an initiator payment task around to process the
        # LockExpired message that our partner will send us.
        # https://github.com/raiden-network/raiden/issues/3146#issuecomment-447378046
        initiator_state = old_initiator_state

    else:
        message_identifier = message_identifier_from_prng(
            pseudo_random_generator)
        lockedtransfer_event = send_lockedtransfer(
            transfer_description=transfer_description,
            channel_state=channel_state,
            message_identifier=message_identifier,
            block_number=block_number,
        )
        assert lockedtransfer_event

        initiator_state = InitiatorTransferState(
            transfer_description=transfer_description,
            channel_identifier=channel_state.identifier,
            transfer=lockedtransfer_event.transfer,
            revealsecret=None,
        )
        events.append(lockedtransfer_event)

    return TransitionResult(initiator_state, events)
示例#2
0
def handle_secretrequest(
    initiator_state: InitiatorTransferState,
    state_change: ReceiveSecretRequest,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random,
) -> TransitionResult:

    is_message_from_target = (
        state_change.sender == initiator_state.transfer_description.target
        and state_change.secrethash
        == initiator_state.transfer_description.secrethash
        and state_change.payment_identifier
        == initiator_state.transfer_description.payment_identifier)

    lock = channel.get_lock(
        channel_state.our_state,
        initiator_state.transfer_description.secrethash,
    )

    is_valid_secretrequest = (is_message_from_target and state_change.amount
                              == initiator_state.transfer_description.amount
                              and state_change.expiration == lock.expiration)

    if is_valid_secretrequest:
        # Reveal the secret to the target node and wait for its confirmation.
        # At this point the transfer is not cancellable anymore as either the lock
        # timeouts or a secret reveal is received.
        #
        # Note: The target might be the first hop
        #
        message_identifier = message_identifier_from_prng(
            pseudo_random_generator)
        transfer_description = initiator_state.transfer_description
        recipient = transfer_description.target
        revealsecret = SendSecretReveal(
            recipient=recipient,
            channel_identifier=CHANNEL_IDENTIFIER_GLOBAL_QUEUE,
            message_identifier=message_identifier,
            secret=transfer_description.secret,
        )

        initiator_state.revealsecret = revealsecret
        iteration = TransitionResult(initiator_state, [revealsecret])

    elif not is_valid_secretrequest and is_message_from_target:
        cancel = EventPaymentSentFailed(
            payment_network_identifier=channel_state.
            payment_network_identifier,
            token_network_identifier=channel_state.token_network_identifier,
            identifier=initiator_state.transfer_description.payment_identifier,
            target=initiator_state.transfer_description.target,
            reason='bad secret request message from target',
        )
        iteration = TransitionResult(None, [cancel])

    else:
        iteration = TransitionResult(initiator_state, list())

    return iteration
示例#3
0
def test_timestamped_event():
    event = EventPaymentSentFailed(make_token_network_registry_address(),
                                   make_address(), 1, make_address(),
                                   "whatever")
    log_time = "2018-09-07T20:02:35.000"

    timestamped = TimestampedEvent(event, log_time)
    assert timestamped.log_time == log_time
    assert timestamped.reason == timestamped.wrapped_event.reason == "whatever"
    assert timestamped.identifier == 1
示例#4
0
def try_new_route(
    channelidentifiers_to_channels: typing.ChannelMap,
    available_routes: typing.List[RouteState],
    transfer_description: TransferDescriptionWithSecretState,
    pseudo_random_generator: random.Random,
    block_number: typing.BlockNumber,
) -> TransitionResult:

    channel_state = next_channel_from_routes(
        available_routes,
        channelidentifiers_to_channels,
        transfer_description.amount,
    )

    events = list()
    if channel_state is None:
        if not available_routes:
            reason = 'there is no route available'
        else:
            reason = 'none of the available routes could be used'

        transfer_failed = EventPaymentSentFailed(
            payment_network_identifier=transfer_description.
            payment_network_identifier,
            token_network_identifier=transfer_description.
            token_network_identifier,
            identifier=transfer_description.payment_identifier,
            target=transfer_description.target,
            reason=reason,
        )
        events.append(transfer_failed)

        initiator_state = None

    else:
        message_identifier = message_identifier_from_prng(
            pseudo_random_generator)
        lockedtransfer_event = send_lockedtransfer(
            transfer_description=transfer_description,
            channel_state=channel_state,
            message_identifier=message_identifier,
            block_number=block_number,
        )
        assert lockedtransfer_event

        initiator_state = InitiatorTransferState(
            transfer_description=transfer_description,
            channel_identifier=channel_state.identifier,
            transfer=lockedtransfer_event.transfer,
            revealsecret=None,
        )
        events.append(lockedtransfer_event)

    return TransitionResult(initiator_state, events)
示例#5
0
def test_timestamped_event():
    event = EventPaymentSentFailed(make_token_network_registry_address(),
                                   make_address(), 1, make_address(),
                                   "whatever")
    log_time = datetime.fromisoformat("2018-09-07T20:02:35.000")

    timestamped = TimestampedEvent(event, log_time)
    assert timestamped.log_time == log_time
    assert isinstance(timestamped.event, EventPaymentSentFailed)
    assert timestamped.reason == timestamped.event.reason == "whatever"
    assert timestamped.identifier == timestamped.event.identifier == 1
示例#6
0
def handle_block(
    initiator_state: InitiatorTransferState,
    state_change: Block,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random,
) -> TransitionResult:
    secrethash = initiator_state.transfer.lock.secrethash
    locked_lock = channel_state.our_state.secrethashes_to_lockedlocks.get(
        secrethash)

    if not locked_lock:
        return TransitionResult(initiator_state, list())

    lock_expiration_threshold = typing.BlockNumber(
        locked_lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2, )
    lock_has_expired, _ = channel.is_lock_expired(
        end_state=channel_state.our_state,
        lock=locked_lock,
        block_number=state_change.block_number,
        lock_expiration_threshold=lock_expiration_threshold,
    )

    if lock_has_expired:
        expired_lock_events = channel.events_for_expired_lock(
            channel_state=channel_state,
            locked_lock=locked_lock,
            pseudo_random_generator=pseudo_random_generator,
        )
        transfer_description = initiator_state.transfer_description
        # TODO: When we introduce multiple transfers per payment this needs to be
        #       reconsidered. As we would want to try other routes once a route
        #       has failed, and a transfer failing does not mean the entire payment
        #       would have to fail.
        #       Related issue: https://github.com/raiden-network/raiden/issues/2329
        transfer_failed = EventPaymentSentFailed(
            payment_network_identifier=transfer_description.
            payment_network_identifier,
            token_network_identifier=transfer_description.
            token_network_identifier,
            identifier=transfer_description.payment_identifier,
            target=transfer_description.target,
            reason="transfer's lock has expired",
        )
        expired_lock_events.append(transfer_failed)
        return TransitionResult(
            None,
            typing.cast(typing.List[Event], expired_lock_events),
        )
    else:
        return TransitionResult(initiator_state, list())
示例#7
0
def test_timestamped_event():
    event = EventPaymentSentFailed(
        factories.make_payment_network_identifier(),
        factories.make_address(),
        1,
        factories.make_address(),
        'whatever',
    )
    log_time = '2018-09-07T20:02:35.000'

    timestamped = TimestampedEvent(event, log_time)
    assert timestamped.log_time == log_time
    assert timestamped.reason == timestamped.wrapped_event.reason == 'whatever'
    assert timestamped.identifier == 1
示例#8
0
def test_event_filter_for_payments():
    token_network_address = factories.make_address()
    secret = factories.make_secret()
    token_network_registry_address = factories.make_token_network_registry_address(
    )
    identifier = 1
    target = factories.make_address()
    event = EventPaymentSentSuccess(
        token_network_registry_address=token_network_registry_address,
        token_network_address=token_network_address,
        identifier=identifier,
        amount=5,
        target=target,
        secret=secret,
        route=[],
    )
    assert event_filter_for_payments(event=event, partner_address=None)
    assert event_filter_for_payments(event=event, partner_address=target)
    assert not event_filter_for_payments(
        event=event, partner_address=factories.make_address())

    event = EventPaymentReceivedSuccess(
        token_network_registry_address=token_network_registry_address,
        token_network_address=token_network_address,
        identifier=identifier,
        amount=5,
        initiator=target,
    )
    assert event_filter_for_payments(event=event, partner_address=None)
    assert event_filter_for_payments(event=event, partner_address=target)
    assert not event_filter_for_payments(
        event=event, partner_address=factories.make_address())

    event = EventPaymentSentFailed(
        token_network_registry_address=factories.
        make_token_network_registry_address(),
        token_network_address=token_network_address,
        identifier=identifier,
        target=target,
        reason="whatever",
    )
    assert event_filter_for_payments(event=event, partner_address=None)
    assert event_filter_for_payments(event=event, partner_address=target)
    assert not event_filter_for_payments(
        event=event, partner_address=factories.make_address())
示例#9
0
def test_event_filter_for_payments():
    secret = factories.make_secret()
    identifier = PaymentID(1)
    target = TargetAddress(factories.make_address())
    event1 = EventPaymentSentSuccess(
        token_network_registry_address=UNIT_TOKEN_NETWORK_REGISTRY_ADDRESS,
        token_network_address=UNIT_TOKEN_NETWORK_ADDRESS,
        identifier=identifier,
        amount=PaymentAmount(5),
        target=target,
        secret=secret,
        route=[],
    )
    assert event_filter_for_payments(event=event1, partner_address=None)
    assert event_filter_for_payments(event=event1,
                                     partner_address=Address(target))
    assert not event_filter_for_payments(
        event=event1, partner_address=factories.make_address())

    initiator = InitiatorAddress(factories.make_address())
    event2 = EventPaymentReceivedSuccess(
        token_network_registry_address=UNIT_TOKEN_NETWORK_REGISTRY_ADDRESS,
        token_network_address=UNIT_TOKEN_NETWORK_ADDRESS,
        identifier=identifier,
        amount=TokenAmount(5),
        initiator=initiator,
    )
    assert event_filter_for_payments(event=event2, partner_address=None)
    assert event_filter_for_payments(event=event2,
                                     partner_address=Address(initiator))
    assert not event_filter_for_payments(
        event=event2, partner_address=factories.make_address())

    event3 = EventPaymentSentFailed(
        token_network_registry_address=UNIT_TOKEN_NETWORK_REGISTRY_ADDRESS,
        token_network_address=UNIT_TOKEN_NETWORK_ADDRESS,
        identifier=identifier,
        target=target,
        reason="whatever",
    )
    assert event_filter_for_payments(event=event3, partner_address=None)
    assert event_filter_for_payments(event=event3,
                                     partner_address=Address(target))
    assert not event_filter_for_payments(
        event=event3, partner_address=factories.make_address())
示例#10
0
def handle_cancelpayment(
        payment_state: InitiatorPaymentState,
        channel_state: NettingChannelState,
) -> TransitionResult:
    """ Cancel the payment. """
    assert can_cancel(payment_state), 'Cannot cancel a transfer after the secret is revealed'

    transfer_description = payment_state.initiator.transfer_description
    cancel_events = cancel_current_route(payment_state)

    cancel = EventPaymentSentFailed(
        payment_network_identifier=channel_state.payment_network_identifier,
        token_network_identifier=channel_state.token_network_identifier,
        identifier=transfer_description.payment_identifier,
        target=transfer_description.target,
        reason='user canceled payment',
    )
    cancel_events.append(cancel)

    return TransitionResult(None, cancel_events)
示例#11
0
def test_v1_event_payment_sent_failed_schema():
    event = EventPaymentSentFailed(
        factories.make_payment_network_identifier(),
        factories.make_address(),
        1,
        factories.make_address(),
        'whatever',
    )
    log_time = '2018-09-07T20:02:35.000'

    timestamped = TimestampedEvent(event, log_time)

    dumped = EventPaymentSentFailedSchema().dump(timestamped)

    expected = {
        'event': 'EventPaymentSentFailed',
        'log_time': log_time,
        'reason': 'whatever',
    }

    assert all(dumped.data.get(key) == value for key, value in expected.items())
示例#12
0
def test_v1_event_payment_sent_failed_schema():
    event = EventPaymentSentFailed(
        token_network_registry_address=UNIT_TOKEN_NETWORK_REGISTRY_ADDRESS,
        token_network_address=UNIT_TOKEN_NETWORK_ADDRESS,
        identifier=PaymentID(1),
        target=TargetAddress(factories.make_address()),
        reason="whatever",
    )
    log_time = datetime.datetime.now()

    timestamped = TimestampedEvent(event, log_time)

    dumped = EventPaymentSentFailedSchema().dump(timestamped)

    expected = {
        "event": "EventPaymentSentFailed",
        "log_time": log_time.isoformat(),
        "reason": "whatever",
    }

    assert all(dumped.get(key) == value for key, value in expected.items())
示例#13
0
def test_event_filter_for_payments():
    token_network_identifier = factories.make_address()
    payment_network_identifier = factories.make_payment_network_identifier()
    identifier = 1
    target = factories.make_address()
    event = EventPaymentSentSuccess(
        payment_network_identifier=payment_network_identifier,
        token_network_identifier=token_network_identifier,
        identifier=identifier,
        amount=5,
        target=target,
    )
    assert event_filter_for_payments(event, token_network_identifier, None)
    assert event_filter_for_payments(event, token_network_identifier, target)
    assert not event_filter_for_payments(event, token_network_identifier,
                                         factories.make_address())

    event = EventPaymentReceivedSuccess(
        payment_network_identifier=payment_network_identifier,
        token_network_identifier=token_network_identifier,
        identifier=identifier,
        amount=5,
        initiator=target,
    )
    assert event_filter_for_payments(event, token_network_identifier, None)
    assert event_filter_for_payments(event, token_network_identifier, target)
    assert not event_filter_for_payments(event, token_network_identifier,
                                         factories.make_address())

    event = EventPaymentSentFailed(
        payment_network_identifier=factories.make_payment_network_identifier(),
        token_network_identifier=token_network_identifier,
        identifier=identifier,
        target=target,
        reason="whatever",
    )
    assert event_filter_for_payments(event, token_network_identifier, None)
    assert event_filter_for_payments(event, token_network_identifier, target)
    assert not event_filter_for_payments(event, token_network_identifier,
                                         factories.make_address())
示例#14
0
def test_v1_event_payment_sent_failed_schema():
    event = EventPaymentSentFailed(
        payment_network_identifier=factories.make_payment_network_identifier(),
        token_network_identifier=factories.make_address(),
        identifier=1,
        target=factories.make_address(),
        reason="whatever",
    )
    log_time = "2018-09-07T20:02:35.000"

    timestamped = TimestampedEvent(event, log_time)

    dumped = EventPaymentSentFailedSchema().dump(timestamped)

    expected = {
        "event": "EventPaymentSentFailed",
        "log_time": log_time,
        "reason": "whatever"
    }

    assert all(
        dumped.data.get(key) == value for key, value in expected.items())
示例#15
0
def test_write_read_events():
    wal = new_wal(state_transition_noop)

    event = EventPaymentSentFailed(
        factories.make_payment_network_identifier(),
        factories.make_address(),
        1,
        factories.make_address(),
        'whatever',
    )
    event_list = [event]

    with pytest.raises(sqlite3.IntegrityError):
        unexisting_state_change_id = 1
        wal.storage.write_events(
            unexisting_state_change_id,
            event_list,
            '2018-08-31T17:38:00.000',
        )

    previous_events = wal.storage.get_events_with_timestamps()

    log_time = '2018-09-07T20:02:35.0000'
    state_change_id = wal.storage.write_state_change('statechangedata',
                                                     log_time)
    wal.storage.write_events(
        state_change_id,
        event_list,
        log_time,
    )

    new_events = wal.storage.get_events_with_timestamps()
    assert len(previous_events) + 1 == len(new_events)

    latest_event = new_events[-1]
    assert isinstance(latest_event, TimestampedEvent)
    assert isinstance(latest_event.wrapped_event, EventPaymentSentFailed)
    assert latest_event.log_time == log_time
示例#16
0
def test_write_read_events():
    wal = new_wal(state_transition_noop)

    event = EventPaymentSentFailed(make_token_network_registry_address(),
                                   make_address(), 1, make_address(),
                                   "whatever")

    with pytest.raises(sqlite3.IntegrityError):
        unexisting_state_change_id = random.getrandbits(16 * 8).to_bytes(
            16, "big")
        wal.storage.write_events([(unexisting_state_change_id, event)])

    previous_events = wal.storage.get_events_with_timestamps()

    state_change_ids = wal.storage.write_state_changes([StateChange()])
    wal.storage.write_events([(state_change_ids[0], event)])

    new_events = wal.storage.get_events_with_timestamps()
    assert len(previous_events) + 1 == len(new_events)

    latest_event = new_events[-1]
    assert isinstance(latest_event, TimestampedEvent)
    assert isinstance(latest_event.wrapped_event, EventPaymentSentFailed)
示例#17
0
def test_write_read_events():
    wal = new_wal()

    event = EventPaymentSentFailed(2, 3, 1, 'address', 'whatever')
    event_list = [event]
    block_number = 10

    with pytest.raises(sqlite3.IntegrityError):
        unexisting_state_change_id = 1
        wal.storage.write_events(
            unexisting_state_change_id,
            block_number,
            event_list,
        )

    previous_events = wal.storage.get_events_by_identifier(
        from_identifier=0,
        to_identifier='latest',
    )

    state_change_id = wal.storage.write_state_change('statechangedata')
    wal.storage.write_events(
        state_change_id,
        block_number,
        event_list,
    )

    new_events = wal.storage.get_events_by_identifier(
        from_identifier=0,
        to_identifier='latest',
    )
    assert len(previous_events) + 1 == len(new_events)

    latest_event = new_events[-1]
    assert latest_event[0] == block_number
    assert isinstance(latest_event[1], EventPaymentSentFailed)
示例#18
0
def handle_action_transfer_direct(
    payment_network_identifier,
    token_network_state,
    state_change,
    pseudo_random_generator,
    block_number,
):
    receiver_address = state_change.receiver_address
    channels = [
        token_network_state.channelidentifiers_to_channels[channel_id]
        for channel_id in token_network_state.
        partneraddresses_to_channelidentifiers[receiver_address]
    ]
    channel_states = views.filter_channels_by_status(
        channels,
        [CHANNEL_STATE_UNUSABLE],
    )
    if channel_states:
        iteration = channel.state_transition(
            channel_states[-1],
            state_change,
            pseudo_random_generator,
            block_number,
        )
        events = iteration.events
    else:
        failure = EventPaymentSentFailed(
            payment_network_identifier,
            state_change.token_network_identifier,
            state_change.identifier,
            receiver_address,
            'Unknown partner channel',
        )
        events = [failure]

    return TransitionResult(token_network_state, events)
示例#19
0
def handle_block(
    initiator_state: InitiatorTransferState,
    state_change: Block,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random,
) -> TransitionResult:
    secrethash = initiator_state.transfer.lock.secrethash
    locked_lock = channel_state.our_state.secrethashes_to_lockedlocks.get(
        secrethash)

    if not locked_lock:
        if channel_state.partner_state.secrethashes_to_lockedlocks.get(
                secrethash):
            return TransitionResult(initiator_state, list())
        else:
            # if lock is not in our or our partner's locked locks then the
            # task can go
            return TransitionResult(None, list())

    lock_expiration_threshold = BlockNumber(
        locked_lock.expiration + DEFAULT_WAIT_BEFORE_LOCK_REMOVAL, )
    lock_has_expired, _ = channel.is_lock_expired(
        end_state=channel_state.our_state,
        lock=locked_lock,
        block_number=state_change.block_number,
        lock_expiration_threshold=lock_expiration_threshold,
    )

    if lock_has_expired:
        expired_lock_events = channel.events_for_expired_lock(
            channel_state=channel_state,
            locked_lock=locked_lock,
            pseudo_random_generator=pseudo_random_generator,
        )
        transfer_description = initiator_state.transfer_description
        # TODO: When we introduce multiple transfers per payment this needs to be
        #       reconsidered. As we would want to try other routes once a route
        #       has failed, and a transfer failing does not mean the entire payment
        #       would have to fail.
        #       Related issue: https://github.com/raiden-network/raiden/issues/2329
        transfer_failed = EventPaymentSentFailed(
            payment_network_identifier=transfer_description.
            payment_network_identifier,
            token_network_identifier=transfer_description.
            token_network_identifier,
            identifier=transfer_description.payment_identifier,
            target=transfer_description.target,
            reason="transfer's lock has expired",
        )
        expired_lock_events.append(transfer_failed)
        lock_exists = channel.lock_exists_in_either_channel_side(
            channel_state=channel_state,
            secrethash=secrethash,
        )

        return TransitionResult(
            # If the lock is either in our state or partner state we keep the
            # task around to wait for the LockExpired messages to sync.
            # Check https://github.com/raiden-network/raiden/issues/3183
            initiator_state if lock_exists else None,
            cast(List[Event], expired_lock_events),
        )
    else:
        return TransitionResult(initiator_state, list())
示例#20
0
def try_new_route(
    addresses_to_channel: Dict[Tuple[TokenNetworkAddress, Address],
                               NettingChannelState],
    nodeaddresses_to_networkstates: NodeNetworkStateMap,
    candidate_route_states: List[RouteState],
    transfer_description: TransferDescriptionWithSecretState,
    pseudo_random_generator: random.Random,
    block_number: BlockNumber,
) -> TransitionResult[Optional[InitiatorTransferState]]:

    initiator_state = None
    events: List[Event] = list()
    route_fee_exceeds_max = False

    channel_state = None
    route_state = None

    reachable_route_states = routes.filter_reachable_routes(
        candidate_route_states, nodeaddresses_to_networkstates)

    for reachable_route_state in reachable_route_states:
        candidate_channel_state = addresses_to_channel[(
            transfer_description.token_network_address,
            reachable_route_state.route[1])]

        amount_with_fee = calculate_safe_amount_with_fee(
            payment_amount=transfer_description.amount,
            estimated_fee=reachable_route_state.estimated_fee,
        )
        # https://github.com/raiden-network/raiden/issues/4751
        # If the transfer amount + fees exceeds a percentage of the
        # initial amount then don't use this route
        max_amount_limit = transfer_description.amount + int(
            transfer_description.amount * MAX_MEDIATION_FEE_PERC)
        if amount_with_fee > max_amount_limit:
            route_fee_exceeds_max = True
            continue

        channel_usability_state = channel.is_channel_usable_for_new_transfer(
            channel_state=candidate_channel_state,
            transfer_amount=amount_with_fee,
            lock_timeout=transfer_description.lock_timeout,
        )
        if channel_usability_state is channel.ChannelUsability.USABLE:
            channel_state = candidate_channel_state
            route_state = reachable_route_state
            break

    if route_state is None:
        if not reachable_route_states:
            reason = "there is no route available"
        else:
            reason = "none of the available routes could be used"

        if route_fee_exceeds_max:
            reason += (
                " and at least one of them exceeded the maximum fee limit "
                "(see https://docs.raiden.network/using-raiden/mediation-fees#frequently-asked-questions)"  # noqa
            )

        transfer_failed = EventPaymentSentFailed(
            token_network_registry_address=transfer_description.
            token_network_registry_address,
            token_network_address=transfer_description.token_network_address,
            identifier=transfer_description.payment_identifier,
            target=transfer_description.target,
            reason=reason,
        )
        events.append(transfer_failed)

        initiator_state = None

    else:
        assert channel_state is not None, "We must have a channel_state if we have a route_state"

        message_identifier = message_identifier_from_prng(
            pseudo_random_generator)
        lockedtransfer_event = send_lockedtransfer(
            transfer_description=transfer_description,
            channel_state=channel_state,
            message_identifier=message_identifier,
            block_number=block_number,
            route_state=route_state,
            route_states=reachable_route_states,
        )

        initiator_state = InitiatorTransferState(
            route=route_state,
            transfer_description=transfer_description,
            channel_identifier=channel_state.identifier,
            transfer=lockedtransfer_event.transfer,
        )
        events.append(lockedtransfer_event)

    return TransitionResult(initiator_state, events)
示例#21
0
def handle_block(
    initiator_state: InitiatorTransferState,
    state_change: Block,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random,
) -> TransitionResult[Optional[InitiatorTransferState]]:
    """ Checks if the lock has expired, and if it has sends a remove expired
    lock and emits the failing events.
    """
    secrethash = initiator_state.transfer.lock.secrethash
    locked_lock = channel_state.our_state.secrethashes_to_lockedlocks.get(
        secrethash)

    if not locked_lock:
        if channel_state.partner_state.secrethashes_to_lockedlocks.get(
                secrethash):
            return TransitionResult(initiator_state, list())
        else:
            # if lock is not in our or our partner's locked locks then the
            # task can go
            return TransitionResult(None, list())

    lock_expiration_threshold = BlockNumber(
        locked_lock.expiration + DEFAULT_WAIT_BEFORE_LOCK_REMOVAL, )
    lock_has_expired, _ = channel.is_lock_expired(
        end_state=channel_state.our_state,
        lock=locked_lock,
        block_number=state_change.block_number,
        lock_expiration_threshold=lock_expiration_threshold,
    )

    events: List[Event] = list()

    if lock_has_expired:
        is_channel_open = channel.get_status(
            channel_state) == CHANNEL_STATE_OPENED
        if is_channel_open:
            expired_lock_events = channel.events_for_expired_lock(
                channel_state=channel_state,
                locked_lock=locked_lock,
                pseudo_random_generator=pseudo_random_generator,
            )
            events.extend(expired_lock_events)

        if initiator_state.received_secret_request:
            reason = 'bad secret request message from target'
        else:
            reason = 'lock expired'

        transfer_description = initiator_state.transfer_description
        payment_identifier = transfer_description.payment_identifier
        # TODO: When we introduce multiple transfers per payment this needs to be
        #       reconsidered. As we would want to try other routes once a route
        #       has failed, and a transfer failing does not mean the entire payment
        #       would have to fail.
        #       Related issue: https://github.com/raiden-network/raiden/issues/2329
        payment_failed = EventPaymentSentFailed(
            payment_network_identifier=transfer_description.
            payment_network_identifier,
            token_network_identifier=transfer_description.
            token_network_identifier,
            identifier=payment_identifier,
            target=transfer_description.target,
            reason=reason,
        )
        unlock_failed = EventUnlockFailed(
            identifier=payment_identifier,
            secrethash=initiator_state.transfer_description.secrethash,
            reason=reason,
        )

        lock_exists = channel.lock_exists_in_either_channel_side(
            channel_state=channel_state,
            secrethash=secrethash,
        )

        return TransitionResult(
            # If the lock is either in our state or partner state we keep the
            # task around to wait for the LockExpired messages to sync.
            # Check https://github.com/raiden-network/raiden/issues/3183
            initiator_state if lock_exists else None,
            events + [payment_failed, unlock_failed],
        )
    else:
        return TransitionResult(initiator_state, events)
示例#22
0
def try_new_route(
    channelidentifiers_to_channels: Dict[ChannelID, NettingChannelState],
    nodeaddresses_to_networkstates: NodeNetworkStateMap,
    candidate_route_states: List[RouteState],
    transfer_description: TransferDescriptionWithSecretState,
    pseudo_random_generator: random.Random,
    block_number: BlockNumber,
) -> TransitionResult[Optional[InitiatorTransferState]]:

    initiator_state = None
    events: List[Event] = list()
    route_fee_exceeds_max = False

    channel_state = None
    route_state = None

    reachable_route_states = routes.filter_reachable_routes(
        candidate_route_states, nodeaddresses_to_networkstates)

    for reachable_route_state in reachable_route_states:
        forward_channel_id = reachable_route_state.forward_channel_id

        candidate_channel_state = forward_channel_id and channelidentifiers_to_channels.get(
            forward_channel_id)

        assert isinstance(candidate_channel_state, NettingChannelState)

        amount_with_fee = calculate_safe_amount_with_fee(
            payment_amount=transfer_description.amount,
            estimated_fee=reachable_route_state.estimated_fee,
        )
        # https://github.com/raiden-network/raiden/issues/4751
        # If the transfer amount + fees exceeds a percentage of the
        # initial amount then don't use this route
        max_amount_limit = transfer_description.amount + int(
            transfer_description.amount * MAX_MEDIATION_FEE_PERC)
        if amount_with_fee > max_amount_limit:
            route_fee_exceeds_max = True
            continue

        is_channel_usable = channel.is_channel_usable_for_new_transfer(
            channel_state=candidate_channel_state,
            transfer_amount=amount_with_fee,
            lock_timeout=transfer_description.lock_timeout,
        )
        if is_channel_usable:
            channel_state = candidate_channel_state
            route_state = reachable_route_state
            break

    if route_state is None:
        if not reachable_route_states:
            reason = "there is no route available"
        else:
            reason = "none of the available routes could be used"

        if route_fee_exceeds_max:
            reason += " and at least one of them exceeded the maximum fee limit"

        transfer_failed = EventPaymentSentFailed(
            token_network_registry_address=transfer_description.
            token_network_registry_address,
            token_network_address=transfer_description.token_network_address,
            identifier=transfer_description.payment_identifier,
            target=transfer_description.target,
            reason=reason,
        )
        events.append(transfer_failed)

        initiator_state = None

    else:
        assert channel_state is not None

        message_identifier = message_identifier_from_prng(
            pseudo_random_generator)
        lockedtransfer_event = send_lockedtransfer(
            transfer_description=transfer_description,
            channel_state=channel_state,
            message_identifier=message_identifier,
            block_number=block_number,
            route_state=route_state,
            route_states=reachable_route_states,
        )
        assert lockedtransfer_event

        initiator_state = InitiatorTransferState(
            route=route_state,
            transfer_description=transfer_description,
            channel_identifier=channel_state.identifier,
            transfer=lockedtransfer_event.transfer,
        )
        events.append(lockedtransfer_event)

    return TransitionResult(initiator_state, events)
示例#23
0
def try_new_route_light(
    channelidentifiers_to_channels: ChannelMap,
    available_routes: List[RouteState],
    transfer_description: TransferDescriptionWithoutSecretState,
    signed_locked_transfer: LockedTransfer
) -> TransitionResult[InitiatorTransferState]:
    channel_state = next_channel_from_routes(
        available_routes=available_routes,
        channelidentifiers_to_channels=channelidentifiers_to_channels,
        transfer_amount=transfer_description.amount,
        initiator=to_canonical_address(transfer_description.initiator)
    )

    events: List[Event] = list()
    if channel_state is None:
        if not available_routes:
            reason = "there is no route available"
        else:
            reason = "none of the available routes could be used"
        # TODO mmartinez handle persistance with status failure?
        transfer_failed = EventPaymentSentFailed(
            payment_network_identifier=transfer_description.payment_network_identifier,
            token_network_identifier=transfer_description.token_network_identifier,
            identifier=transfer_description.payment_identifier,
            target=transfer_description.target,
            reason=reason,
        )
        events.append(transfer_failed)

        initiator_state = None

    else:
        received_lock = signed_locked_transfer.lock

        calculated_lt_event, merkletree = create_sendlockedtransfer(
            channel_state,
            signed_locked_transfer.initiator,
            signed_locked_transfer.target,
            signed_locked_transfer.locked_amount,
            signed_locked_transfer.message_identifier,
            signed_locked_transfer.payment_identifier,
            signed_locked_transfer.payment_hash_invoice,
            received_lock.expiration,
            received_lock.secrethash,
        )

        calculated_transfer = calculated_lt_event.transfer
        lock = calculated_transfer.lock
        channel_state.our_state.balance_proof = calculated_transfer.balance_proof
        channel_state.our_state.merkletree = merkletree
        channel_state.our_state.secrethashes_to_lockedlocks[lock.secrethash] = lock

        lockedtransfer_event = SendLockedTransferLight(signed_locked_transfer.recipient,
                                                       signed_locked_transfer.channel_identifier,
                                                       signed_locked_transfer.message_identifier,
                                                       signed_locked_transfer)

        # Check that the constructed merkletree is equals to the sent by the light client.
        calculated_locksroot = merkleroot(merkletree)
        if signed_locked_transfer.locksroot.__eq__(calculated_locksroot):
            initiator_state = InitiatorTransferState(
                transfer_description=transfer_description,
                channel_identifier=channel_state.identifier,
                transfer=calculated_transfer,
                revealsecret=None,
            )
            store_signed_lt = StoreMessageEvent(signed_locked_transfer.message_identifier,
                                                signed_locked_transfer.payment_identifier, 1, signed_locked_transfer,
                                                True)

            events.append(lockedtransfer_event)
            events.append(store_signed_lt)

        else:
            transfer_failed = EventPaymentSentFailed(
                payment_network_identifier=transfer_description.payment_network_identifier,
                token_network_identifier=transfer_description.token_network_identifier,
                identifier=transfer_description.payment_identifier,
                target=transfer_description.target,
                reason="Received locksroot {} doesnt match with expected one {}".format(
                    signed_locked_transfer.locksroot.hex(), calculated_locksroot.hex()),
            )
            # FIXME mmartinez same
            events.append(transfer_failed)

            initiator_state = None

    return TransitionResult(initiator_state, events)