Exemplo n.º 1
0
def test_channelstate_send_lockedtransfer():
    """Sending a mediated transfer must update the participant state.

    This tests only the state of the sending node, without synchronisation.
    """
    our_model1, _ = create_model(70)
    partner_model1, _ = create_model(100)
    channel_state = create_channel_from_models(our_model1, partner_model1)

    lock_amount = 30
    lock_expiration = 10
    lock_secret = sha3(b'test_end_state')
    lock_secrethash = sha3(lock_secret)

    lock = HashTimeLockState(
        lock_amount,
        lock_expiration,
        lock_secrethash,
    )

    payment_identifier = 1
    message_identifier = random.randint(0, UINT64_MAX)
    transfer_target = factories.make_address()
    transfer_initiator = factories.make_address()
    registry_address = factories.make_address()

    channel.send_lockedtransfer(
        registry_address,
        channel_state,
        transfer_initiator,
        transfer_target,
        lock_amount,
        message_identifier,
        payment_identifier,
        lock_expiration,
        lock_secrethash,
    )

    our_model2 = our_model1._replace(
        distributable=our_model1.distributable - lock_amount,
        amount_locked=lock_amount,
        next_nonce=2,
        merkletree_leaves=[lock.lockhash],
    )
    partner_model2 = partner_model1

    assert_partner_state(channel_state.our_state, channel_state.partner_state,
                         our_model2)
    assert_partner_state(channel_state.partner_state, channel_state.our_state,
                         partner_model2)
Exemplo n.º 2
0
def send_lockedtransfer(
    transfer_description: TransferDescriptionWithSecretState,
    channel_state: NettingChannelState,
    message_identifier: MessageID,
    block_number: BlockNumber,
) -> SendLockedTransfer:
    """ Create a mediated transfer using channel. """
    assert channel_state.token_network_identifier == transfer_description.token_network_identifier

    lock_expiration = get_initial_lock_expiration(
        block_number,
        channel_state.reveal_timeout,
    )

    lockedtransfer_event = channel.send_lockedtransfer(
        channel_state,
        transfer_description.initiator,
        transfer_description.target,
        cast(
            PaymentAmount,
            transfer_description.amount,
        ),
        message_identifier,
        transfer_description.payment_identifier,
        lock_expiration,
        transfer_description.secrethash,
    )
    return lockedtransfer_event
Exemplo n.º 3
0
def send_lockedtransfer(
    transfer_description: TransferDescriptionWithSecretState,
    channel_state: NettingChannelState,
    message_identifier: typing.MessageID,
    block_number: typing.BlockNumber,
) -> SendLockedTransfer:
    """ Create a mediated transfer using channel.

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

    lock_expiration = get_initial_lock_expiration(
        block_number,
        channel_state.reveal_timeout,
    )

    lockedtransfer_event = channel.send_lockedtransfer(
        channel_state,
        transfer_description.initiator,
        transfer_description.target,
        transfer_description.amount,
        message_identifier,
        transfer_description.payment_identifier,
        lock_expiration,
        transfer_description.secrethash,
    )
    return lockedtransfer_event
Exemplo n.º 4
0
def send_lockedtransfer(
        initiator_state: InitiatorTransferState,
        channel_state: NettingChannelState,
        message_identifier,
        block_number: typing.BlockNumber,
) -> SendLockedTransfer:
    """ Create a mediated transfer using channel.

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

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

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

    initiator_state.transfer = lockedtransfer_event.transfer

    return lockedtransfer_event
Exemplo n.º 5
0
def send_lockedtransfer(
    transfer_description: TransferDescriptionWithSecretState,
    channel_state: NettingChannelState,
    message_identifier: MessageID,
    block_number: BlockNumber,
    route_state: RouteState,
    route_states: List[RouteState],
) -> SendLockedTransfer:
    """ Create a mediated transfer using channel. """
    assert channel_state.token_network_address == transfer_description.token_network_address

    lock_expiration = channel.get_safe_initial_expiration(
        block_number, channel_state.reveal_timeout,
        transfer_description.lock_timeout)

    # The payment amount and the fee amount must be included in the locked
    # amount, as a guarantee to the mediator that the fee will be claimable
    # on-chain.
    total_amount = calculate_safe_amount_with_fee(
        payment_amount=transfer_description.amount,
        estimated_fee=route_state.estimated_fee)

    lockedtransfer_event = channel.send_lockedtransfer(
        channel_state=channel_state,
        initiator=transfer_description.initiator,
        target=transfer_description.target,
        amount=total_amount,
        message_identifier=message_identifier,
        payment_identifier=transfer_description.payment_identifier,
        expiration=lock_expiration,
        secrethash=transfer_description.secrethash,
        route_states=routes.prune_route_table(route_states=route_states,
                                              selected_route=route_state),
    )
    return lockedtransfer_event
Exemplo n.º 6
0
def send_lockedtransfer(
    transfer_description: TransferDescriptionWithSecretState,
    channel_state: NettingChannelState,
    message_identifier: MessageID,
    block_number: BlockNumber,
) -> SendLockedTransfer:
    """ Create a mediated transfer using channel. """
    assert channel_state.token_network_identifier == transfer_description.token_network_identifier

    lock_expiration = get_initial_lock_expiration(block_number, channel_state.reveal_timeout)

    # The payment amount and the fee amount must be included in the locked
    # amount, as a guarantee to the mediator that the fee will be claimable
    # on-chain.
    total_amount = PaymentWithFeeAmount(
        transfer_description.amount + transfer_description.allocated_fee
    )

    lockedtransfer_event = channel.send_lockedtransfer(
        channel_state=channel_state,
        initiator=transfer_description.initiator,
        target=transfer_description.target,
        amount=total_amount,
        message_identifier=message_identifier,
        payment_identifier=transfer_description.payment_identifier,
        payment_hash_invoice=transfer_description.payment_hash_invoice,
        expiration=lock_expiration,
        secrethash=transfer_description.secrethash,
    )
    return lockedtransfer_event
Exemplo n.º 7
0
def send_lockedtransfer(
        initiator_state: InitiatorTransferState,
        channel_state: NettingChannelState,
        message_identifier,
        block_number: typing.BlockNumber,
) -> SendLockedTransfer:
    """ Create a mediated transfer using channel.

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

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

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

    initiator_state.transfer = lockedtransfer_event.transfer

    return lockedtransfer_event
Exemplo n.º 8
0
def next_transfer_pair(
    payer_transfer: LockedTransferSignedState,
    available_routes: List['RouteState'],
    channelidentifiers_to_channels: Dict,
    pseudo_random_generator: random.Random,
    block_number: typing.BlockNumber,
):
    """ Given a payer transfer tries a new route to proceed with the mediation.
    Args:
        payer_transfer: The transfer received from the payer_channel.
        available_routes: Current available routes that may be used, it's
            assumed that the routes list is ordered from best to worst.
        channelidentifiers_to_channels: All the channels available for this
            transfer.
        pseudo_random_generator: Number generator to generate a message id.
        block_number: The current block number.
    """
    transfer_pair = None
    mediated_events = list()
    lock_timeout = payer_transfer.lock.expiration - block_number

    payee_channel = next_channel_from_routes(
        available_routes,
        channelidentifiers_to_channels,
        payer_transfer.lock.amount,
        lock_timeout,
    )

    if payee_channel:
        assert payee_channel.settle_timeout >= lock_timeout
        assert payee_channel.token_address == payer_transfer.token

        message_identifier = message_identifier_from_prng(
            pseudo_random_generator)
        lockedtransfer_event = channel.send_lockedtransfer(
            payee_channel,
            payer_transfer.initiator,
            payer_transfer.target,
            payer_transfer.lock.amount,
            message_identifier,
            payer_transfer.payment_identifier,
            payer_transfer.lock.expiration,
            payer_transfer.lock.secrethash,
        )
        assert lockedtransfer_event

        transfer_pair = MediationPairState(
            payer_transfer,
            payee_channel.partner_state.address,
            lockedtransfer_event.transfer,
        )

        mediated_events = [lockedtransfer_event]

    return (
        transfer_pair,
        mediated_events,
    )
Exemplo n.º 9
0
def next_transfer_pair(registry_address: typing.Address,
                       payer_transfer: LockedTransferSignedState,
                       available_routes: List['RouteState'],
                       channelidentifiers_to_channels: Dict,
                       pseudo_random_generator: random.Random,
                       timeout_blocks: int, block_number: int):
    """ Given a payer transfer tries a new route to proceed with the mediation.
    Args:
        payer_transfer: The transfer received from the payer_channel.
        routes: Current available routes that may be used, it's assumed that
            the routes list is ordered from best to worst.
        timeout_blocks: Base number of available blocks used to compute
            the lock timeout.
        block_number: The current block number.
    """
    assert timeout_blocks > 0
    assert timeout_blocks <= payer_transfer.lock.expiration - block_number

    transfer_pair = None
    mediated_events = list()

    payee_channel = next_channel_from_routes(
        available_routes,
        channelidentifiers_to_channels,
        payer_transfer.lock.amount,
        timeout_blocks,
    )

    if payee_channel:
        assert payee_channel.reveal_timeout < timeout_blocks
        assert payee_channel.token_address == payer_transfer.token

        lock_timeout = timeout_blocks - payee_channel.reveal_timeout
        lock_expiration = lock_timeout + block_number

        message_identifier = message_identifier_from_prng(
            pseudo_random_generator)
        lockedtransfer_event = channel.send_lockedtransfer(
            registry_address, payee_channel, payer_transfer.initiator,
            payer_transfer.target, payer_transfer.lock.amount,
            message_identifier, payer_transfer.payment_identifier,
            lock_expiration, payer_transfer.lock.secrethash)
        assert lockedtransfer_event

        transfer_pair = MediationPairState(
            payer_transfer,
            payee_channel.partner_state.address,
            lockedtransfer_event.transfer,
        )

        mediated_events = [lockedtransfer_event]

    return (
        transfer_pair,
        mediated_events,
    )
Exemplo n.º 10
0
def make_mediated_transfer(registry_address,
                           from_channel,
                           partner_channel,
                           initiator,
                           target,
                           lock,
                           pkey,
                           secret=None):
    """ Helper to create and register a mediated transfer from `from_channel` to
    `partner_channel`."""
    payment_identifier = channel.get_next_nonce(from_channel.our_state)
    message_identifier = random.randint(0, UINT64_MAX)

    lockedtransfer = channel.send_lockedtransfer(
        registry_address,
        from_channel,
        initiator,
        target,
        lock.amount,
        message_identifier,
        payment_identifier,
        lock.expiration,
        lock.secrethash,
    )
    mediated_transfer_msg = LockedTransfer.from_event(lockedtransfer)

    address = privatekey_to_address(pkey)
    sign_key = PrivateKey(pkey)
    mediated_transfer_msg.sign(sign_key, address)

    # compute the signature
    balance_proof = balanceproof_from_envelope(mediated_transfer_msg)
    lockedtransfer.balance_proof = balance_proof

    # if this fails it's not the right key for the current `from_channel`
    assert mediated_transfer_msg.sender == from_channel.our_state.address
    receive_lockedtransfer = lockedtransfersigned_from_message(
        mediated_transfer_msg)

    channel.handle_receive_lockedtransfer(
        partner_channel,
        receive_lockedtransfer,
    )

    if secret is not None:
        random_sender = make_address()

        from_secretreveal = ReceiveSecretReveal(secret, random_sender)
        channel.handle_receive_secretreveal(from_channel, from_secretreveal)

        partner_secretreveal = ReceiveSecretReveal(secret, random_sender)
        channel.handle_receive_secretreveal(partner_channel,
                                            partner_secretreveal)

    return mediated_transfer_msg
Exemplo n.º 11
0
def make_mediated_transfer(
        from_channel,
        partner_channel,
        initiator,
        target,
        lock,
        pkey,
        secret=None,
):
    """ Helper to create and register a mediated transfer from `from_channel` to
    `partner_channel`."""
    payment_identifier = channel.get_next_nonce(from_channel.our_state)
    message_identifier = random.randint(0, UINT64_MAX)

    lockedtransfer = channel.send_lockedtransfer(
        from_channel,
        initiator,
        target,
        lock.amount,
        message_identifier,
        payment_identifier,
        lock.expiration,
        lock.secrethash,
    )
    mediated_transfer_msg = LockedTransfer.from_event(lockedtransfer)

    sign_key = PrivateKey(pkey)
    mediated_transfer_msg.sign(sign_key, NETWORKNAME_TO_ID[TESTS])

    # compute the signature
    balance_proof = balanceproof_from_envelope(mediated_transfer_msg)
    lockedtransfer.balance_proof = balance_proof

    # if this fails it's not the right key for the current `from_channel`
    assert mediated_transfer_msg.sender == from_channel.our_state.address
    receive_lockedtransfer = lockedtransfersigned_from_message(mediated_transfer_msg)

    channel.handle_receive_lockedtransfer(
        partner_channel,
        receive_lockedtransfer,
    )

    if secret is not None:
        secrethash = sha3(secret)

        channel.register_secret(from_channel, secret, secrethash)
        channel.register_secret(partner_channel, secret, secrethash)

    return mediated_transfer_msg
Exemplo n.º 12
0
def make_mediated_transfer(
        from_channel,
        partner_channel,
        initiator,
        target,
        lock,
        pkey,
        secret=None,
):
    """ Helper to create and register a mediated transfer from `from_channel` to
    `partner_channel`."""
    payment_identifier = channel.get_next_nonce(from_channel.our_state)
    message_identifier = random.randint(0, UINT64_MAX)

    lockedtransfer = channel.send_lockedtransfer(
        from_channel,
        initiator,
        target,
        lock.amount,
        message_identifier,
        payment_identifier,
        lock.expiration,
        lock.secrethash,
    )
    mediated_transfer_msg = LockedTransfer.from_event(lockedtransfer)

    sign_key = PrivateKey(pkey)
    mediated_transfer_msg.sign(sign_key)

    # compute the signature
    balance_proof = balanceproof_from_envelope(mediated_transfer_msg)
    lockedtransfer.balance_proof = balance_proof

    # if this fails it's not the right key for the current `from_channel`
    assert mediated_transfer_msg.sender == from_channel.our_state.address
    receive_lockedtransfer = lockedtransfersigned_from_message(mediated_transfer_msg)

    channel.handle_receive_lockedtransfer(
        partner_channel,
        receive_lockedtransfer,
    )

    if secret is not None:
        secrethash = sha3(secret)

        channel.register_secret(from_channel, secret, secrethash)
        channel.register_secret(partner_channel, secret, secrethash)

    return mediated_transfer_msg
Exemplo n.º 13
0
def make_transfers_pair(number_of_channels: int,
                        amount: int = UNIT_TRANSFER_AMOUNT,
                        block_number: int = 5) -> MediatorTransfersPair:

    deposit = 5 * amount
    defaults = create_properties(
        NettingChannelStateProperties(
            our_state=NettingChannelEndStateProperties(balance=deposit),
            partner_state=NettingChannelEndStateProperties(balance=deposit),
            open_transaction=TransactionExecutionStatusProperties(
                finished_block_number=10),
        ))
    properties_list = [
        NettingChannelStateProperties(
            canonical_identifier=make_canonical_identifier(
                channel_identifier=i),
            our_state=NettingChannelEndStateProperties(
                address=ChannelSet.ADDRESSES[0],
                privatekey=ChannelSet.PKEYS[0]),
            partner_state=NettingChannelEndStateProperties(
                address=ChannelSet.ADDRESSES[i + 1],
                privatekey=ChannelSet.PKEYS[i + 1]),
        ) for i in range(number_of_channels)
    ]
    channels = make_channel_set(properties_list, defaults)

    lock_expiration = block_number + UNIT_REVEAL_TIMEOUT * 2
    pseudo_random_generator = random.Random()
    transfers_pairs = list()

    for payer_index in range(number_of_channels - 1):
        payee_index = payer_index + 1

        receiver_channel = channels[payer_index]
        received_transfer = create(
            LockedTransferSignedStateProperties(
                amount=amount,
                expiration=lock_expiration,
                payment_identifier=UNIT_TRANSFER_IDENTIFIER,
                canonical_identifier=receiver_channel.canonical_identifier,
                sender=channels.partner_address(payer_index),
                pkey=channels.partner_privatekeys[payer_index],
            ))

        is_valid, _, msg = channel.handle_receive_lockedtransfer(
            receiver_channel, received_transfer)
        assert is_valid, msg

        message_identifier = message_identifier_from_prng(
            pseudo_random_generator)
        lockedtransfer_event = channel.send_lockedtransfer(
            channel_state=channels[payee_index],
            initiator=UNIT_TRANSFER_INITIATOR,
            target=UNIT_TRANSFER_TARGET,
            amount=amount,
            message_identifier=message_identifier,
            payment_identifier=UNIT_TRANSFER_IDENTIFIER,
            expiration=lock_expiration,
            secrethash=UNIT_SECRETHASH,
        )
        assert lockedtransfer_event

        lock_timeout = lock_expiration - block_number
        assert mediator.is_channel_usable(
            candidate_channel_state=channels[payee_index],
            transfer_amount=amount,
            lock_timeout=lock_timeout,
        )
        sent_transfer = lockedtransfer_event.transfer

        pair = MediationPairState(received_transfer,
                                  lockedtransfer_event.recipient,
                                  sent_transfer)
        transfers_pairs.append(pair)

    return MediatorTransfersPair(
        channels=channels,
        transfers_pair=transfers_pairs,
        amount=amount,
        block_number=block_number,
        block_hash=make_block_hash(),
    )
Exemplo n.º 14
0
def make_transfers_pair(privatekeys, amount, block_number):
    transfers_pair = list()
    channel_map = dict()
    pseudo_random_generator = random.Random()

    addresses = list()
    for pkey in privatekeys:
        pubkey = pkey.public_key.format(compressed=False)
        address = publickey_to_address(pubkey)
        addresses.append(address)

    key_address = list(zip(privatekeys, addresses))

    deposit_amount = amount * 5
    channels_state = {
        address: make_channel(
            our_address=HOP1,
            our_balance=deposit_amount,
            partner_balance=deposit_amount,
            partner_address=address,
            token_address=UNIT_TOKEN_ADDRESS,
            token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS,
        )
        for address in addresses
    }

    lock_expiration = block_number + UNIT_REVEAL_TIMEOUT * 2
    for (payer_key,
         payer_address), payee_address in zip(key_address[:-1], addresses[1:]):
        pay_channel = channels_state[payee_address]
        receive_channel = channels_state[payer_address]

        received_transfer = make_signed_transfer(
            amount=amount,
            initiator=UNIT_TRANSFER_INITIATOR,
            target=UNIT_TRANSFER_TARGET,
            expiration=lock_expiration,
            secret=UNIT_SECRET,
            payment_identifier=UNIT_TRANSFER_IDENTIFIER,
            channel_identifier=receive_channel.identifier,
            pkey=payer_key,
            sender=payer_address,
        )

        is_valid, _, msg = channel.handle_receive_lockedtransfer(
            receive_channel,
            received_transfer,
        )
        assert is_valid, msg

        message_identifier = message_identifier_from_prng(
            pseudo_random_generator)
        lockedtransfer_event = channel.send_lockedtransfer(
            channel_state=pay_channel,
            initiator=UNIT_TRANSFER_INITIATOR,
            target=UNIT_TRANSFER_TARGET,
            amount=amount,
            message_identifier=message_identifier,
            payment_identifier=UNIT_TRANSFER_IDENTIFIER,
            expiration=lock_expiration,
            secrethash=UNIT_SECRETHASH,
        )
        assert lockedtransfer_event
        lock_timeout = lock_expiration - block_number
        assert mediator.is_channel_usable(
            candidate_channel_state=pay_channel,
            transfer_amount=amount,
            lock_timeout=lock_timeout,
        )
        sent_transfer = lockedtransfer_event.transfer

        pair = MediationPairState(
            received_transfer,
            lockedtransfer_event.recipient,
            sent_transfer,
        )
        transfers_pair.append(pair)

        channel_map[receive_channel.identifier] = receive_channel
        channel_map[pay_channel.identifier] = pay_channel

        assert channel.is_lock_locked(receive_channel.partner_state,
                                      UNIT_SECRETHASH)
        assert channel.is_lock_locked(pay_channel.our_state, UNIT_SECRETHASH)

    return channel_map, transfers_pair
Exemplo n.º 15
0
def make_transfers_pair(
        number_of_channels: int,
        amount: int = UNIT_TRANSFER_AMOUNT,
        block_number: int = 5,
) -> MediatorTransfersPair:

    deposit = 5 * amount
    defaults = create_properties(NettingChannelStateProperties(
        our_state=NettingChannelEndStateProperties(balance=deposit),
        partner_state=NettingChannelEndStateProperties(balance=deposit),
        open_transaction=TransactionExecutionStatusProperties(finished_block_number=10),
    ))
    properties_list = [
        NettingChannelStateProperties(
            identifier=i,
            our_state=NettingChannelEndStateProperties(
                address=ChannelSet.ADDRESSES[0],
                privatekey=ChannelSet.PKEYS[0],
            ),
            partner_state=NettingChannelEndStateProperties(
                address=ChannelSet.ADDRESSES[i + 1],
                privatekey=ChannelSet.PKEYS[i + 1],
            ),
        )
        for i in range(number_of_channels)
    ]
    channels = make_channel_set(properties_list, defaults)

    lock_expiration = block_number + UNIT_REVEAL_TIMEOUT * 2
    pseudo_random_generator = random.Random()
    transfers_pairs = list()

    for payer_index in range(number_of_channels - 1):
        payee_index = payer_index + 1

        receiver_channel = channels[payer_index]
        received_transfer = make_signed_transfer(
            amount=amount,
            initiator=UNIT_TRANSFER_INITIATOR,
            target=UNIT_TRANSFER_TARGET,
            expiration=lock_expiration,
            secret=UNIT_SECRET,
            payment_identifier=UNIT_TRANSFER_IDENTIFIER,
            channel_identifier=receiver_channel.identifier,
            pkey=channels.partner_privatekeys[payer_index],
            sender=channels.partner_address(payer_index),
        )

        is_valid, _, msg = channel.handle_receive_lockedtransfer(
            receiver_channel,
            received_transfer,
        )
        assert is_valid, msg

        message_identifier = message_identifier_from_prng(pseudo_random_generator)
        lockedtransfer_event = channel.send_lockedtransfer(
            channel_state=channels[payee_index],
            initiator=UNIT_TRANSFER_INITIATOR,
            target=UNIT_TRANSFER_TARGET,
            amount=amount,
            message_identifier=message_identifier,
            payment_identifier=UNIT_TRANSFER_IDENTIFIER,
            expiration=lock_expiration,
            secrethash=UNIT_SECRETHASH,
        )
        assert lockedtransfer_event

        lock_timeout = lock_expiration - block_number
        assert mediator.is_channel_usable(
            candidate_channel_state=channels[payee_index],
            transfer_amount=amount,
            lock_timeout=lock_timeout,
        )
        sent_transfer = lockedtransfer_event.transfer

        pair = MediationPairState(
            received_transfer,
            lockedtransfer_event.recipient,
            sent_transfer,
        )
        transfers_pairs.append(pair)

    return MediatorTransfersPair(channels, transfers_pairs, amount, block_number)
Exemplo n.º 16
0
def next_transfer_pair(
        payer_transfer: LockedTransferSignedState,
        available_routes: List['RouteState'],
        channelidentifiers_to_channels: Dict,
        pseudo_random_generator: random.Random,
        timeout_blocks: int,
        block_number: int,
):
    """ Given a payer transfer tries a new route to proceed with the mediation.
    Args:
        payer_transfer: The transfer received from the payer_channel.
        routes: Current available routes that may be used, it's assumed that
            the routes list is ordered from best to worst.
        timeout_blocks: Base number of available blocks used to compute
            the lock timeout.
        block_number: The current block number.
    """
    assert timeout_blocks > 0
    assert timeout_blocks <= payer_transfer.lock.expiration - block_number

    transfer_pair = None
    mediated_events = list()

    payee_channel = next_channel_from_routes(
        available_routes,
        channelidentifiers_to_channels,
        payer_transfer.lock.amount,
        timeout_blocks,
    )

    if payee_channel:
        assert payee_channel.reveal_timeout < timeout_blocks
        assert payee_channel.token_address == payer_transfer.token

        lock_timeout = timeout_blocks - payee_channel.reveal_timeout
        lock_expiration = lock_timeout + block_number

        message_identifier = message_identifier_from_prng(pseudo_random_generator)
        lockedtransfer_event = channel.send_lockedtransfer(
            payee_channel,
            payer_transfer.initiator,
            payer_transfer.target,
            payer_transfer.lock.amount,
            message_identifier,
            payer_transfer.payment_identifier,
            lock_expiration,
            payer_transfer.lock.secrethash,
        )
        assert lockedtransfer_event

        transfer_pair = MediationPairState(
            payer_transfer,
            payee_channel.partner_state.address,
            lockedtransfer_event.transfer,
        )

        mediated_events = [lockedtransfer_event]

    return (
        transfer_pair,
        mediated_events,
    )