def test_receive_lockedtransfer_invalidnonce(raiden_network, number_of_nodes, deposit, token_addresses, reveal_timeout, network_wait): app0, app1, app2 = raiden_network token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address) assert token_network_address channel0 = get_channelstate(app0, app1, token_network_address) amount = 10 payment_identifier = PaymentID(1) secrethash = transfer( initiator_app=app0, target_app=app2, token_address=token_address, amount=PaymentAmount(10), identifier=payment_identifier, timeout=network_wait * number_of_nodes, ) repeated_nonce = Nonce(1) expiration = reveal_timeout * 2 mediated_transfer_message = LockedTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=make_message_identifier(), payment_identifier=payment_identifier, nonce=repeated_nonce, token_network_address=token_network_address, token=token_address, channel_identifier=channel0.identifier, transferred_amount=TokenAmount(amount), locked_amount=TokenAmount(amount), recipient=app1.raiden.address, locksroot=make_locksroot(), lock=Lock(amount=PaymentWithFeeAmount(amount), expiration=expiration, secrethash=UNIT_SECRETHASH), target=app2.raiden.address, initiator=app0.raiden.address, signature=EMPTY_SIGNATURE, metadata=Metadata(routes=[ RouteMetadata(route=[app1.raiden.address, app2.raiden.address]) ]), ) sign_and_inject(mediated_transfer_message, app0.raiden.signer, app1) with block_timeout_for_transfer_by_secrethash(app1.raiden, secrethash): wait_assert( assert_synced_channel_state, token_network_address, app0, deposit - amount, [], app1, deposit + amount, [], )
def test_send_to_device(matrix_transports): transport0, transport1 = matrix_transports received_messages0 = set() received_messages1 = set() message_handler0 = MessageHandler(received_messages0) message_handler1 = MessageHandler(received_messages1) raiden_service0 = MockRaidenService(message_handler0) raiden_service1 = MockRaidenService(message_handler1) transport1._receive_to_device = MagicMock() transport0.start(raiden_service0, [], "") transport1.start(raiden_service1, [], "") transport0.start_health_check(raiden_service1.address) transport1.start_health_check(raiden_service0.address) message = Processed(message_identifier=1, signature=EMPTY_SIGNATURE) transport0._raiden_service.sign(message) transport0.send_to_device(raiden_service1.address, message) transport1._receive_to_device.assert_not_called() message = ToDevice(message_identifier=1, signature=EMPTY_SIGNATURE) transport0._raiden_service.sign(message) transport0.send_to_device(raiden_service1.address, message) with gevent.Timeout(2): wait_assert(transport1._receive_to_device.assert_called)
def run_test_receive_lockedtransfer_invalidnonce(raiden_network, number_of_nodes, deposit, token_addresses, reveal_timeout, network_wait): app0, app1, app2 = raiden_network token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address) channel0 = get_channelstate(app0, app1, token_network_identifier) amount = 10 transfer( initiator_app=app0, target_app=app2, token_address=token_address, amount=amount, identifier=1, timeout=network_wait * number_of_nodes, ) amount = 10 payment_identifier = 1 repeated_nonce = 1 expiration = reveal_timeout * 2 mediated_transfer_message = LockedTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=random.randint(0, UINT64_MAX), payment_identifier=payment_identifier, payment_hash_invoice=EMPTY_PAYMENT_HASH_INVOICE, nonce=repeated_nonce, token_network_address=token_network_identifier, token=token_address, channel_identifier=channel0.identifier, transferred_amount=amount, locked_amount=amount, recipient=app1.raiden.address, locksroot=UNIT_SECRETHASH, lock=Lock(amount=amount, expiration=expiration, secrethash=UNIT_SECRETHASH), target=app2.raiden.address, initiator=app0.raiden.address, fee=0, ) sign_and_inject(mediated_transfer_message, app0.raiden.signer, app1) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit - amount, [], app1, deposit + amount, [], )
def run_test_mediated_transfer_with_entire_deposit( raiden_network, number_of_nodes, token_addresses, deposit, network_wait, ): app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) transfer( initiator_app=app0, target_app=app2, token_address=token_address, amount=deposit, identifier=1, timeout=network_wait * number_of_nodes, ) transfer( initiator_app=app2, target_app=app0, token_address=token_address, amount=deposit * 2, identifier=2, timeout=network_wait * number_of_nodes, ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit * 2, [], app1, 0, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit * 2, [], app2, 0, [], )
def assert_channels(raiden_network, token_network_identifier, deposit): pairs = list(zip(raiden_network, raiden_network[1:] + [raiden_network[0]])) for first, second in pairs: wait_assert( assert_synced_channel_state, token_network_identifier, first, deposit, [], second, deposit, [], )
def assert_channels(raiden_network, token_network_identifier, deposit): pairs = list(zip(raiden_network, raiden_network[1:] + [raiden_network[0]])) for first, second in pairs: wait_assert( assert_synced_channel_state, token_network_identifier, first, deposit, [], second, deposit, [], )
def test_mediated_transfer_with_entire_deposit( raiden_network, number_of_nodes, token_addresses, deposit, network_wait, public_and_private_rooms, ): app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) mediated_transfer( app0, app2, token_network_identifier, deposit, timeout=network_wait * number_of_nodes, ) mediated_transfer( app2, app0, token_network_identifier, deposit * 2, timeout=network_wait * number_of_nodes, ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit * 2, [], app1, 0, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit * 2, [], app2, 0, [], )
def test_mediated_transfer_with_entire_deposit( raiden_network, token_addresses, deposit, network_wait, ): app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) mediated_transfer( app0, app2, token_network_identifier, deposit, timeout=network_wait, ) mediated_transfer( app2, app0, token_network_identifier, deposit * 2, timeout=network_wait, ) wait_assert( token_network_identifier, app0, deposit * 2, [], app1, 0, [], func=assert_synched_channel_state, timeout=network_wait, ) wait_assert( token_network_identifier, app1, deposit * 2, [], app2, 0, [], func=assert_synched_channel_state, timeout=network_wait, )
def test_mediated_transfer_messages_out_of_order( raiden_network, number_of_nodes, deposit, token_addresses, network_wait, skip_if_not_matrix, ): app0, app1, app2 = raiden_network _patch_transport(app1.raiden.transport) _patch_transport(app2.raiden.transport) token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) amount = 10 mediated_transfer( app0, app2, token_network_identifier, amount, timeout=network_wait * number_of_nodes, ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit - amount, [], app1, deposit + amount, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit - amount, [], app2, deposit + amount, [], )
def test_mediated_transfer( raiden_network, number_of_nodes, deposit, token_addresses, network_wait, ): app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) amount = 10 mediated_transfer( app0, app2, token_network_identifier, amount, timeout=network_wait * number_of_nodes, ) wait_assert( token_network_identifier, app0, deposit - amount, [], app1, deposit + amount, [], func=assert_synced_channel_state, timeout=network_wait, ) wait_assert( token_network_identifier, app1, deposit - amount, [], app2, deposit + amount, [], func=assert_synced_channel_state, timeout=network_wait, )
def assert_channels(raiden_network: List[App], token_network_address: TokenNetworkAddress, deposit: TokenAmount) -> None: pairs = list(zip(raiden_network, raiden_network[1:] + [raiden_network[0]])) for first, second in pairs: wait_assert( assert_synced_channel_state, token_network_address, first, deposit, [], second, deposit, [], )
def test_mediated_transfer_with_entire_deposit( raiden_network, number_of_nodes, token_addresses, deposit, network_wait, public_and_private_rooms, ): app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) mediated_transfer( app0, app2, token_network_identifier, deposit, timeout=network_wait * number_of_nodes, ) mediated_transfer( app2, app0, token_network_identifier, deposit * 2, timeout=network_wait * number_of_nodes, ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit * 2, [], app1, 0, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit * 2, [], app2, 0, [], )
def test_pfs_send_capacity_updates_during_mediated_transfer( raiden_network, number_of_nodes, deposit, token_addresses, network_wait): app0, app1 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state, token_network_registry_address, token_address) # Mock send_text on the PFS room transport0 = app0.raiden.transport pfs_room_name = make_room_alias(transport0.chain_id, PATH_FINDING_BROADCASTING_ROOM) pfs_room = transport0._broadcast_rooms.get(pfs_room_name) # need to assert for mypy that pfs_room is not None assert isinstance(pfs_room, Room) pfs_room.send_text = MagicMock(spec=pfs_room.send_text) amount = PaymentAmount(10) secrethash = transfer( initiator_app=app0, target_app=app1, token_address=token_address, amount=amount, identifier=PaymentID(1), timeout=network_wait * number_of_nodes, ) with block_timeout_for_transfer_by_secrethash(app1.raiden, secrethash): wait_assert( assert_succeeding_transfer_invariants, token_network_address, app0, deposit - amount, [], app1, deposit + amount, [], ) # We expect one PFSCapacityUpdate when locking and one when unlocking assert pfs_room.send_text.call_count == 2 assert "PFSCapacityUpdate" in str(pfs_room.send_text.call_args_list[0])
def test_mediated_transfer_messages_out_of_order( raiden_network, number_of_nodes, deposit, token_addresses, network_wait, skip_if_not_matrix, ): app0, app1, app2 = raiden_network _patch_transport(app1.raiden.transport) _patch_transport(app2.raiden.transport) token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) amount = 10 mediated_transfer( app0, app2, token_network_identifier, amount, timeout=network_wait * number_of_nodes, ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit - amount, [], app1, deposit + amount, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit - amount, [], app2, deposit + amount, [], )
def test_mediated_transfer( raiden_network, number_of_nodes, deposit, token_addresses, network_wait ): app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state, token_network_registry_address, token_address ) amount = PaymentAmount(10) secrethash = transfer( initiator_app=app0, target_app=app2, token_address=token_address, amount=amount, identifier=PaymentID(1), timeout=network_wait * number_of_nodes, ) with block_timeout_for_transfer_by_secrethash(app1.raiden, secrethash): wait_assert( assert_succeeding_transfer_invariants, token_network_address, app0, deposit - amount, [], app1, deposit + amount, [], ) with block_timeout_for_transfer_by_secrethash(app1.raiden, secrethash): wait_assert( assert_succeeding_transfer_invariants, token_network_address, app1, deposit - amount, [], app2, deposit + amount, [], )
def test_mediated_transfer( raiden_network, number_of_nodes, deposit, token_addresses, network_wait, ): app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) amount = 10 mediated_transfer( app0, app2, token_network_identifier, amount, timeout=network_wait * number_of_nodes, ) wait_assert( token_network_identifier, app0, deposit - amount, [], app1, deposit + amount, [], func=assert_synched_channel_state, timeout=network_wait, ) wait_assert( token_network_identifier, app1, deposit - amount, [], app2, deposit + amount, [], func=assert_synched_channel_state, timeout=network_wait, )
def run_test_refund_transfer( raiden_chain, number_of_nodes, token_addresses, deposit, network_wait, retry_timeout, ): """A failed transfer must send a refund back. TODO: - Unlock the token on refund #1091 - Clear the merkletree and update the locked amount #193 - Remove the refund message type #490""" # Topology: # # 0 -> 1 -> 2 # app0, app1, app2 = raiden_chain token_address = token_addresses[0] payment_network_identifier = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), payment_network_identifier, token_address, ) # make a transfer to test the path app0 -> app1 -> app2 identifier_path = 1 amount_path = 1 transfer( initiator_app=app0, target_app=app2, token_address=token_address, amount=amount_path, identifier=identifier_path, timeout=network_wait * number_of_nodes, ) # drain the channel app1 -> app2 identifier_drain = 2 amount_drain = deposit * 8 // 10 transfer( initiator_app=app1, target_app=app2, token_address=token_address, amount=amount_drain, identifier=identifier_drain, timeout=network_wait, ) # wait for the nodes to sync gevent.sleep(1) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit - amount_path, [], app1, deposit + amount_path, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit - amount_path - amount_drain, [], app2, deposit + amount_path + amount_drain, [], ) # app0 -> app1 -> app2 is the only available path, but the channel app1 -> # app2 doesn't have capacity, so a refund will be sent on app1 -> app0 identifier_refund = 3 amount_refund = 50 payment_status = app0.raiden.mediated_transfer_async( token_network_identifier, amount_refund, app2.raiden.address, identifier_refund, ) msg = 'there is no path with capacity, the transfer must fail' assert payment_status.payment_done.wait() is False, msg gevent.sleep(0.2) # A lock structure with the correct amount send_locked = raiden_events_search_for_item( app0.raiden, SendLockedTransfer, {'transfer': { 'lock': { 'amount': amount_refund } }}, ) assert send_locked secrethash = send_locked.transfer.lock.secrethash send_refund = raiden_events_search_for_item(app1.raiden, SendRefundTransfer, {}) assert send_refund lock = send_locked.transfer.lock refund_lock = send_refund.transfer.lock assert lock.amount == refund_lock.amount assert lock.secrethash assert lock.expiration assert lock.secrethash == refund_lock.secrethash # Both channels have the amount locked because of the refund message with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit - amount_path, [lock], app1, deposit + amount_path, [refund_lock], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit - amount_path - amount_drain, [], app2, deposit + amount_path + amount_drain, [], ) # Additional checks for LockExpired causing nonce mismatch after refund transfer: # https://github.com/raiden-network/raiden/issues/3146#issuecomment-447378046 # At this point make sure that the initiator has not deleted the payment task assert secrethash in state_from_raiden( app0.raiden).payment_mapping.secrethashes_to_task # Wait for lock lock expiration but make sure app0 never processes LockExpired with dont_handle_lock_expired_mock(app0): wait_for_block( raiden=app0.raiden, block_number=channel.get_sender_expiration_threshold(lock) + 1, retry_timeout=retry_timeout, ) # make sure that app0 still has the payment task for the secrethash # https://github.com/raiden-network/raiden/issues/3183 assert secrethash in state_from_raiden( app0.raiden).payment_mapping.secrethashes_to_task # make sure that app1 sent a lock expired message for the secrethash send_lock_expired = raiden_events_search_for_item( app1.raiden, SendLockExpired, {'secrethash': secrethash}, ) assert send_lock_expired # make sure that app0 never got it state_changes = app0.raiden.wal.storage.get_statechanges_by_identifier( 0, 'latest') assert not search_for_item( state_changes, ReceiveLockExpired, {'secrethash': secrethash}, ) # Out of the handicapped app0 transport. # Now wait till app0 receives and processes LockExpired receive_lock_expired = wait_for_state_change( app0.raiden, ReceiveLockExpired, {'secrethash': secrethash}, retry_timeout, ) # And also till app1 received the processed wait_for_state_change( app1.raiden, ReceiveProcessed, {'message_identifier': receive_lock_expired.message_identifier}, retry_timeout, ) # make sure app1 queue has cleared the SendLockExpired chain_state1 = views.state_from_app(app1) queues1 = views.get_all_messagequeues(chain_state=chain_state1) result = [(queue_id, queue) for queue_id, queue in queues1.items() if queue_id.recipient == app0.raiden.address and queue] assert not result # and now wait for 1 more block so that the payment task can be deleted wait_for_block( raiden=app0.raiden, block_number=app0.raiden.get_block_number() + 1, retry_timeout=retry_timeout, ) # and since the lock expired message has been sent and processed then the # payment task should have been deleted from both nodes # https://github.com/raiden-network/raiden/issues/3183 assert secrethash not in state_from_raiden( app0.raiden).payment_mapping.secrethashes_to_task assert secrethash not in state_from_raiden( app1.raiden).payment_mapping.secrethashes_to_task
def test_matrix_message_retry( local_matrix_servers, retry_interval_initial, retry_interval_max, retries_before_backoff, broadcast_rooms, ): """ Test the retry mechanism implemented into the matrix client. The test creates a transport and sends a message. Given that the receiver was online, the initial message is sent but the receiver doesn't respond in time and goes offline. The retrier should then wait for the `retry_interval` duration to pass and send the message again but this won't work because the receiver is offline. Once the receiver comes back again, the message should be sent again. """ partner_address = factories.make_address() transport = MatrixTransport( config=MatrixTransportConfig( broadcast_rooms=broadcast_rooms, retries_before_backoff=retries_before_backoff, retry_interval_initial=retry_interval_initial, retry_interval_max=retry_interval_max, server=local_matrix_servers[0], available_servers=[local_matrix_servers[0]], ), environment=Environment.DEVELOPMENT, ) transport._send_raw = MagicMock() raiden_service = MockRaidenService(None) transport.start(raiden_service, [], None) transport.log = MagicMock() # Receiver is online transport._address_mgr._address_to_reachabilitystate[partner_address] = ReachabilityState( AddressReachability.REACHABLE, datetime.now() ) queueid = QueueIdentifier( recipient=partner_address, canonical_identifier=CANONICAL_IDENTIFIER_UNORDERED_QUEUE ) chain_state = raiden_service.wal.state_manager.current_state retry_queue: _RetryQueue = transport._get_retrier(partner_address) assert bool(retry_queue), "retry_queue not running" # Send the initial message message = Processed(message_identifier=0, signature=EMPTY_SIGNATURE) transport._raiden_service.sign(message) chain_state.queueids_to_queues[queueid] = [message] retry_queue.enqueue_unordered(message) gevent.idle() assert transport._send_raw.call_count == 1 # Receiver goes offline transport._address_mgr._address_to_reachabilitystate[partner_address] = ReachabilityState( AddressReachability.UNREACHABLE, datetime.now() ) with gevent.Timeout(retry_interval_initial + 2): wait_assert( transport.log.debug.assert_called_with, "Partner not reachable. Skipping.", partner=to_checksum_address(partner_address), status=AddressReachability.UNREACHABLE, ) # Retrier did not call send_raw given that the receiver is still offline assert transport._send_raw.call_count == 1 # Receiver comes back online transport._address_mgr._address_to_reachabilitystate[partner_address] = ReachabilityState( AddressReachability.REACHABLE, datetime.now() ) # Retrier should send the message again with gevent.Timeout(retry_interval_initial + 2): while transport._send_raw.call_count != 2: gevent.sleep(0.1) transport.stop() transport.greenlet.get()
def test_mediated_transfer_with_entire_deposit(raiden_network, number_of_nodes, token_addresses, deposit, network_wait): app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state, token_network_registry_address, token_address) fee1 = int(deposit * INTERNAL_ROUTING_DEFAULT_FEE_PERC) secrethash = transfer_and_assert_path( path=raiden_network, token_address=token_address, amount=deposit - fee1, identifier=1, timeout=network_wait * number_of_nodes, ) with block_timeout_for_transfer_by_secrethash(app1.raiden, secrethash): wait_assert( assert_succeeding_transfer_invariants, token_network_address, app0, 0, [], app1, deposit * 2, [], ) with block_timeout_for_transfer_by_secrethash(app2.raiden, secrethash): wait_assert( assert_succeeding_transfer_invariants, token_network_address, app1, fee1, [], app2, deposit * 2 - fee1, [], ) app2_capacity = 2 * deposit - fee1 fee2 = int(round(app2_capacity * INTERNAL_ROUTING_DEFAULT_FEE_PERC)) reverse_path = list(raiden_network[::-1]) transfer_and_assert_path( path=reverse_path, token_address=token_address, amount=app2_capacity - fee2, identifier=2, timeout=network_wait * number_of_nodes, ) with block_timeout_for_transfer_by_secrethash(app1.raiden, secrethash): wait_assert( assert_succeeding_transfer_invariants, token_network_address, app0, 2 * deposit - fee2, [], app1, fee2, [], ) with block_timeout_for_transfer_by_secrethash(app2.raiden, secrethash): wait_assert( assert_succeeding_transfer_invariants, token_network_address, app1, deposit * 2, [], app2, 0, [], )
def test_receive_lockedtransfer_invalidnonce( raiden_network, number_of_nodes, deposit, token_addresses, reveal_timeout, network_wait, ): app0, app1, app2 = raiden_network token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) channel0 = get_channelstate(app0, app1, token_network_identifier) amount = 10 mediated_transfer( app0, app2, token_network_identifier, amount, timeout=network_wait * number_of_nodes, ) amount = 10 payment_identifier = 1 repeated_nonce = 1 expiration = reveal_timeout * 2 mediated_transfer_message = LockedTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=random.randint(0, UINT64_MAX), payment_identifier=payment_identifier, nonce=repeated_nonce, token_network_address=token_network_identifier, token=token_address, channel_identifier=channel0.identifier, transferred_amount=amount, locked_amount=amount, recipient=app1.raiden.address, locksroot=UNIT_SECRETHASH, lock=Lock(amount, expiration, UNIT_SECRETHASH), target=app2.raiden.address, initiator=app0.raiden.address, fee=0, ) sign_and_inject( mediated_transfer_message, app0.raiden.private_key, app0.raiden.address, app1, ) wait_assert( token_network_identifier, app0, deposit - amount, [], app1, deposit + amount, [], func=assert_synced_channel_state, timeout=network_wait, )
def test_refund_transfer(raiden_chain, token_addresses, deposit, retry_timeout): """A failed transfer must send a refund back. TODO: - Unlock the token on refund #1091 - Clear the pending locks and update the locked amount #193 - Remove the refund message type #490""" # Topology: # # 0 -> 1 -> 2 # app0, app1, app2 = raiden_chain token_address = token_addresses[0] token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), token_network_registry_address, token_address) # make a transfer to test the path app0 -> app1 -> app2 identifier_path = PaymentID(1) amount_path = PaymentAmount(1) with block_offset_timeout(app0.raiden): transfer( initiator_app=app0, target_app=app2, token_address=token_address, amount=amount_path, identifier=identifier_path, ) # drain the channel app1 -> app2 identifier_drain = PaymentID(2) amount_drain = PaymentAmount(deposit * 8 // 10) transfer_timeout = block_offset_timeout(app1.raiden) with transfer_timeout: transfer( initiator_app=app1, target_app=app2, token_address=token_address, amount=amount_drain, identifier=identifier_drain, ) wait_assert( assert_synced_channel_state, token_network_address, app0, deposit - amount_path, [], app1, deposit + amount_path, [], ) wait_assert( assert_synced_channel_state, token_network_address, app1, deposit - amount_path - amount_drain, [], app2, deposit + amount_path + amount_drain, [], ) # app0 -> app1 -> app2 is the only available path, but the channel app1 -> # app2 doesn't have capacity, so a refund will be sent on app1 -> app0 identifier_refund = PaymentID(3) amount_refund = PaymentAmount(50) fee = calculate_fee_for_amount(amount_refund) fee_margin = calculate_fee_margin(amount_refund, fee) amount_refund_with_fees = amount_refund + fee + fee_margin payment_status = app0.raiden.mediated_transfer_async( token_network_address, amount_refund, app2.raiden.address, identifier_refund) msg = "there is no path with capacity, the transfer must fail" assert isinstance(payment_status.payment_done.wait(), EventPaymentSentFailed), msg # A lock structure with the correct amount send_locked = raiden_events_search_for_item( app0.raiden, SendLockedTransfer, {"transfer": { "lock": { "amount": amount_refund_with_fees } }}, ) assert send_locked secrethash = send_locked.transfer.lock.secrethash send_refund = raiden_events_search_for_item(app1.raiden, SendRefundTransfer, {}) assert send_refund lock = send_locked.transfer.lock refund_lock = send_refund.transfer.lock assert lock.amount == refund_lock.amount assert lock.secrethash assert lock.expiration assert lock.secrethash == refund_lock.secrethash # Both channels have the amount locked because of the refund message with transfer_timeout: wait_assert( assert_synced_channel_state, token_network_address, app0, deposit - amount_path, [lock], app1, deposit + amount_path, [refund_lock], ) wait_assert( assert_synced_channel_state, token_network_address, app1, deposit - amount_path - amount_drain, [], app2, deposit + amount_path + amount_drain, [], ) # Additional checks for LockExpired causing nonce mismatch after refund transfer: # https://github.com/raiden-network/raiden/issues/3146#issuecomment-447378046 # At this point make sure that the initiator has not deleted the payment task assert secrethash in state_from_raiden( app0.raiden).payment_mapping.secrethashes_to_task # Wait for lock lock expiration but make sure app0 never processes LockExpired with dont_handle_lock_expired_mock(app0): wait_for_block( raiden=app0.raiden, block_number=BlockNumber( channel.get_sender_expiration_threshold(lock.expiration) + 1), retry_timeout=retry_timeout, ) # make sure that app0 still has the payment task for the secrethash # https://github.com/raiden-network/raiden/issues/3183 assert secrethash in state_from_raiden( app0.raiden).payment_mapping.secrethashes_to_task # make sure that app1 sent a lock expired message for the secrethash send_lock_expired = raiden_events_search_for_item( app1.raiden, SendLockExpired, {"secrethash": secrethash}) assert send_lock_expired # make sure that app0 never got it state_changes = app0.raiden.wal.storage.get_statechanges_by_range( RANGE_ALL_STATE_CHANGES) assert not search_for_item(state_changes, ReceiveLockExpired, {"secrethash": secrethash}) # Out of the handicapped app0 transport. # Now wait till app0 receives and processes LockExpired receive_lock_expired = wait_for_state_change(app0.raiden, ReceiveLockExpired, {"secrethash": secrethash}, retry_timeout) # And also till app1 received the processed wait_for_state_change( app1.raiden, ReceiveProcessed, {"message_identifier": receive_lock_expired.message_identifier}, retry_timeout, ) # make sure app1 queue has cleared the SendLockExpired chain_state1 = views.state_from_app(app1) queues1 = views.get_all_messagequeues(chain_state=chain_state1) result = [(queue_id, queue) for queue_id, queue in queues1.items() if queue_id.recipient == app0.raiden.address and queue] assert not result # and now wait for 1 more block so that the payment task can be deleted wait_for_block( raiden=app0.raiden, block_number=app0.raiden.get_block_number() + 1, retry_timeout=retry_timeout, ) # and since the lock expired message has been sent and processed then the # payment task should have been deleted from both nodes # https://github.com/raiden-network/raiden/issues/3183 assert secrethash not in state_from_raiden( app0.raiden).payment_mapping.secrethashes_to_task assert secrethash not in state_from_raiden( app1.raiden).payment_mapping.secrethashes_to_task
def test_refund_messages(raiden_chain, token_addresses, deposit, network_wait): # The network has the following topology: # # App0 <---> App1 <---> App2 app0, app1, app2 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking token_address = token_addresses[0] token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), token_network_registry_address, token_address) # Exhaust the channel App1 <-> App2 (to force the refund transfer) # Here we make a single-hop transfer, no fees are charged so we should # send the whole deposit amount to drain the channel. transfer( initiator_app=app1, target_app=app2, token_address=token_address, amount=deposit, identifier=1, ) refund_amount = deposit // 2 refund_fees = calculate_fee_for_amount(refund_amount) refund_amount_with_fees = refund_amount + refund_fees identifier = 1 payment_status = app0.raiden.mediated_transfer_async( token_network_address, refund_amount, app2.raiden.address, identifier) msg = "Must fail, there are no routes available" assert isinstance(payment_status.payment_done.wait(), EventPaymentSentFailed), msg # The transfer from app0 to app2 failed, so the balances did change. # Since the refund is not unlocked both channels have the corresponding # amount locked (issue #1091) send_lockedtransfer = raiden_events_search_for_item( app0.raiden, SendLockedTransfer, {"transfer": { "lock": { "amount": refund_amount_with_fees } }}, ) assert send_lockedtransfer send_refundtransfer = raiden_events_search_for_item( app1.raiden, SendRefundTransfer, {}) assert send_refundtransfer with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_address, app0, deposit, [send_lockedtransfer.transfer.lock], app1, deposit, [send_refundtransfer.transfer.lock], ) # This channel was exhausted to force the refund transfer except for the fees with gevent.Timeout(network_wait): wait_assert(assert_synced_channel_state, token_network_address, app1, 0, [], app2, deposit * 2, [])
def test_mediated_transfer_messages_out_of_order( # pylint: disable=unused-argument raiden_network, deposit, token_addresses, network_wait): """Raiden must properly handle repeated locked transfer messages.""" app0, app1, app2 = raiden_network app1_wait_for_message = WaitForMessage() app2_wait_for_message = WaitForMessage() app1.raiden.message_handler = app1_wait_for_message app2.raiden.message_handler = app2_wait_for_message secret = factories.make_secret(0) secrethash = sha256_secrethash(secret) # Save the messages, these will be processed again app1_mediatedtransfer = app1_wait_for_message.wait_for_message( LockedTransfer, {"lock": { "secrethash": secrethash }}) app2_mediatedtransfer = app2_wait_for_message.wait_for_message( LockedTransfer, {"lock": { "secrethash": secrethash }}) # Wait until the node receives a reveal secret to redispatch the locked # transfer message app1_revealsecret = app1_wait_for_message.wait_for_message( RevealSecret, {"secret": secret}) app2_revealsecret = app2_wait_for_message.wait_for_message( RevealSecret, {"secret": secret}) token_address = token_addresses[0] chain_state = views.state_from_app(app0) token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state, token_network_registry_address, token_address) amount = 10 identifier = 1 transfer_received = app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=amount, target=app2.raiden.address, identifier=identifier, secret=secret, ) # - Wait until reveal secret is received to replay the message # - The secret is revealed backwards, app2 should be first # - The locked transfer is sent before the secret reveal, so the mediated # transfers async results must be set and `get_nowait` can be used app2_revealsecret.get(timeout=network_wait) mediated_transfer_msg = app2_mediatedtransfer.get_nowait() app2.raiden.message_handler.handle_message_lockedtransfer( app2.raiden, mediated_transfer_msg) app1_revealsecret.get(timeout=network_wait) app1.raiden.message_handler.handle_message_lockedtransfer( app1.raiden, app1_mediatedtransfer.get_nowait()) transfer_received.payment_done.wait() with block_timeout_for_transfer_by_secrethash(app1.raiden, secrethash): wait_assert( assert_succeeding_transfer_invariants, token_network_address, app0, deposit - amount, [], app1, deposit + amount, [], ) with block_timeout_for_transfer_by_secrethash(app2.raiden, secrethash): wait_assert( assert_succeeding_transfer_invariants, token_network_address, app1, deposit - amount, [], app2, deposit + amount, [], )
def test_different_view_of_last_bp_during_unlock( raiden_chain: List[App], number_of_nodes, token_addresses, deposit, network_wait, retry_timeout, blockchain_type, ): """Test for https://github.com/raiden-network/raiden/issues/3196#issuecomment-449163888""" # Topology: # # 0 -> 1 -> 2 # app0, app1, app2 = raiden_chain token_address = token_addresses[0] token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), token_network_registry_address, token_address) assert token_network_address token_proxy = app0.raiden.proxy_manager.token(token_address) initial_balance0 = token_proxy.balance_of(app0.raiden.address) initial_balance1 = token_proxy.balance_of(app1.raiden.address) # make a transfer to test the path app0 -> app1 -> app2 identifier_path = PaymentID(1) amount_path = PaymentAmount(1) transfer( initiator_app=app0, target_app=app2, token_address=token_address, amount=amount_path, identifier=identifier_path, timeout=network_wait * number_of_nodes, ) # drain the channel app1 -> app2 identifier_drain = PaymentID(2) amount_drain = PaymentAmount(deposit * 8 // 10) transfer( initiator_app=app1, target_app=app2, token_address=token_address, amount=amount_drain, identifier=identifier_drain, timeout=network_wait, ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_address, app0, deposit - amount_path, [], app1, deposit + amount_path, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_address, app1, deposit - amount_path - amount_drain, [], app2, deposit + amount_path + amount_drain, [], ) # app0 -> app1 -> app2 is the only available path, but the channel app1 -> # app2 doesn't have capacity, so a refund will be sent on app1 -> app0 identifier_refund = PaymentID(3) amount_refund = PaymentAmount(50) fee = calculate_fee_for_amount(amount_refund) fee_margin = calculate_fee_margin(amount_refund, fee) amount_refund_with_fees = amount_refund + fee + fee_margin payment_status = app0.raiden.mediated_transfer_async( token_network_address, amount_refund, TargetAddress(app2.raiden.address), identifier_refund) msg = "there is no path with capacity, the transfer must fail" assert isinstance(payment_status.payment_done.wait(), EventPaymentSentFailed), msg # A lock structure with the correct amount send_locked = raiden_events_search_for_item( app0.raiden, SendLockedTransfer, {"transfer": { "lock": { "amount": amount_refund_with_fees } }}, ) assert send_locked secrethash = send_locked.transfer.lock.secrethash send_refund = raiden_events_search_for_item(app1.raiden, SendRefundTransfer, {}) assert send_refund lock = send_locked.transfer.lock refund_lock = send_refund.transfer.lock assert lock.amount == refund_lock.amount assert lock.secrethash assert lock.expiration assert lock.secrethash == refund_lock.secrethash # Both channels have the amount locked because of the refund message with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_address, app0, deposit - amount_path, [lock], app1, deposit + amount_path, [refund_lock], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_address, app1, deposit - amount_path - amount_drain, [], app2, deposit + amount_path + amount_drain, [], ) # Additional checks for LockExpired causing nonce mismatch after refund transfer: # https://github.com/raiden-network/raiden/issues/3146#issuecomment-447378046 # At this point make sure that the initiator has not deleted the payment task assert secrethash in state_from_raiden( app0.raiden).payment_mapping.secrethashes_to_task with dont_handle_node_change_network_state(): # now app1 goes offline app1.raiden.stop() app1.raiden.greenlet.get() assert not app1.raiden # Wait for lock expiration so that app0 sends a LockExpired wait_for_block( raiden=app0.raiden, block_number=BlockNumber( channel.get_sender_expiration_threshold(lock.expiration) + 1), retry_timeout=retry_timeout, ) # make sure that app0 sent a lock expired message for the secrethash wait_for_raiden_event(app0.raiden, SendLockExpired, {"secrethash": secrethash}, retry_timeout) # now app0 closes the channel RaidenAPI(app0.raiden).channel_close( registry_address=token_network_registry_address, token_address=token_address, partner_address=app1.raiden.address, ) count = 0 on_raiden_event_original = app1.raiden.raiden_event_handler.on_raiden_event def patched_on_raiden_event(raiden, chain_state, event): if type(event) == ContractSendChannelUpdateTransfer: nonlocal count count += 1 on_raiden_event_original(raiden, chain_state, event) setattr(app1.raiden.raiden_event_handler, "on_raiden_event", patched_on_raiden_event) # NOQA # and now app1 comes back online app1.raiden.start() # test for https://github.com/raiden-network/raiden/issues/3216 assert count == 1, "Update transfer should have only been called once during restart" channel_identifier = get_channelstate(app0, app1, token_network_address).identifier # and we wait for settlement wait_for_settle( raiden=app0.raiden, token_network_registry_address=token_network_registry_address, token_address=token_address, channel_ids=[channel_identifier], retry_timeout=app0.raiden.alarm.sleep_time, ) timeout = 30 if blockchain_type == "parity" else 10 with gevent.Timeout(timeout): unlock_app0 = wait_for_state_change( app0.raiden, ContractReceiveChannelBatchUnlock, {"receiver": app0.raiden.address}, retry_timeout, ) assert unlock_app0 assert unlock_app0.returned_tokens == amount_refund_with_fees with gevent.Timeout(timeout): unlock_app1 = wait_for_state_change( app1.raiden, ContractReceiveChannelBatchUnlock, {"receiver": app1.raiden.address}, retry_timeout, ) assert unlock_app1 assert unlock_app1.returned_tokens == amount_refund_with_fees final_balance0 = token_proxy.balance_of(app0.raiden.address) final_balance1 = token_proxy.balance_of(app1.raiden.address) assert final_balance0 - deposit - initial_balance0 == -1 assert final_balance1 - deposit - initial_balance1 == 1
def run_test_refund_transfer_after_2nd_hop( raiden_chain, number_of_nodes, token_addresses, deposit, network_wait, ): """Test the refund transfer sent due to failure after 2nd hop""" # Topology: # # 0 -> 1 -> 2 -> 3 # app0, app1, app2, app3 = raiden_chain token_address = token_addresses[0] payment_network_identifier = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), payment_network_identifier, token_address, ) # make a transfer to test the path app0 -> app1 -> app2 -> app3 identifier_path = 1 amount_path = 1 transfer( initiator_app=app0, target_app=app3, token_address=token_address, amount=amount_path, identifier=identifier_path, timeout=network_wait * number_of_nodes, ) # drain the channel app2 -> app3 identifier_drain = 2 amount_drain = deposit * 8 // 10 transfer( initiator_app=app2, target_app=app3, token_address=token_address, amount=amount_drain, identifier=identifier_drain, timeout=network_wait, ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit - amount_path, [], app1, deposit + amount_path, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit - amount_path, [], app2, deposit + amount_path, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app2, deposit - amount_path - amount_drain, [], app3, deposit + amount_path + amount_drain, [], ) # app0 -> app1 -> app2 > app3 is the only available path, but the channel # app2 -> app3 doesn't have capacity, so a refund will be sent on # app2 -> app1 -> app0 identifier_refund = 3 amount_refund = 50 payment_status = app0.raiden.mediated_transfer_async( token_network_identifier, amount_refund, app3.raiden.address, identifier_refund, ) msg = 'there is no path with capacity, the transfer must fail' assert payment_status.payment_done.wait() is False, msg gevent.sleep(0.2) # Lock structures with the correct amount send_locked1 = raiden_events_search_for_item( app0.raiden, SendLockedTransfer, {'transfer': { 'lock': { 'amount': amount_refund } }}, ) assert send_locked1 send_refund1 = raiden_events_search_for_item(app1.raiden, SendRefundTransfer, {}) assert send_refund1 lock1 = send_locked1.transfer.lock refund_lock1 = send_refund1.transfer.lock assert lock1.amount == refund_lock1.amount assert lock1.secrethash == refund_lock1.secrethash send_locked2 = raiden_events_search_for_item( app1.raiden, SendLockedTransfer, {'transfer': { 'lock': { 'amount': amount_refund } }}, ) assert send_locked2 send_refund2 = raiden_events_search_for_item(app2.raiden, SendRefundTransfer, {}) assert send_refund2 lock2 = send_locked2.transfer.lock refund_lock2 = send_refund2.transfer.lock assert lock2.amount == refund_lock2.amount assert lock2.secrethash assert lock2.expiration # channels have the amount locked because of the refund message with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit - amount_path, [lock1], app1, deposit + amount_path, [refund_lock1], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit - amount_path, [lock2], app2, deposit + amount_path, [refund_lock2], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app2, deposit - amount_path - amount_drain, [], app3, deposit + amount_path + amount_drain, [], )
def run_test_different_view_of_last_bp_during_unlock( raiden_chain, number_of_nodes, token_addresses, deposit, network_wait, retry_timeout, blockchain_type, ): """Test for https://github.com/raiden-network/raiden/issues/3196#issuecomment-449163888""" # Topology: # # 0 -> 1 -> 2 # app0, app1, app2 = raiden_chain token_address = token_addresses[0] payment_network_identifier = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), payment_network_identifier, token_address, ) token_proxy = app0.raiden.chain.token(token_address) initial_balance0 = token_proxy.balance_of(app0.raiden.address) initial_balance1 = token_proxy.balance_of(app1.raiden.address) # make a transfer to test the path app0 -> app1 -> app2 identifier_path = 1 amount_path = 1 transfer( initiator_app=app0, target_app=app2, token_address=token_address, amount=amount_path, identifier=identifier_path, timeout=network_wait * number_of_nodes, ) # drain the channel app1 -> app2 identifier_drain = 2 amount_drain = deposit * 8 // 10 transfer( initiator_app=app1, target_app=app2, token_address=token_address, amount=amount_drain, identifier=identifier_drain, timeout=network_wait, ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit - amount_path, [], app1, deposit + amount_path, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit - amount_path - amount_drain, [], app2, deposit + amount_path + amount_drain, [], ) # app0 -> app1 -> app2 is the only available path, but the channel app1 -> # app2 doesn't have capacity, so a refund will be sent on app1 -> app0 identifier_refund = 3 amount_refund = 50 payment_status = app0.raiden.mediated_transfer_async( token_network_identifier, amount_refund, app2.raiden.address, identifier_refund, ) msg = 'there is no path with capacity, the transfer must fail' assert payment_status.payment_done.wait() is False, msg gevent.sleep(0.2) # A lock structure with the correct amount send_locked = raiden_events_search_for_item( app0.raiden, SendLockedTransfer, {'transfer': { 'lock': { 'amount': amount_refund } }}, ) assert send_locked secrethash = send_locked.transfer.lock.secrethash send_refund = raiden_events_search_for_item(app1.raiden, SendRefundTransfer, {}) assert send_refund lock = send_locked.transfer.lock refund_lock = send_refund.transfer.lock assert lock.amount == refund_lock.amount assert lock.secrethash assert lock.expiration assert lock.secrethash == refund_lock.secrethash # Both channels have the amount locked because of the refund message with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit - amount_path, [lock], app1, deposit + amount_path, [refund_lock], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit - amount_path - amount_drain, [], app2, deposit + amount_path + amount_drain, [], ) # Additional checks for LockExpired causing nonce mismatch after refund transfer: # https://github.com/raiden-network/raiden/issues/3146#issuecomment-447378046 # At this point make sure that the initiator has not deleted the payment task assert secrethash in state_from_raiden( app0.raiden).payment_mapping.secrethashes_to_task with dont_handle_node_change_network_state(): # now app1 goes offline app1.raiden.stop() app1.raiden.get() assert not app1.raiden # Wait for lock expiration so that app0 sends a LockExpired wait_for_block( raiden=app0.raiden, block_number=channel.get_sender_expiration_threshold(lock) + 1, retry_timeout=retry_timeout, ) # make sure that app0 sent a lock expired message for the secrethash wait_for_raiden_event( app0.raiden, SendLockExpired, {'secrethash': secrethash}, retry_timeout, ) # now app0 closes the channel RaidenAPI(app0.raiden).channel_close( registry_address=payment_network_identifier, token_address=token_address, partner_address=app1.raiden.address, ) count = 0 original_update = app1.raiden.raiden_event_handler.handle_contract_send_channelupdate def patched_update(raiden, event): nonlocal count count += 1 original_update(raiden, event) app1.raiden.raiden_event_handler.handle_contract_send_channelupdate = patched_update # and now app1 comes back online app1.raiden.start() # test for https://github.com/raiden-network/raiden/issues/3216 assert count == 1, 'Update transfer should have only been called once during restart' channel_identifier = get_channelstate(app0, app1, token_network_identifier).identifier # and we wait for settlement wait_for_settle( raiden=app0.raiden, payment_network_id=payment_network_identifier, token_address=token_address, channel_ids=[channel_identifier], retry_timeout=app0.raiden.alarm.sleep_time, ) timeout = 30 if blockchain_type == 'parity' else 10 with gevent.Timeout(timeout): unlock_app0 = wait_for_state_change( app0.raiden, ContractReceiveChannelBatchUnlock, {'participant': app0.raiden.address}, retry_timeout, ) assert unlock_app0.returned_tokens == 50 with gevent.Timeout(timeout): unlock_app1 = wait_for_state_change( app1.raiden, ContractReceiveChannelBatchUnlock, {'participant': app1.raiden.address}, retry_timeout, ) assert unlock_app1.returned_tokens == 50 final_balance0 = token_proxy.balance_of(app0.raiden.address) final_balance1 = token_proxy.balance_of(app1.raiden.address) assert final_balance0 - deposit - initial_balance0 == -1 assert final_balance1 - deposit - initial_balance1 == 1
def test_matrix_message_sync(matrix_transports): transport0, transport1 = matrix_transports transport0_messages = set() transport1_messages = set() transport0_message_handler = MessageHandler(transport0_messages) transport1_message_handler = MessageHandler(transport1_messages) raiden_service0 = MockRaidenService(transport0_message_handler) raiden_service1 = MockRaidenService(transport1_message_handler) raiden_service1.handle_and_track_state_changes = MagicMock() transport0.start(raiden_service0, [], None) transport1.start(raiden_service1, [], None) latest_auth_data = f"{transport1._user_id}/{transport1._client.api.token}" update_transport_auth_data = ActionUpdateTransportAuthData( latest_auth_data) with gevent.Timeout(2): wait_assert( raiden_service1.handle_and_track_state_changes.assert_called_with, [update_transport_auth_data], ) transport0.start_health_check(transport1._raiden_service.address) transport1.start_health_check(transport0._raiden_service.address) queue_identifier = QueueIdentifier( recipient=transport1._raiden_service.address, canonical_identifier=factories.UNIT_CANONICAL_ID, ) raiden0_queues = views.get_all_messagequeues( views.state_from_raiden(raiden_service0)) raiden0_queues[queue_identifier] = [] for i in range(5): message = Processed(message_identifier=i, signature=EMPTY_SIGNATURE) raiden0_queues[queue_identifier].append(message) transport0._raiden_service.sign(message) transport0.send_async(queue_identifier, message) with Timeout(TIMEOUT_MESSAGE_RECEIVE): while not len(transport0_messages) == 5: gevent.sleep(0.1) while not len(transport1_messages) == 5: gevent.sleep(0.1) # transport1 receives the `Processed` messages sent by transport0 for i in range(5): assert any(m.message_identifier == i for m in transport1_messages) # transport0 answers with a `Delivered` for each `Processed` for i in range(5): assert any(m.delivered_message_identifier == i for m in transport0_messages) # Clear out queue raiden0_queues[queue_identifier] = [] transport1.stop() wait_for_peer_unreachable(transport0, transport1._raiden_service.address) assert latest_auth_data # Send more messages while the other end is offline for i in range(10, 15): message = Processed(message_identifier=i, signature=EMPTY_SIGNATURE) raiden0_queues[queue_identifier].append(message) transport0._raiden_service.sign(message) transport0.send_async(queue_identifier, message) # Should fetch the 5 messages sent while transport1 was offline transport1.start(transport1._raiden_service, [], latest_auth_data) transport1.start_health_check(transport0._raiden_service.address) with gevent.Timeout(TIMEOUT_MESSAGE_RECEIVE): while len(transport1_messages) != 10: gevent.sleep(0.1) while len(transport0_messages) != 10: gevent.sleep(0.1) # transport1 receives the 5 new `Processed` messages sent by transport0 for i in range(10, 15): assert any(m.message_identifier == i for m in transport1_messages) # transport0 answers with a `Delivered` for each one of the new `Processed` for i in range(10, 15): assert any(m.delivered_message_identifier == i for m in transport0_messages)
def test_refund_messages(raiden_chain: List[RaidenService], token_addresses, deposit): # The network has the following topology: # # App0 <---> App1 <---> App2 app0, app1, app2 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking token_address = token_addresses[0] token_network_registry_address = app0.default_registry.address token_network_address = views.get_token_network_address_by_token_address( views.state_from_raiden(app0), token_network_registry_address, token_address) assert token_network_address, "token_address must be registered by the fixtures." identifier = PaymentID(1) # Exhaust the channel App1 <-> App2 (to force the refund transfer) # Here we make a single-hop transfer, no fees are charged so we should # send the whole deposit amount to drain the channel. transfer( initiator_app=app1, target_app=app2, token_address=token_address, amount=deposit, identifier=identifier, routes=[[app1.address, app2.address]], ) refund_amount = deposit // 2 refund_fees = calculate_fee_for_amount(refund_amount) fee_margin = calculate_fee_margin(refund_amount, refund_fees) refund_amount_with_fees = refund_amount + refund_fees + fee_margin payment_status = app0.mediated_transfer_async( token_network_address=token_network_address, amount=refund_amount, target=TargetAddress(app2.address), identifier=identifier, route_states=[ create_route_state_for_route( apps=raiden_chain, token_address=token_address, fee_estimate=FeeAmount( round(INTERNAL_ROUTING_DEFAULT_FEE_PERC * refund_amount)), ) ], ) msg = "Must fail, there are no routes available" assert isinstance(payment_status.payment_done.wait(), EventPaymentSentFailed), msg # The transfer from app0 to app2 failed, so the balances did change. # Since the refund is not unlocked both channels have the corresponding # amount locked (issue #1091) send_lockedtransfer = raiden_events_search_for_item( app0, SendLockedTransfer, {"transfer": { "lock": { "amount": refund_amount_with_fees } }}, ) assert send_lockedtransfer send_refundtransfer = raiden_events_search_for_item( app1, SendRefundTransfer, {}) assert send_refundtransfer with block_offset_timeout(app1): wait_assert( func=assert_synced_channel_state, token_network_address=token_network_address, app0=app0, balance0=deposit, pending_locks0=[send_lockedtransfer.transfer.lock], app1=app1, balance1=deposit, pending_locks1=[send_refundtransfer.transfer.lock], ) # This channel was exhausted to force the refund transfer except for the fees wait_assert( func=assert_succeeding_transfer_invariants, token_network_address=token_network_address, app0=app1, balance0=0, pending_locks0=[], app1=app2, balance1=deposit * 2, pending_locks1=[], )
def test_refund_transfer_after_2nd_hop(raiden_chain, number_of_nodes, token_addresses, deposit, network_wait): """Test the refund transfer sent due to failure after 2nd hop""" # Topology: # # 0 -> 1 -> 2 -> 3 # app0, app1, app2, app3 = raiden_chain token_address = token_addresses[0] token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), token_network_registry_address, token_address) # make a transfer to test the path app0 -> app1 -> app2 -> app3 identifier_path = PaymentID(1) amount_path = PaymentAmount(1) transfer( initiator_app=app0, target_app=app3, token_address=token_address, amount=amount_path, identifier=identifier_path, timeout=network_wait * number_of_nodes, ) # drain the channel app2 -> app3 identifier_drain = PaymentID(2) amount_drain = PaymentAmount(deposit * 8 // 10) transfer( initiator_app=app2, target_app=app3, token_address=token_address, amount=amount_drain, identifier=identifier_drain, timeout=network_wait, ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_address, app0, deposit - amount_path, [], app1, deposit + amount_path, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_address, app1, deposit - amount_path, [], app2, deposit + amount_path, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_address, app2, deposit - amount_path - amount_drain, [], app3, deposit + amount_path + amount_drain, [], ) # app0 -> app1 -> app2 > app3 is the only available path, but the channel # app2 -> app3 doesn't have capacity, so a refund will be sent on # app2 -> app1 -> app0 identifier_refund = PaymentID(3) amount_refund = PaymentAmount(50) fee = calculate_fee_for_amount(amount_refund) fee_margin = calculate_fee_margin(amount_refund, fee) amount_refund_with_fees = amount_refund + fee + fee_margin payment_status = app0.raiden.mediated_transfer_async( token_network_address, amount_refund, app3.raiden.address, identifier_refund) msg = "there is no path with capacity, the transfer must fail" assert isinstance(payment_status.payment_done.wait(), EventPaymentSentFailed), msg # Lock structures with the correct amount send_locked1 = raiden_events_search_for_item( app0.raiden, SendLockedTransfer, {"transfer": { "lock": { "amount": amount_refund_with_fees } }}, ) assert send_locked1 send_refund1 = raiden_events_search_for_item(app1.raiden, SendRefundTransfer, {}) assert send_refund1 lock1 = send_locked1.transfer.lock refund_lock1 = send_refund1.transfer.lock assert lock1.amount == refund_lock1.amount assert lock1.secrethash == refund_lock1.secrethash send_locked2 = raiden_events_search_for_item( app1.raiden, SendLockedTransfer, {"transfer": { "lock": { "amount": amount_refund_with_fees } }}, ) assert send_locked2 send_refund2 = raiden_events_search_for_item(app2.raiden, SendRefundTransfer, {}) assert send_refund2 lock2 = send_locked2.transfer.lock refund_lock2 = send_refund2.transfer.lock assert lock2.amount == refund_lock2.amount assert lock2.secrethash assert lock2.expiration # channels have the amount locked because of the refund message with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_address, app0, deposit - amount_path, [lock1], app1, deposit + amount_path, [refund_lock1], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_address, app1, deposit - amount_path, [lock2], app2, deposit + amount_path, [refund_lock2], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_address, app2, deposit - amount_path - amount_drain, [], app3, deposit + amount_path + amount_drain, [], )
def run_test_refund_messages(raiden_chain, token_addresses, deposit, network_wait): # The network has the following topology: # # App0 <---> App1 <---> App2 app0, app1, app2 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking token_address = token_addresses[0] payment_network_identifier = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), payment_network_identifier, token_address, ) # Exhaust the channel App1 <-> App2 (to force the refund transfer) exhaust_amount = deposit transfer( initiator_app=app1, target_app=app2, token_address=token_address, amount=exhaust_amount, identifier=1, ) refund_amount = deposit // 2 identifier = 1 payment_status = app0.raiden.mediated_transfer_async( token_network_identifier, refund_amount, app2.raiden.address, identifier, ) msg = 'Must fail, there are no routes available' assert payment_status.payment_done.wait() is False, msg # The transfer from app0 to app2 failed, so the balances did change. # Since the refund is not unlocked both channels have the corresponding # amount locked (issue #1091) send_lockedtransfer = raiden_events_search_for_item( app0.raiden, SendLockedTransfer, {'transfer': { 'lock': { 'amount': refund_amount } }}, ) assert send_lockedtransfer send_refundtransfer = raiden_events_search_for_item( app1.raiden, SendRefundTransfer, {}) assert send_refundtransfer with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit, [send_lockedtransfer.transfer.lock], app1, deposit, [send_refundtransfer.transfer.lock], ) # This channel was exhausted to force the refund transfer with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, 0, [], app2, deposit * 2, [], )
def run_test_mediated_transfer_messages_out_of_order(raiden_network, deposit, token_addresses, network_wait): """Raiden must properly handle repeated locked transfer messages.""" app0, app1, app2 = raiden_network app1_wait_for_message = WaitForMessage() app2_wait_for_message = WaitForMessage() app1.raiden.message_handler = app1_wait_for_message app2.raiden.message_handler = app2_wait_for_message secret = factories.make_secret(0) secrethash = sha3(secret) # Save the messages, these will be processed again app1_mediatedtransfer = app1_wait_for_message.wait_for_message( LockedTransfer, {"lock": { "secrethash": secrethash }}) app2_mediatedtransfer = app2_wait_for_message.wait_for_message( LockedTransfer, {"lock": { "secrethash": secrethash }}) # Wait until the node receives a reveal secret to redispatch the locked # transfer message app1_revealsecret = app1_wait_for_message.wait_for_message( RevealSecret, {"secret": secret}) app2_revealsecret = app2_wait_for_message.wait_for_message( RevealSecret, {"secret": secret}) token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address) amount = 10 identifier = 1 transfer_received = app0.raiden.start_mediated_transfer_with_secret( token_network_identifier=token_network_identifier, amount=amount, fee=0, target=app2.raiden.address, identifier=identifier, payment_hash_invoice=EMPTY_PAYMENT_HASH_INVOICE, secret=secret, ) # - Wait until reveal secret is received to replay the message # - The secret is revealed backwards, app2 should be first # - The locked transfer is sent before the secret reveal, so the mediated # transfers async results must be set and `get_nowait` can be used app2_revealsecret.get(timeout=network_wait) mediated_transfer_msg = app2_mediatedtransfer.get_nowait() app2.raiden.message_handler.handle_message_lockedtransfer( app2.raiden, mediated_transfer_msg) app1_revealsecret.get(timeout=network_wait) app1.raiden.message_handler.handle_message_lockedtransfer( app1.raiden, app1_mediatedtransfer.get_nowait()) transfer_received.payment_done.wait() with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit - amount, [], app1, deposit + amount, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit - amount, [], app2, deposit + amount, [], )
def test_mediated_transfer_with_entire_deposit(raiden_network, number_of_nodes, token_addresses, deposit, network_wait) -> None: app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state, token_network_registry_address, token_address) # The test uses internal routing at the moment, that's why this is set like that. # However, the actual calculated fee is 3 instead of the 4 calculated here, therefore # the amounts are adjusted below fee1 = FeeAmount(int(deposit * INTERNAL_ROUTING_DEFAULT_FEE_PERC)) fee_margin1 = calculate_fee_margin(deposit, fee1) fee_difference = 1 secrethash = transfer_and_assert_path( path=raiden_network, token_address=token_address, amount=deposit - fee1 - fee_margin1, identifier=PaymentID(1), timeout=network_wait * number_of_nodes, ) with block_timeout_for_transfer_by_secrethash(app1.raiden, secrethash): wait_assert( func=assert_succeeding_transfer_invariants, token_network_address=token_network_address, app0=app0, balance0=0, pending_locks0=[], app1=app1, balance1=deposit * 2, pending_locks1=[], ) with block_timeout_for_transfer_by_secrethash(app2.raiden, secrethash): wait_assert( func=assert_succeeding_transfer_invariants, token_network_address=token_network_address, app0=app1, balance0=fee1 - fee_difference, pending_locks0=[], app1=app2, balance1=deposit * 2 - fee1 + fee_difference, pending_locks1=[], ) app2_capacity = 2 * deposit - fee1 fee2 = FeeAmount( int(round(app2_capacity * INTERNAL_ROUTING_DEFAULT_FEE_PERC))) fee_margin2 = calculate_fee_margin(app2_capacity, fee2) reverse_path = list(raiden_network[::-1]) transfer_and_assert_path( path=reverse_path, token_address=token_address, amount=app2_capacity - fee2 - fee_margin2, identifier=PaymentID(2), timeout=network_wait * number_of_nodes, ) with block_timeout_for_transfer_by_secrethash(app1.raiden, secrethash): wait_assert( func=assert_succeeding_transfer_invariants, token_network_address=token_network_address, app0=app0, balance0=2 * deposit - fee2 + fee_difference, pending_locks0=[], app1=app1, balance1=fee2 - fee_difference, pending_locks1=[], ) with block_timeout_for_transfer_by_secrethash(app2.raiden, secrethash): wait_assert( func=assert_succeeding_transfer_invariants, token_network_address=token_network_address, app0=app1, balance0=deposit * 2 - fee_difference, pending_locks0=[], app1=app2, balance1=fee_difference, pending_locks1=[], )
def run_test_mediated_transfer_with_allocated_fee(raiden_network, number_of_nodes, deposit, token_addresses, network_wait): app0, app1, app2, app3 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address) fee = 5 amount = 10 transfer( initiator_app=app0, target_app=app3, token_address=token_address, amount=amount, identifier=1, fee=fee, timeout=network_wait * number_of_nodes, ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit - amount - fee, [], app1, deposit + amount + fee, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit - amount - fee, [], app2, deposit + amount + fee, [], ) app1_app2_channel_state = views.get_channelstate_by_token_network_and_partner( chain_state=views.state_from_raiden(app1.raiden), token_network_id=token_network_identifier, partner_address=app2.raiden.address, ) # Let app1 consume all of the allocated mediation fee action_set_fee = ActionChannelSetFee( canonical_identifier=app1_app2_channel_state.canonical_identifier, mediation_fee=fee) app1.raiden.handle_state_change(state_change=action_set_fee) transfer( initiator_app=app0, target_app=app3, token_address=token_address, amount=amount, identifier=2, fee=fee, timeout=network_wait * number_of_nodes, ) # The fees have been consumed exclusively by app1 with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit - 2 * (amount + fee), [], app1, deposit + 2 * (amount + fee), [], ) # app2's poor soul gets no mediation fees on the second transfer. # Only the first transfer had a fee which was paid to app2 though # app2 doesn't set its fee but it would still receive the complete # locked amount = transfer amount + fee. # However app1 received from app0 two transfers # which it sent to app2. The first transfer # to app2 included the fee as it did not deduct # any fee (the channel's fee was 0). # The second transfer's fee was deducted by # app1 (provided we've set the fee of the channel) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit - (amount * 2) - fee, [], app2, deposit + (amount * 2) + fee, [], )
def test_receive_lockedtransfer_invalidnonce( raiden_network, number_of_nodes, deposit, token_addresses, reveal_timeout, network_wait, ): app0, app1, app2 = raiden_network token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) channel0 = get_channelstate(app0, app1, token_network_identifier) amount = 10 mediated_transfer( app0, app2, token_network_identifier, amount, timeout=network_wait * number_of_nodes, ) amount = 10 payment_identifier = 1 repeated_nonce = 1 expiration = reveal_timeout * 2 mediated_transfer_message = LockedTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=random.randint(0, UINT64_MAX), payment_identifier=payment_identifier, nonce=repeated_nonce, token_network_address=token_network_identifier, token=token_address, channel_identifier=channel0.identifier, transferred_amount=amount, locked_amount=amount, recipient=app1.raiden.address, locksroot=UNIT_SECRETHASH, lock=Lock(amount, expiration, UNIT_SECRETHASH), target=app2.raiden.address, initiator=app0.raiden.address, fee=0, ) sign_and_inject( mediated_transfer_message, app0.raiden.private_key, app0.raiden.address, app1, ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit - amount, [], app1, deposit + amount, [], )
def test_matrix_message_retry(local_matrix_servers, private_rooms, retry_interval, retries_before_backoff, global_rooms): """ Test the retry mechanism implemented into the matrix client. The test creates a transport and sends a message. Given that the receiver was online, the initial message is sent but the receiver doesn't respond in time and goes offline. The retrier should then wait for the `retry_interval` duration to pass and send the message again but this won't work because the receiver is offline. Once the receiver comes back again, the message should be sent again. """ partner_address = factories.make_address() transport = MatrixTransport({ "global_rooms": global_rooms, "retries_before_backoff": retries_before_backoff, "retry_interval": retry_interval, "server": local_matrix_servers[0], "server_name": local_matrix_servers[0].netloc, "available_servers": [local_matrix_servers[0]], "private_rooms": private_rooms, }) transport._send_raw = MagicMock() raiden_service = MockRaidenService(None) transport.start(raiden_service, raiden_service.message_handler, None) transport.log = MagicMock() # Receiver is online transport._address_mgr._address_to_reachability[ partner_address] = AddressReachability.REACHABLE queueid = QueueIdentifier( recipient=partner_address, canonical_identifier=CANONICAL_IDENTIFIER_GLOBAL_QUEUE) chain_state = raiden_service.wal.state_manager.current_state retry_queue: _RetryQueue = transport._get_retrier(partner_address) assert bool(retry_queue), "retry_queue not running" # Send the initial message message = Processed(message_identifier=0, signature=EMPTY_SIGNATURE) transport._raiden_service.sign(message) chain_state.queueids_to_queues[queueid] = [message] retry_queue.enqueue_global(message) gevent.idle() assert transport._send_raw.call_count == 1 # Receiver goes offline transport._address_mgr._address_to_reachability[ partner_address] = AddressReachability.UNREACHABLE with gevent.Timeout(retry_interval + 2): wait_assert( transport.log.debug.assert_called_with, "Partner not reachable. Skipping.", partner=to_checksum_address(partner_address), status=AddressReachability.UNREACHABLE, ) # Retrier did not call send_raw given that the receiver is still offline assert transport._send_raw.call_count == 1 # Receiver comes back online transport._address_mgr._address_to_reachability[ partner_address] = AddressReachability.REACHABLE # Retrier should send the message again with gevent.Timeout(retry_interval + 2): while transport._send_raw.call_count != 2: gevent.sleep(0.1) transport.stop() transport.get()