Beispiel #1
0
def handle_block(target_state, channel_state, block_number):
    """ After Raiden learns about a new block this function must be called to
    handle expiration of the hash time lock.
    """
    transfer = target_state.transfer
    secret_known = channel.is_secret_known(
        channel_state.partner_state,
        transfer.lock.hashlock,
    )

    if not secret_known and block_number > transfer.lock.expiration:
        # XXX: emit the event only once
        failed = EventWithdrawFailed(
            identifier=transfer.identifier,
            hashlock=transfer.lock.hashlock,
            reason='lock expired',
        )
        target_state.state = 'expired'
        events = [failed]

    elif target_state.state != 'waiting_close':  # only emit the close event once
        events = events_for_close(target_state, channel_state, block_number)
    else:
        events = list()

    iteration = TransitionResult(target_state, events)
    return iteration
Beispiel #2
0
def clear_if_finalized(iteration):
    """ Clears the state if the transfer was either completed or failed. """
    state = iteration.new_state

    if state is None:
        return iteration

    if state.from_transfer.secret is None and state.block_number > state.from_transfer.expiration:
        failed = EventWithdrawFailed(
            identifier=state.from_transfer.identifier,
            hashlock=state.from_transfer.hashlock,
            reason='lock expired',
        )
        iteration = TransitionResult(None, [failed])

    elif state.state == 'balance_proof':
        transfer_success = EventTransferReceivedSuccess(
            state.from_transfer.identifier,
            state.from_transfer.amount,
            state.from_transfer.initiator,
        )

        unlock_success = EventWithdrawSuccess(
            state.from_transfer.identifier,
            state.from_transfer.hashlock,
        )
        iteration = TransitionResult(None, [transfer_success, unlock_success])

    return iteration
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
Beispiel #4
0
def handle_inittarget(
    state_change,
    channel_state,
    pseudo_random_generator,
    block_number,
):
    """ Handles an ActionInitTarget state change. """
    transfer = state_change.transfer
    route = state_change.route

    target_state = TargetTransferState(
        route,
        transfer,
    )

    assert channel_state.identifier == transfer.balance_proof.channel_address
    is_valid, _, errormsg = channel.handle_receive_lockedtransfer(
        channel_state,
        transfer,
    )

    safe_to_wait = is_safe_to_wait(
        transfer.lock.expiration,
        channel_state.reveal_timeout,
        block_number,
    )

    # if there is not enough time to safely withdraw the token on-chain
    # silently let the transfer expire.
    if is_valid and safe_to_wait:
        message_identifier = message_identifier_from_prng(
            pseudo_random_generator)
        recipient = transfer.initiator
        queue_name = b'global'
        secret_request = SendSecretRequest(
            recipient,
            queue_name,
            message_identifier,
            transfer.payment_identifier,
            transfer.lock.amount,
            transfer.lock.secrethash,
        )

        iteration = TransitionResult(target_state, [secret_request])
    else:
        if not is_valid:
            failure_reason = errormsg
        elif not safe_to_wait:
            failure_reason = 'lock expiration is not safe'

        withdraw_failed = EventWithdrawFailed(
            identifier=transfer.payment_identifier,
            secrethash=transfer.lock.secrethash,
            reason=failure_reason,
        )
        iteration = TransitionResult(target_state, [withdraw_failed])

    return iteration
Beispiel #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'
            withdraw_failed = EventWithdrawFailed(
                pair.payer_transfer.payment_identifier,
                pair.payer_transfer.lock.secrethash,
                'lock expired',
            )
            events.append(withdraw_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
Beispiel #6
0
def handle_inittarget(state_change, channel_state, block_number):
    """ Handles an ActionInitTarget state change. """
    transfer = state_change.transfer
    route = state_change.route

    target_state = TargetTransferState(
        route,
        transfer,
    )

    assert channel_state.identifier == transfer.balance_proof.channel_address
    is_valid, errormsg = channel.handle_receive_mediatedtransfer(
        channel_state,
        transfer,
    )

    safe_to_wait = is_safe_to_wait(
        transfer.lock.expiration,
        channel_state.reveal_timeout,
        block_number,
    )

    # if there is not enough time to safely withdraw the token on-chain
    # silently let the transfer expire.
    if is_valid and safe_to_wait:
        secret_request = SendSecretRequest(
            transfer.identifier,
            transfer.lock.amount,
            transfer.lock.hashlock,
            transfer.initiator,
        )

        iteration = TransitionResult(target_state, [secret_request])
    else:
        if not is_valid:
            failure_reason = errormsg
        elif not safe_to_wait:
            failure_reason = 'lock expiration is not safe'

        withdraw_failed = EventWithdrawFailed(
            identifier=transfer.identifier,
            hashlock=transfer.lock.hashlock,
            reason=failure_reason,
        )
        iteration = TransitionResult(target_state, [withdraw_failed])

    return iteration