Example #1
0
def events_for_expired_locks(
        mediator_state: MediatorTransferState,
        channelidentifiers_to_channels: typing.ChannelMap,
        block_number: typing.BlockNumber,
        pseudo_random_generator: random.Random,
):
    events = list()

    transfer_pair: MediationPairState
    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
        locked_lock = channel_state.our_state.secrethashes_to_lockedlocks.get(secrethash)
        lock_expired = channel.is_lock_expired(
            end_state=channel_state.our_state,
            locked_lock=locked_lock,
            secrethash=secrethash,
            block_number=block_number,
        )
        if locked_lock and lock_expired:
            # Lock has expired, cleanup...
            transfer_pair.payee_state = 'payee_expired'
            expired_lock_events = channel.events_for_expired_lock(
                channel_state,
                secrethash,
                locked_lock,
                pseudo_random_generator,
            )
            events.extend(expired_lock_events)
    return events
Example #2
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 = (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,
        )
        return TransitionResult(None, expired_lock_events)
    else:
        return TransitionResult(initiator_state, list())
Example #3
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)

    lock_expired = channel.is_lock_expired(
        end_state=channel_state.our_state,
        locked_lock=locked_lock,
        secrethash=secrethash,
        block_number=state_change.block_number,
    )
    if locked_lock and lock_expired:
        # Lock has expired, cleanup...
        expired_lock_events = channel.events_for_expired_lock(
            channel_state,
            secrethash,
            locked_lock,
            pseudo_random_generator,
        )

        iteration = TransitionResult(None, expired_lock_events)
        return iteration

    return TransitionResult(initiator_state, list())
Example #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
Example #5
0
def handle_onchain_secretreveal(
    initiator_state: InitiatorTransferState,
    state_change: ContractReceiveSecretReveal,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random,
    block_number: BlockNumber,
) -> TransitionResult[Optional[InitiatorTransferState]]:
    """ When a secret is revealed on-chain all nodes learn the secret.

    This check the on-chain secret corresponds to the one used by the
    initiator, and if valid a new balance proof is sent to the next hop with
    the current lock removed from the pending locks and the transferred amount
    updated.
    """
    iteration: TransitionResult[Optional[InitiatorTransferState]]
    secret = state_change.secret
    secrethash = initiator_state.transfer_description.secrethash
    is_valid_secret = is_valid_secret_reveal(state_change=state_change,
                                             transfer_secrethash=secrethash)
    is_channel_open = channel.get_status(
        channel_state) == ChannelState.STATE_OPENED
    is_lock_expired = state_change.block_number > initiator_state.transfer.lock.expiration

    is_lock_unlocked = is_valid_secret and not is_lock_expired

    if is_lock_unlocked:
        channel.register_onchain_secret(
            channel_state=channel_state,
            secret=secret,
            secrethash=secrethash,
            secret_reveal_block_number=state_change.block_number,
        )

    lock = initiator_state.transfer.lock
    expired = channel.is_lock_expired(
        end_state=channel_state.our_state,
        lock=lock,
        block_number=block_number,
        lock_expiration_threshold=lock.expiration,
    )

    if is_lock_unlocked and is_channel_open and not expired:
        events = events_for_unlock_lock(
            initiator_state,
            channel_state,
            state_change.secret,
            state_change.secrethash,
            pseudo_random_generator,
            block_number,
        )
        iteration = TransitionResult(None, events)
    else:
        events = list()
        iteration = TransitionResult(initiator_state, events)

    return iteration
Example #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())
Example #7
0
def handle_offchain_secretreveal(
    initiator_state: InitiatorTransferState,
    state_change: ReceiveSecretReveal,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random,
    block_number: BlockNumber,
) -> TransitionResult[Optional[InitiatorTransferState]]:
    """ Once the next hop proves it knows the secret, the initiator can unlock
    the mediated transfer.

    This will validate the secret, and if valid a new balance proof is sent to
    the next hop with the current lock removed from the pending locks and the
    transferred amount updated.
    """
    iteration: TransitionResult[Optional[InitiatorTransferState]]
    valid_reveal = is_valid_secret_reveal(
        state_change=state_change,
        transfer_secrethash=initiator_state.transfer_description.secrethash,
    )
    sent_by_partner = state_change.sender == channel_state.partner_state.address
    is_channel_open = channel.get_status(
        channel_state) == ChannelState.STATE_OPENED

    lock = initiator_state.transfer.lock
    expired = channel.is_lock_expired(
        end_state=channel_state.our_state,
        lock=lock,
        block_number=block_number,
        lock_expiration_threshold=lock.expiration,
    )

    if valid_reveal and is_channel_open and sent_by_partner and not expired:
        events = events_for_unlock_lock(
            initiator_state=initiator_state,
            channel_state=channel_state,
            secret=state_change.secret,
            secrethash=state_change.secrethash,
            pseudo_random_generator=pseudo_random_generator,
            block_number=block_number,
        )
        iteration = TransitionResult(None, events)
    else:
        events = list()
        iteration = TransitionResult(initiator_state, events)

    return iteration
Example #8
0
def handle_block(
    target_state: TargetTransferState,
    channel_state: NettingChannelState,
    block_number: BlockNumber,
    block_hash: BlockHash,
) -> TransitionResult[TargetTransferState]:
    """ After Raiden learns about a new block this function must be called to
    handle expiration of the hash time lock.
    """
    transfer = target_state.transfer
    events = list()
    lock = transfer.lock

    secret_known = channel.is_secret_known(
        channel_state.partner_state,
        lock.secrethash,
    )
    lock_has_expired, _ = channel.is_lock_expired(
        end_state=channel_state.our_state,
        lock=lock,
        block_number=block_number,
        lock_expiration_threshold=channel.get_receiver_expiration_threshold(
            lock),
    )

    if lock_has_expired and target_state.state != 'expired':
        failed = EventUnlockClaimFailed(
            identifier=transfer.payment_identifier,
            secrethash=transfer.lock.secrethash,
            reason=f'lock expired',
        )
        target_state.state = TargetTransferState.EXPIRED
        events = [failed]
    elif secret_known:
        events = events_for_onchain_secretreveal(
            target_state=target_state,
            channel_state=channel_state,
            block_number=block_number,
            block_hash=block_hash,
        )

    return TransitionResult(target_state, events)
Example #9
0
def events_for_expired_locks(
    mediator_state: MediatorTransferState,
    channelidentifiers_to_channels: typing.ChannelMap,
    block_number: typing.BlockNumber,
    pseudo_random_generator: random.Random,
):
    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
        locked_lock = channel_state.our_state.secrethashes_to_lockedlocks.get(
            secrethash)

        if locked_lock:
            lock_expiration_threshold = (
                locked_lock.expiration +
                DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2)
            has_lock_expired, _ = channel.is_lock_expired(
                end_state=channel_state.our_state,
                lock=locked_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,
                    secrethash,
                    locked_lock,
                    pseudo_random_generator,
                )
                events.extend(expired_lock_events)
    return events
Example #10
0
def handle_block(
    target_state: TargetTransferState,
    channel_state: NettingChannelState,
    block_number: typing.BlockNumber,
):
    """ After Raiden learns about a new block this function must be called to
    handle expiration of the hash time lock.
    """
    transfer = target_state.transfer
    events = list()
    lock = transfer.lock

    secret_known = channel.is_secret_known(
        channel_state.partner_state,
        lock.secrethash,
    )
    lock_has_expired, _ = channel.is_lock_expired(
        end_state=channel_state.our_state,
        lock=lock,
        block_number=block_number,
        lock_expiration_threshold=typing.BlockNumber(
            lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS, ),
    )

    if lock_has_expired and target_state.state != 'expired':
        failed = EventUnlockClaimFailed(
            identifier=transfer.payment_identifier,
            secrethash=transfer.lock.secrethash,
            reason=f'lock expired',
        )
        target_state.state = 'expired'
        events = [failed]
    elif secret_known:
        events = events_for_onchain_secretreveal(
            target_state,
            channel_state,
            block_number,
        )

    return TransitionResult(target_state, events)
Example #11
0
def handle_block(
        target_state: TargetTransferState,
        channel_state: NettingChannelState,
        block_number: BlockNumber,
):
    """ After Raiden learns about a new block this function must be called to
    handle expiration of the hash time lock.
    """
    transfer = target_state.transfer
    events = list()
    lock = transfer.lock

    secret_known = channel.is_secret_known(
        channel_state.partner_state,
        lock.secrethash,
    )
    lock_has_expired, _ = channel.is_lock_expired(
        end_state=channel_state.our_state,
        lock=lock,
        block_number=block_number,
        lock_expiration_threshold=channel.get_receiver_expiration_threshold(lock),
    )

    if lock_has_expired and target_state.state != 'expired':
        failed = EventUnlockClaimFailed(
            identifier=transfer.payment_identifier,
            secrethash=transfer.lock.secrethash,
            reason=f'lock expired',
        )
        target_state.state = TargetTransferState.EXPIRED
        events = [failed]
    elif secret_known:
        events = events_for_onchain_secretreveal(
            target_state,
            channel_state,
            block_number,
        )

    return TransitionResult(target_state, events)
Example #12
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)
Example #13
0
def events_for_expired_pairs(
        channelidentifiers_to_channels: typing.ChannelMap,
        transfers_pair: typing.List[MediationPairState],
        waiting_transfer: WaitingTransferState,
        block_number: typing.BlockNumber,
) -> typing.List[Event]:
    """ Informational events for expired locks. """
    pending_transfers_pairs = get_pending_transfer_pairs(transfers_pair)

    events = list()
    for pair in pending_transfers_pairs:
        payer_balance_proof = pair.payer_transfer.balance_proof
        payer_channel = channelidentifiers_to_channels.get(payer_balance_proof.channel_identifier)
        payer_lock_expiration_threshold = (
            pair.payer_transfer.lock.expiration +
            DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2
        )
        has_payer_lock_expired, _ = channel.is_lock_expired(
            end_state=payer_channel.our_state,
            lock=pair.payer_transfer.lock,
            block_number=block_number,
            lock_expiration_threshold=payer_lock_expiration_threshold,
        )
        has_payer_transfer_expired = (
            has_payer_lock_expired 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_claim_failed = EventUnlockClaimFailed(
                pair.payer_transfer.payment_identifier,
                pair.payer_transfer.lock.secrethash,
                'lock expired',
            )
            events.append(unlock_claim_failed)

    if waiting_transfer and waiting_transfer.state != 'expired':
        waiting_transfer.state = 'expired'
        unlock_claim_failed = EventUnlockClaimFailed(
            waiting_transfer.transfer.payment_identifier,
            waiting_transfer.transfer.lock.secrethash,
            'lock expired',
        )
        events.append(unlock_claim_failed)

    return events
Example #14
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())