def set_expired_pairs(transfers_pair, block_number):
    """ Set the state of expired transfers, and return the failed events. """
    pending_transfers_pairs = get_pending_transfer_pairs(transfers_pair)

    events = list()
    for pair in pending_transfers_pairs:
        if block_number > pair.payer_transfer.expiration:
            assert pair.payee_state == 'payee_expired'
            assert pair.payee_transfer.expiration < pair.payer_transfer.expiration

            if pair.payer_state != 'payer_expired':
                pair.payer_state = 'payer_expired'
                withdraw_failed = EventWithdrawFailed(
                    pair.payer_transfer.identifier,
                    pair.payer_transfer.hashlock,
                    'lock expired',
                )
                events.append(withdraw_failed)

        elif block_number > pair.payee_transfer.expiration:
            assert pair.payee_state not in STATE_TRANSFER_PAID
            assert pair.payee_transfer.expiration < pair.payer_transfer.expiration

            if pair.payee_state != 'payee_expired':
                pair.payee_state = 'payee_expired'
                unlock_failed = EventUnlockFailed(
                    pair.payee_transfer.identifier,
                    pair.payee_transfer.hashlock,
                    'lock expired',
                )
                events.append(unlock_failed)

    return events
예제 #2
0
def events_for_cancel_current_route(transfer_description):
    unlock_failed = EventUnlockFailed(
        identifier=transfer_description.identifier,
        secrethash=transfer_description.secrethash,
        reason='route was canceled',
    )
    return [unlock_failed]
예제 #3
0
def events_for_cancel_current_route(
    transfer_description: TransferDescriptionWithSecretState, ) -> List[Event]:
    unlock_failed = EventUnlockFailed(
        identifier=transfer_description.payment_identifier,
        secrethash=transfer_description.secrethash,
        reason="route was canceled",
    )
    return [unlock_failed]
예제 #4
0
def events_to_remove_expired_locks(
        mediator_state: MediatorTransferState,
        channelidentifiers_to_channels: typing.ChannelMap,
        block_number: typing.BlockNumber,
        pseudo_random_generator: random.Random,
):
    """ Clear the channels which have expired locks.

    This only considers the *sent* transfers, received transfers can only be
    updated by the partner.
    """
    events = list()

    for transfer_pair in mediator_state.transfers_pair:
        balance_proof = transfer_pair.payee_transfer.balance_proof
        channel_identifier = balance_proof.channel_identifier
        channel_state = channelidentifiers_to_channels.get(channel_identifier)

        assert channel_state, "Couldn't find channel for channel_id: {}".format(channel_identifier)

        secrethash = mediator_state.secrethash
        lock = None
        if secrethash in channel_state.our_state.secrethashes_to_lockedlocks:
            assert secrethash not in channel_state.our_state.secrethashes_to_unlockedlocks
            lock = channel_state.our_state.secrethashes_to_lockedlocks.get(secrethash)
        elif secrethash in channel_state.our_state.secrethashes_to_unlockedlocks:
            lock = channel_state.our_state.secrethashes_to_unlockedlocks.get(secrethash)

        if lock:
            lock_expiration_threshold = (
                lock.expiration +
                DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2
            )
            has_lock_expired, _ = channel.is_lock_expired(
                end_state=channel_state.our_state,
                lock=lock,
                block_number=block_number,
                lock_expiration_threshold=lock_expiration_threshold,
            )

            if has_lock_expired:
                transfer_pair.payee_state = 'payee_expired'
                expired_lock_events = channel.events_for_expired_lock(
                    channel_state=channel_state,
                    locked_lock=lock,
                    pseudo_random_generator=pseudo_random_generator,
                )
                events.extend(expired_lock_events)

                unlock_failed = EventUnlockFailed(
                    transfer_pair.payee_transfer.payment_identifier,
                    transfer_pair.payee_transfer.lock.secrethash,
                    'lock expired',
                )
                events.append(unlock_failed)

    return events
예제 #5
0
def set_expired_pairs(transfers_pair, block_number):
    """ Set the state transfers to the expired state and return the failed events."""
    pending_transfers_pairs = get_pending_transfer_pairs(transfers_pair)

    events = list()
    for pair in pending_transfers_pairs:
        has_payee_transfer_expired = (
            block_number > pair.payee_transfer.lock.expiration and
            pair.payee_state != 'payee_expired'
        )
        has_payer_transfer_expired = (
            block_number > pair.payer_transfer.lock.expiration and
            pair.payer_state != 'payer_expired'
        )

        if has_payer_transfer_expired:
            # For safety, the correct behavior is:
            #
            # - If the payee has been paid, then the payer must pay too.
            #
            #   And the corollary:
            #
            # - If the payer transfer has expired, then the payee transfer must
            #   have expired too.
            #
            # The problem is that this corollary cannot be asserted. If a user
            # is running Raiden without a monitoring service, then it may go
            # offline after having paid a transfer to a payee, but without
            # getting a balance proof of the payer, and once it comes back
            # online the transfer may have expired.
            #
            # assert pair.payee_state == 'payee_expired'

            pair.payer_state = 'payer_expired'
            unlock_failed = EventUnlockClaimFailed(
                pair.payer_transfer.payment_identifier,
                pair.payer_transfer.lock.secrethash,
                'lock expired',
            )
            events.append(unlock_failed)

        elif has_payee_transfer_expired:
            pair.payee_state = 'payee_expired'
            unlock_failed = EventUnlockFailed(
                pair.payee_transfer.payment_identifier,
                pair.payee_transfer.lock.secrethash,
                'lock expired',
            )
            events.append(unlock_failed)

    return events
예제 #6
0
def events_for_cancel_current_route(
    route_state: RouteState, transfer_description: TransferDescriptionWithSecretState
) -> List[Event]:
    return [
        EventUnlockFailed(
            identifier=transfer_description.payment_identifier,
            secrethash=transfer_description.secrethash,
            reason="route was canceled",
        ),
        EventRouteFailed(
            secrethash=transfer_description.secrethash,
            route=route_state.route,
            token_network_address=transfer_description.token_network_address,
        ),
    ]
예제 #7
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)
예제 #8
0
def try_new_route(state):
    assert state.route is None, 'cannot try a new route while one is being used'

    # TODO:
    # - Route ranking. An upper layer should rate each route to optimize
    #   the fee price/quality of each route and add a rate from in the range
    #   [0.0,1.0].
    # - Add in a policy per route:
    #   - filtering, e.g. so the user may have a per route maximum transfer
    #     value based on fixed value or reputation.
    #   - reveal time computation
    #   - These policy details are better hidden from this implementation and
    #     changes should be applied through the use of Route state changes.

    # Find a single route that may fulfill the request, this uses a single
    # route intentionally
    try_route = None
    while state.routes.available_routes:
        route = state.routes.available_routes.pop(0)

        if route.available_balance < state.transfer.amount:
            state.routes.ignored_routes.append(route)
        else:
            try_route = route
            break

    unlock_failed = None
    if state.message:
        unlock_failed = EventUnlockFailed(
            identifier=state.transfer.identifier,
            hashlock=state.transfer.hashlock,
            reason='route was canceled',
        )

    if try_route is None:
        # No available route has sufficient balance for the current transfer,
        # cancel it.
        #
        # At this point we can just discard all the state data, this is only
        # valid because we are the initiator and we know that the secret was
        # not released.
        transfer_failed = EventTransferSentFailed(
            identifier=state.transfer.identifier,
            reason='no route available',
        )

        events = [transfer_failed]
        if unlock_failed:
            events.append(unlock_failed)
        iteration = TransitionResult(None, events)

    else:
        state.route = try_route

        secret = next(state.random_generator)
        hashlock = sha3(secret)

        # The initiator doesn't need to learn the secret, so there is no need
        # to decrement reveal_timeout from the lock timeout.
        #
        # The lock_expiration could be set to a value larger than
        # settle_timeout, this is not useful since the next hop will take this
        # channel settle_timeout as an upper limit for expiration.
        #
        # The two nodes will most likely disagree on latest block, as far as
        # the expiration goes this is no problem.
        lock_expiration = state.block_number + try_route.settle_timeout

        identifier = state.transfer.identifier

        transfer = LockedTransferState(
            identifier,
            state.transfer.amount,
            state.transfer.token,
            state.transfer.initiator,
            state.transfer.target,
            lock_expiration,
            hashlock,
            secret,
        )

        message = SendMediatedTransfer(
            transfer.identifier,
            transfer.token,
            transfer.amount,
            transfer.hashlock,
            state.our_address,
            transfer.target,
            lock_expiration,
            try_route.node_address,
        )

        state.transfer = transfer
        state.message = message

        events = [message]
        if unlock_failed:
            events.append(unlock_failed)

        iteration = TransitionResult(state, events)

    return iteration