Пример #1
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 = 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)
        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,
            typing.cast(typing.List[Event], expired_lock_events),
        )
    else:
        return TransitionResult(initiator_state, list())
Пример #2
0
def iteration_from_sub(payment_state, iteration):
    if iteration.new_state:
        payment_state.initiator = iteration.new_state
        return TransitionResult(payment_state, iteration.events)
    return iteration
Пример #3
0
def subdispatch_to_paymenttask(
    chain_state: ChainState, state_change: StateChange, secrethash: SecretHash
) -> TransitionResult[ChainState]:
    block_number = chain_state.block_number
    block_hash = chain_state.block_hash
    sub_task = chain_state.payment_mapping.secrethashes_to_task.get(secrethash)
    events: List[Event] = list()

    if sub_task:
        pseudo_random_generator = chain_state.pseudo_random_generator
        sub_iteration: Union[
            TransitionResult[Optional[InitiatorPaymentState]],
            TransitionResult[Optional[MediatorTransferState]],
            TransitionResult[Optional[TargetTransferState]],
        ]

        if isinstance(sub_task, InitiatorTask):
            token_network_address = sub_task.token_network_address
            token_network_state = get_token_network_by_address(chain_state, token_network_address)

            if token_network_state:
                channel_identifier_map = token_network_state.channelidentifiers_to_channels
                sub_iteration = initiator_manager.state_transition(
                    payment_state=sub_task.manager_state,
                    state_change=state_change,
                    channelidentifiers_to_channels=channel_identifier_map,
                    addresses_to_channel=chain_state.addresses_to_channel,
                    nodeaddresses_to_networkstates=chain_state.nodeaddresses_to_networkstates,
                    pseudo_random_generator=pseudo_random_generator,
                    block_number=block_number,
                )
                events = sub_iteration.events

                if sub_iteration.new_state is None:
                    del chain_state.payment_mapping.secrethashes_to_task[secrethash]

        elif isinstance(sub_task, MediatorTask):
            token_network_address = sub_task.token_network_address
            token_network_state = get_token_network_by_address(chain_state, token_network_address)

            if token_network_state:
                channelids_to_channels = token_network_state.channelidentifiers_to_channels
                sub_iteration = mediator.state_transition(
                    mediator_state=sub_task.mediator_state,
                    state_change=state_change,
                    channelidentifiers_to_channels=channelids_to_channels,
                    addresses_to_channel=chain_state.addresses_to_channel,
                    nodeaddresses_to_networkstates=chain_state.nodeaddresses_to_networkstates,
                    pseudo_random_generator=pseudo_random_generator,
                    block_number=block_number,
                    block_hash=block_hash,
                )
                events = sub_iteration.events

                if sub_iteration.new_state is None:
                    del chain_state.payment_mapping.secrethashes_to_task[secrethash]

        elif isinstance(sub_task, TargetTask):
            token_network_address = sub_task.token_network_address
            channel_identifier = sub_task.channel_identifier

            channel_state = views.get_channelstate_by_canonical_identifier(
                chain_state=chain_state,
                canonical_identifier=CanonicalIdentifier(
                    chain_identifier=chain_state.chain_id,
                    token_network_address=token_network_address,
                    channel_identifier=channel_identifier,
                ),
            )

            if channel_state:
                sub_iteration = target.state_transition(
                    target_state=sub_task.target_state,
                    state_change=state_change,
                    channel_state=channel_state,
                    pseudo_random_generator=pseudo_random_generator,
                    block_number=block_number,
                )
                events = sub_iteration.events

                if sub_iteration.new_state is None:
                    del chain_state.payment_mapping.secrethashes_to_task[secrethash]

    return TransitionResult(chain_state, events)
Пример #4
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)
Пример #5
0
def handle_transferrefundcancelroute(
    payment_state: InitiatorPaymentState,
    state_change: ReceiveTransferRefundCancelRoute,
    channelidentifiers_to_channels: initiator.ChannelMap,
    pseudo_random_generator: random.Random,
    block_number: typing.BlockNumber,
) -> TransitionResult:

    channel_identifier = payment_state.initiator.channel_identifier
    channel_state = channelidentifiers_to_channels[channel_identifier]
    refund_transfer = state_change.transfer
    original_transfer = payment_state.initiator.transfer

    if channel_state.close_transaction:
        closed_block_number = channel_state.close_transaction.finished_block_number
    else:
        closed_block_number = None

    # This is overcommiting, since the initiator knows the secret it doesn't
    # need to wait for reveal_timeout blocks.
    timeout_blocks = mediator.get_timeout_blocks(
        channel_state.settle_timeout,
        closed_block_number,
        original_transfer.lock.expiration,
        block_number,
    )
    lock_timeout = timeout_blocks - channel_state.reveal_timeout
    maximum_expiration = lock_timeout + block_number

    is_valid_lock = (
        refund_transfer.lock.secrethash == original_transfer.lock.secrethash
        and refund_transfer.lock.amount == original_transfer.lock.amount
        and refund_transfer.lock.expiration <= maximum_expiration)

    is_valid_refund = channel.refund_transfer_matches_received(
        refund_transfer,
        original_transfer,
    )

    events = list()
    if is_valid_lock and is_valid_refund:
        is_valid, channel_events, _ = channel.handle_receive_refundtransfercancelroute(
            channel_state,
            refund_transfer,
        )

        events.extend(channel_events)

        if is_valid:
            old_description = payment_state.initiator.transfer_description
            transfer_description = TransferDescriptionWithSecretState(
                old_description.payment_identifier,
                old_description.amount,
                old_description.token_network_identifier,
                old_description.initiator,
                old_description.target,
                state_change.secret,
            )
            payment_state.initiator.transfer_description = transfer_description

            sub_iteration = handle_cancelroute(
                payment_state,
                state_change,
                channelidentifiers_to_channels,
                pseudo_random_generator,
                block_number,
            )

            events.extend(sub_iteration.events)
            if sub_iteration.new_state is None:
                payment_state = None

    iteration = TransitionResult(payment_state, events)

    return iteration
Пример #6
0
def state_transition_noop(state, state_change):  # pylint: disable=unused-argument
    return TransitionResult(state, list())
Пример #7
0
def state_transition(
    payment_state: Optional[InitiatorPaymentState],
    state_change: StateChange,
    channelidentifiers_to_channels: Dict[ChannelID, NettingChannelState],
    nodeaddresses_to_networkstates: NodeNetworkStateMap,
    pseudo_random_generator: random.Random,
    block_number: BlockNumber,
) -> TransitionResult[Optional[InitiatorPaymentState]]:
    # pylint: disable=unidiomatic-typecheck
    iteration: TransitionResult[Optional[InitiatorPaymentState]]

    if type(state_change) == Block:
        assert isinstance(state_change, Block), MYPY_ANNOTATION
        assert payment_state, "Block state changes should be accompanied by a valid payment state"
        iteration = handle_block(
            payment_state=payment_state,
            state_change=state_change,
            channelidentifiers_to_channels=channelidentifiers_to_channels,
            pseudo_random_generator=pseudo_random_generator,
            block_number=block_number,
        )
    elif type(state_change) == ActionInitInitiator:
        assert isinstance(state_change, ActionInitInitiator), MYPY_ANNOTATION
        iteration = handle_init(
            payment_state=payment_state,
            state_change=state_change,
            channelidentifiers_to_channels=channelidentifiers_to_channels,
            nodeaddresses_to_networkstates=nodeaddresses_to_networkstates,
            pseudo_random_generator=pseudo_random_generator,
            block_number=block_number,
        )
    elif type(state_change) == ReceiveTransferCancelRoute:
        assert isinstance(state_change,
                          ReceiveTransferCancelRoute), MYPY_ANNOTATION
        assert (
            payment_state
        ), "ReceiveTransferCancelRoute should be accompanied by a valid payment state"
        iteration = handle_failroute(payment_state=payment_state,
                                     state_change=state_change)
    elif type(state_change) == ActionTransferReroute:
        assert isinstance(state_change, ActionTransferReroute), MYPY_ANNOTATION
        msg = "ActionTransferReroute should be accompanied by a valid payment state"
        assert payment_state, msg
        iteration = handle_transferreroute(
            payment_state=payment_state,
            state_change=state_change,
            channelidentifiers_to_channels=channelidentifiers_to_channels,
            nodeaddresses_to_networkstates=nodeaddresses_to_networkstates,
            pseudo_random_generator=pseudo_random_generator,
            block_number=block_number,
        )
    elif type(state_change) == ReceiveSecretRequest:
        assert isinstance(state_change, ReceiveSecretRequest), MYPY_ANNOTATION
        assert payment_state, "ReceiveSecretRequest should be accompanied by a valid payment state"
        iteration = handle_secretrequest(
            payment_state=payment_state,
            state_change=state_change,
            channelidentifiers_to_channels=channelidentifiers_to_channels,
            pseudo_random_generator=pseudo_random_generator,
            block_number=block_number,
        )
    elif type(state_change) == ActionCancelPayment:
        assert isinstance(state_change, ActionCancelPayment), MYPY_ANNOTATION
        assert payment_state, "ActionCancelPayment should be accompanied by a valid payment state"
        iteration = handle_cancelpayment(
            payment_state=payment_state,
            channelidentifiers_to_channels=channelidentifiers_to_channels,
        )
    elif type(state_change) == ReceiveSecretReveal:
        assert isinstance(state_change, ReceiveSecretReveal), MYPY_ANNOTATION
        assert payment_state, "ReceiveSecretReveal should be accompanied by a valid payment state"
        iteration = handle_offchain_secretreveal(
            payment_state=payment_state,
            state_change=state_change,
            channelidentifiers_to_channels=channelidentifiers_to_channels,
            pseudo_random_generator=pseudo_random_generator,
            block_number=block_number,
        )
    elif type(state_change) == ReceiveLockExpired:
        assert isinstance(state_change, ReceiveLockExpired), MYPY_ANNOTATION
        assert payment_state, "ReceiveLockExpired should be accompanied by a valid payment state"
        iteration = handle_lock_expired(
            payment_state=payment_state,
            state_change=state_change,
            channelidentifiers_to_channels=channelidentifiers_to_channels,
            block_number=block_number,
        )
    elif type(state_change) == ContractReceiveSecretReveal:
        assert isinstance(state_change,
                          ContractReceiveSecretReveal), MYPY_ANNOTATION
        msg = "ContractReceiveSecretReveal should be accompanied by a valid payment state"
        assert payment_state, msg
        iteration = handle_onchain_secretreveal(
            payment_state=payment_state,
            state_change=state_change,
            channelidentifiers_to_channels=channelidentifiers_to_channels,
            pseudo_random_generator=pseudo_random_generator,
            block_number=block_number,
        )
    else:
        iteration = TransitionResult(payment_state, list())

    return clear_if_finalized(iteration)
Пример #8
0
def state_transition(state, state_change):
    """ State machine for a node mediating a transfer. """
    # pylint: disable=too-many-branches
    # Notes:
    # - A user cannot cancel a mediated transfer after it was initiated, she
    #   may only reject to mediate before hand. This is because the mediator
    #   doesn't control the secret reveal and needs to wait for the lock
    #   expiration before safely discarding the transfer.

    iteration = TransitionResult(state, list())

    if state is None:
        if isinstance(state_change, ActionInitMediator):
            routes = state_change.routes

            from_route = state_change.from_route
            from_transfer = state_change.from_transfer

            state = MediatorState(
                state_change.our_address,
                routes,
                state_change.block_number,
                from_transfer.hashlock,
            )

            iteration = mediate_transfer(state, from_route, from_transfer)

    elif state.secret is None:
        if isinstance(state_change, Block):
            iteration = handle_block(state, state_change)

        elif isinstance(state_change, ActionRouteChange):
            iteration = handle_routechange(state, state_change)

        elif isinstance(state_change, ReceiveTransferRefund):
            iteration = handle_refundtransfer(state, state_change)

        elif isinstance(state_change, ReceiveSecretReveal):
            iteration = handle_secretreveal(state, state_change)

        elif isinstance(state_change, ContractReceiveWithdraw):
            iteration = handle_contractwithdraw(state, state_change)

    else:
        if isinstance(state_change, Block):
            iteration = handle_block(state, state_change)

        elif isinstance(state_change, ActionRouteChange):
            iteration = handle_routechange(state, state_change)

        if isinstance(state_change, ReceiveSecretReveal):
            iteration = handle_secretreveal(state, state_change)

        elif isinstance(state_change, ReceiveBalanceProof):
            iteration = handle_balanceproof(state, state_change)

        elif isinstance(state_change, ContractReceiveWithdraw):
            iteration = handle_contractwithdraw(state, state_change)

    # this is the place for paranoia
    if iteration.new_state is not None:
        sanity_check(iteration.new_state)

    return clear_if_finalized(iteration)
Пример #9
0
def state_transition(
        mediator_state,
        state_change,
        channelidentifiers_to_channels,
        pseudo_random_generator,
        block_number,
):
    """ State machine for a node mediating a transfer. """
    # pylint: disable=too-many-branches
    # Notes:
    # - A user cannot cancel a mediated transfer after it was initiated, she
    #   may only reject to mediate before hand. This is because the mediator
    #   doesn't control the secret reveal and needs to wait for the lock
    #   expiration before safely discarding the transfer.

    iteration = TransitionResult(mediator_state, list())

    if isinstance(state_change, ActionInitMediator):
        if mediator_state is None:
            iteration = handle_init(
                state_change,
                channelidentifiers_to_channels,
                pseudo_random_generator,
                block_number,
            )

    elif isinstance(state_change, Block):
        iteration = handle_block(
            mediator_state,
            state_change,
            channelidentifiers_to_channels,
            pseudo_random_generator,
            block_number,
        )

    elif isinstance(state_change, ReceiveTransferRefund):
        iteration = handle_refundtransfer(
            mediator_state,
            state_change,
            channelidentifiers_to_channels,
            pseudo_random_generator,
            block_number,
        )

    elif isinstance(state_change, ReceiveSecretReveal):
        iteration = handle_secretreveal(
            mediator_state,
            state_change,
            channelidentifiers_to_channels,
            pseudo_random_generator,
            block_number,
        )

    elif isinstance(state_change, ContractReceiveSecretReveal):
        iteration = handle_secretreveal(
            mediator_state,
            state_change,
            channelidentifiers_to_channels,
            pseudo_random_generator,
            block_number,
        )

    elif isinstance(state_change, ReceiveUnlock):
        iteration = handle_unlock(
            mediator_state,
            state_change,
            channelidentifiers_to_channels,
        )

    elif isinstance(state_change, ReceiveLockExpired):
        iteration = handle_lock_expired(
            mediator_state,
            state_change,
            channelidentifiers_to_channels,
        )

    # this is the place for paranoia
    if iteration.new_state is not None:
        sanity_check(iteration.new_state)

    return clear_if_finalized(iteration)
Пример #10
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)
Пример #11
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

    # 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:
        # 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
        secret = transfer_description.secret
        revealsecret = SendSecretReveal(
            recipient=Address(recipient),
            channel_identifier=CHANNEL_IDENTIFIER_GLOBAL_QUEUE,
            message_identifier=message_identifier,
            secret=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
Пример #12
0
def state_transtion_acc(state, state_change):
    state = state or AccState()
    state.state_changes.append(state_change)
    return TransitionResult(state, list())
Пример #13
0
def subdispatch_to_paymenttask(node_state, state_change, hashlock):
    block_number = node_state.block_number
    sub_task = node_state.payment_mapping.hashlocks_to_task.get(hashlock)
    events = list()

    if sub_task:
        if isinstance(sub_task, PaymentMappingState.InitiatorTask):
            payment_network_identifier = sub_task.payment_network_identifier
            token_address = sub_task.token_address

            token_network_state = get_token_network(
                node_state,
                payment_network_identifier,
                token_address,
            )

            if token_network_state:
                sub_iteration = initiator_manager.state_transition(
                    sub_task.manager_state,
                    state_change,
                    token_network_state.channelidentifiers_to_channels,
                    block_number,
                )
                events = sub_iteration.events

        elif isinstance(sub_task, PaymentMappingState.MediatorTask):
            payment_network_identifier = sub_task.payment_network_identifier
            token_address = sub_task.token_address

            token_network_state = get_token_network(
                node_state,
                payment_network_identifier,
                token_address,
            )

            if token_network_state:
                sub_iteration = mediator.state_transition(
                    sub_task.mediator_state,
                    state_change,
                    token_network_state.channelidentifiers_to_channels,
                    block_number,
                )
                events = sub_iteration.events

        elif isinstance(sub_task, PaymentMappingState.TargetTask):
            payment_network_identifier = sub_task.payment_network_identifier
            token_address = sub_task.token_address
            channel_identifier = sub_task.channel_identifier

            channel_state = views.get_channelstate_by_tokenaddress(
                node_state,
                payment_network_identifier,
                token_address,
                channel_identifier,
            )

            if channel_state:
                sub_iteration = target.state_transition(
                    sub_task.target_state,
                    state_change,
                    channel_state,
                    block_number,
                )
                events = sub_iteration.events

    return TransitionResult(node_state, events)
Пример #14
0
def handle_node_init(node_state, state_change):
    node_state = NodeState(state_change.block_number)
    events = list()
    return TransitionResult(node_state, events)
Пример #15
0
def state_transition(
    target_state: TargetTransferState,
    state_change: StateChange,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random,
    block_number: BlockNumber,
) -> TransitionResult[Optional[TargetTransferState]]:
    """ State machine for the target node of a mediated transfer. """
    # pylint: disable=too-many-branches,unidiomatic-typecheck

    iteration = TransitionResult(target_state, list())
    if type(state_change) == ActionInitTarget:
        assert isinstance(state_change, ActionInitTarget), MYPY_ANNOTATION
        if target_state is None:
            iteration = handle_inittarget(
                state_change,
                channel_state,
                pseudo_random_generator,
                block_number,
            )
    elif type(state_change) == Block:
        assert isinstance(state_change, Block), MYPY_ANNOTATION
        assert state_change.block_number == block_number

        iteration = handle_block(
            target_state=target_state,
            channel_state=channel_state,
            block_number=state_change.block_number,
            block_hash=state_change.block_hash,
        )
    elif type(state_change) == ReceiveSecretReveal:
        assert isinstance(state_change, ReceiveSecretReveal), MYPY_ANNOTATION
        iteration = handle_offchain_secretreveal(
            target_state=target_state,
            state_change=state_change,
            channel_state=channel_state,
            pseudo_random_generator=pseudo_random_generator,
            block_number=block_number,
        )
    elif type(state_change) == ContractReceiveSecretReveal:
        assert isinstance(state_change,
                          ContractReceiveSecretReveal), MYPY_ANNOTATION
        iteration = handle_onchain_secretreveal(
            target_state,
            state_change,
            channel_state,
        )
    elif type(state_change) == ReceiveUnlock:
        assert isinstance(state_change, ReceiveUnlock), MYPY_ANNOTATION
        iteration = handle_unlock(
            target_state,
            state_change,
            channel_state,
        )
    elif type(state_change) == ReceiveLockExpired:
        assert isinstance(state_change, ReceiveLockExpired), MYPY_ANNOTATION
        iteration = handle_lock_expired(
            target_state,
            state_change,
            channel_state,
            block_number,
        )

    sanity_check(
        old_state=target_state,
        new_state=iteration.new_state,
        channel_state=channel_state,
    )

    return iteration
Пример #16
0
def secret_learned(
        state,
        channelidentifiers_to_channels,
        pseudo_random_generator,
        block_number,
        secret,
        secrethash,
        payee_address,
        new_payee_state,
        from_onchain_secretreveal,
):
    """ Set the state of the `payee_address` transfer, check the secret is
    being revealed backwards, and if necessary send out RevealSecret,
    SendBalanceProof, and Unlocks.
    """
    assert new_payee_state in STATE_SECRET_KNOWN

    # TODO: if any of the transfers is in expired state, event for byzantine behavior

    if state.secret is None:
        if from_onchain_secretreveal:
            set_onchain_secret(
                state,
                channelidentifiers_to_channels,
                secret,
                secrethash,
            )
        else:
            set_secret(
                state,
                channelidentifiers_to_channels,
                secret,
                secrethash,
            )

        # This task only needs to unlock if the channel is closed when the
        # secret is learned, otherwise the channel task will do it
        # automatically
        unlock = events_for_unlock_if_closed(
            channelidentifiers_to_channels,
            state.transfers_pair,
            secret,
            secrethash,
        )
    else:
        unlock = []

    wrong_order = set_payee_state_and_check_reveal_order(
        state.transfers_pair,
        payee_address,
        new_payee_state,
    )

    secret_reveal = events_for_revealsecret(
        state.transfers_pair,
        secret,
        pseudo_random_generator,
    )

    balance_proof = events_for_balanceproof(
        channelidentifiers_to_channels,
        state.transfers_pair,
        pseudo_random_generator,
        block_number,
        secret,
        secrethash,
    )

    iteration = TransitionResult(
        state,
        wrong_order + secret_reveal + balance_proof + unlock,
    )

    return iteration
Пример #17
0
def handle_inittarget(
    state_change: ActionInitTarget,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random,
    block_number: BlockNumber,
) -> TransitionResult[Optional[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 = channel.handle_receive_lockedtransfer(
        channel_state,
        transfer,
    )

    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.
        if safe_to_wait:
            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,
            )
            channel_events.append(secret_request)

        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.
        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
Пример #18
0
def mediate_transfer(
        state,
        possible_routes,
        payer_channel,
        channelidentifiers_to_channels,
        pseudo_random_generator,
        payer_transfer,
        block_number,
):
    """ Try a new route or fail back to a refund.

    The mediator can safely try a new route knowing that the tokens from
    payer_transfer will cover the expenses of the mediation. If there is no
    route available that may be used at the moment of the call the mediator may
    send a refund back to the payer, allowing the payer to try a different
    route.
    """
    transfer_pair = None

    available_routes = filter_used_routes(
        state.transfers_pair,
        possible_routes,
    )

    assert payer_channel.partner_state.address == payer_transfer.balance_proof.sender

    transfer_pair, mediated_events = next_transfer_pair(
        payer_transfer,
        available_routes,
        channelidentifiers_to_channels,
        pseudo_random_generator,
        block_number,
    )

    # If none of the available_routes could be used, try a refund
    if transfer_pair is None:
        if state.transfers_pair:
            original_pair = state.transfers_pair[0]
            original_transfer = original_pair.payer_transfer
            original_channel = get_payer_channel(
                channelidentifiers_to_channels,
                original_pair,
            )
        else:
            original_channel = payer_channel
            original_transfer = payer_transfer

        refund_events = events_for_refund_transfer(
            original_channel,
            original_transfer,
            pseudo_random_generator,
            block_number,
        )

        iteration = TransitionResult(state, refund_events)

    else:
        # the list must be ordered from high to low expiration, expiration
        # handling depends on it
        state.transfers_pair.append(transfer_pair)
        iteration = TransitionResult(state, mediated_events)

    return iteration
Пример #19
0
def handle_transferreroute(
    payment_state: InitiatorPaymentState,
    state_change: ActionTransferReroute,
    channelidentifiers_to_channels: Dict[ChannelID, NettingChannelState],
    nodeaddresses_to_networkstates: NodeNetworkStateMap,
    pseudo_random_generator: random.Random,
    block_number: BlockNumber,
) -> TransitionResult[InitiatorPaymentState]:

    try:
        initiator_state = payment_state.initiator_transfers[
            state_change.transfer.lock.secrethash]
        channel_identifier = initiator_state.channel_identifier
        channel_state = channelidentifiers_to_channels[channel_identifier]
    except KeyError:
        return TransitionResult(payment_state, list())

    refund_transfer = state_change.transfer
    original_transfer = initiator_state.transfer

    is_valid_lock = (
        refund_transfer.lock.secrethash == original_transfer.lock.secrethash
        and refund_transfer.lock.amount == original_transfer.lock.amount and
        refund_transfer.lock.expiration == original_transfer.lock.expiration)

    is_valid_refund = channel.refund_transfer_matches_transfer(
        refund_transfer, original_transfer)
    is_valid, channel_events, _ = channel.handle_receive_lockedtransfer(
        channel_state, refund_transfer)

    if not is_valid_lock or not is_valid_refund or not is_valid:
        return TransitionResult(payment_state, list())

    events: List[Event] = []
    events.extend(channel_events)

    filtered_route_states = routes.filter_acceptable_routes(
        route_states=payment_state.routes,
        blacklisted_channel_ids=payment_state.cancelled_channels)

    old_description = initiator_state.transfer_description
    transfer_description = TransferDescriptionWithSecretState(
        token_network_registry_address=old_description.
        token_network_registry_address,
        payment_identifier=old_description.payment_identifier,
        amount=old_description.amount,
        token_network_address=old_description.token_network_address,
        initiator=old_description.initiator,
        target=old_description.target,
        secret=state_change.secret,
        secrethash=state_change.secrethash,
    )

    sub_iteration = initiator.try_new_route(
        channelidentifiers_to_channels=channelidentifiers_to_channels,
        nodeaddresses_to_networkstates=nodeaddresses_to_networkstates,
        candidate_route_states=filtered_route_states,
        transfer_description=transfer_description,
        pseudo_random_generator=pseudo_random_generator,
        block_number=block_number,
    )

    events.extend(sub_iteration.events)

    if sub_iteration.new_state is None:
        # 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
        return TransitionResult(payment_state, events)

    new_transfer = sub_iteration.new_state.transfer
    payment_state.initiator_transfers[
        new_transfer.lock.secrethash] = sub_iteration.new_state

    return TransitionResult(payment_state, events)
Пример #20
0
def handle_transferrefundcancelroute(
        payment_state: InitiatorPaymentState,
        state_change: ReceiveTransferRefundCancelRoute,
        channelidentifiers_to_channels: typing.ChannelMap,
        pseudo_random_generator: random.Random,
        block_number: typing.BlockNumber,
) -> TransitionResult:

    channel_identifier = payment_state.initiator.channel_identifier
    channel_state = channelidentifiers_to_channels[channel_identifier]
    refund_transfer = state_change.transfer
    original_transfer = payment_state.initiator.transfer

    is_valid_lock = (
        refund_transfer.lock.secrethash == original_transfer.lock.secrethash and
        refund_transfer.lock.amount == original_transfer.lock.amount and
        refund_transfer.lock.expiration == original_transfer.lock.expiration
    )

    is_valid_refund = channel.refund_transfer_matches_received(
        refund_transfer,
        original_transfer,
    )

    events = list()
    if is_valid_lock and is_valid_refund:
        is_valid, channel_events, _ = channel.handle_receive_refundtransfercancelroute(
            channel_state,
            refund_transfer,
        )

        events.extend(channel_events)

        if is_valid:
            old_description = payment_state.initiator.transfer_description
            transfer_description = TransferDescriptionWithSecretState(
                old_description.payment_network_identifier,
                old_description.payment_identifier,
                old_description.amount,
                old_description.token_network_identifier,
                old_description.initiator,
                old_description.target,
                state_change.secret,
            )
            payment_state.initiator.transfer_description = transfer_description

            sub_iteration = handle_cancelroute(
                payment_state,
                state_change,
                channelidentifiers_to_channels,
                pseudo_random_generator,
                block_number,
            )

            events.extend(sub_iteration.events)
            if sub_iteration.new_state is None:
                payment_state = None

    iteration = TransitionResult(payment_state, events)

    return iteration
Пример #21
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)
Пример #22
0
def state_transition(
        payment_state: InitiatorPaymentState,
        state_change: StateChange,
        channelidentifiers_to_channels: typing.ChannelMap,
        pseudo_random_generator: random.Random,
        block_number: typing.BlockNumber,
) -> TransitionResult:
    # pylint: disable=unidiomatic-typecheck
    if type(state_change) == Block:
        iteration = handle_block(
            payment_state,
            state_change,
            channelidentifiers_to_channels,
            pseudo_random_generator,
        )
    elif type(state_change) == ActionInitInitiator:
        iteration = handle_init(
            payment_state,
            state_change,
            channelidentifiers_to_channels,
            pseudo_random_generator,
            block_number,
        )
    elif type(state_change) == ReceiveSecretRequest:
        channel_identifier = payment_state.initiator.channel_identifier
        channel_state = channelidentifiers_to_channels[channel_identifier]
        sub_iteration = initiator.handle_secretrequest(
            payment_state.initiator,
            state_change,
            channel_state,
            pseudo_random_generator,
        )
        iteration = iteration_from_sub(payment_state, sub_iteration)
    elif type(state_change) == ActionCancelRoute:
        iteration = handle_cancelroute(
            payment_state,
            state_change,
            channelidentifiers_to_channels,
            pseudo_random_generator,
            block_number,
        )
    elif type(state_change) == ReceiveTransferRefundCancelRoute:
        iteration = handle_transferrefundcancelroute(
            payment_state,
            state_change,
            channelidentifiers_to_channels,
            pseudo_random_generator,
            block_number,
        )
    elif type(state_change) == ActionCancelPayment:
        channel_identifier = payment_state.initiator.channel_identifier
        channel_state = channelidentifiers_to_channels[channel_identifier]
        iteration = handle_cancelpayment(
            payment_state,
            channel_state,
        )
    elif type(state_change) == ReceiveSecretReveal:
        iteration = handle_secretreveal(
            payment_state,
            state_change,
            channelidentifiers_to_channels,
            pseudo_random_generator,
        )
    elif type(state_change) == ContractReceiveSecretReveal:
        iteration = handle_onchain_secretreveal(
            payment_state,
            state_change,
            channelidentifiers_to_channels,
        )
    else:
        iteration = TransitionResult(payment_state, list())

    sanity_check(iteration.new_state)

    return iteration
Пример #23
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)
    if not is_message_from_target:
        return TransitionResult(initiator_state, list())

    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"

    if initiator_state.received_secret_request:
        # A secret request was received earlier, all subsequent are ignored
        # as it might be an attack.
        return TransitionResult(initiator_state, list())

    # 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 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=Address(recipient),
            message_identifier=message_identifier,
            secret=transfer_description.secret,
            canonical_identifier=CANONICAL_IDENTIFIER_UNORDERED_QUEUE,
        )

        initiator_state.transfer_state = "transfer_secret_revealed"
        initiator_state.received_secret_request = True
        return TransitionResult(initiator_state, [revealsecret])
    else:
        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,
        )
        return TransitionResult(initiator_state, [invalid_request])
Пример #24
0
def state_transition(
    target_state,
    state_change,
    channel_state,
    pseudo_random_generator,
    block_number,
):
    """ State machine for the target node of a mediated transfer. """
    # pylint: disable=too-many-branches,unidiomatic-typecheck

    iteration = TransitionResult(target_state, list())
    if type(state_change) == ActionInitTarget:
        if target_state is None:
            iteration = handle_inittarget(
                state_change,
                channel_state,
                pseudo_random_generator,
                block_number,
            )
    elif type(state_change) == Block:
        assert state_change.block_number == block_number

        iteration = handle_block(
            target_state,
            channel_state,
            state_change.block_number,
        )
    elif type(state_change) == ReceiveSecretReveal:
        iteration = handle_offchain_secretreveal(
            target_state=target_state,
            state_change=state_change,
            channel_state=channel_state,
            pseudo_random_generator=pseudo_random_generator,
            block_number=block_number,
        )
    elif type(state_change) == ContractReceiveSecretReveal:
        iteration = handle_onchain_secretreveal(
            target_state,
            state_change,
            channel_state,
        )
    elif type(state_change) == ReceiveUnlock:
        iteration = handle_unlock(
            target_state,
            state_change,
            channel_state,
        )
    elif type(state_change) == ReceiveLockExpired:
        iteration = handle_lock_expired(
            target_state,
            state_change,
            channel_state,
            block_number,
        )

    sanity_check(
        old_state=target_state,
        new_state=iteration.new_state,
        channel_state=channel_state,
    )

    return iteration
Пример #25
0
def handle_transferrefund(payment_state, state_change,
                          channelidentifiers_to_channels, block_number):

    channel_identifier = payment_state.initiator.channel_identifier
    channel_state = channelidentifiers_to_channels[channel_identifier]
    refund_transfer = state_change.transfer
    original_transfer = payment_state.initiator.transfer
    maximum_expiration = original_transfer.lock.expiration - channel_state.reveal_timeout

    if channel_state.close_transaction:
        closed_block_number = channel_state.close_transaction.finished_block_number
    else:
        closed_block_number = None

    # This is overcommiting, since the initiator knows the secret it doesn't
    # need to wait for reveal_timeout blocks.
    timeout_blocks = mediator.get_timeout_blocks2(
        channel_state.settle_timeout,
        closed_block_number,
        original_transfer.lock.expiration,
        block_number,
    )
    lock_timeout = timeout_blocks - channel_state.reveal_timeout
    maximum_expiration = lock_timeout + block_number

    is_valid_lock = (
        refund_transfer.lock.hashlock == original_transfer.lock.hashlock
        and refund_transfer.lock.amount == original_transfer.lock.amount
        and refund_transfer.lock.expiration <= maximum_expiration)

    is_valid_refund = mediator.is_valid_refund2(
        original_transfer,
        refund_transfer,
    )

    events = list()
    if is_valid_lock and is_valid_refund:
        is_valid, _ = channel.handle_receive_refundtransfer(
            channel_state,
            refund_transfer,
        )

        if is_valid:
            old_description = payment_state.initiator.transfer_description
            transfer_description = TransferDescriptionWithSecretState(
                old_description.identifier,
                old_description.amount,
                old_description.registry,
                old_description.token,
                old_description.initiator,
                old_description.target,
                state_change.secret,
            )
            payment_state.initiator.transfer_description = transfer_description

            sub_iteration = handle_cancelroute(
                payment_state,
                state_change,
                channelidentifiers_to_channels,
                block_number,
            )

            events = sub_iteration.events
            if sub_iteration.new_state is None:
                payment_state = None

    iteration = TransitionResult(payment_state, events)

    return iteration
Пример #26
0
 def empty_state_transition(chain_state, state_change):  # pylint: disable=unused-argument
     return TransitionResult(chain_state, list())
Пример #27
0
def subdispatch_to_paymenttask(
    chain_state: ChainState,
    state_change: StateChange,
    secrethash: typing.SecretHash,
) -> TransitionResult:
    block_number = chain_state.block_number
    sub_task = chain_state.payment_mapping.secrethashes_to_task.get(secrethash)
    events = list()
    sub_iteration = None

    if sub_task:
        pseudo_random_generator = chain_state.pseudo_random_generator

        if isinstance(sub_task, InitiatorTask):
            token_network_identifier = sub_task.token_network_identifier
            token_network_state = views.get_token_network_by_identifier(
                chain_state,
                token_network_identifier,
            )
            if token_network_state:
                sub_iteration = initiator_manager.state_transition(
                    sub_task.manager_state,
                    state_change,
                    token_network_state.channelidentifiers_to_channels,
                    pseudo_random_generator,
                    block_number,
                )
                events = sub_iteration.events

        elif isinstance(sub_task, MediatorTask):
            token_network_identifier = sub_task.token_network_identifier
            token_network_state = views.get_token_network_by_identifier(
                chain_state,
                token_network_identifier,
            )

            if token_network_state:
                sub_iteration = mediator.state_transition(
                    sub_task.mediator_state,
                    state_change,
                    token_network_state.channelidentifiers_to_channels,
                    pseudo_random_generator,
                    block_number,
                )
                events = sub_iteration.events

        elif isinstance(sub_task, TargetTask):
            token_network_identifier = sub_task.token_network_identifier
            channel_identifier = sub_task.channel_identifier
            token_network_state = views.get_token_network_by_identifier(
                chain_state,
                token_network_identifier,
            )

            channel_state = views.get_channelstate_by_token_network_identifier(
                chain_state,
                token_network_identifier,
                channel_identifier,
            )

            if channel_state:
                sub_iteration = target.state_transition(
                    sub_task.target_state,
                    state_change,
                    channel_state,
                    pseudo_random_generator,
                    block_number,
                )
                events = sub_iteration.events

        if sub_iteration and sub_iteration.new_state is None:
            del chain_state.payment_mapping.secrethashes_to_task[secrethash]

    return TransitionResult(chain_state, events)
Пример #28
0
 def empty_state_transition(chain_state, state_change):
     return TransitionResult(chain_state, list())
Пример #29
0
def handle_state_change(
    chain_state: ChainState, state_change: StateChange
) -> TransitionResult[ChainState]:  # pragma: no cover

    if type(state_change) == Block:
        assert isinstance(state_change, Block), MYPY_ANNOTATION
        iteration = handle_block(chain_state, state_change)
    elif type(state_change) == ActionChannelClose:
        assert isinstance(state_change, ActionChannelClose), MYPY_ANNOTATION
        iteration = handle_token_network_action(chain_state, state_change)
    elif type(state_change) == ActionChannelWithdraw:
        assert isinstance(state_change, ActionChannelWithdraw), MYPY_ANNOTATION
        iteration = subdispatch_by_canonical_id(
            chain_state=chain_state,
            canonical_identifier=state_change.canonical_identifier,
            state_change=state_change,
        )
    elif type(state_change) == ActionChannelSetRevealTimeout:
        assert isinstance(state_change, ActionChannelSetRevealTimeout), MYPY_ANNOTATION
        iteration = subdispatch_by_canonical_id(
            chain_state=chain_state,
            canonical_identifier=state_change.canonical_identifier,
            state_change=state_change,
        )
    elif type(state_change) == ActionChangeNodeNetworkState:
        assert isinstance(state_change, ActionChangeNodeNetworkState), MYPY_ANNOTATION
        iteration = handle_action_change_node_network_state(chain_state, state_change)
    elif type(state_change) == ActionInitInitiator:
        assert isinstance(state_change, ActionInitInitiator), MYPY_ANNOTATION
        iteration = handle_action_init_initiator(chain_state, state_change)
    elif type(state_change) == ActionInitMediator:
        assert isinstance(state_change, ActionInitMediator), MYPY_ANNOTATION
        iteration = handle_action_init_mediator(chain_state, state_change)
    elif type(state_change) == ActionInitTarget:
        assert isinstance(state_change, ActionInitTarget), MYPY_ANNOTATION
        iteration = handle_action_init_target(chain_state, state_change)
    elif type(state_change) == ReceiveTransferCancelRoute:
        assert isinstance(state_change, ReceiveTransferCancelRoute), MYPY_ANNOTATION
        iteration = handle_receive_transfer_cancel_route(chain_state, state_change)
    elif type(state_change) == ActionTransferReroute:
        assert isinstance(state_change, ActionTransferReroute), MYPY_ANNOTATION
        iteration = handle_action_transfer_reroute(chain_state, state_change)
    elif type(state_change) == ContractReceiveNewTokenNetworkRegistry:
        assert isinstance(state_change, ContractReceiveNewTokenNetworkRegistry), MYPY_ANNOTATION
        iteration = handle_contract_receive_new_token_network_registry(chain_state, state_change)
    elif type(state_change) == ContractReceiveNewTokenNetwork:
        assert isinstance(state_change, ContractReceiveNewTokenNetwork), MYPY_ANNOTATION
        iteration = handle_contract_receive_new_token_network(chain_state, state_change)
    elif type(state_change) == ContractReceiveChannelBatchUnlock:
        assert isinstance(state_change, ContractReceiveChannelBatchUnlock), MYPY_ANNOTATION
        iteration = handle_token_network_action(chain_state, state_change)
    elif type(state_change) == ContractReceiveChannelNew:
        assert isinstance(state_change, ContractReceiveChannelNew), MYPY_ANNOTATION
        iteration = handle_token_network_action(chain_state, state_change)
    elif type(state_change) == ContractReceiveChannelWithdraw:
        assert isinstance(state_change, ContractReceiveChannelWithdraw), MYPY_ANNOTATION
        iteration = handle_token_network_action(chain_state, state_change)
    elif type(state_change) == ContractReceiveChannelClosed:
        assert isinstance(state_change, ContractReceiveChannelClosed), MYPY_ANNOTATION
        iteration = handle_contract_receive_channel_closed(chain_state, state_change)
    elif type(state_change) == ContractReceiveChannelDeposit:
        assert isinstance(state_change, ContractReceiveChannelDeposit), MYPY_ANNOTATION
        iteration = handle_token_network_action(chain_state, state_change)
    elif type(state_change) == ContractReceiveChannelSettled:
        assert isinstance(state_change, ContractReceiveChannelSettled), MYPY_ANNOTATION
        iteration = handle_token_network_action(chain_state, state_change)
    elif type(state_change) == ContractReceiveSecretReveal:
        assert isinstance(state_change, ContractReceiveSecretReveal), MYPY_ANNOTATION
        iteration = handle_contract_receive_secret_reveal(chain_state, state_change)
    elif type(state_change) == ContractReceiveUpdateTransfer:
        assert isinstance(state_change, ContractReceiveUpdateTransfer), MYPY_ANNOTATION
        iteration = handle_token_network_action(chain_state, state_change)
    elif type(state_change) == ReceiveDelivered:
        assert isinstance(state_change, ReceiveDelivered), MYPY_ANNOTATION
        iteration = handle_receive_delivered(chain_state, state_change)
    elif type(state_change) == ReceiveSecretReveal:
        assert isinstance(state_change, ReceiveSecretReveal), MYPY_ANNOTATION
        iteration = handle_receive_secret_reveal(chain_state, state_change)
    elif type(state_change) == ReceiveTransferRefund:
        assert isinstance(state_change, ReceiveTransferRefund), MYPY_ANNOTATION
        iteration = handle_receive_transfer_refund(chain_state, state_change)
    elif type(state_change) == ReceiveSecretRequest:
        assert isinstance(state_change, ReceiveSecretRequest), MYPY_ANNOTATION
        iteration = handle_receive_secret_request(chain_state, state_change)
    elif type(state_change) == ReceiveProcessed:
        assert isinstance(state_change, ReceiveProcessed), MYPY_ANNOTATION
        iteration = handle_receive_processed(chain_state, state_change)
    elif type(state_change) == ReceiveUnlock:
        assert isinstance(state_change, ReceiveUnlock), MYPY_ANNOTATION
        iteration = handle_receive_unlock(chain_state, state_change)
    elif type(state_change) == ReceiveLockExpired:
        assert isinstance(state_change, ReceiveLockExpired), MYPY_ANNOTATION
        iteration = handle_receive_lock_expired(chain_state, state_change)
    elif type(state_change) == ReceiveWithdrawRequest:
        assert isinstance(state_change, ReceiveWithdrawRequest), MYPY_ANNOTATION
        iteration = handle_receive_withdraw_request(chain_state, state_change)
    elif type(state_change) == ReceiveWithdrawConfirmation:
        assert isinstance(state_change, ReceiveWithdrawConfirmation), MYPY_ANNOTATION
        iteration = handle_receive_withdraw_confirmation(chain_state, state_change)
    elif type(state_change) == ReceiveWithdrawExpired:
        assert isinstance(state_change, ReceiveWithdrawExpired), MYPY_ANNOTATION
        iteration = handle_receive_withdraw_expired(chain_state, state_change)
    else:
        iteration = TransitionResult(chain_state, [])

    chain_state = iteration.new_state
    assert chain_state is not None, "chain_state must be set"
    return iteration
Пример #30
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,
    )

    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=typing.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:
        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',
        )

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

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

    return iteration