예제 #1
0
def handle_secretrequest(
    initiator_state: InitiatorTransferState,
    state_change: ReceiveSecretRequest,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random,
) -> TransitionResult[InitiatorTransferState]:

    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,
    )

    already_received_secret_request = initiator_state.received_secret_request

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

    if already_received_secret_request and is_message_from_target:
        # A secret request was received earlier, all subsequent are ignored
        # as it might be an attack
        iteration = TransitionResult(initiator_state, list())

    elif is_valid_secretrequest and is_message_from_target:
        # 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=Address(recipient),
            channel_identifier=CHANNEL_IDENTIFIER_GLOBAL_QUEUE,
            message_identifier=message_identifier,
            secret=transfer_description.secret,
        )

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

    elif not is_valid_secretrequest and is_message_from_target:
        initiator_state.received_secret_request = True
        iteration = TransitionResult(initiator_state, list())

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

    return iteration
예제 #2
0
def handle_send_secret_reveal_light(
    initiator_state: InitiatorTransferState,
    state_change: ActionSendSecretRevealLight
) -> TransitionResult[InitiatorTransferState]:
    # 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 = state_change.reveal_secret.message_identifier
    transfer_description = initiator_state.transfer_description
    recipient = transfer_description.target
    revealsecret = SendSecretRevealLight(
        sender= Address(state_change.sender),
        recipient=Address(recipient),
        channel_identifier=CHANNEL_IDENTIFIER_GLOBAL_QUEUE,
        message_identifier=message_identifier,
        secret=state_change.reveal_secret.secret,
        signed_secret_reveal=state_change.reveal_secret
    )

    initiator_state.revealsecret = revealsecret
    initiator_state.received_secret_request = True
    store_message_event = StoreMessageEvent(message_identifier, transfer_description.payment_identifier, 7,
                                            state_change.reveal_secret, True)
    iteration = TransitionResult(initiator_state, [revealsecret, store_message_event])
    return iteration
예제 #3
0
def handle_secretrequest_light(
    initiator_state: InitiatorTransferState,
    state_change: ReceiveSecretRequestLight,
    channel_state: NettingChannelState
) -> TransitionResult[InitiatorTransferState]:
    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
    )

    # This should not ever happen. This task clears itself when the lock is
    # removed.
    assert lock is not None, "channel is does not have the transfer's lock"

    already_received_secret_request = initiator_state.received_secret_request

    # lock.amount includes the fees, transfer_description.amount is the actual
    # payment amount, for the transfer to be valid and the unlock allowed the
    # target must receive an amount between these values.
    is_valid_secretrequest = (
        state_change.amount <= lock.amount
        and state_change.amount >= initiator_state.transfer_description.amount
        and state_change.expiration == lock.expiration
        ## and initiator_state.transfer_description.secret != EMPTY_SECRET
    )

    if already_received_secret_request and is_message_from_target:
        # A secret request was received earlier, all subsequent are ignored
        # as it might be an attack
        iteration = TransitionResult(initiator_state, list())

    elif is_valid_secretrequest and is_message_from_target:
        store_event = StoreMessageEvent(state_change.secret_request_message.message_identifier,
                                        state_change.payment_identifier, 5, state_change.secret_request_message, True)
        initiator_state.received_secret_request = True
        iteration = TransitionResult(initiator_state, [store_event])

    elif not is_valid_secretrequest and is_message_from_target:
        initiator_state.received_secret_request = True
        iteration = TransitionResult(initiator_state, list())

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

    return iteration
예제 #4
0
def send_lockedtransfer(
        initiator_state: InitiatorTransferState,
        channel_state: NettingChannelState,
        message_identifier,
        block_number: typing.BlockNumber,
) -> SendLockedTransfer:
    """ Create a mediated transfer using channel.

    Raises:
        AssertionError: If the channel does not have enough capacity.
    """
    transfer_token_address = initiator_state.transfer_description.token_network_identifier
    assert channel_state.token_network_identifier == transfer_token_address

    transfer_description = initiator_state.transfer_description
    lock_expiration = get_initial_lock_expiration(
        block_number,
        channel_state.settle_timeout,
    )

    lockedtransfer_event = channel.send_lockedtransfer(
        channel_state,
        transfer_description.initiator,
        transfer_description.target,
        transfer_description.amount,
        message_identifier,
        transfer_description.payment_identifier,
        lock_expiration,
        transfer_description.secrethash,
    )
    assert lockedtransfer_event

    initiator_state.transfer = lockedtransfer_event.transfer

    return lockedtransfer_event
예제 #5
0
def handle_secretrequest(
    initiator_state: InitiatorTransferState,
    state_change: ReceiveSecretRequest,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random,
) -> TransitionResult:

    request_from_target = (state_change.sender
                           == initiator_state.transfer_description.target
                           and state_change.secrethash
                           == initiator_state.transfer_description.secrethash)

    is_valid_payment_id = (state_change.payment_identifier == initiator_state.
                           transfer_description.payment_identifier)

    valid_secretrequest = (request_from_target and is_valid_payment_id
                           and state_change.amount
                           == initiator_state.transfer_description.amount)

    invalid_secretrequest = request_from_target and (
        is_valid_payment_id
        or state_change.amount != initiator_state.transfer_description.amount)

    if 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
        queue_name = b'global'
        revealsecret = SendRevealSecret(
            recipient,
            queue_name,
            message_identifier,
            transfer_description.secret,
        )

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

    elif invalid_secretrequest:
        cancel = EventPaymentSentFailed(
            payment_network_identifier=channel_state.
            payment_network_identifier,
            token_network_identifier=channel_state.token_network_identifer,
            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
예제 #6
0
def send_lockedtransfer(
        initiator_state: InitiatorTransferState,
        channel_state: NettingChannelState,
        message_identifier,
        block_number: typing.BlockNumber,
) -> SendLockedTransfer:
    """ Create a mediated transfer using channel.

    Raises:
        AssertionError: If the channel does not have enough capacity.
    """
    transfer_token_address = initiator_state.transfer_description.token_network_identifier
    assert channel_state.token_network_identifier == transfer_token_address

    transfer_description = initiator_state.transfer_description
    lock_expiration = get_initial_lock_expiration(
        block_number,
        channel_state.settle_timeout,
    )

    lockedtransfer_event = channel.send_lockedtransfer(
        channel_state,
        transfer_description.initiator,
        transfer_description.target,
        transfer_description.amount,
        message_identifier,
        transfer_description.payment_identifier,
        lock_expiration,
        transfer_description.secrethash,
    )
    assert lockedtransfer_event

    initiator_state.transfer = lockedtransfer_event.transfer

    return lockedtransfer_event
예제 #7
0
파일: initiator.py 프로젝트: wooqii/raiden
def send_mediatedtransfer(
    initiator_state: InitiatorTransferState,
    channel_state: NettingChannelState,
    block_number: typing.BlockNumber,
) -> SendMediatedTransfer:
    """ Create a mediated transfer using channel.
    Raises:
        AssertionError: If the channel does not have enough capacity.
    """
    assert channel_state.token_address == initiator_state.transfer_description.token

    transfer_description = initiator_state.transfer_description
    lock_expiration = get_initial_lock_expiration(
        block_number,
        channel_state.settle_timeout,
    )

    mediatedtransfer_event = channel.send_mediatedtransfer(
        channel_state,
        transfer_description.initiator,
        transfer_description.target,
        transfer_description.amount,
        transfer_description.identifier,
        lock_expiration,
        transfer_description.hashlock,
    )
    assert mediatedtransfer_event

    initiator_state.transfer = mediatedtransfer_event.transfer

    return mediatedtransfer_event
예제 #8
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
예제 #9
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)
예제 #10
0
def handle_secretrequest(
        initiator_state: InitiatorTransferState,
        state_change: ReceiveSecretRequest,
        pseudo_random_generator: random.Random,
) -> TransitionResult:

    request_from_target = (
        state_change.sender == initiator_state.transfer_description.target and
        state_change.secrethash == initiator_state.transfer_description.secrethash
    )

    is_valid_payment_id = (
        state_change.payment_identifier == initiator_state.transfer_description.payment_identifier
    )

    valid_secretrequest = (
        request_from_target and
        is_valid_payment_id and
        state_change.amount == initiator_state.transfer_description.amount
    )

    invalid_secretrequest = request_from_target and (
        is_valid_payment_id or
        state_change.amount != initiator_state.transfer_description.amount
    )

    if 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
        queue_name = b'global'
        revealsecret = SendRevealSecret(
            recipient,
            queue_name,
            message_identifier,
            transfer_description.secret,
        )

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

    elif invalid_secretrequest:
        cancel = EventTransferSentFailed(
            identifier=initiator_state.transfer_description.payment_identifier,
            reason='bad secret request message from target',
        )
        iteration = TransitionResult(None, [cancel])

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

    return iteration
예제 #11
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)
예제 #12
0
파일: initiator.py 프로젝트: wooqii/raiden
def handle_secretrequest(
    initiator_state: InitiatorTransferState,
    state_change: ReceiveSecretRequest,
) -> TransitionResult:

    request_from_target = (state_change.sender
                           == initiator_state.transfer_description.target
                           and state_change.hashlock
                           == initiator_state.transfer_description.hashlock)

    valid_secretrequest = (request_from_target and state_change.identifier
                           == initiator_state.transfer_description.identifier
                           and state_change.amount
                           == initiator_state.transfer_description.amount)

    invalid_secretrequest = request_from_target and (
        state_change.identifier !=
        initiator_state.transfer_description.identifier
        or state_change.amount != initiator_state.transfer_description.amount)

    if 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
        #
        transfer_description = initiator_state.transfer_description
        reveal_secret = SendRevealSecret(
            transfer_description.identifier,
            transfer_description.secret,
            transfer_description.token,
            transfer_description.target,
        )

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

    elif invalid_secretrequest:
        cancel = EventTransferSentFailed(
            identifier=initiator_state.transfer_description.identifier,
            reason='bad secret request message from target',
        )
        iteration = TransitionResult(None, [cancel])

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

    return iteration
예제 #13
0
def test_initiator_task_view():
    """Test transfer_tasks_view(), which is used to generate the output of the
    pending transfers API, with an initiator task.
    """
    channel_id = factories.UNIT_CHANNEL_ID
    secret = factories.make_secret()
    payment_hash_invoice = factories.make_payment_hash_invoice()
    transfer = factories.create(
        factories.LockedTransferUnsignedStateProperties(
            secret=secret, payment_hash_invoice=payment_hash_invoice))
    secrethash = transfer.lock.secrethash
    transfer_description = TransferDescriptionWithSecretState(
        payment_network_identifier=factories.UNIT_PAYMENT_NETWORK_IDENTIFIER,
        payment_identifier=transfer.payment_identifier,
        payment_hash_invoice=transfer.payment_hash_invoice,
        amount=transfer.balance_proof.locked_amount,
        allocated_fee=0,
        token_network_identifier=factories.UNIT_TOKEN_NETWORK_ADDRESS,
        initiator=transfer.initiator,
        target=transfer.target,
        secret=secret,
    )
    transfer_state = InitiatorTransferState(
        transfer_description=transfer_description,
        channel_identifier=channel_id,
        transfer=transfer,
        revealsecret=None,
    )
    payment_state = InitiatorPaymentState({secrethash: transfer_state})
    task = InitiatorTask(
        token_network_identifier=factories.UNIT_TOKEN_NETWORK_ADDRESS,
        manager_state=payment_state)
    payment_mapping = {secrethash: task}

    view = transfer_tasks_view(payment_mapping)

    assert len(view) == 1
    pending_transfer = view[0]
    assert pending_transfer.get("role") == "initiator"
    balance_proof = transfer.balance_proof
    assert pending_transfer.get("channel_identifier") == str(
        balance_proof.channel_identifier)
    assert pending_transfer.get("locked_amount") == str(
        balance_proof.locked_amount)
    assert pending_transfer.get("transferred_amount") == str(
        balance_proof.transferred_amount)
예제 #14
0
def test_initiator_task_view():
    """Test transfer_tasks_view(), which is used to generate the output of the
    pending transfers API, with an initiator task.
    """
    channel_id = factories.UNIT_CHANNEL_ID
    secret = factories.make_secret()
    transfer = factories.create(
        factories.LockedTransferUnsignedStateProperties(secret=secret))
    secrethash = transfer.lock.secrethash
    transfer_description = TransferDescriptionWithSecretState(
        token_network_registry_address=factories.
        UNIT_TOKEN_NETWORK_REGISTRY_ADDRESS,
        payment_identifier=transfer.payment_identifier,
        amount=transfer.balance_proof.locked_amount,
        token_network_address=factories.UNIT_TOKEN_NETWORK_ADDRESS,
        initiator=transfer.initiator,
        target=transfer.target,
        secret=secret,
        secrethash=sha256_secrethash(secret),
    )
    transfer_state = InitiatorTransferState(
        route=RouteState(route=[transfer.initiator, transfer.target],
                         forward_channel_id=channel_id),
        transfer_description=transfer_description,
        channel_identifier=channel_id,
        transfer=transfer,
    )
    payment_state = InitiatorPaymentState(
        routes=[], initiator_transfers={secrethash: transfer_state})
    task = InitiatorTask(
        token_network_address=factories.UNIT_TOKEN_NETWORK_ADDRESS,
        manager_state=payment_state)
    payment_mapping = {secrethash: cast(TransferTask, task)}

    view = transfer_tasks_view(payment_mapping)

    assert len(view) == 1
    pending_transfer = view[0]
    assert pending_transfer.get("role") == "initiator"
    balance_proof = transfer.balance_proof
    assert pending_transfer.get("channel_identifier") == str(
        balance_proof.channel_identifier)
    assert pending_transfer.get("locked_amount") == str(
        balance_proof.locked_amount)
    assert pending_transfer.get("transferred_amount") == str(
        balance_proof.transferred_amount)
예제 #15
0
파일: initiator.py 프로젝트: wooqii/raiden
def try_new_route(
    channelidentifiers_to_channels: ChannelMap,
    available_routes: typing.List[RouteState],
    transfer_description: TransferDescriptionWithSecretState,
    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 = EventTransferSentFailed(
            identifier=transfer_description.identifier,
            reason=reason,
        )
        events.append(transfer_failed)

        initiator_state = None

    else:
        initiator_state = InitiatorTransferState(
            transfer_description,
            channel_state.identifier,
        )

        mediatedtransfer_event = send_mediatedtransfer(
            initiator_state,
            channel_state,
            block_number,
        )
        assert mediatedtransfer_event

        events.append(mediatedtransfer_event)

    return TransitionResult(initiator_state, events)
예제 #16
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)
예제 #17
0
def handle_secretrequest(
    initiator_state: InitiatorTransferState,
    state_change: ReceiveSecretRequest,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random,
) -> TransitionResult[InitiatorTransferState]:

    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)

    # This should not ever happen. This task clears itself when the lock is
    # removed.
    assert lock is not None, "channel is does not have the transfer's lock"

    already_received_secret_request = initiator_state.received_secret_request

    # transfer_description.amount is the actual payment amount without fees.
    # For the transfer to be valid and the unlock allowed the target must
    # receive at least that amount.
    is_valid_secretrequest = (
        state_change.amount >= initiator_state.transfer_description.amount
        and state_change.expiration == lock.expiration
        and initiator_state.transfer_description.secret != ABSENT_SECRET)

    if already_received_secret_request and is_message_from_target:
        # A secret request was received earlier, all subsequent are ignored
        # as it might be an attack
        iteration = TransitionResult(initiator_state, list())

    elif is_valid_secretrequest and is_message_from_target:
        # 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=Address(recipient),
            message_identifier=message_identifier,
            secret=transfer_description.secret,
            canonical_identifier=CANONICAL_IDENTIFIER_GLOBAL_QUEUE,
        )

        initiator_state.transfer_state = "transfer_secret_revealed"
        initiator_state.received_secret_request = True
        iteration = TransitionResult(initiator_state, [revealsecret])

    elif not is_valid_secretrequest and is_message_from_target:
        initiator_state.received_secret_request = True
        invalid_request = EventInvalidSecretRequest(
            payment_identifier=state_change.payment_identifier,
            intended_amount=initiator_state.transfer_description.amount,
            actual_amount=state_change.amount,
        )
        iteration = TransitionResult(initiator_state, [invalid_request])

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

    return iteration
예제 #18
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)
예제 #19
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 = BlockExpiration(
        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 and initiator_state.transfer_state != "transfer_expired":
        is_channel_open = channel.get_status(
            channel_state) == ChannelState.STATE_OPENED
        if is_channel_open:
            expired_lock_events = channel.send_lock_expired(
                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(
            token_network_registry_address=transfer_description.
            token_network_registry_address,
            token_network_address=transfer_description.token_network_address,
            identifier=payment_identifier,
            target=transfer_description.target,
            reason=reason,
        )
        route_failed = EventRouteFailed(
            secrethash=secrethash,
            route=initiator_state.route.route,
            token_network_address=transfer_description.token_network_address,
        )
        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)
        initiator_state.transfer_state = "transfer_expired"

        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, route_failed, unlock_failed],
        )
    else:
        return TransitionResult(initiator_state, events)
예제 #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)