Beispiel #1
0
def handle_offchain_secretreveal_light(
    initiator_state: InitiatorTransferState,
    state_change: ReceiveSecretRevealLight,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random
) -> TransitionResult[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 merkle tree and the
    transferred amount updated.
    """
    iteration: TransitionResult[InitiatorTransferState]
    valid_reveal = is_valid_secret_reveal(
        state_change=state_change,
        transfer_secrethash=initiator_state.transfer_description.secrethash,
        secret=state_change.secret,
    )
    sent_by_partner = state_change.sender == channel_state.partner_state.address
    is_channel_open = channel.get_status(channel_state) == CHANNEL_STATE_OPENED

    if valid_reveal and is_channel_open and sent_by_partner:
        unlock_events = events_for_unlock_base(
            initiator_state=initiator_state,
            channel_state=channel_state,
            secret=state_change.secret,
        )

        transfer_description = initiator_state.transfer_description

        message_identifier = message_identifier_from_prng(pseudo_random_generator)
        unlock_lock = channel.send_unlock(
            channel_state=channel_state,
            message_identifier=message_identifier,
            payment_identifier=transfer_description.payment_identifier,
            secret=state_change.secret,
            secrethash=state_change.secrethash,
        )
        unlock_msg = Unlock.from_event(unlock_lock)

        store_received_secret_reveal_event = StoreMessageEvent(state_change.secret_reveal_message.message_identifier,
                                                               transfer_description.payment_identifier, 9,
                                                               state_change.secret_reveal_message,
                                                               True)
        store_created_unlock_event = StoreMessageEvent(message_identifier, transfer_description.payment_identifier, 11,
                                                       unlock_msg, False)

        events = list()
        events.append(store_received_secret_reveal_event)
        events.append(store_created_unlock_event)
        events.extend(unlock_events)
        iteration = TransitionResult(None, events)
    else:
        events = list()
        iteration = TransitionResult(initiator_state, events)

    return iteration
Beispiel #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
Beispiel #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
Beispiel #4
0
def handle_send_secret_request_light(
    target_state: TargetTransferState,
    state_change: ActionSendSecretRequestLight,
    channel_state: NettingChannelState,
    block_number: BlockNumber
) -> TransitionResult[TargetTransferState]:
    """ Handles an ActionInitTarget state change. """
    transfer = target_state.transfer

    assert channel_state.identifier == transfer.balance_proof.channel_identifier

    events = list()

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

    # If there is not enough time to safely unlock the lock on-chain
    # silently let the transfer expire. The target task must be created to
    # handle the ReceiveLockExpired state change, which will clear the
    # expired lock.
    #
    # We add a new validation.
    # It is verified that if there was an invoice it was paid successfully,
    # if it was not, the payment is interrupted
    # by not generating an event send secret request
    if safe_to_wait:
        secret_request_light = SendSecretRequestLight(
            sender=Address(target_state.transfer.target),
            recipient=Address(target_state.transfer.initiator),
            channel_identifier=CHANNEL_IDENTIFIER_GLOBAL_QUEUE,
            message_identifier=state_change.secret_request.message_identifier,
            payment_identifier=transfer.payment_identifier,
            amount=transfer.lock.amount,
            expiration=transfer.lock.expiration,
            secrethash=transfer.lock.secrethash,
            signed_secret_request=state_change.secret_request
        )

        store_secret_request_event = StoreMessageEvent(state_change.secret_request.message_identifier,
                                                       transfer.payment_identifier,
                                                       5,
                                                       state_change.secret_request,
                                                       True)
        events.append(secret_request_light)
        events.append(store_secret_request_event)

    iteration = TransitionResult(target_state, events)
    return iteration
Beispiel #5
0
def handle_send_secret_reveal_light(
    target_state: TargetTransferState,
    state_change: ActionSendSecretRevealLight
) -> TransitionResult[TargetTransferState]:
    message_identifier = state_change.reveal_secret.message_identifier
    transfer = target_state.transfer
    # The recipiant is the initiator of the payment cause the light client is the target of the payment
    recipient = transfer.initiator
    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
    )
    store_reveal_secret_event = StoreMessageEvent(message_identifier, transfer.payment_identifier, 9,
                                                  state_change.reveal_secret, True)
    iteration = TransitionResult(target_state, [revealsecret, store_reveal_secret_event])
    return iteration
Beispiel #6
0
def handle_unlock_light(
    target_state: TargetTransferState,
    state_change: ReceiveUnlockLight,
    channel_state: NettingChannelState,
) -> TransitionResult[TargetTransferState]:
    """ Handles a ReceiveUnlockLight state change. """

    is_valid, events, _ = channel.handle_unlock_light(channel_state, state_change)
    next_target_state: Optional[TargetTransferState] = target_state

    if is_valid:
        transfer = target_state.transfer
        payment_received_success = EventPaymentReceivedSuccess(
            payment_network_identifier=channel_state.payment_network_identifier,
            token_network_identifier=TokenNetworkID(channel_state.token_network_identifier),
            identifier=transfer.payment_identifier,
            amount=TokenAmount(transfer.lock.amount),
            initiator=transfer.initiator,
        )

        unlock_success = EventUnlockClaimSuccess(
            transfer.payment_identifier, transfer.lock.secrethash
        )

        store_unlock_message = StoreMessageEvent(
            state_change.signed_unlock.message_identifier,
            state_change.signed_unlock.payment_identifier,
            11,
            state_change.signed_unlock,
            True
        )

        events.extend([payment_received_success, unlock_success, store_unlock_message])
        next_target_state = None

    return TransitionResult(next_target_state, events)
Beispiel #7
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)
Beispiel #8
0
def handle_offchain_secretreveal_light(
    target_state: TargetTransferState,
    state_change: ReceiveSecretRevealLight,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random,
    block_number: BlockNumber,
) -> TransitionResult[TargetTransferState]:
    """ Validates and handles a ReceiveSecretReveal state change. """
    valid_secret = is_valid_secret_reveal(
        state_change=state_change,
        transfer_secrethash=target_state.transfer.lock.secrethash,
        secret=state_change.secret,
    )
    has_transfer_expired = channel.is_transfer_expired(
        transfer=target_state.transfer, affected_channel=channel_state, block_number=block_number
    )

    if valid_secret and not has_transfer_expired:
        # TODO mmarcosmartinez7 this cannot be done without LC interaction
        # channel.register_offchain_secret(
        #     channel_state=channel_state,
        #     secret=state_change.secret,
        #     secrethash=state_change.secrethash,
        # )

        route = target_state.route
        message_identifier = message_identifier_from_prng(pseudo_random_generator)
        target_state.state = TargetTransferState.OFFCHAIN_SECRET_REVEAL
        target_state.secret = state_change.secret
        recipient = route.node_address

        # Store reveal secret 7, create reveal secret 9 and store it for LC signing.

        received_reveal_secret = state_change.secret_reveal_message
        reveal_secret_to_send_event = SendSecretReveal(
            recipient=recipient,
            channel_identifier=CHANNEL_IDENTIFIER_GLOBAL_QUEUE,
            message_identifier=message_identifier,
            secret=target_state.secret,
        )
        reveal_secret_to_send_msg = message_from_sendevent(reveal_secret_to_send_event)

        store_received_reveal = StoreMessageEvent(
            received_reveal_secret.message_identifier,
            target_state.transfer.payment_identifier,
            7,
            received_reveal_secret,
            True
        )

        store_reveal_to_send = StoreMessageEvent(
            message_identifier,
            target_state.transfer.payment_identifier,
            9,
            reveal_secret_to_send_msg,
            False
        )

        iteration = TransitionResult(target_state, [store_received_reveal, store_reveal_to_send])

    else:
        # TODO: event for byzantine behavior
        iteration = TransitionResult(target_state, list())

    return iteration
Beispiel #9
0
def handle_inittarget_light(
    state_change: ActionInitTargetLight,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random,
    block_number: BlockNumber,
    storage
) -> TransitionResult[TargetTransferState]:
    """ Handles an ActionInitTarget state change. """
    transfer = state_change.transfer
    route = state_change.route

    assert channel_state.identifier == transfer.balance_proof.channel_identifier
    is_valid, channel_events, errormsg, handle_invoice_result = channel.handle_receive_lockedtransfer_light(
        channel_state, transfer, storage
    )

    if is_valid:
        # A valid balance proof does not mean the payment itself is still valid.
        # e.g. the lock may be near expiration or have expired. This is fine. The
        # message with an unusable lock must be handled to properly synchronize the
        # local view of the partner's channel state, allowing the next balance
        # proofs to be handled. This however, must only be done once, which is
        # enforced by the nonce increasing sequentially, which is verified by
        # the handler handle_receive_lockedtransfer.
        target_state = TargetTransferState(route, 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 unlock the lock on-chain
        # silently let the transfer expire. The target task must be created to
        # handle the ReceiveLockExpired state change, which will clear the
        # expired lock.
        #
        # We add a new validation.
        # It is verified that if there was an invoice it was paid successfully,
        # if it was not, the payment is interrupted
        # by not generating an event send secret request
        if safe_to_wait and handle_invoice_result['is_valid']:
            payment = LightClientPayment(
                state_change.transfer.target, state_change.transfer.initiator,
                False,
                channel_state.token_network_identifier,
                transfer.lock.amount,
                str(date.today()),
                LightClientPaymentStatus.Pending,
                transfer.payment_identifier
            )

            payment_exists = LightClientService.get_light_client_payment(payment.payment_id, storage)
            if not payment_exists:
                LightClientMessageHandler.store_light_client_payment(payment, storage)

            message_identifier = message_identifier_from_prng(pseudo_random_generator)
            recipient = transfer.initiator
            secret_request = SendSecretRequest(
                recipient=Address(recipient),
                channel_identifier=CHANNEL_IDENTIFIER_GLOBAL_QUEUE,
                message_identifier=message_identifier,
                payment_identifier=transfer.payment_identifier,
                amount=transfer.lock.amount,
                expiration=transfer.lock.expiration,
                secrethash=transfer.lock.secrethash,
            )

            store_locked_transfer_event = StoreMessageEvent(transfer.message_identifier, transfer.payment_identifier, 1,
                                                            state_change.signed_lockedtransfer, True)

            secret_request_message = SecretRequest.from_event(secret_request)
            store_secret_request_event = StoreMessageEvent(message_identifier, transfer.payment_identifier, 5,
                                                           secret_request_message, False)
            channel_events.append(store_secret_request_event)
            channel_events.append(store_locked_transfer_event)

        iteration = TransitionResult(target_state, channel_events)
    else:
        # If the balance proof is not valid, do *not* create a task. Otherwise it's
        # possible for an attacker to send multiple invalid transfers, and increase
        # the memory usage of this Node.
        assert errormsg, "handle_receive_lockedtransfer should return error msg if not valid"
        unlock_failed = EventUnlockClaimFailed(
            identifier=transfer.payment_identifier,
            secrethash=transfer.lock.secrethash,
            reason=errormsg,
        )
        channel_events.append(unlock_failed)
        iteration = TransitionResult(None, channel_events)

    return iteration