Beispiel #1
0
def sanity_check(
        old_state: Optional[TargetTransferState],
        new_state: Optional[TargetTransferState],
        channel_state: NettingChannelState,
) -> None:
    was_running = old_state is not None
    is_running = new_state is not None
    is_cleared = was_running and not is_running

    if is_cleared:
        lock = channel.get_lock(
            end_state=channel_state.partner_state,
            secrethash=old_state.transfer.lock.secrethash,
        )
        assert lock is None, 'The lock must be cleared once the task exists'
    elif is_running:
        # old_state can be None if the task is starting
        lock = channel.get_lock(
            end_state=channel_state.partner_state,
            secrethash=new_state.transfer.lock.secrethash,
        )
        assert lock is not None, 'The lock must not be cleared while the task is running'
Beispiel #2
0
def events_for_unlock_if_closed(
        channelidentifiers_to_channels,
        transfers_pair,
        secret,
        secrethash):
    """ Unlock on chain if the payer channel is closed and the secret is known.
    If a channel is closed because of another task a balance proof will not be
    received, so there is no reason to wait for the unsafe region before
    calling close.
    This may break the reverse reveal order:
        Path: A -- B -- C -- B -- D
        B learned the secret from D and has revealed to C.
        C has not confirmed yet.
        channel(A, B).closed is True.
        B will unlock on channel(A, B) before C's confirmation.
        A may learn the secret faster than other nodes.
    """
    events = list()
    pending_transfers_pairs = get_pending_transfer_pairs(transfers_pair)

    for pair in pending_transfers_pairs:
        payer_channel = get_payer_channel(channelidentifiers_to_channels, pair)

        payer_channel_open = channel.get_status(payer_channel) == CHANNEL_STATE_OPENED

        # The unlock is done by the channel
        if not payer_channel_open:
            pair.payer_state = 'payer_waiting_unlock'

            partner_state = payer_channel.partner_state
            lock = channel.get_lock(partner_state, secrethash)
            unlock_proof = channel.compute_proof_for_lock(
                partner_state,
                secret,
                lock,
            )
            unlock = ContractSendChannelBatchUnlock(
                payer_channel.token_network_identifier,
                payer_channel.identifier,
                [unlock_proof],
            )
            events.append(unlock)

    return events
def setup_initiator_tests(
        amount=UNIT_TRANSFER_AMOUNT,
        partner_balance=EMPTY,
        our_address=EMPTY,
        partner_address=EMPTY,
        block_number=1,
) -> InitiatorSetup:
    """Commonly used setup code for initiator manager and channel"""
    prng = random.Random()

    channel1 = factories.make_channel(
        our_balance=amount,
        partner_balance=partner_balance,
        our_address=our_address,
        partner_address=partner_address,
        token_address=UNIT_TOKEN_ADDRESS,
        token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS,
    )
    channel_map = {channel1.identifier: channel1}
    available_routes = [factories.route_from_channel(channel1)]
    current_state = make_initiator_manager_state(
        available_routes,
        factories.UNIT_TRANSFER_DESCRIPTION,
        channel_map,
        prng,
        block_number,
    )

    lock = channel.get_lock(
        channel1.our_state,
        current_state.initiator.transfer_description.secrethash,
    )
    setup = InitiatorSetup(
        current_state=current_state,
        block_number=block_number,
        channel=channel1,
        channel_map=channel_map,
        available_routes=available_routes,
        prng=prng,
        lock=lock,
    )
    return setup
Beispiel #4
0
def events_for_withdraw_if_closed(channelidentifiers_to_channels,
                                  transfers_pair, secret, secrethash):
    """ Withdraw on chain if the payer channel is closed and the secret is known.
    If a channel is closed because of another task a balance proof will not be
    received, so there is no reason to wait for the unsafe region before
    calling close.
    This may break the reverse reveal order:
        Path: A -- B -- C -- B -- D
        B learned the secret from D and has revealed to C.
        C has not confirmed yet.
        channel(A, B).closed is True.
        B will withdraw on channel(A, B) before C's confirmation.
        A may learn the secret faster than other nodes.
    """
    events = list()
    pending_transfers_pairs = get_pending_transfer_pairs(transfers_pair)

    for pair in pending_transfers_pairs:
        payer_channel = get_payer_channel(channelidentifiers_to_channels, pair)

        payer_channel_open = channel.get_status(
            payer_channel) == CHANNEL_STATE_OPENED

        # The withdraw is done by the channel
        if not payer_channel_open:
            pair.payer_state = 'payer_waiting_withdraw'

            partner_state = payer_channel.partner_state
            lock = channel.get_lock(partner_state, secrethash)
            unlock_proof = channel.compute_proof_for_lock(
                partner_state,
                secret,
                lock,
            )
            withdraw = ContractSendChannelWithdraw(
                payer_channel.identifier,
                [unlock_proof],
            )
            events.append(withdraw)

    return events
Beispiel #5
0
def handle_lock_expired(
        mediator_state: MediatorTransferState,
        state_change: ReceiveLockExpired,
        channelidentifiers_to_channels: typing.ChannelMap,
):
    events = list()

    transfer_pair: MediationPairState
    for transfer_pair in mediator_state.transfers_pair:
        balance_proof = transfer_pair.payer_transfer.balance_proof
        channel_state = channelidentifiers_to_channels.get(balance_proof.channel_identifier)

        if not channel_state:
            return TransitionResult(mediator_state, list())

        result = channel.handle_receive_lock_expired(channel_state, state_change)
        if not channel.get_lock(result.new_state.partner_state, mediator_state.secrethash):
            transfer_pair.payer_state = 'payer_expired'
            events.extend(result.events)

    return TransitionResult(mediator_state, events)
def handle_lock_expired(
        payment_state: InitiatorPaymentState,
        state_change: ReceiveLockExpired,
        channelidentifiers_to_channels: ChannelMap,
        pseudo_random_generator: random.Random,
        block_number: BlockNumber,
) -> TransitionResult:
    """Initiator also needs to handle LockExpired messages when refund transfers are involved.

    A -> B -> C

    - A sends locked transfer to B
    - B attempted to forward to C but has not enough capacity
    - B sends a refund transfer with the same secrethash back to A
    - When the lock expires B will also send a LockExpired message to A
    - A needs to be able to properly process it

    Related issue: https://github.com/raiden-network/raiden/issues/3183
"""
    channel_identifier = payment_state.initiator.channel_identifier
    channel_state = channelidentifiers_to_channels[channel_identifier]
    secrethash = payment_state.initiator.transfer.lock.secrethash
    result = channel.handle_receive_lock_expired(
        channel_state=channel_state,
        state_change=state_change,
        block_number=block_number,
    )

    if not channel.get_lock(result.new_state.partner_state, secrethash):
        transfer = payment_state.initiator.transfer
        unlock_failed = EventUnlockClaimFailed(
            identifier=transfer.payment_identifier,
            secrethash=transfer.lock.secrethash,
            reason='Lock expired',
        )
        result.events.append(unlock_failed)

    return TransitionResult(payment_state, result.events)
Beispiel #7
0
def handle_lock_expired(
    payment_state: InitiatorPaymentState,
    state_change: ReceiveLockExpired,
    channelidentifiers_to_channels: typing.ChannelMap,
    pseudo_random_generator: random.Random,
    block_number: typing.BlockNumber,
) -> TransitionResult:
    """Initiator also needs to handle LockExpired messages when refund transfers are involved.

    A -> B -> C

    - A sends locked transfer to B
    - B attempted to forward to C but has not enough capacity
    - B sends a refund transfer with the same secrethash back to A
    - When the lock expires B will also send a LockExpired message to A
    - A needs to be able to properly process it

    Related issue: https://github.com/raiden-network/raiden/issues/3183
"""
    channel_identifier = payment_state.initiator.channel_identifier
    channel_state = channelidentifiers_to_channels[channel_identifier]
    secrethash = payment_state.initiator.transfer.lock.secrethash
    result = channel.handle_receive_lock_expired(
        channel_state=channel_state,
        state_change=state_change,
        block_number=block_number,
    )

    if not channel.get_lock(result.new_state.partner_state, secrethash):
        transfer = payment_state.initiator.transfer
        unlock_failed = EventUnlockClaimFailed(
            identifier=transfer.payment_identifier,
            secrethash=transfer.lock.secrethash,
            reason='Lock expired',
        )
        result.events.append(unlock_failed)

    return TransitionResult(payment_state, result.events)
Beispiel #8
0
def handle_lock_expired(
    target_state: TargetTransferState,
    state_change: ReceiveLockExpired,
    channel_state: NettingChannelState,
    block_number: BlockNumber,
) -> TransitionResult[Optional[TargetTransferState]]:
    """Remove expired locks from channel states."""
    result = channel.handle_receive_lock_expired(
        channel_state=channel_state, state_change=state_change, block_number=block_number
    )
    assert result.new_state, "handle_receive_lock_expired should not delete the task"

    if not channel.get_lock(result.new_state.partner_state, target_state.transfer.lock.secrethash):
        transfer = target_state.transfer
        unlock_failed = EventUnlockClaimFailed(
            identifier=transfer.payment_identifier,
            secrethash=transfer.lock.secrethash,
            reason="Lock expired",
        )
        result.events.append(unlock_failed)
        return TransitionResult(None, result.events)

    return TransitionResult(target_state, result.events)
Beispiel #9
0
def handle_lock_expired(
        mediator_state: MediatorTransferState,
        state_change: ReceiveLockExpired,
        channelidentifiers_to_channels: typing.ChannelMap,
        block_number: typing.BlockNumber,
):
    events = list()

    for transfer_pair in mediator_state.transfers_pair:
        balance_proof = transfer_pair.payer_transfer.balance_proof
        channel_state = channelidentifiers_to_channels.get(balance_proof.channel_identifier)

        if not channel_state:
            return TransitionResult(mediator_state, list())

        result = channel.handle_receive_lock_expired(
            channel_state=channel_state,
            state_change=state_change,
            block_number=block_number,
        )
        events.extend(result.events)
        if not channel.get_lock(result.new_state.partner_state, mediator_state.secrethash):
            transfer_pair.payer_state = 'payer_expired'

    if mediator_state.waiting_transfer:
        waiting_channel = channelidentifiers_to_channels[
            mediator_state.waiting_transfer.transfer.balance_proof.channel_identifier
        ]
        result = channel.handle_receive_lock_expired(
            channel_state=waiting_channel,
            state_change=state_change,
            block_number=block_number,
        )
        events.extend(result.events)

    return TransitionResult(mediator_state, events)
Beispiel #10
0
def handle_lock_expired(
        target_state: TargetTransferState,
        state_change: ReceiveLockExpired,
        channel_state: NettingChannelState,
        block_number: BlockNumber,
):
    """Remove expired locks from channel states."""
    result = channel.handle_receive_lock_expired(
        channel_state=channel_state,
        state_change=state_change,
        block_number=block_number,
    )

    if not channel.get_lock(result.new_state.partner_state, target_state.transfer.lock.secrethash):
        transfer = target_state.transfer
        unlock_failed = EventUnlockClaimFailed(
            identifier=transfer.payment_identifier,
            secrethash=transfer.lock.secrethash,
            reason='Lock expired',
        )
        result.events.append(unlock_failed)
        return TransitionResult(None, result.events)

    return TransitionResult(target_state, result.events)
Beispiel #11
0
def test_lock_expiry(raiden_network, token_addresses, deposit):
    """Test lock expiry and removal."""
    alice_app, bob_app = raiden_network
    token_address = token_addresses[0]
    token_network_identifier = views.get_token_network_identifier_by_token_address(
        views.state_from_app(alice_app),
        alice_app.raiden.default_registry.address,
        token_address,
    )

    hold_event_handler = HoldOffChainSecretRequest()
    wait_message_handler = WaitForMessage()
    bob_app.raiden.message_handler = wait_message_handler
    bob_app.raiden.raiden_event_handler = hold_event_handler

    token_network = views.get_token_network_by_identifier(
        views.state_from_app(alice_app),
        token_network_identifier,
    )

    channel_state = get_channelstate(alice_app, bob_app, token_network_identifier)
    channel_identifier = channel_state.identifier

    assert channel_identifier in token_network.partneraddresses_to_channelidentifiers[
        bob_app.raiden.address
    ]

    alice_to_bob_amount = 10
    identifier = 1
    target = bob_app.raiden.address
    transfer_1_secret = factories.make_secret(0)
    transfer_1_secrethash = sha3(transfer_1_secret)
    transfer_2_secret = factories.make_secret(1)
    transfer_2_secrethash = sha3(transfer_2_secret)

    hold_event_handler.hold_secretrequest_for(secrethash=transfer_1_secrethash)
    transfer1_received = wait_message_handler.wait_for_message(
        LockedTransfer,
        {'lock': {'secrethash': transfer_1_secrethash}},
    )
    transfer2_received = wait_message_handler.wait_for_message(
        LockedTransfer,
        {'lock': {'secrethash': transfer_2_secrethash}},
    )
    remove_expired_lock_received = wait_message_handler.wait_for_message(
        LockExpired,
        {'secrethash': transfer_1_secrethash},
    )

    alice_app.raiden.start_mediated_transfer_with_secret(
        token_network_identifier,
        alice_to_bob_amount,
        target,
        identifier,
        transfer_1_secret,
    )
    transfer1_received.wait()

    alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_identifier)
    lock = channel.get_lock(alice_bob_channel_state.our_state, transfer_1_secrethash)

    # This is the current state of the protocol:
    #
    #    A -> B LockedTransfer
    #    B -> A SecretRequest
    #    - protocol didn't continue
    assert_synced_channel_state(
        token_network_identifier,
        alice_app, deposit, [lock],
        bob_app, deposit, [],
    )

    # Verify lock is registered in both channel states
    alice_channel_state = get_channelstate(alice_app, bob_app, token_network_identifier)
    assert transfer_1_secrethash in alice_channel_state.our_state.secrethashes_to_lockedlocks

    bob_channel_state = get_channelstate(bob_app, alice_app, token_network_identifier)
    assert transfer_1_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks

    alice_chain_state = views.state_from_raiden(alice_app.raiden)
    assert transfer_1_secrethash in alice_chain_state.payment_mapping.secrethashes_to_task

    remove_expired_lock_received.wait()

    alice_channel_state = get_channelstate(alice_app, bob_app, token_network_identifier)
    assert transfer_1_secrethash not in alice_channel_state.our_state.secrethashes_to_lockedlocks

    # Verify Bob received the message and processed the LockExpired message
    bob_channel_state = get_channelstate(bob_app, alice_app, token_network_identifier)
    assert transfer_1_secrethash not in bob_channel_state.partner_state.secrethashes_to_lockedlocks

    alice_chain_state = views.state_from_raiden(alice_app.raiden)
    assert transfer_1_secrethash not in alice_chain_state.payment_mapping.secrethashes_to_task

    # Make another transfer
    alice_to_bob_amount = 10
    identifier = 2

    hold_event_handler.hold_secretrequest_for(secrethash=transfer_2_secrethash)

    alice_app.raiden.start_mediated_transfer_with_secret(
        token_network_identifier,
        alice_to_bob_amount,
        target,
        identifier,
        transfer_2_secret,
    )
    transfer2_received.wait()

    # Make sure the other transfer still exists
    alice_chain_state = views.state_from_raiden(alice_app.raiden)
    assert transfer_2_secrethash in alice_chain_state.payment_mapping.secrethashes_to_task

    bob_channel_state = get_channelstate(bob_app, alice_app, token_network_identifier)
    assert transfer_2_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks
Beispiel #12
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=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
Beispiel #13
0
def test_batch_unlock(raiden_network, token_addresses, secret_registry_address,
                      deposit):
    """Batch unlock can be called after the channel is settled."""
    alice_app, bob_app = raiden_network
    registry_address = alice_app.raiden.default_registry.address
    token_address = token_addresses[0]
    token_proxy = alice_app.raiden.chain.token(token_address)
    token_network_identifier = views.get_token_network_identifier_by_token_address(
        views.state_from_app(alice_app),
        alice_app.raiden.default_registry.address,
        token_address,
    )

    hold_event_handler = HoldOffChainSecretRequest()
    bob_app.raiden.raiden_event_handler = hold_event_handler

    # Take a snapshot early on
    alice_app.raiden.wal.snapshot()

    token_network = views.get_token_network_by_identifier(
        views.state_from_app(alice_app),
        token_network_identifier,
    )

    channel_identifier = get_channelstate(alice_app, bob_app,
                                          token_network_identifier).identifier

    assert channel_identifier in token_network.partneraddresses_to_channelidentifiers[
        bob_app.raiden.address]

    alice_initial_balance = token_proxy.balance_of(alice_app.raiden.address)
    bob_initial_balance = token_proxy.balance_of(bob_app.raiden.address)

    # Take snapshot before transfer
    alice_app.raiden.wal.snapshot()

    alice_to_bob_amount = 10
    identifier = 1
    target = bob_app.raiden.address
    secret = sha3(target)
    secrethash = sha3(secret)

    hold_event_handler.hold_secretrequest_for(secrethash=secrethash)

    alice_app.raiden.start_mediated_transfer_with_secret(
        token_network_identifier,
        alice_to_bob_amount,
        target,
        identifier,
        secret,
    )

    gevent.sleep(1)  # wait for the messages to be exchanged

    alice_bob_channel_state = get_channelstate(alice_app, bob_app,
                                               token_network_identifier)
    lock = channel.get_lock(alice_bob_channel_state.our_state, secrethash)

    # This is the current state of the protocol:
    #
    #    A -> B LockedTransfer
    #    B -> A SecretRequest
    #    - protocol didn't continue
    assert_synced_channel_state(
        token_network_identifier,
        alice_app,
        deposit,
        [lock],
        bob_app,
        deposit,
        [],
    )

    # Take a snapshot early on
    alice_app.raiden.wal.snapshot()

    our_balance_proof = alice_bob_channel_state.our_state.balance_proof

    # Test WAL restore to return the latest channel state
    restored_channel_state = channel_state_until_state_change(
        raiden=alice_app.raiden,
        payment_network_identifier=alice_app.raiden.default_registry.address,
        token_address=token_address,
        channel_identifier=alice_bob_channel_state.identifier,
        state_change_identifier='latest',
    )

    our_restored_balance_proof = restored_channel_state.our_state.balance_proof
    assert our_balance_proof == our_restored_balance_proof

    # A ChannelClose event will be generated, this will be polled by both apps
    # and each must start a task for calling settle
    RaidenAPI(bob_app.raiden).channel_close(
        registry_address,
        token_address,
        alice_app.raiden.address,
    )

    secret_registry_proxy = alice_app.raiden.chain.secret_registry(
        secret_registry_address, )
    secret_registry_proxy.register_secret(secret)

    assert lock, 'the lock must still be part of the node state'
    msg = 'the secret must be registered before the lock expires'
    assert lock.expiration > alice_app.raiden.get_block_number(), msg
    assert lock.secrethash == sha3(secret)

    waiting.wait_for_settle(
        alice_app.raiden,
        registry_address,
        token_address,
        [alice_bob_channel_state.identifier],
        alice_app.raiden.alarm.sleep_time,
    )

    token_network = views.get_token_network_by_identifier(
        views.state_from_app(bob_app),
        token_network_identifier,
    )

    assert channel_identifier in token_network.partneraddresses_to_channelidentifiers[
        alice_app.raiden.address]

    # wait for the node to call batch unlock
    with gevent.Timeout(10):
        wait_for_batch_unlock(
            bob_app,
            token_network_identifier,
            alice_bob_channel_state.partner_state.address,
            alice_bob_channel_state.our_state.address,
        )

    token_network = views.get_token_network_by_identifier(
        views.state_from_app(bob_app),
        token_network_identifier,
    )

    assert channel_identifier not in token_network.partneraddresses_to_channelidentifiers[
        alice_app.raiden.address]

    alice_new_balance = alice_initial_balance + deposit - alice_to_bob_amount
    bob_new_balance = bob_initial_balance + deposit + alice_to_bob_amount

    assert token_proxy.balance_of(
        alice_app.raiden.address) == alice_new_balance
    assert token_proxy.balance_of(bob_app.raiden.address) == bob_new_balance
Beispiel #14
0
def test_lock_expiry(raiden_network, token_addresses, deposit):
    """Test lock expiry and removal."""
    alice_app, bob_app = raiden_network
    token_address = token_addresses[0]
    token_network_identifier = views.get_token_network_identifier_by_token_address(
        views.state_from_app(alice_app),
        alice_app.raiden.default_registry.address,
        token_address,
    )

    hold_event_handler = HoldOffChainSecretRequest()
    wait_message_handler = WaitForMessage()
    bob_app.raiden.message_handler = wait_message_handler
    bob_app.raiden.raiden_event_handler = hold_event_handler

    token_network = views.get_token_network_by_identifier(
        views.state_from_app(alice_app),
        token_network_identifier,
    )

    channel_state = get_channelstate(alice_app, bob_app,
                                     token_network_identifier)
    channel_identifier = channel_state.identifier

    assert channel_identifier in token_network.partneraddresses_to_channelidentifiers[
        bob_app.raiden.address]

    alice_to_bob_amount = 10
    identifier = 1
    target = bob_app.raiden.address
    transfer_1_secret = factories.make_secret(0)
    transfer_1_secrethash = sha3(transfer_1_secret)
    transfer_2_secret = factories.make_secret(1)
    transfer_2_secrethash = sha3(transfer_2_secret)

    hold_event_handler.hold_secretrequest_for(secrethash=transfer_1_secrethash)
    transfer1_received = wait_message_handler.wait_for_message(
        LockedTransfer,
        {'lock': {
            'secrethash': transfer_1_secrethash
        }},
    )
    transfer2_received = wait_message_handler.wait_for_message(
        LockedTransfer,
        {'lock': {
            'secrethash': transfer_2_secrethash
        }},
    )
    remove_expired_lock_received = wait_message_handler.wait_for_message(
        LockExpired,
        {'secrethash': transfer_1_secrethash},
    )

    alice_app.raiden.start_mediated_transfer_with_secret(
        token_network_identifier,
        alice_to_bob_amount,
        target,
        identifier,
        transfer_1_secret,
    )
    transfer1_received.wait()

    alice_bob_channel_state = get_channelstate(alice_app, bob_app,
                                               token_network_identifier)
    lock = channel.get_lock(alice_bob_channel_state.our_state,
                            transfer_1_secrethash)

    # This is the current state of the protocol:
    #
    #    A -> B LockedTransfer
    #    B -> A SecretRequest
    #    - protocol didn't continue
    assert_synced_channel_state(
        token_network_identifier,
        alice_app,
        deposit,
        [lock],
        bob_app,
        deposit,
        [],
    )

    # Verify lock is registered in both channel states
    alice_channel_state = get_channelstate(alice_app, bob_app,
                                           token_network_identifier)
    assert transfer_1_secrethash in alice_channel_state.our_state.secrethashes_to_lockedlocks

    bob_channel_state = get_channelstate(bob_app, alice_app,
                                         token_network_identifier)
    assert transfer_1_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks

    alice_chain_state = views.state_from_raiden(alice_app.raiden)
    assert transfer_1_secrethash in alice_chain_state.payment_mapping.secrethashes_to_task

    remove_expired_lock_received.wait()

    alice_channel_state = get_channelstate(alice_app, bob_app,
                                           token_network_identifier)
    assert transfer_1_secrethash not in alice_channel_state.our_state.secrethashes_to_lockedlocks

    # Verify Bob received the message and processed the LockExpired message
    bob_channel_state = get_channelstate(bob_app, alice_app,
                                         token_network_identifier)
    assert transfer_1_secrethash not in bob_channel_state.partner_state.secrethashes_to_lockedlocks

    alice_chain_state = views.state_from_raiden(alice_app.raiden)
    assert transfer_1_secrethash not in alice_chain_state.payment_mapping.secrethashes_to_task

    # Make another transfer
    alice_to_bob_amount = 10
    identifier = 2

    hold_event_handler.hold_secretrequest_for(secrethash=transfer_2_secrethash)

    alice_app.raiden.start_mediated_transfer_with_secret(
        token_network_identifier,
        alice_to_bob_amount,
        target,
        identifier,
        transfer_2_secret,
    )
    transfer2_received.wait()

    # Make sure the other transfer still exists
    alice_chain_state = views.state_from_raiden(alice_app.raiden)
    assert transfer_2_secrethash in alice_chain_state.payment_mapping.secrethashes_to_task

    bob_channel_state = get_channelstate(bob_app, alice_app,
                                         token_network_identifier)
    assert transfer_2_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks
Beispiel #15
0
def test_batch_unlock(raiden_network, token_addresses, secret_registry_address,
                      deposit):
    """Batch unlock can be called after the channel is settled."""
    alice_app, bob_app = raiden_network
    registry_address = alice_app.raiden.default_registry.address
    token_address = token_addresses[0]
    token_proxy = alice_app.raiden.chain.token(token_address)
    token_network_identifier = views.get_token_network_identifier_by_token_address(
        views.state_from_app(alice_app),
        alice_app.raiden.default_registry.address,
        token_address,
    )

    token_network = views.get_token_network_by_identifier(
        views.state_from_app(alice_app),
        token_network_identifier,
    )

    channel_identifier = get_channelstate(alice_app, bob_app,
                                          token_network_identifier).identifier

    assert channel_identifier in token_network.partneraddresses_to_channels[
        bob_app.raiden.address]

    alice_initial_balance = token_proxy.balance_of(alice_app.raiden.address)
    bob_initial_balance = token_proxy.balance_of(bob_app.raiden.address)

    alice_to_bob_amount = 10
    identifier = 1
    secret = pending_mediated_transfer(
        raiden_network,
        token_network_identifier,
        alice_to_bob_amount,
        identifier,
    )
    secrethash = sha3(secret)

    alice_bob_channel_state = get_channelstate(alice_app, bob_app,
                                               token_network_identifier)
    lock = channel.get_lock(alice_bob_channel_state.our_state, secrethash)

    # This is the current state of the protocol:
    #
    #    A -> B LockedTransfer
    #    B -> A SecretRequest
    #    - protocol didn't continue
    assert_synced_channel_state(
        token_network_identifier,
        alice_app,
        deposit,
        [lock],
        bob_app,
        deposit,
        [],
    )

    # A ChannelClose event will be generated, this will be polled by both apps
    # and each must start a task for calling settle
    RaidenAPI(bob_app.raiden).channel_close(
        registry_address,
        token_address,
        alice_app.raiden.address,
    )

    secret_registry_proxy = alice_app.raiden.chain.secret_registry(
        secret_registry_address, )
    secret_registry_proxy.register_secret(secret)

    assert lock, 'the lock must still be part of the node state'
    msg = 'the secret must be registered before the lock expires'
    assert lock.expiration > alice_app.raiden.get_block_number(), msg
    assert lock.secrethash == sha3(secret)

    waiting.wait_for_settle(
        alice_app.raiden,
        registry_address,
        token_address,
        [alice_bob_channel_state.identifier],
        alice_app.raiden.alarm.sleep_time,
    )

    token_network = views.get_token_network_by_identifier(
        views.state_from_app(bob_app),
        token_network_identifier,
    )

    assert channel_identifier in token_network.partneraddresses_to_channels[
        alice_app.raiden.address]

    # wait for the node to call batch unlock
    with gevent.Timeout(10):
        wait_for_batch_unlock(
            bob_app,
            token_network_identifier,
            alice_bob_channel_state.partner_state.address,
            alice_bob_channel_state.our_state.address,
        )

    token_network = views.get_token_network_by_identifier(
        views.state_from_app(bob_app),
        token_network_identifier,
    )

    assert channel_identifier not in token_network.partneraddresses_to_channels[
        alice_app.raiden.address]

    alice_new_balance = alice_initial_balance + deposit - alice_to_bob_amount
    bob_new_balance = bob_initial_balance + deposit + alice_to_bob_amount

    assert token_proxy.balance_of(
        alice_app.raiden.address) == alice_new_balance
    assert token_proxy.balance_of(bob_app.raiden.address) == bob_new_balance
Beispiel #16
0
def test_unlock(raiden_network, token_addresses, deposit):
    """Unlock can be called on a closed channel."""
    alice_app, bob_app = raiden_network
    registry_address = alice_app.raiden.default_registry.address
    token_address = token_addresses[0]
    token_proxy = alice_app.raiden.chain.token(token_address)
    token_network_identifier = views.get_token_network_identifier_by_token_address(
        views.state_from_app(alice_app),
        alice_app.raiden.default_registry.address,
        token_address,
    )

    alice_initial_balance = token_proxy.balance_of(alice_app.raiden.address)
    bob_initial_balance = token_proxy.balance_of(bob_app.raiden.address)

    alice_to_bob_amount = 10
    identifier = 1
    secret = pending_mediated_transfer(
        raiden_network,
        token_network_identifier,
        alice_to_bob_amount,
        identifier,
    )
    secrethash = sha3(secret)

    # This is the current state of the protocol:
    #
    #    A -> B LockedTransfer
    #    B -> A SecretRequest
    #    - protocol didn't continue

    alice_bob_channel = get_channelstate(alice_app, bob_app, token_network_identifier)
    bob_alice_channel = get_channelstate(bob_app, alice_app, token_network_identifier)

    lock = channel.get_lock(alice_bob_channel.our_state, secrethash)
    assert lock

    assert_synched_channel_state(
        token_network_identifier,
        alice_app, deposit, [lock],
        bob_app, deposit, [],
    )

    # get proof, that locked transfermessage was in merkle tree, with locked.root
    unlock_proof = channel.compute_proof_for_lock(
        alice_bob_channel.our_state,
        secret,
        lock,
    )

    assert validate_proof(
        unlock_proof.merkle_proof,
        merkleroot(bob_alice_channel.partner_state.merkletree),
        sha3(lock.encoded),
    )
    assert unlock_proof.lock_encoded == lock.encoded
    assert unlock_proof.secret == secret

    # A ChannelClose event will be generated, this will be polled by both apps
    # and each must start a task for calling settle
    RaidenAPI(bob_app.raiden).channel_close(
        registry_address,
        token_address,
        alice_app.raiden.address,
    )

    # Unlock will not be called because the secret was not revealed
    assert lock.expiration > alice_app.raiden.chain.block_number()
    assert lock.secrethash == sha3(secret)

    nettingchannel_proxy = bob_app.raiden.chain.netting_channel(
        bob_alice_channel.identifier,
    )
    nettingchannel_proxy.unlock(unlock_proof)

    waiting.wait_for_settle(
        alice_app.raiden,
        registry_address,
        token_address,
        [alice_bob_channel.identifier],
        alice_app.raiden.alarm.wait_time,
    )

    alice_bob_channel = get_channelstate(alice_app, bob_app, token_network_identifier)
    bob_alice_channel = get_channelstate(bob_app, alice_app, token_network_identifier)

    alice_netted_balance = alice_initial_balance + deposit - alice_to_bob_amount
    bob_netted_balance = bob_initial_balance + deposit + alice_to_bob_amount

    assert token_proxy.balance_of(alice_app.raiden.address) == alice_netted_balance
    assert token_proxy.balance_of(bob_app.raiden.address) == bob_netted_balance

    # Now let's query the WAL to see if the state changes were logged as expected
    state_changes = alice_app.raiden.wal.storage.get_statechanges_by_identifier(
        from_identifier=0,
        to_identifier='latest',
    )

    alice_bob_channel = get_channelstate(alice_app, bob_app, token_network_identifier)
    bob_alice_channel = get_channelstate(bob_app, alice_app, token_network_identifier)

    assert must_contain_entry(state_changes, ContractReceiveChannelUnlock, {
        'payment_network_identifier': registry_address,
        'token_address': token_address,
        'channel_identifier': alice_bob_channel.identifier,
        'secrethash': secrethash,
        'secret': secret,
        'receiver': bob_app.raiden.address,
    })
Beispiel #17
0
def test_lock_expiry(
    raiden_network: List[App], token_addresses: List[TokenAddress], deposit: TokenAmount
) -> None:
    """Test lock expiry and removal."""
    alice_app, bob_app = raiden_network
    token_address = token_addresses[0]
    token_network_address = views.get_token_network_address_by_token_address(
        views.state_from_app(alice_app), alice_app.raiden.default_registry.address, token_address
    )
    assert token_network_address

    hold_event_handler = bob_app.raiden.raiden_event_handler
    wait_message_handler = bob_app.raiden.message_handler

    msg = "hold event handler necessary to control messages"
    assert isinstance(hold_event_handler, HoldRaidenEventHandler), msg
    assert isinstance(wait_message_handler, WaitForMessage), msg

    token_network = views.get_token_network_by_address(
        views.state_from_app(alice_app), token_network_address
    )
    assert token_network

    channel_state = get_channelstate(alice_app, bob_app, token_network_address)
    channel_identifier = channel_state.identifier

    assert (
        channel_identifier
        in token_network.partneraddresses_to_channelidentifiers[bob_app.raiden.address]
    )

    alice_to_bob_amount = PaymentAmount(10)
    identifier = factories.make_payment_id()
    target = TargetAddress(bob_app.raiden.address)
    transfer_1_secret = factories.make_secret(0)
    transfer_1_secrethash = sha256_secrethash(transfer_1_secret)
    transfer_2_secret = factories.make_secret(1)
    transfer_2_secrethash = sha256_secrethash(transfer_2_secret)

    hold_event_handler.hold_secretrequest_for(secrethash=transfer_1_secrethash)
    transfer1_received = wait_message_handler.wait_for_message(
        LockedTransfer, {"lock": {"secrethash": transfer_1_secrethash}}
    )
    transfer2_received = wait_message_handler.wait_for_message(
        LockedTransfer, {"lock": {"secrethash": transfer_2_secrethash}}
    )
    remove_expired_lock_received = wait_message_handler.wait_for_message(
        LockExpired, {"secrethash": transfer_1_secrethash}
    )

    alice_app.raiden.start_mediated_transfer_with_secret(
        token_network_address=token_network_address,
        amount=alice_to_bob_amount,
        target=target,
        identifier=identifier,
        secret=transfer_1_secret,
    )
    transfer1_received.wait()

    alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_address)
    lock = channel.get_lock(alice_bob_channel_state.our_state, transfer_1_secrethash)
    assert lock

    # This is the current state of the protocol:
    #
    #    A -> B LockedTransfer
    #    B -> A SecretRequest
    #    - protocol didn't continue
    assert_synced_channel_state(
        token_network_address, alice_app, Balance(deposit), [lock], bob_app, Balance(deposit), []
    )

    # Verify lock is registered in both channel states
    alice_channel_state = get_channelstate(alice_app, bob_app, token_network_address)
    assert transfer_1_secrethash in alice_channel_state.our_state.secrethashes_to_lockedlocks

    bob_channel_state = get_channelstate(bob_app, alice_app, token_network_address)
    assert transfer_1_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks

    alice_chain_state = views.state_from_raiden(alice_app.raiden)
    assert transfer_1_secrethash in alice_chain_state.payment_mapping.secrethashes_to_task

    remove_expired_lock_received.wait()

    alice_channel_state = get_channelstate(alice_app, bob_app, token_network_address)
    assert transfer_1_secrethash not in alice_channel_state.our_state.secrethashes_to_lockedlocks

    # Verify Bob received the message and processed the LockExpired message
    bob_channel_state = get_channelstate(bob_app, alice_app, token_network_address)
    assert transfer_1_secrethash not in bob_channel_state.partner_state.secrethashes_to_lockedlocks

    alice_chain_state = views.state_from_raiden(alice_app.raiden)
    assert transfer_1_secrethash not in alice_chain_state.payment_mapping.secrethashes_to_task

    # Make another transfer
    alice_to_bob_amount = PaymentAmount(10)
    identifier = factories.make_payment_id()

    hold_event_handler.hold_secretrequest_for(secrethash=transfer_2_secrethash)

    alice_app.raiden.start_mediated_transfer_with_secret(
        token_network_address=token_network_address,
        amount=alice_to_bob_amount,
        target=target,
        identifier=identifier,
        secret=transfer_2_secret,
    )
    transfer2_received.wait()

    # Make sure the other transfer still exists
    alice_chain_state = views.state_from_raiden(alice_app.raiden)
    assert transfer_2_secrethash in alice_chain_state.payment_mapping.secrethashes_to_task

    bob_channel_state = get_channelstate(bob_app, alice_app, token_network_address)
    assert transfer_2_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks
def test_regression_send_refund():
    """Regression test for discarded refund transfer.

    The handle_refundtransfer used to discard events from the channel state
    machine, which led to the state being updated but the message to the
    partner was never sent.

    Also, for issue: https://github.com/raiden-network/raiden/issues/3170
    It was noticed that when receiving the same refund transfer twice, the mediator
    would detect an invalid refund and clear the mediator state. So the test also
    checks that mediator rejects the duplicate transfer and keeps the mediator
    state unchanged.
    """
    pseudo_random_generator = random.Random()
    setup = factories.make_transfers_pair(3)

    mediator_state = MediatorTransferState(
        secrethash=UNIT_SECRETHASH,
        routes=setup.channels.get_routes(),
    )
    mediator_state.transfers_pair = setup.transfers_pair

    last_pair = setup.transfers_pair[-1]
    channel_identifier = last_pair.payee_transfer.balance_proof.channel_identifier
    lock_expiration = last_pair.payee_transfer.lock.expiration

    received_transfer = factories.make_signed_transfer_state(
        amount=UNIT_TRANSFER_AMOUNT,
        initiator=UNIT_TRANSFER_INITIATOR,
        target=UNIT_TRANSFER_TARGET,
        expiration=lock_expiration,
        secret=UNIT_SECRET,
        payment_identifier=UNIT_TRANSFER_IDENTIFIER,
        channel_identifier=channel_identifier,
        pkey=setup.channels.partner_privatekeys[2],
        sender=setup.channels.partner_address(2),
    )

    # All three channels have been used
    routes = []

    refund_state_change = ReceiveTransferRefund(
        transfer=received_transfer,
        routes=routes,
    )

    iteration = mediator.handle_refundtransfer(
        mediator_state=mediator_state,
        mediator_state_change=refund_state_change,
        channelidentifiers_to_channels=setup.channel_map,
        nodeaddresses_to_networkstates=setup.channels.
        nodeaddresses_to_networkstates,
        pseudo_random_generator=pseudo_random_generator,
        block_number=setup.block_number,
    )

    first_pair = setup.transfers_pair[0]
    first_payer_transfer = first_pair.payer_transfer
    payer_channel = mediator.get_payer_channel(setup.channel_map, first_pair)
    lock = channel.get_lock(
        end_state=payer_channel.partner_state,
        secrethash=UNIT_SECRETHASH,
    )
    token_network_identifier = first_payer_transfer.balance_proof.token_network_identifier
    assert search_for_item(
        iteration.events, SendRefundTransfer, {
            'recipient': setup.channels.partner_address(0),
            'queue_identifier': {
                'recipient':
                setup.channels.partner_address(0),
                'channel_identifier':
                first_payer_transfer.balance_proof.channel_identifier,
            },
            'transfer': {
                'payment_identifier': UNIT_TRANSFER_IDENTIFIER,
                'token': UNIT_TOKEN_ADDRESS,
                'balance_proof': {
                    'transferred_amount': 0,
                    'locked_amount': 10,
                    'locksroot': lock.lockhash,
                    'token_network_identifier': token_network_identifier,
                    'channel_identifier':
                    first_payer_transfer.balance_proof.channel_identifier,
                    'chain_id': first_payer_transfer.balance_proof.chain_id,
                },
                'lock': {
                    'amount': lock.amount,
                    'expiration': lock.expiration,
                    'secrethash': lock.secrethash,
                },
                'initiator': UNIT_TRANSFER_INITIATOR,
                'target': UNIT_TRANSFER_TARGET,
            },
        })

    duplicate_iteration = mediator.handle_refundtransfer(
        mediator_state=iteration.new_state,
        mediator_state_change=refund_state_change,
        channelidentifiers_to_channels=setup.channel_map,
        nodeaddresses_to_networkstates=setup.channels.
        nodeaddresses_to_networkstates,
        pseudo_random_generator=pseudo_random_generator,
        block_number=setup.block_number,
    )

    assert search_for_item(duplicate_iteration.events, SendRefundTransfer,
                           {}) is None

    assert duplicate_iteration.new_state is not None
    assert duplicate_iteration.new_state == iteration.new_state
Beispiel #19
0
def test_batch_unlock(raiden_network, token_addresses, secret_registry_address, deposit):
    """Batch unlock can be called after the channel is settled."""
    alice_app, bob_app = raiden_network
    registry_address = alice_app.raiden.default_registry.address
    token_address = token_addresses[0]
    token_proxy = alice_app.raiden.chain.token(token_address)
    token_network_identifier = views.get_token_network_identifier_by_token_address(
        views.state_from_app(alice_app),
        alice_app.raiden.default_registry.address,
        token_address,
    )

    alice_initial_balance = token_proxy.balance_of(alice_app.raiden.address)
    bob_initial_balance = token_proxy.balance_of(bob_app.raiden.address)

    alice_to_bob_amount = 10
    identifier = 1
    secret = pending_mediated_transfer(
        raiden_network,
        token_network_identifier,
        alice_to_bob_amount,
        identifier,
    )
    secrethash = sha3(secret)

    secret_registry_proxy = alice_app.raiden.chain.secret_registry(
        secret_registry_address,
    )
    secret_registry_proxy.register_secret(secret)

    alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_identifier)
    lock = channel.get_lock(alice_bob_channel_state.our_state, secrethash)

    assert lock
    assert lock.expiration > alice_app.raiden.get_block_number()
    assert lock.secrethash == sha3(secret)

    # This is the current state of the protocol:
    #
    #    A -> B LockedTransfer
    #    B -> A SecretRequest
    #    - protocol didn't continue
    assert_synched_channel_state(
        token_network_identifier,
        alice_app, deposit, [lock],
        bob_app, deposit, [],
    )

    # A ChannelClose event will be generated, this will be polled by both apps
    # and each must start a task for calling settle
    RaidenAPI(bob_app.raiden).channel_close(
        registry_address,
        token_address,
        alice_app.raiden.address,
    )

    waiting.wait_for_settle(
        alice_app.raiden,
        registry_address,
        token_address,
        [alice_bob_channel_state.identifier],
        alice_app.raiden.alarm.sleep_time,
    )

    # wait for the node to call batch unlock
    with gevent.Timeout(10):
        wait_for_batch_unlock(
            alice_app,
            token_network_identifier,
            alice_bob_channel_state.partner_state.address,
            alice_bob_channel_state.our_state.address,
        )

    alice_new_balance = alice_initial_balance + deposit - alice_to_bob_amount
    bob_new_balance = bob_initial_balance + deposit + alice_to_bob_amount

    assert token_proxy.balance_of(alice_app.raiden.address) == alice_new_balance
    assert token_proxy.balance_of(bob_app.raiden.address) == bob_new_balance
Beispiel #20
0
def run_test_lock_expiry(raiden_network, token_addresses, deposit):
    alice_app, bob_app = raiden_network
    token_address = token_addresses[0]
    token_network_identifier = views.get_token_network_identifier_by_token_address(
        views.state_from_app(alice_app), alice_app.raiden.default_registry.address, token_address
    )

    hold_event_handler = bob_app.raiden.raiden_event_handler
    wait_message_handler = bob_app.raiden.message_handler

    token_network = views.get_token_network_by_identifier(
        views.state_from_app(alice_app), token_network_identifier
    )

    channel_state = get_channelstate(alice_app, bob_app, token_network_identifier)
    channel_identifier = channel_state.identifier

    assert (
        channel_identifier
        in token_network.partneraddresses_to_channelidentifiers[bob_app.raiden.address]
    )

    alice_to_bob_amount = 10
    identifier = 1
    target = bob_app.raiden.address
    transfer_1_secret = factories.make_secret(0)
    transfer_1_secrethash = sha3(transfer_1_secret)
    transfer_2_secret = factories.make_secret(1)
    transfer_2_secrethash = sha3(transfer_2_secret)

    hold_event_handler.hold_secretrequest_for(secrethash=transfer_1_secrethash)
    transfer1_received = wait_message_handler.wait_for_message(
        LockedTransfer, {"lock": {"secrethash": transfer_1_secrethash}}
    )
    transfer2_received = wait_message_handler.wait_for_message(
        LockedTransfer, {"lock": {"secrethash": transfer_2_secrethash}}
    )
    remove_expired_lock_received = wait_message_handler.wait_for_message(
        LockExpired, {"secrethash": transfer_1_secrethash}
    )

    alice_app.raiden.start_mediated_transfer_with_secret(
        token_network_identifier=token_network_identifier,
        amount=alice_to_bob_amount,
        fee=0,
        target=target,
        identifier=identifier,
        payment_hash_invoice=EMPTY_PAYMENT_HASH_INVOICE,
        secret=transfer_1_secret,
    )
    transfer1_received.wait()

    alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_identifier)
    lock = channel.get_lock(alice_bob_channel_state.our_state, transfer_1_secrethash)

    # This is the current state of the protocol:
    #
    #    A -> B LockedTransfer
    #    B -> A SecretRequest
    #    - protocol didn't continue
    assert_synced_channel_state(
        token_network_identifier, alice_app, deposit, [lock], bob_app, deposit, []
    )

    # Verify lock is registered in both channel states
    alice_channel_state = get_channelstate(alice_app, bob_app, token_network_identifier)
    assert transfer_1_secrethash in alice_channel_state.our_state.secrethashes_to_lockedlocks

    bob_channel_state = get_channelstate(bob_app, alice_app, token_network_identifier)
    assert transfer_1_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks

    alice_chain_state = views.state_from_raiden(alice_app.raiden)
    assert transfer_1_secrethash in alice_chain_state.payment_mapping.secrethashes_to_task

    remove_expired_lock_received.wait()

    alice_channel_state = get_channelstate(alice_app, bob_app, token_network_identifier)
    assert transfer_1_secrethash not in alice_channel_state.our_state.secrethashes_to_lockedlocks

    # Verify Bob received the message and processed the LockExpired message
    bob_channel_state = get_channelstate(bob_app, alice_app, token_network_identifier)
    assert transfer_1_secrethash not in bob_channel_state.partner_state.secrethashes_to_lockedlocks

    alice_chain_state = views.state_from_raiden(alice_app.raiden)
    assert transfer_1_secrethash not in alice_chain_state.payment_mapping.secrethashes_to_task

    # Make another transfer
    alice_to_bob_amount = 10
    identifier = 2

    hold_event_handler.hold_secretrequest_for(secrethash=transfer_2_secrethash)

    alice_app.raiden.start_mediated_transfer_with_secret(
        token_network_identifier=token_network_identifier,
        amount=alice_to_bob_amount,
        fee=0,
        target=target,
        identifier=identifier,
        payment_hash_invoice=EMPTY_PAYMENT_HASH_INVOICE,
        secret=transfer_2_secret,
    )
    transfer2_received.wait()

    # Make sure the other transfer still exists
    alice_chain_state = views.state_from_raiden(alice_app.raiden)
    assert transfer_2_secrethash in alice_chain_state.payment_mapping.secrethashes_to_task

    bob_channel_state = get_channelstate(bob_app, alice_app, token_network_identifier)
    assert transfer_2_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks
def test_regression_send_refund():
    """Regression test for discarded refund transfer.

    The handle_refundtransfer used to discard events from the channel state
    machine, which led to the state being updated but the message to the
    partner was never sent.

    Also, for issue: https://github.com/raiden-network/raiden/issues/3170
    It was noticed that when receiving the same refund transfer twice, the mediator
    would detect an invalid refund and clear the mediator state. So the test also
    checks that mediator rejects the duplicate transfer and keeps the mediator
    state unchanged.
    """
    pseudo_random_generator = random.Random()
    setup = factories.make_transfers_pair(3)

    mediator_state = MediatorTransferState(UNIT_SECRETHASH)
    mediator_state.transfers_pair = setup.transfers_pair

    last_pair = setup.transfers_pair[-1]
    channel_identifier = last_pair.payee_transfer.balance_proof.channel_identifier
    lock_expiration = last_pair.payee_transfer.lock.expiration

    received_transfer = factories.make_signed_transfer(
        amount=UNIT_TRANSFER_AMOUNT,
        initiator=UNIT_TRANSFER_INITIATOR,
        target=UNIT_TRANSFER_TARGET,
        expiration=lock_expiration,
        secret=UNIT_SECRET,
        payment_identifier=UNIT_TRANSFER_IDENTIFIER,
        channel_identifier=channel_identifier,
        pkey=setup.channels.partner_privatekeys[2],
        sender=setup.channels.partner_address(2),
    )

    # All three channels have been used
    routes = []

    refund_state_change = ReceiveTransferRefund(
        transfer=received_transfer,
        routes=routes,
    )

    iteration = mediator.handle_refundtransfer(
        mediator_state=mediator_state,
        mediator_state_change=refund_state_change,
        channelidentifiers_to_channels=setup.channel_map,
        pseudo_random_generator=pseudo_random_generator,
        block_number=setup.block_number,
    )

    first_pair = setup.transfers_pair[0]
    first_payer_transfer = first_pair.payer_transfer
    payer_channel = mediator.get_payer_channel(setup.channel_map, first_pair)
    lock = channel.get_lock(
        end_state=payer_channel.partner_state,
        secrethash=UNIT_SECRETHASH,
    )
    token_network_identifier = first_payer_transfer.balance_proof.token_network_identifier
    assert must_contain_entry(iteration.events, SendRefundTransfer, {
        'recipient': setup.channels.partner_address(0),
        'queue_identifier': {
            'recipient': setup.channels.partner_address(0),
            'channel_identifier': first_payer_transfer.balance_proof.channel_identifier,
        },
        'transfer': {
            'payment_identifier': UNIT_TRANSFER_IDENTIFIER,
            'token': UNIT_TOKEN_ADDRESS,
            'balance_proof': {
                'transferred_amount': 0,
                'locked_amount': 10,
                'locksroot': lock.lockhash,
                'token_network_identifier': token_network_identifier,
                'channel_identifier': first_payer_transfer.balance_proof.channel_identifier,
                'chain_id': first_payer_transfer.balance_proof.chain_id,
            },
            'lock': {
                'amount': lock.amount,
                'expiration': lock.expiration,
                'secrethash': lock.secrethash,
            },
            'initiator': UNIT_TRANSFER_INITIATOR,
            'target': UNIT_TRANSFER_TARGET,
        },
    })

    duplicate_iteration = mediator.handle_refundtransfer(
        mediator_state=iteration.new_state,
        mediator_state_change=refund_state_change,
        channelidentifiers_to_channels=setup.channel_map,
        pseudo_random_generator=pseudo_random_generator,
        block_number=setup.block_number,
    )

    assert must_contain_entry(duplicate_iteration.events, SendRefundTransfer, {}) is None

    assert duplicate_iteration.new_state is not None
    assert duplicate_iteration.new_state == iteration.new_state
Beispiel #22
0
def test_batch_unlock(raiden_network, token_addresses, secret_registry_address, deposit):
    """Batch unlock can be called after the channel is settled."""
    alice_app, bob_app = raiden_network
    registry_address = alice_app.raiden.default_registry.address
    token_address = token_addresses[0]
    token_proxy = alice_app.raiden.chain.token(token_address)
    token_network_identifier = views.get_token_network_identifier_by_token_address(
        views.state_from_app(alice_app),
        alice_app.raiden.default_registry.address,
        token_address,
    )

    hold_event_handler = HoldOffChainSecretRequest()
    bob_app.raiden.raiden_event_handler = hold_event_handler

    # Take a snapshot early on
    alice_app.raiden.wal.snapshot()

    token_network = views.get_token_network_by_identifier(
        views.state_from_app(alice_app),
        token_network_identifier,
    )

    channel_identifier = get_channelstate(alice_app, bob_app, token_network_identifier).identifier

    assert channel_identifier in token_network.partneraddresses_to_channelidentifiers[
        bob_app.raiden.address
    ]

    alice_initial_balance = token_proxy.balance_of(alice_app.raiden.address)
    bob_initial_balance = token_proxy.balance_of(bob_app.raiden.address)

    # Take snapshot before transfer
    alice_app.raiden.wal.snapshot()

    alice_to_bob_amount = 10
    identifier = 1
    target = bob_app.raiden.address
    secret = sha3(target)
    secrethash = sha3(secret)

    hold_event_handler.hold_secretrequest_for(secrethash=secrethash)

    alice_app.raiden.start_mediated_transfer_with_secret(
        token_network_identifier,
        alice_to_bob_amount,
        target,
        identifier,
        secret,
    )

    gevent.sleep(1)  # wait for the messages to be exchanged

    alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_identifier)
    lock = channel.get_lock(alice_bob_channel_state.our_state, secrethash)

    # This is the current state of the protocol:
    #
    #    A -> B LockedTransfer
    #    B -> A SecretRequest
    #    - protocol didn't continue
    assert_synced_channel_state(
        token_network_identifier,
        alice_app, deposit, [lock],
        bob_app, deposit, [],
    )

    # Take a snapshot early on
    alice_app.raiden.wal.snapshot()

    our_balance_proof = alice_bob_channel_state.our_state.balance_proof

    # Test WAL restore to return the latest channel state
    restored_channel_state = channel_state_until_state_change(
        raiden=alice_app.raiden,
        payment_network_identifier=alice_app.raiden.default_registry.address,
        token_address=token_address,
        channel_identifier=alice_bob_channel_state.identifier,
        state_change_identifier='latest',
    )

    our_restored_balance_proof = restored_channel_state.our_state.balance_proof
    assert our_balance_proof == our_restored_balance_proof

    # A ChannelClose event will be generated, this will be polled by both apps
    # and each must start a task for calling settle
    RaidenAPI(bob_app.raiden).channel_close(
        registry_address,
        token_address,
        alice_app.raiden.address,
    )

    secret_registry_proxy = alice_app.raiden.chain.secret_registry(
        secret_registry_address,
    )
    secret_registry_proxy.register_secret(secret)

    assert lock, 'the lock must still be part of the node state'
    msg = 'the secret must be registered before the lock expires'
    assert lock.expiration > alice_app.raiden.get_block_number(), msg
    assert lock.secrethash == sha3(secret)

    waiting.wait_for_settle(
        alice_app.raiden,
        registry_address,
        token_address,
        [alice_bob_channel_state.identifier],
        alice_app.raiden.alarm.sleep_time,
    )

    token_network = views.get_token_network_by_identifier(
        views.state_from_app(bob_app),
        token_network_identifier,
    )

    assert channel_identifier in token_network.partneraddresses_to_channelidentifiers[
        alice_app.raiden.address
    ]

    # wait for the node to call batch unlock
    with gevent.Timeout(10):
        wait_for_batch_unlock(
            bob_app,
            token_network_identifier,
            alice_bob_channel_state.partner_state.address,
            alice_bob_channel_state.our_state.address,
        )

    token_network = views.get_token_network_by_identifier(
        views.state_from_app(bob_app),
        token_network_identifier,
    )

    assert channel_identifier not in token_network.partneraddresses_to_channelidentifiers[
        alice_app.raiden.address
    ]

    alice_new_balance = alice_initial_balance + deposit - alice_to_bob_amount
    bob_new_balance = bob_initial_balance + deposit + alice_to_bob_amount

    assert token_proxy.balance_of(alice_app.raiden.address) == alice_new_balance
    assert token_proxy.balance_of(bob_app.raiden.address) == bob_new_balance
Beispiel #23
0
def test_batch_unlock(raiden_network, token_addresses, secret_registry_address, deposit):
    """Tests that batch unlock is properly called.

    This test will start a single incomplete transfer, the secret will be
    revealed *on-chain*. The node that receives the tokens has to call unlock,
    the node that doesn't gain anything does nothing.
    """
    alice_app, bob_app = raiden_network
    alice_address = alice_app.raiden.address
    bob_address = bob_app.raiden.address

    token_network_registry_address = alice_app.raiden.default_registry.address
    token_address = token_addresses[0]
    token_network_address = views.get_token_network_address_by_token_address(
        views.state_from_app(alice_app), token_network_registry_address, token_address
    )
    assert token_network_address

    hold_event_handler = bob_app.raiden.raiden_event_handler

    # Take a snapshot early on
    alice_app.raiden.wal.snapshot()

    canonical_identifier = get_channelstate(
        alice_app, bob_app, token_network_address
    ).canonical_identifier

    assert is_channel_registered(alice_app, bob_app, canonical_identifier)
    assert is_channel_registered(bob_app, alice_app, canonical_identifier)

    token_proxy = alice_app.raiden.proxy_manager.token(token_address)
    alice_initial_balance = token_proxy.balance_of(alice_app.raiden.address)
    bob_initial_balance = token_proxy.balance_of(bob_app.raiden.address)

    # Take snapshot before transfer
    alice_app.raiden.wal.snapshot()

    alice_to_bob_amount = 10
    identifier = 1
    secret = Secret(sha3(bob_address))
    secrethash = sha256_secrethash(secret)

    secret_request_event = hold_event_handler.hold_secretrequest_for(secrethash=secrethash)

    alice_app.raiden.start_mediated_transfer_with_secret(
        token_network_address=token_network_address,
        amount=alice_to_bob_amount,
        target=bob_address,
        identifier=identifier,
        secret=secret,
    )

    secret_request_event.get()  # wait for the messages to be exchanged

    alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_address)
    lock = channel.get_lock(alice_bob_channel_state.our_state, secrethash)
    assert lock

    # This is the current state of the protocol:
    #
    #    A -> B LockedTransfer
    #    B -> A SecretRequest
    #    - protocol didn't continue
    assert_synced_channel_state(
        token_network_address, alice_app, deposit, [lock], bob_app, deposit, []
    )

    # Test WAL restore to return the latest channel state
    alice_app.raiden.wal.snapshot()
    our_balance_proof = alice_bob_channel_state.our_state.balance_proof
    restored_channel_state = channel_state_until_state_change(
        raiden=alice_app.raiden,
        canonical_identifier=alice_bob_channel_state.canonical_identifier,
        state_change_identifier=HIGH_STATECHANGE_ULID,
    )
    assert restored_channel_state
    our_restored_balance_proof = restored_channel_state.our_state.balance_proof
    assert our_balance_proof == our_restored_balance_proof

    # Close the channel before revealing the secret off-chain. This will leave
    # a pending lock in the channel which has to be unlocked on-chain.
    #
    # The token network will emit a ChannelClose event, this will be polled by
    # both apps and each must start a task for calling settle.
    RaidenAPI(bob_app.raiden).channel_close(
        token_network_registry_address, token_address, alice_app.raiden.address
    )

    # The secret has to be registered manually because Bob never learned the
    # secret. The test is holding the SecretRequest to ensure the off-chain
    # unlock will not happen and the channel is closed with a pending lock.
    #
    # Alternatives would be to hold the unlock messages, or to stop and restart
    # the apps after the channel is closed.
    secret_registry_proxy = alice_app.raiden.proxy_manager.secret_registry(secret_registry_address)
    secret_registry_proxy.register_secret(secret=secret)

    msg = (
        "The lock must still be part of the node state for the test to proceed, "
        "otherwise there is not unlock to be done."
    )
    assert lock, msg

    msg = (
        "The secret must be registered before the lock expires, in order for "
        "the unlock to happen on-chain. Otherwise the test will fail on the "
        "expected balances."
    )
    assert lock.expiration > alice_app.raiden.get_block_number(), msg
    assert lock.secrethash == sha256_secrethash(secret)

    waiting.wait_for_settle(
        alice_app.raiden,
        token_network_registry_address,
        token_address,
        [alice_bob_channel_state.identifier],
        alice_app.raiden.alarm.sleep_time,
    )

    msg = "The channel_state must not have been cleared, one of the ends has pending locks to do."
    assert is_channel_registered(alice_app, bob_app, canonical_identifier), msg
    assert is_channel_registered(bob_app, alice_app, canonical_identifier), msg

    msg = (
        "Timeout while waiting for the unlock to be mined. This may happen if "
        "transaction is rejected, not mined, or the node's alarm task is "
        "not running."
    )
    with gevent.Timeout(seconds=30, exception=AssertionError(msg)):
        # Wait for both nodes (Bob and Alice) to see the on-chain unlock
        wait_for_batch_unlock(
            app=alice_app,
            token_network_address=token_network_address,
            receiver=bob_address,
            sender=alice_address,
        )
        wait_for_batch_unlock(
            app=bob_app,
            token_network_address=token_network_address,
            receiver=bob_address,
            sender=alice_address,
        )

    msg = (
        "The nodes have done the unlock, and both ends have seen it, now the "
        "channel must be cleared"
    )
    assert not is_channel_registered(alice_app, bob_app, canonical_identifier), msg
    assert not is_channel_registered(bob_app, alice_app, canonical_identifier), msg

    alice_new_balance = alice_initial_balance + deposit - alice_to_bob_amount
    bob_new_balance = bob_initial_balance + deposit + alice_to_bob_amount

    msg = "Unexpected end balance after channel settlement with batch unlock."
    assert token_proxy.balance_of(alice_app.raiden.address) == alice_new_balance, msg
    assert token_proxy.balance_of(bob_app.raiden.address) == bob_new_balance, msg
Beispiel #24
0
def test_settled_lock(token_addresses, raiden_network, deposit):
    """ Any transfer following a secret revealed must update the locksroot, so
    that an attacker cannot reuse a secret to double claim a lock."""
    app0, app1 = raiden_network
    registry_address = app0.raiden.default_registry.address
    token_address = token_addresses[0]
    amount = 30
    token_network_identifier = views.get_token_network_identifier_by_token_address(
        views.state_from_app(app0),
        app0.raiden.default_registry.address,
        token_address,
    )

    address0 = app0.raiden.address
    address1 = app1.raiden.address

    deposit0 = deposit
    deposit1 = deposit

    token_proxy = app0.raiden.chain.token(token_address)
    initial_balance0 = token_proxy.balance_of(address0)
    initial_balance1 = token_proxy.balance_of(address1)

    # Using a pending mediated transfer because this allows us to compute the
    # merkle proof
    identifier = 1
    secret = pending_mediated_transfer(
        raiden_network,
        token_network_identifier,
        amount,
        identifier,
    )
    secrethash = sha3(secret)

    # Compute the merkle proof for the pending transfer, and then unlock
    channelstate_0_1 = get_channelstate(app0, app1, token_network_identifier)
    lock = channel.get_lock(channelstate_0_1.our_state, secrethash)
    unlock_proof = channel.compute_proof_for_lock(
        channelstate_0_1.our_state,
        secret,
        lock,
    )
    claim_lock(raiden_network, identifier, token_network_identifier, secret)

    # Make a new transfer
    direct_transfer(app0, app1, token_network_identifier, amount, identifier=1)
    RaidenAPI(app1.raiden).channel_close(
        registry_address,
        token_address,
        app0.raiden.address,
    )

    # The direct transfer locksroot must not contain the unlocked lock, the
    # unlock must fail.
    netting_channel = app1.raiden.chain.netting_channel(channelstate_0_1.identifier)
    with pytest.raises(Exception):
        netting_channel.unlock(UnlockProofState(unlock_proof, lock.encoded, secret))

    waiting.wait_for_settle(
        app1.raiden,
        app1.raiden.default_registry.address,
        token_address,
        [channelstate_0_1.identifier],
        app1.raiden.alarm.wait_time,
    )

    expected_balance0 = initial_balance0 + deposit0 - amount * 2
    expected_balance1 = initial_balance1 + deposit1 + amount * 2

    assert token_proxy.balance_of(address0) == expected_balance0
    assert token_proxy.balance_of(address1) == expected_balance1
Beispiel #25
0
def test_batch_unlock_after_restart(raiden_network, token_addresses, deposit):
    """Simulate the case where:
    - A sends B a transfer
    - B sends A a transfer
    - Secrets were never revealed
    - B closes channel
    - A crashes
    - Wait for settle
    - Wait for unlock from B
    - Restart A
    At this point, the current unlock logic will try to unlock
    iff the node gains from unlocking. Which means that the node will try to unlock
    either side. In the above scenario, each node will unlock its side.
    This test makes sure that we do NOT invalidate A's unlock transaction based
    on the ContractReceiveChannelBatchUnlock caused by B's unlock.
    """
    alice_app, bob_app = raiden_network
    registry_address = alice_app.raiden.default_registry.address
    token_address = token_addresses[0]
    token_network_address = views.get_token_network_address_by_token_address(
        chain_state=views.state_from_app(alice_app),
        token_network_registry_address=alice_app.raiden.default_registry.address,
        token_address=token_address,
    )
    assert token_network_address
    timeout = 10

    token_network = views.get_token_network_by_address(
        chain_state=views.state_from_app(alice_app), token_network_address=token_network_address
    )
    assert token_network

    channel_identifier = get_channelstate(alice_app, bob_app, token_network_address).identifier

    assert (
        channel_identifier
        in token_network.partneraddresses_to_channelidentifiers[bob_app.raiden.address]
    )

    alice_to_bob_amount = 10
    identifier = 1

    alice_transfer_secret = Secret(sha3(alice_app.raiden.address))
    alice_transfer_secrethash = sha256_secrethash(alice_transfer_secret)

    bob_transfer_secret = Secret(sha3(bob_app.raiden.address))
    bob_transfer_secrethash = sha256_secrethash(bob_transfer_secret)

    alice_transfer_hold = bob_app.raiden.raiden_event_handler.hold_secretrequest_for(
        secrethash=alice_transfer_secrethash
    )
    bob_transfer_hold = alice_app.raiden.raiden_event_handler.hold_secretrequest_for(
        secrethash=bob_transfer_secrethash
    )

    alice_app.raiden.start_mediated_transfer_with_secret(
        token_network_address=token_network_address,
        amount=alice_to_bob_amount,
        target=bob_app.raiden.address,
        identifier=identifier,
        secret=alice_transfer_secret,
    )

    bob_app.raiden.start_mediated_transfer_with_secret(
        token_network_address=token_network_address,
        amount=alice_to_bob_amount,
        target=alice_app.raiden.address,
        identifier=identifier + 1,
        secret=bob_transfer_secret,
    )

    alice_transfer_hold.wait(timeout=timeout)
    bob_transfer_hold.wait(timeout=timeout)

    alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_address)
    alice_lock = channel.get_lock(alice_bob_channel_state.our_state, alice_transfer_secrethash)
    bob_lock = channel.get_lock(alice_bob_channel_state.partner_state, bob_transfer_secrethash)
    assert alice_lock
    assert bob_lock

    # This is the current state of protocol:
    #
    #    A -> B LockedTransfer
    #    - protocol didn't continue
    assert_synced_channel_state(
        token_network_address=token_network_address,
        app0=alice_app,
        balance0=deposit,
        pending_locks0=[alice_lock],
        app1=bob_app,
        balance1=deposit,
        pending_locks1=[bob_lock],
    )

    # A ChannelClose event will be generated, this will be polled by both apps
    # and each must start a task for calling settle
    RaidenAPI(bob_app.raiden).channel_close(
        registry_address=registry_address,
        token_address=token_address,
        partner_address=alice_app.raiden.address,
    )

    # wait for the close transaction to be mined, this is necessary to compute
    # the timeout for the settle
    with gevent.Timeout(timeout):
        waiting.wait_for_close(
            raiden=alice_app.raiden,
            token_network_registry_address=registry_address,
            token_address=token_address,
            channel_ids=[alice_bob_channel_state.identifier],
            retry_timeout=alice_app.raiden.alarm.sleep_time,
        )

    channel_closed = raiden_state_changes_search_for_item(
        bob_app.raiden,
        ContractReceiveChannelClosed,
        {
            "canonical_identifier": {
                "token_network_address": token_network_address,
                "channel_identifier": alice_bob_channel_state.identifier,
            }
        },
    )
    assert isinstance(channel_closed, ContractReceiveChannelClosed)
    settle_max_wait_block = BlockNumber(
        channel_closed.block_number + alice_bob_channel_state.settle_timeout * 2
    )

    settle_timeout = BlockTimeout(
        RuntimeError("settle did not happen"),
        bob_app.raiden,
        settle_max_wait_block,
        alice_app.raiden.alarm.sleep_time,
    )
    with settle_timeout:
        waiting.wait_for_settle(
            raiden=alice_app.raiden,
            token_network_registry_address=registry_address,
            token_address=token_address,
            channel_ids=[alice_bob_channel_state.identifier],
            retry_timeout=alice_app.raiden.alarm.sleep_time,
        )

    with gevent.Timeout(timeout):
        wait_for_batch_unlock(
            app=bob_app,
            token_network_address=token_network_address,
            receiver=alice_bob_channel_state.partner_state.address,
            sender=alice_bob_channel_state.our_state.address,
        )

    alice_app.start()

    with gevent.Timeout(timeout):
        wait_for_batch_unlock(
            app=alice_app,
            token_network_address=token_network_address,
            receiver=alice_bob_channel_state.partner_state.address,
            sender=alice_bob_channel_state.our_state.address,
        )
Beispiel #26
0
def test_lock_expiry(raiden_network, token_addresses, secret_registry_address,
                     deposit):
    """Test lock expiry and removal."""
    alice_app, bob_app = raiden_network
    token_address = token_addresses[0]
    token_network_identifier = views.get_token_network_identifier_by_token_address(
        views.state_from_app(alice_app),
        alice_app.raiden.default_registry.address,
        token_address,
    )

    token_network = views.get_token_network_by_identifier(
        views.state_from_app(alice_app),
        token_network_identifier,
    )

    channel_state = get_channelstate(alice_app, bob_app,
                                     token_network_identifier)
    channel_identifier = channel_state.identifier

    assert channel_identifier in token_network.partneraddresses_to_channels[
        bob_app.raiden.address]

    alice_to_bob_amount = 10
    identifier = 1
    transfer_1_secret = pending_mediated_transfer(
        raiden_network,
        token_network_identifier,
        alice_to_bob_amount,
        identifier,
    )
    transfer_1_secrethash = sha3(transfer_1_secret)

    alice_bob_channel_state = get_channelstate(alice_app, bob_app,
                                               token_network_identifier)
    lock = channel.get_lock(alice_bob_channel_state.our_state,
                            transfer_1_secrethash)

    # This is the current state of the protocol:
    #
    #    A -> B LockedTransfer
    #    B -> A SecretRequest
    #    - protocol didn't continue
    assert_synced_channel_state(
        token_network_identifier,
        alice_app,
        deposit,
        [lock],
        bob_app,
        deposit,
        [],
    )

    # Verify lock is registered in both channel states
    alice_channel_state = get_channelstate(alice_app, bob_app,
                                           token_network_identifier)
    assert transfer_1_secrethash in alice_channel_state.our_state.secrethashes_to_lockedlocks

    bob_channel_state = get_channelstate(bob_app, alice_app,
                                         token_network_identifier)
    assert transfer_1_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks

    alice_chain_state = views.state_from_raiden(alice_app.raiden)
    assert transfer_1_secrethash in alice_chain_state.payment_mapping.secrethashes_to_task

    # Wait for the expiration to trigger with some additional buffer
    # time for processing (+2) blocks.
    waiting.wait_for_block(
        alice_app.raiden,
        lock.expiration + (DEFAULT_NUMBER_OF_CONFIRMATIONS_BLOCK + 2),
        DEFAULT_RETRY_TIMEOUT,
    )

    alice_channel_state = get_channelstate(alice_app, bob_app,
                                           token_network_identifier)
    assert transfer_1_secrethash not in alice_channel_state.our_state.secrethashes_to_lockedlocks

    # Verify Bob received the message and processed the LockExpired message
    bob_channel_state = get_channelstate(bob_app, alice_app,
                                         token_network_identifier)
    assert transfer_1_secrethash not in bob_channel_state.partner_state.secrethashes_to_lockedlocks

    alice_chain_state = views.state_from_raiden(alice_app.raiden)
    assert transfer_1_secrethash not in alice_chain_state.payment_mapping.secrethashes_to_task

    # Make another transfer
    alice_to_bob_amount = 10
    identifier = 2
    transfer_2_secret = pending_mediated_transfer(
        raiden_network,
        token_network_identifier,
        alice_to_bob_amount,
        identifier,
    )
    transfer_2_secrethash = sha3(transfer_2_secret)

    # Make sure the other transfer still exists
    alice_chain_state = views.state_from_raiden(alice_app.raiden)
    assert transfer_2_secrethash in alice_chain_state.payment_mapping.secrethashes_to_task

    bob_channel_state = get_channelstate(bob_app, alice_app,
                                         token_network_identifier)
    assert transfer_2_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks
Beispiel #27
0
def test_regression_send_refund():
    """Regression test for discarded refund transfer.

    The handle_refundtransfer used to discard events from the channel state
    machine, which led to the state being updated but the message to the
    partner was never sent.
    """
    pseudo_random_generator = random.Random()
    setup = factories.make_transfers_pair(3)

    mediator_state = MediatorTransferState(UNIT_SECRETHASH)
    mediator_state.transfers_pair = setup.transfers_pair

    last_pair = setup.transfers_pair[-1]
    channel_identifier = last_pair.payee_transfer.balance_proof.channel_identifier
    lock_expiration = last_pair.payee_transfer.lock.expiration

    received_transfer = factories.make_signed_transfer(
        amount=UNIT_TRANSFER_AMOUNT,
        initiator=UNIT_TRANSFER_INITIATOR,
        target=UNIT_TRANSFER_TARGET,
        expiration=lock_expiration,
        secret=UNIT_SECRET,
        payment_identifier=UNIT_TRANSFER_IDENTIFIER,
        channel_identifier=channel_identifier,
        pkey=setup.channels.partner_privatekeys[2],
        sender=setup.channels.partner_address(2),
    )

    # All three channels have been used
    routes = []

    refund_state_change = ReceiveTransferRefund(
        transfer=received_transfer,
        routes=routes,
    )

    iteration = mediator.handle_refundtransfer(
        mediator_state=mediator_state,
        mediator_state_change=refund_state_change,
        channelidentifiers_to_channels=setup.channel_map,
        pseudo_random_generator=pseudo_random_generator,
        block_number=setup.block_number,
    )

    first_pair = setup.transfers_pair[0]
    first_payer_transfer = first_pair.payer_transfer
    payer_channel = mediator.get_payer_channel(setup.channel_map, first_pair)
    lock = channel.get_lock(
        end_state=payer_channel.partner_state,
        secrethash=UNIT_SECRETHASH,
    )
    token_network_identifier = first_payer_transfer.balance_proof.token_network_identifier
    assert must_contain_entry(
        iteration.events, SendRefundTransfer, {
            'recipient': setup.channels.partner_address(0),
            'queue_identifier': {
                'recipient':
                setup.channels.partner_address(0),
                'channel_identifier':
                first_payer_transfer.balance_proof.channel_identifier,
            },
            'transfer': {
                'payment_identifier': UNIT_TRANSFER_IDENTIFIER,
                'token': UNIT_TOKEN_ADDRESS,
                'balance_proof': {
                    'transferred_amount': 0,
                    'locked_amount': 10,
                    'locksroot': lock.lockhash,
                    'token_network_identifier': token_network_identifier,
                    'channel_identifier':
                    first_payer_transfer.balance_proof.channel_identifier,
                    'chain_id': first_payer_transfer.balance_proof.chain_id,
                },
                'lock': {
                    'amount': lock.amount,
                    'expiration': lock.expiration,
                    'secrethash': lock.secrethash,
                },
                'initiator': UNIT_TRANSFER_INITIATOR,
                'target': UNIT_TRANSFER_TARGET,
            },
        })
Beispiel #28
0
def handle_secretrequest(
    initiator_state: InitiatorTransferState,
    state_change: ReceiveSecretRequest,
    channel_state: NettingChannelState,
    pseudo_random_generator: random.Random,
) -> TransitionResult[InitiatorTransferState]:

    is_message_from_target = (
        state_change.sender == initiator_state.transfer_description.target
        and state_change.secrethash
        == initiator_state.transfer_description.secrethash
        and state_change.payment_identifier
        == initiator_state.transfer_description.payment_identifier)

    lock = channel.get_lock(channel_state.our_state,
                            initiator_state.transfer_description.secrethash)

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

    already_received_secret_request = initiator_state.received_secret_request

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

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

    elif is_valid_secretrequest and is_message_from_target:
        # Reveal the secret to the target node and wait for its confirmation.
        # At this point the transfer is not cancellable anymore as either the lock
        # timeouts or a secret reveal is received.
        #
        # Note: The target might be the first hop
        #
        message_identifier = message_identifier_from_prng(
            pseudo_random_generator)
        transfer_description = initiator_state.transfer_description
        recipient = transfer_description.target
        revealsecret = SendSecretReveal(
            recipient=Address(recipient),
            message_identifier=message_identifier,
            secret=transfer_description.secret,
            canonical_identifier=CANONICAL_IDENTIFIER_GLOBAL_QUEUE,
        )

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

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

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

    return iteration
def test_regression_send_refund():
    """Regression test for discarded refund transfer.

    The handle_refundtransfer used to discard events from the channel state
    machine, which led to the state being updated but the message to the
    partner was never sent.

    Also, for issue: https://github.com/raiden-network/raiden/issues/3170
    It was noticed that when receiving the same refund transfer twice, the mediator
    would detect an invalid refund and clear the mediator state. So the test also
    checks that mediator rejects the duplicate transfer and keeps the mediator
    state unchanged.
    """
    pseudo_random_generator = random.Random()
    setup = factories.make_transfers_pair(3)

    mediator_state = MediatorTransferState(secrethash=UNIT_SECRETHASH,
                                           routes=[])
    mediator_state.transfers_pair = setup.transfers_pair

    last_pair = setup.transfers_pair[-1]
    canonical_identifier = last_pair.payee_transfer.balance_proof.canonical_identifier
    lock_expiration = last_pair.payee_transfer.lock.expiration

    received_transfer = factories.create(
        factories.LockedTransferSignedStateProperties(
            expiration=lock_expiration,
            payment_identifier=UNIT_TRANSFER_IDENTIFIER,
            canonical_identifier=canonical_identifier,
            sender=setup.channels.partner_address(2),
            pkey=setup.channels.partner_privatekeys[2],
            message_identifier=factories.make_message_identifier(),
        ))

    # All three channels have been used
    refund_state_change = ReceiveTransferRefund(
        transfer=received_transfer,
        balance_proof=received_transfer.balance_proof,
        sender=received_transfer.balance_proof.sender,  # pylint: disable=no-member
    )

    iteration = mediator.handle_refundtransfer(
        mediator_state=mediator_state,
        mediator_state_change=refund_state_change,
        channelidentifiers_to_channels=setup.channel_map,
        nodeaddresses_to_networkstates=setup.channels.
        nodeaddresses_to_networkstates,
        pseudo_random_generator=pseudo_random_generator,
        block_number=setup.block_number,
    )

    first_pair = setup.transfers_pair[0]
    first_payer_transfer = first_pair.payer_transfer
    payer_channel = mediator.get_payer_channel(setup.channel_map, first_pair)
    lock = channel.get_lock(end_state=payer_channel.partner_state,
                            secrethash=UNIT_SECRETHASH)
    token_network_address = first_payer_transfer.balance_proof.token_network_address
    assert search_for_item(
        iteration.events,
        SendRefundTransfer,
        {
            "recipient": setup.channels.partner_address(0),
            "queue_identifier": {
                "recipient": setup.channels.partner_address(0),
                "canonical_identifier": {
                    "chain_identifier":
                    first_payer_transfer.balance_proof.chain_id,
                    "token_network_address":
                    token_network_address,
                    "channel_identifier":
                    first_payer_transfer.balance_proof.channel_identifier,
                },
            },
            "transfer": {
                "payment_identifier": UNIT_TRANSFER_IDENTIFIER,
                "token": UNIT_TOKEN_ADDRESS,
                "balance_proof": {
                    "transferred_amount": 0,
                    "locked_amount": UNIT_TRANSFER_AMOUNT,
                    "locksroot": keccak(lock.encoded),
                    "token_network_address": token_network_address,
                    "channel_identifier":
                    first_payer_transfer.balance_proof.channel_identifier,
                    "chain_id": first_payer_transfer.balance_proof.chain_id,
                },
                "lock": {
                    "amount": lock.amount,
                    "expiration": lock.expiration,
                    "secrethash": lock.secrethash,
                },
                "initiator": UNIT_TRANSFER_INITIATOR,
                "target": UNIT_TRANSFER_TARGET,
            },
        },
    )

    duplicate_iteration = mediator.handle_refundtransfer(
        mediator_state=iteration.new_state,
        mediator_state_change=refund_state_change,
        channelidentifiers_to_channels=setup.channel_map,
        nodeaddresses_to_networkstates=setup.channels.
        nodeaddresses_to_networkstates,
        pseudo_random_generator=pseudo_random_generator,
        block_number=setup.block_number,
    )

    assert search_for_item(duplicate_iteration.events, SendRefundTransfer,
                           {}) is None

    assert duplicate_iteration.new_state is not None
    assert duplicate_iteration.new_state == iteration.new_state
Beispiel #30
0
def run_test_batch_unlock_after_restart(raiden_network, token_addresses,
                                        deposit):
    """Simulate the case where:
    - A sends B a transfer
    - B sends A a transfer
    - Secrets were never revealed
    - B closes channel
    - A crashes
    - Wait for settle
    - Wait for unlock from B
    - Restart A
    At this point, the current unlock logic will try to unlock
    iff the node gains from unlocking. Which means that the node will try to unlock
    either side. In the above scenario, each node will unlock its side.
    This test makes sure that we do NOT invalidate A's unlock transaction based
    on the ContractReceiveChannelBatchUnlock caused by B's unlock.
    """
    alice_app, bob_app = raiden_network
    registry_address = alice_app.raiden.default_registry.address
    token_address = token_addresses[0]
    token_network_identifier = views.get_token_network_identifier_by_token_address(
        chain_state=views.state_from_app(alice_app),
        payment_network_id=alice_app.raiden.default_registry.address,
        token_address=token_address,
    )

    hold_event_handler = HoldOffChainSecretRequest()
    bob_app.raiden.raiden_event_handler = hold_event_handler
    alice_app.raiden.raiden_event_handler = hold_event_handler

    token_network = views.get_token_network_by_identifier(
        chain_state=views.state_from_app(alice_app),
        token_network_id=token_network_identifier)

    channel_identifier = get_channelstate(alice_app, bob_app,
                                          token_network_identifier).identifier

    assert (channel_identifier
            in token_network.partneraddresses_to_channelidentifiers[
                bob_app.raiden.address])

    alice_to_bob_amount = 10
    identifier = 1

    alice_transfer_secret = sha3(alice_app.raiden.address)
    alice_transfer_secrethash = sha3(alice_transfer_secret)

    bob_transfer_secret = sha3(bob_app.raiden.address)
    bob_transfer_secrethash = sha3(bob_transfer_secret)

    alice_transfer_hold = hold_event_handler.hold_secretrequest_for(
        secrethash=alice_transfer_secrethash)
    bob_transfer_hold = hold_event_handler.hold_secretrequest_for(
        secrethash=bob_transfer_secrethash)

    alice_app.raiden.start_mediated_transfer_with_secret(
        token_network_identifier=token_network_identifier,
        amount=alice_to_bob_amount,
        fee=0,
        target=bob_app.raiden.address,
        identifier=identifier,
        secret=alice_transfer_secret,
    )

    bob_app.raiden.start_mediated_transfer_with_secret(
        token_network_identifier=token_network_identifier,
        amount=alice_to_bob_amount,
        fee=0,
        target=alice_app.raiden.address,
        identifier=identifier + 1,
        secret=bob_transfer_secret,
    )

    alice_transfer_hold.wait()
    bob_transfer_hold.wait()

    alice_bob_channel_state = get_channelstate(alice_app, bob_app,
                                               token_network_identifier)
    alice_lock = channel.get_lock(alice_bob_channel_state.our_state,
                                  alice_transfer_secrethash)
    bob_lock = channel.get_lock(alice_bob_channel_state.partner_state,
                                bob_transfer_secrethash)

    # This is the current state of protocol:
    #
    #    A -> B LockedTransfer
    #    - protocol didn't continue
    assert_synced_channel_state(
        token_network_identifier=token_network_identifier,
        app0=alice_app,
        balance0=deposit,
        pending_locks0=[alice_lock],
        app1=bob_app,
        balance1=deposit,
        pending_locks1=[bob_lock],
    )

    # A ChannelClose event will be generated, this will be polled by both apps
    # and each must start a task for calling settle
    RaidenAPI(bob_app.raiden).channel_close(
        registry_address=registry_address,
        token_address=token_address,
        partner_address=alice_app.raiden.address,
    )

    alice_app.stop()

    waiting.wait_for_settle(
        raiden=alice_app.raiden,
        payment_network_id=registry_address,
        token_address=token_address,
        channel_ids=[alice_bob_channel_state.identifier],
        retry_timeout=alice_app.raiden.alarm.sleep_time,
    )

    # wait for the node to call batch unlock
    timeout = 10
    with gevent.Timeout(timeout):
        wait_for_batch_unlock(
            app=bob_app,
            token_network_id=token_network_identifier,
            participant=alice_bob_channel_state.partner_state.address,
            partner=alice_bob_channel_state.our_state.address,
        )

    alice_app.start()

    with gevent.Timeout(timeout):
        wait_for_batch_unlock(
            app=alice_app,
            token_network_id=token_network_identifier,
            participant=alice_bob_channel_state.partner_state.address,
            partner=alice_bob_channel_state.our_state.address,
        )
Beispiel #31
0
def test_state_wait_secretrequest_invalid_amount_and_sender():
    amount = UNIT_TRANSFER_AMOUNT
    block_number = 1
    pseudo_random_generator = random.Random()

    channel1 = factories.make_channel(
        our_balance=amount,
        token_address=UNIT_TOKEN_ADDRESS,
        token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS,
    )
    channel_map = {channel1.identifier: channel1}
    available_routes = [factories.route_from_channel(channel1)]
    current_state = make_initiator_manager_state(
        available_routes,
        factories.UNIT_TRANSFER_DESCRIPTION,
        channel_map,
        pseudo_random_generator,
        block_number,
    )

    lock = channel.get_lock(
        channel1.our_state,
        current_state.initiator.transfer_description.secrethash,
    )

    state_change = ReceiveSecretRequest(
        UNIT_TRANSFER_IDENTIFIER,
        lock.amount + 1,
        lock.expiration,
        lock.secrethash,
        UNIT_TRANSFER_INITIATOR,
    )

    iteration = initiator_manager.state_transition(
        current_state,
        state_change,
        channel_map,
        pseudo_random_generator,
        block_number,
    )

    assert len(iteration.events) == 0
    assert iteration.new_state.initiator.received_secret_request is False

    # Now the proper target sends the message, this should be applied
    state_change_2 = ReceiveSecretRequest(
        UNIT_TRANSFER_IDENTIFIER,
        lock.amount,
        lock.expiration,
        lock.secrethash,
        UNIT_TRANSFER_TARGET,
    )

    iteration2 = initiator_manager.state_transition(
        iteration.new_state,
        state_change_2,
        channel_map,
        pseudo_random_generator,
        block_number,
    )

    assert iteration2.new_state.initiator.received_secret_request is True
    assert isinstance(iteration2.events[0], SendSecretReveal)