def test_pfs_handler_handle_paymentsentsuccess_without_feedback_token(): ( raiden, pfs_handler, token_network_registry_address, token_network_address, route, _, ) = setup_pfs_handler_test(set_feedback_token=False) payment_id = make_payment_id() amount = PaymentAmount(123) target = TargetAddress(route[-1]) raiden.targets_to_identifiers_to_statuses[target][payment_id] = Mock() route_failed_event = EventPaymentSentSuccess( token_network_registry_address=token_network_registry_address, token_network_address=token_network_address, identifier=payment_id, amount=amount, target=TargetAddress(target), secret=make_secret(), route=route, ) with patch("raiden.raiden_event_handler.post_pfs_feedback" ) as pfs_feedback_handler: pfs_handler.on_raiden_events( raiden=raiden, chain_state=cast( ChainState, raiden.wal.state_manager.current_state), # type: ignore events=[route_failed_event], ) assert not pfs_feedback_handler.called
def test_channel_withdraw( raiden_network: List[App], token_addresses: List[TokenAddress], deposit: TokenAmount, retry_timeout: float, ) -> None: """ Withdraw funds after a mediated transfer.""" alice_app, bob_app = raiden_network token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(alice_app), alice_app.raiden.default_registry.address, token_address ) assert token_network_address token_proxy = bob_app.raiden.proxy_manager.token(token_address, BLOCK_ID_LATEST) bob_initial_balance = token_proxy.balance_of(bob_app.raiden.address) message_handler = WaitForMessage() bob_app.raiden.message_handler = message_handler alice_to_bob_amount = PaymentAmount(10) identifier = PaymentID(1) target = TargetAddress(bob_app.raiden.address) secret = factories.make_secret() payment_status = alice_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=alice_to_bob_amount, target=target, identifier=identifier, secret=secret, ) wait_for_unlock = bob_app.raiden.message_handler.wait_for_message( Unlock, {"payment_identifier": identifier} ) with block_offset_timeout(alice_app.raiden): wait_for_unlock.get() msg = ( f"transfer from {to_checksum_address(alice_app.raiden.address)} " f"to {to_checksum_address(bob_app.raiden.address)} failed." ) assert payment_status.payment_done.get(), msg total_withdraw = WithdrawAmount(deposit + alice_to_bob_amount) bob_alice_channel_state = get_channelstate(bob_app, alice_app, token_network_address) bob_app.raiden.withdraw( canonical_identifier=bob_alice_channel_state.canonical_identifier, total_withdraw=total_withdraw, ) waiting.wait_for_withdraw_complete( raiden=bob_app.raiden, canonical_identifier=bob_alice_channel_state.canonical_identifier, total_withdraw=total_withdraw, retry_timeout=retry_timeout, ) bob_balance_after_withdraw = token_proxy.balance_of(bob_app.raiden.address) assert bob_initial_balance + total_withdraw == bob_balance_after_withdraw
def test_close_regression(raiden_network, deposit, token_addresses): """ The python api was using the wrong balance proof to close the channel, thus the close was failing if a transfer was made. """ app0, app1 = raiden_network token_address = token_addresses[0] api1 = RaidenAPI(app0.raiden) api2 = RaidenAPI(app1.raiden) registry_address = app0.raiden.default_registry.address channel_list = api1.get_channel_list(registry_address, token_address, app1.raiden.address) channel12 = channel_list[0] token_proxy = app0.raiden.proxy_manager.token(token_address, BLOCK_ID_LATEST) node1_balance_before = token_proxy.balance_of(api1.address) node2_balance_before = token_proxy.balance_of(api2.address) # Initialize app2 balance proof and close the channel amount = PaymentAmount(10) identifier = PaymentID(42) secret, secrethash = factories.make_secret_with_hash() timeout = block_offset_timeout(app1.raiden, "Transfer timed out.") with watch_for_unlock_failures(*raiden_network), timeout: assert api1.transfer_and_wait( registry_address=registry_address, token_address=token_address, amount=amount, target=TargetAddress(api2.address), identifier=identifier, secret=secret, ) timeout.exception_to_throw = ValueError( "Waiting for transfer received success in the WAL timed out.") result = waiting.wait_for_received_transfer_result( raiden=app1.raiden, payment_identifier=identifier, amount=amount, retry_timeout=app1.raiden.alarm.sleep_time, secrethash=secrethash, ) msg = f"Unexpected transfer result: {str(result)}" assert result == waiting.TransferWaitResult.UNLOCKED, msg api2.channel_close(registry_address, token_address, api1.address) waiting.wait_for_settle( app0.raiden, app0.raiden.default_registry.address, token_address, [channel12.identifier], app0.raiden.alarm.sleep_time, ) node1_expected_balance = node1_balance_before + deposit - amount node2_expected_balance = node2_balance_before + deposit + amount assert token_proxy.balance_of(api1.address) == node1_expected_balance assert token_proxy.balance_of(api2.address) == node2_expected_balance
def test_regression_revealsecret_after_secret( raiden_network: List[App], token_addresses: List[TokenAddress]) -> None: """ A RevealSecret message received after a Unlock message must be cleanly handled. """ app0, app1, app2 = raiden_network token = token_addresses[0] identifier = PaymentID(1) 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) assert token_network_address, "The fixtures must register the token" payment_status = app0.raiden.mediated_transfer_async( token_network_address, amount=PaymentAmount(1), target=TargetAddress(app2.raiden.address), identifier=identifier, ) with watch_for_unlock_failures(*raiden_network): assert payment_status.payment_done.wait() assert app1.raiden.wal, "The fixtures must start the app." event = raiden_events_search_for_item(app1.raiden, SendSecretReveal, {}) assert event reveal_secret = RevealSecret( message_identifier=make_message_identifier(), secret=event.secret, signature=EMPTY_SIGNATURE, ) app2.raiden.sign(reveal_secret) app1.raiden.on_messages([reveal_secret])
def event_filter_for_payments( event: Event, chain_state: Optional[ChainState] = None, partner_address: Optional[Address] = None, token_address: Optional[TokenAddress] = None, ) -> bool: """Filters payment history related events depending on given arguments - If no other args are given, all payment related events match. - If a token network identifier is given then only payment events for that match. - If a partner is also given then if the event is a payment sent event and the target matches it's returned. If it's a payment received and the initiator matches. then it's returned. - If a token address is given then all events are filtered to be about that token. """ sent_and_target_matches = isinstance( event, (EventPaymentSentFailed, EventPaymentSentSuccess) ) and (partner_address is None or event.target == TargetAddress(partner_address)) received_and_initiator_matches = isinstance(event, EventPaymentReceivedSuccess) and ( partner_address is None or event.initiator == InitiatorAddress(partner_address) ) token_address_matches = True if token_address: assert chain_state, "Filtering for token_address without a chain state is an error" token_network = get_token_network_by_address( chain_state=chain_state, token_network_address=event.token_network_address, # type: ignore ) if not token_network: token_address_matches = False else: token_address_matches = token_address == token_network.token_address return token_address_matches and (sent_and_target_matches or received_and_initiator_matches)
def test_mediated_transfer_calls_pfs(raiden_network, token_addresses): app0, = 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) with patch("raiden.routing.query_paths", return_value=([], None)) as patched: app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=10, target=factories.HOP1, identifier=1, secret=b"1" * 32, ) assert not patched.called # Setup PFS config app0.raiden.config["pfs_config"] = PFSConfig( info=PFSInfo( url="mock-address", chain_id=app0.raiden.rpc_client.chain_id, token_network_registry_address=token_network_registry_address, payment_address=factories.make_address(), message="", operator="", version="", price=TokenAmount(0), ), maximum_fee=TokenAmount(100), iou_timeout=BlockNumber(100), max_paths=5, ) app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=11, target=factories.HOP2, identifier=2, secret=b"2" * 32, ) assert patched.call_count == 1 # Mediator should not re-query PFS locked_transfer = factories.create( factories.LockedTransferProperties( amount=TokenAmount(5), initiator=factories.HOP1, target=TargetAddress(factories.HOP2), sender=factories.HOP1, pkey=factories.HOP1_KEY, token=token_address, canonical_identifier=factories.make_canonical_identifier( token_network_address=token_network_address), )) app0.raiden.mediate_mediated_transfer(locked_transfer) assert patched.call_count == 1
def test_automatic_secret_registration( raiden_chain: List[App], token_addresses: List[TokenAddress] ) -> None: app0, app1 = raiden_chain 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 hold_event_handler = app1.raiden.raiden_event_handler message_handler = app1.raiden.message_handler msg = "hold event handler necessary to control messages" assert isinstance(hold_event_handler, HoldRaidenEventHandler), msg assert isinstance(message_handler, WaitForMessage), msg amount = PaymentAmount(100) identifier = factories.make_payment_id() target = TargetAddress(app1.raiden.address) (secret, secrethash) = factories.make_secret_with_hash() hold_event_handler.hold_secretrequest_for(secrethash=secrethash) locked_transfer_received = message_handler.wait_for_message(LockedTransfer, {}) app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=amount, target=target, identifier=identifier, secret=secret, ) # Wait for app1 to receive the locked transfer. locked_transfer_received.wait() # Stop app0 to avoid sending the unlock, this must be done after the locked # transfer is sent. app0.raiden.transport.stop() reveal_secret = RevealSecret( message_identifier=MessageID(random.randint(0, UINT64_MAX)), secret=secret, signature=EMPTY_SIGNATURE, ) app0.raiden.sign(reveal_secret) message_handler.on_messages(app1.raiden, [reveal_secret]) chain_state = views.state_from_app(app1) secrethash = sha256_secrethash(secret) target_task = chain_state.payment_mapping.secrethashes_to_task[secrethash] lock_expiration = target_task.target_state.transfer.lock.expiration # type: ignore app1.raiden.proxy_manager.client.wait_until_block(target_block_number=lock_expiration) assert app1.raiden.default_secret_registry.is_secret_registered( secrethash=secrethash, block_identifier=BLOCK_ID_LATEST )
def handle_message_lockedtransfer( raiden: "RaidenService", message: LockedTransfer # pylint: disable=unused-argument ) -> List[StateChange]: secrethash = message.lock.secrethash # We must check if the secret was registered against the latest block, # even if the block is forked away and the transaction that registers # the secret is removed from the blockchain. The rationale here is that # someone else does know the secret, regardless of the chain state, so # the node must not use it to start a payment. # # For this particular case, it's preferable to use `latest` instead of # having a specific block_hash, because it's preferable to know if the secret # was ever known, rather than having a consistent view of the blockchain. registered = raiden.default_secret_registry.is_secret_registered( secrethash=secrethash, block_identifier=BLOCK_ID_LATEST ) if registered: log.warning( f"Ignoring received locked transfer with secrethash {to_hex(secrethash)} " f"since it is already registered in the secret registry" ) return [] assert message.sender, "Invalid message dispatched, it should be signed" from_transfer = lockedtransfersigned_from_message(message) from_hop = HopState( node_address=message.sender, # pylint: disable=E1101 channel_identifier=from_transfer.balance_proof.channel_identifier, ) if message.target == TargetAddress(raiden.address): raiden.immediate_health_check_for(Address(message.initiator)) return [ ActionInitTarget( from_hop=from_hop, transfer=from_transfer, balance_proof=from_transfer.balance_proof, sender=from_transfer.balance_proof.sender, ) ] else: route_states = routing.resolve_routes( routes=message.metadata.routes, token_network_address=from_transfer.balance_proof.token_network_address, chain_state=views.state_from_raiden(raiden), ) return [ ActionInitMediator( from_hop=from_hop, route_states=route_states, from_transfer=from_transfer, balance_proof=from_transfer.balance_proof, sender=from_transfer.balance_proof.sender, ) ]
def test_pfs_handler_handle_paymentsentsuccess_with_feedback_token(): ( raiden, pfs_handler, token_network_registry_address, token_network_address, route, feedback_uuid, ) = setup_pfs_handler_test(set_feedback_token=True) payment_id = make_payment_id() amount = PaymentAmount(123) target = TargetAddress(route[-1]) raiden.targets_to_identifiers_to_statuses[target][payment_id] = Mock() route_failed_event = EventPaymentSentSuccess( token_network_registry_address=token_network_registry_address, token_network_address=token_network_address, identifier=payment_id, amount=amount, target=TargetAddress(target), secret=make_secret(), route=route, ) with patch("raiden.raiden_event_handler.post_pfs_feedback" ) as pfs_feedback_handler: pfs_handler.on_raiden_event( raiden=raiden, chain_state=cast( ChainState, raiden.wal.state_manager.current_state), # type: ignore event=route_failed_event, ) assert pfs_feedback_handler.called assert pfs_feedback_handler.call_args == call( pfs_config=raiden.config.pfs_config, route=route, routing_mode=RoutingMode.PRIVATE, successful=True, token=feedback_uuid, token_network_address=token_network_address, )
def test_insufficient_funds(raiden_network: List[RaidenService], token_addresses, deposit): app0, app1 = raiden_network token_address = token_addresses[0] result = RaidenAPI(app0).transfer_and_wait( app0.default_registry.address, token_address, deposit + 1, target=TargetAddress(app1.address), ) assert isinstance(result.payment_done.get(), EventPaymentSentFailed)
def test_same_addresses_for_payment(raiden_network: List[RaidenService], token_addresses): app0, _ = raiden_network api0 = RaidenAPI(app0) registry_address = app0.default_registry.address token_address = token_addresses[0] with pytest.raises(SamePeerAddress): api0.transfer_and_wait( registry_address=registry_address, token_address=token_address, target=TargetAddress(app0.address), amount=PaymentAmount(1), )
def _transfer_expired( initiator_app: App, target_app: App, token_address: TokenAddress, amount: PaymentAmount, identifier: PaymentID, timeout: Optional[float] = None, ) -> SecretHash: assert identifier is not None, "The identifier must be provided" assert isinstance(target_app.raiden.message_handler, WaitForMessage) # This timeout has to be larger then the lock expiration. The lock # expiration unit is block numbers, and its value is defined relative to # the node's reveal timeout configuration. For the integration tests the # reveal timeout is chosen proportionally to the number of nodes, 90 # seconds is a rough default that should work with the standard # configuration. if timeout is None: timeout = 90 secret, secrethash = make_secret_with_hash() wait_for_remove_expired_lock = target_app.raiden.message_handler.wait_for_message( LockExpired, {"secrethash": secrethash} ) token_network_registry_address = initiator_app.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state=views.state_from_app(initiator_app), token_network_registry_address=token_network_registry_address, token_address=token_address, ) assert token_network_address payment_status = initiator_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=amount, target=TargetAddress(target_app.raiden.address), identifier=identifier, secret=secret, secrethash=secrethash, ) with Timeout(seconds=timeout): wait_for_remove_expired_lock.get() msg = ( f"transfer from {to_checksum_address(initiator_app.raiden.address)} " f"to {to_checksum_address(target_app.raiden.address)} did not expire." ) assert payment_status.payment_done.get() is False, msg return secrethash
def test_received_lockedtransfer_closedchannel( raiden_network: List[RaidenService], reveal_timeout, token_addresses, deposit ): app0, app1 = raiden_network registry_address = app0.default_registry.address token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_raiden(app0), app0.default_registry.address, token_address ) assert token_network_address channel0 = get_channelstate(app0, app1, token_network_address) RaidenAPI(app1).channel_close(registry_address, token_address, app0.address) app0.rpc_client.wait_until_block( target_block_number=BlockNumber(app0.rpc_client.block_number() + 1) ) # Now receive one mediated transfer for the closed channel lock_amount = LockedAmount(10) payment_identifier = PaymentID(1) expiration = reveal_timeout * 2 mediated_transfer_message = LockedTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=make_message_identifier(), payment_identifier=payment_identifier, nonce=Nonce(1), token_network_address=token_network_address, token=token_address, channel_identifier=channel0.identifier, transferred_amount=TokenAmount(0), locked_amount=lock_amount, recipient=app1.address, locksroot=make_locksroot(), lock=Lock( amount=PaymentWithFeeAmount(lock_amount), expiration=expiration, secrethash=UNIT_SECRETHASH, ), target=TargetAddress(app1.address), initiator=InitiatorAddress(app0.address), signature=EMPTY_SIGNATURE, metadata=Metadata(routes=[RouteMetadata(route=[app1.address])]), ) sign_and_inject(mediated_transfer_message, app0.signer, app1) # The local state must not change since the channel is already closed assert_synced_channel_state(token_network_address, app0, deposit, [], app1, deposit, [])
def _transfer_unlocked( initiator_app: RaidenService, target_app: RaidenService, token_address: TokenAddress, amount: PaymentAmount, identifier: PaymentID, timeout: Optional[float] = None, expect_unlock_failures: bool = False, route_states: List[RouteState] = None, ) -> SecretHash: assert isinstance(target_app.message_handler, WaitForMessage) if timeout is None: timeout = 10 wait_for_unlock = target_app.message_handler.wait_for_message( Unlock, {"payment_identifier": identifier}) token_network_registry_address = initiator_app.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state=views.state_from_raiden(initiator_app), token_network_registry_address=token_network_registry_address, token_address=token_address, ) assert token_network_address secret, secrethash = make_secret_with_hash() payment_status = initiator_app.mediated_transfer_async( token_network_address=token_network_address, amount=amount, target=TargetAddress(target_app.address), identifier=identifier, secret=secret, secrethash=secrethash, route_states=route_states, ) apps = [initiator_app, target_app] with watch_for_unlock_failures( *apps) if not expect_unlock_failures else nullcontext(): with Timeout(seconds=timeout): wait_for_unlock.get() msg = ( f"transfer from {to_checksum_address(initiator_app.address)} " f"to {to_checksum_address(target_app.address)} failed.") assert payment_status.payment_done.get(), msg return secrethash
def test_event_filter_for_payments(): secret = factories.make_secret() identifier = PaymentID(1) target = TargetAddress(factories.make_address()) event1 = EventPaymentSentSuccess( token_network_registry_address=UNIT_TOKEN_NETWORK_REGISTRY_ADDRESS, token_network_address=UNIT_TOKEN_NETWORK_ADDRESS, identifier=identifier, amount=PaymentAmount(5), target=target, secret=secret, route=[], ) assert event_filter_for_payments(event=event1, partner_address=None) assert event_filter_for_payments(event=event1, partner_address=Address(target)) assert not event_filter_for_payments( event=event1, partner_address=factories.make_address()) initiator = InitiatorAddress(factories.make_address()) event2 = EventPaymentReceivedSuccess( token_network_registry_address=UNIT_TOKEN_NETWORK_REGISTRY_ADDRESS, token_network_address=UNIT_TOKEN_NETWORK_ADDRESS, identifier=identifier, amount=TokenAmount(5), initiator=initiator, ) assert event_filter_for_payments(event=event2, partner_address=None) assert event_filter_for_payments(event=event2, partner_address=Address(initiator)) assert not event_filter_for_payments( event=event2, partner_address=factories.make_address()) event3 = EventPaymentSentFailed( token_network_registry_address=UNIT_TOKEN_NETWORK_REGISTRY_ADDRESS, token_network_address=UNIT_TOKEN_NETWORK_ADDRESS, identifier=identifier, target=target, reason="whatever", ) assert event_filter_for_payments(event=event3, partner_address=None) assert event_filter_for_payments(event=event3, partner_address=Address(target)) assert not event_filter_for_payments( event=event3, partner_address=factories.make_address())
def test_failsfast_lockedtransfer_exceeding_distributable( raiden_network: List[RaidenService], token_addresses, deposit ): app0, app1 = raiden_network 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 payment_status = app0.mediated_transfer_async( token_network_address, deposit * 2, TargetAddress(app1.address), identifier=PaymentID(1) ) assert isinstance(payment_status.payment_done.get(timeout=5), EventPaymentSentFailed) assert payment_status.payment_done.successful() assert_synced_channel_state(token_network_address, app0, deposit, [], app1, deposit, [])
def test_failfast_lockedtransfer_nochannel(raiden_network: List[RaidenService], token_addresses): """When the node has no channels it should fail without raising exceptions.""" token_address = token_addresses[0] app0, app1 = raiden_network amount = 10 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 must be registered by the fixtures." payment_status = app0.mediated_transfer_async( token_network_address, PaymentAmount(amount), TargetAddress(app1.address), identifier=PaymentID(1), ) assert isinstance(payment_status.payment_done.get(), EventPaymentSentFailed)
def test_receive_lockedtransfer_invalidrecipient( raiden_network: List[RaidenService], token_addresses, reveal_timeout, deposit ): app0, app1 = raiden_network token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_raiden(app0), app0.default_registry.address, token_address ) assert token_network_address channel0 = get_channelstate(app0, app1, token_network_address) payment_identifier = PaymentID(1) invalid_recipient = make_address() lock_amount = LockedAmount(10) expiration = reveal_timeout * 2 mediated_transfer_message = LockedTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=make_message_identifier(), payment_identifier=payment_identifier, nonce=Nonce(1), token_network_address=token_network_address, token=token_address, channel_identifier=channel0.identifier, transferred_amount=TokenAmount(0), locked_amount=lock_amount, recipient=invalid_recipient, locksroot=make_locksroot(), lock=Lock( amount=PaymentWithFeeAmount(lock_amount), expiration=expiration, secrethash=UNIT_SECRETHASH, ), target=TargetAddress(app1.address), initiator=InitiatorAddress(app0.address), signature=EMPTY_SIGNATURE, metadata=Metadata(routes=[RouteMetadata(route=[app1.address])]), ) sign_and_inject(mediated_transfer_message, app0.signer, app1) assert_synced_channel_state(token_network_address, app0, deposit, [], app1, deposit, [])
def test_v1_event_payment_sent_failed_schema(): event = EventPaymentSentFailed( token_network_registry_address=UNIT_TOKEN_NETWORK_REGISTRY_ADDRESS, token_network_address=UNIT_TOKEN_NETWORK_ADDRESS, identifier=PaymentID(1), target=TargetAddress(factories.make_address()), reason="whatever", ) log_time = datetime.datetime.now() timestamped = TimestampedEvent(event, log_time) dumped = EventPaymentSentFailedSchema().dump(timestamped) expected = { "event": "EventPaymentSentFailed", "log_time": log_time.isoformat(), "reason": "whatever", } assert all(dumped.get(key) == value for key, value in expected.items())
def _transfer_secret_not_requested( initiator_app: App, target_app: App, token_address: TokenAddress, amount: PaymentAmount, identifier: PaymentID, timeout: Optional[float] = None, ) -> SecretHash: if timeout is None: timeout = 10 secret, secrethash = make_secret_with_hash() assert isinstance(target_app.raiden.raiden_event_handler, HoldRaidenEventHandler) hold_secret_request = target_app.raiden.raiden_event_handler.hold( SendSecretRequest, {"secrethash": secrethash}) token_network_registry_address = initiator_app.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state=views.state_from_app(initiator_app), token_network_registry_address=token_network_registry_address, token_address=token_address, ) assert token_network_address initiator_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=amount, target=TargetAddress(target_app.raiden.address), identifier=identifier, secret=secret, secrethash=secrethash, ) with Timeout(seconds=timeout): hold_secret_request.get() return secrethash
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 test_pending_transfers_endpoint(raiden_network: List[RaidenService], token_addresses): initiator, mediator, target = raiden_network token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_raiden(mediator), mediator.default_registry.address, token_address) assert token_network_address amount_to_send = PaymentAmount(150) # Remove when https://github.com/raiden-network/raiden/issues/4982 is tackled expected_fee = FeeAmount( int(amount_to_send * INTERNAL_ROUTING_DEFAULT_FEE_PERC)) fee_margin = calculate_fee_margin(amount_to_send, expected_fee) # This is 0,4% of ~150, so ~1.2 which gets rounded to 1 actual_fee = 1 identifier = PaymentID(42) initiator_server = prepare_api_server(initiator) mediator_server = prepare_api_server(mediator) target_server = prepare_api_server(target) target.message_handler = target_wait = WaitForMessage() mediator.message_handler = mediator_wait = WaitForMessage() secret = factories.make_secret() secrethash = sha256_secrethash(secret) request = grequests.get( api_url_for(mediator_server, "pending_transfers_resource_by_token", token_address=token_address)) response = request.send().response assert response.status_code == 200 and response.content == b"[]" target_hold = target.raiden_event_handler assert isinstance( target_hold, HoldRaidenEventHandler), "test app must use HoldRaidenEventHandler" target_hold.hold_secretrequest_for(secrethash=secrethash) initiator.mediated_transfer_async( token_network_address=token_network_address, amount=PaymentAmount(amount_to_send - expected_fee - fee_margin), target=TargetAddress(target.address), identifier=identifier, secret=secret, route_states=[ create_route_state_for_route( apps=raiden_network, token_address=token_address, fee_estimate=expected_fee, ) ], ) transfer_arrived = target_wait.wait_for_message(LockedTransfer, {"payment_identifier": 42}) transfer_arrived.wait(timeout=30.0) for server in (initiator_server, mediator_server, target_server): request = grequests.get( api_url_for(server, "pending_transfers_resource")) response = request.send().response assert response.status_code == 200 content = json.loads(response.content) assert len(content) == 1 assert content[0]["payment_identifier"] == str(identifier) if server == target_server: assert content[0]["locked_amount"] == str(amount_to_send - actual_fee) else: assert content[0]["locked_amount"] == str(amount_to_send) assert content[0]["token_address"] == to_checksum_address( token_address) assert content[0]["token_network_address"] == to_checksum_address( token_network_address) mediator_unlock = mediator_wait.wait_for_message(Unlock, {}) target_unlock = target_wait.wait_for_message(Unlock, {}) target_hold.release_secretrequest_for(target, secrethash) gevent.joinall({mediator_unlock, target_unlock}, raise_error=True) for server in (initiator_server, mediator_server, target_server): request = grequests.get( api_url_for(server, "pending_transfers_resource")) response = request.send().response assert response.status_code == 200 and response.content == b"[]" request = grequests.get( api_url_for( initiator_server, "pending_transfers_resource_by_token", token_address=to_hex(b"notaregisteredtokenn"), )) response = request.send().response assert response.status_code == 404 and b"Token" in response.content request = grequests.get( api_url_for( target_server, "pending_transfers_resource_by_token_and_partner", token_address=token_address, partner_address=to_hex(b"~nonexistingchannel~"), )) response = request.send().response assert response.status_code == 404 and b"Channel" in response.content
def test_payment_statuses_are_restored( # pylint: disable=unused-argument raiden_network: List[App], token_addresses: List[TokenAddress], network_wait: float): """ Test that when the Raiden is restarted, the dictionary of `targets_to_identifiers_to_statuses` is populated before the transport is started. This should happen because if a client gets restarted during a transfer cycle, once restarted, the client will proceed with the cycle until the transfer is successfully sent. However, the dictionary `targets_to_identifiers_to_statuses` will not contain the payment identifiers that were originally registered when the previous client started the transfers. Related issue: https://github.com/raiden-network/raiden/issues/3432 """ 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) assert token_network_address target_address = TargetAddress(app1.raiden.address) # make a few transfers from app0 to app1 amount = PaymentAmount(1) spent_amount = TokenAmount(7) for identifier in range(spent_amount): # Make sure the transfer is not completed secret = make_secret(identifier) assert isinstance(app0.raiden.raiden_event_handler, HoldRaidenEventHandler) # for mypy app0.raiden.raiden_event_handler.hold(SendSecretReveal, {"secret": secret}) identifier = identifier + 1 payment_status = app0.raiden.mediated_transfer_async( token_network_address=token_network_address, amount=amount, target=target_address, identifier=PaymentID(identifier), secret=secret, ) assert payment_status.payment_identifier == identifier app0_restart = App( config=app0.config, rpc_client=app0.raiden.rpc_client, proxy_manager=app0.raiden.proxy_manager, query_start_block=BlockNumber(0), default_registry=app0.raiden.default_registry, default_secret_registry=app0.raiden.default_secret_registry, default_service_registry=app0.raiden.default_service_registry, default_one_to_n_address=app0.raiden.default_one_to_n_address, default_msc_address=app0.raiden.default_msc_address, transport=MatrixTransport( config=app0.raiden.config.transport, environment=app0.raiden.config.environment_type), raiden_event_handler=RaidenEventHandler(), message_handler=MessageHandler(), routing_mode=RoutingMode.PRIVATE, ) app0.stop() del app0 # from here on the app0_restart should be used # stop app1 to make sure that we don't complete the transfers before our checks app1.stop() app0_restart.start() # Check that the payment statuses were restored properly after restart for identifier in range(spent_amount): identifier = PaymentID(identifier + 1) mapping = app0_restart.raiden.targets_to_identifiers_to_statuses status = mapping[target_address][identifier] assert status.amount == 1 assert status.payment_identifier == identifier assert status.token_network_address == token_network_address app1.start() # now that our checks are done start app1 again with watch_for_unlock_failures(*raiden_network): waiting.wait_for_healthy(app0_restart.raiden, app1.raiden.address, network_wait) waiting.wait_for_payment_balance( raiden=app1.raiden, token_network_registry_address=token_network_registry_address, token_address=token_address, partner_address=app0_restart.raiden.address, target_address=Address(target_address), target_balance=spent_amount, retry_timeout=network_wait, ) # Check that payments are completed after both nodes come online after restart for identifier in range(spent_amount): assert raiden_events_search_for_item( app0_restart.raiden, EventPaymentSentSuccess, { "identifier": identifier + 1, "amount": 1 }, )
def test_regression_multiple_revealsecret( raiden_network: List[App], token_addresses: List[TokenAddress]) -> None: """ Multiple RevealSecret messages arriving at the same time must be handled properly. Unlock handling followed these steps: The Unlock message arrives The secret is registered The channel is updated and the correspoding lock is removed * A balance proof for the new channel state is created and sent to the payer The channel is unregistered for the given secrethash The step marked with an asterisk above introduced a context-switch. This allowed a second Reveal Unlock message to be handled before the channel was unregistered. And because the channel was already updated an exception was raised for an unknown secret. """ app0, app1 = raiden_network token = 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) assert token_network_address channelstate_0_1 = get_channelstate(app0, app1, token_network_address) payment_identifier = PaymentID(1) secret, secrethash = make_secret_with_hash() expiration = BlockExpiration(app0.raiden.get_block_number() + 100) lock_amount = PaymentWithFeeAmount(10) lock = Lock(amount=lock_amount, expiration=expiration, secrethash=secrethash) nonce = Nonce(1) transferred_amount = TokenAmount(0) mediated_transfer = LockedTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=make_message_identifier(), payment_identifier=payment_identifier, nonce=nonce, token_network_address=token_network_address, token=token, channel_identifier=channelstate_0_1.identifier, transferred_amount=transferred_amount, locked_amount=LockedAmount(lock_amount), recipient=app1.raiden.address, locksroot=Locksroot(lock.lockhash), lock=lock, target=TargetAddress(app1.raiden.address), initiator=InitiatorAddress(app0.raiden.address), signature=EMPTY_SIGNATURE, metadata=Metadata(routes=[ RouteMetadata(route=[app0.raiden.address, app1.raiden.address]) ]), ) app0.raiden.sign(mediated_transfer) app1.raiden.on_messages([mediated_transfer]) reveal_secret = RevealSecret(message_identifier=make_message_identifier(), secret=secret, signature=EMPTY_SIGNATURE) app0.raiden.sign(reveal_secret) token_network_address = channelstate_0_1.token_network_address unlock = Unlock( chain_id=UNIT_CHAIN_ID, message_identifier=make_message_identifier(), payment_identifier=payment_identifier, nonce=Nonce(mediated_transfer.nonce + 1), token_network_address=token_network_address, channel_identifier=channelstate_0_1.identifier, transferred_amount=TokenAmount(lock_amount), locked_amount=LockedAmount(0), locksroot=LOCKSROOT_OF_NO_LOCKS, secret=secret, signature=EMPTY_SIGNATURE, ) app0.raiden.sign(unlock) messages = [unlock, reveal_secret] receive_method = app1.raiden.on_messages wait = set( gevent.spawn_later(0.1, receive_method, [data]) for data in messages) gevent.joinall(wait, raise_error=True)
def test_receive_secrethashtransfer_unknown( raiden_network: List[RaidenService], token_addresses): app0 = raiden_network[0] token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_raiden(app0), app0.default_registry.address, token_address) assert token_network_address other_key = HOP1_KEY other_signer = LocalSigner(other_key) canonical_identifier = factories.make_canonical_identifier( token_network_address=token_network_address) amount = TokenAmount(10) locksroot = Locksroot(make_32bytes()) refund_transfer_message = factories.create( factories.RefundTransferProperties( payment_identifier=PaymentID(1), nonce=Nonce(1), token=token_address, canonical_identifier=canonical_identifier, transferred_amount=amount, recipient=TargetAddress(app0.address), locksroot=locksroot, amount=amount, secret=UNIT_SECRET, )) sign_and_inject(refund_transfer_message, other_signer, app0) unlock = Unlock( chain_id=UNIT_CHAIN_ID, message_identifier=make_message_identifier(), payment_identifier=PaymentID(1), nonce=Nonce(1), channel_identifier=canonical_identifier.channel_identifier, token_network_address=token_network_address, transferred_amount=amount, locked_amount=LockedAmount(0), locksroot=locksroot, secret=UNIT_SECRET, signature=EMPTY_SIGNATURE, ) sign_and_inject(unlock, other_signer, app0) secret_request_message = SecretRequest( message_identifier=make_message_identifier(), payment_identifier=PaymentID(1), secrethash=UNIT_SECRETHASH, amount=PaymentAmount(1), expiration=refund_transfer_message.lock.expiration, signature=EMPTY_SIGNATURE, ) sign_and_inject(secret_request_message, other_signer, app0) reveal_secret_message = RevealSecret( message_identifier=make_message_identifier(), secret=UNIT_SECRET, signature=EMPTY_SIGNATURE) sign_and_inject(reveal_secret_message, other_signer, app0)
def test_clear_closed_queue(raiden_network: List[App], token_addresses, network_wait): """ Closing a channel clears the respective message queue. """ app0, app1 = raiden_network hold_event_handler = app1.raiden.raiden_event_handler assert isinstance(hold_event_handler, HoldRaidenEventHandler) registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] chain_state0 = views.state_from_app(app0) token_network_address = views.get_token_network_address_by_token_address( chain_state0, app0.raiden.default_registry.address, token_address) assert token_network_address token_network = views.get_token_network_by_address(chain_state0, token_network_address) assert token_network channel_identifier = get_channelstate(app0, app1, token_network_address).identifier assert (channel_identifier in token_network.partneraddresses_to_channelidentifiers[ app1.raiden.address]) target = app1.raiden.address secret = Secret(keccak(target)) secrethash = sha256_secrethash(secret) hold_event_handler.hold_secretrequest_for(secrethash=secrethash) # make an unconfirmed transfer to ensure the nodes have communicated amount = PaymentAmount(10) payment_identifier = PaymentID(1337) app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=amount, target=TargetAddress(target), identifier=payment_identifier, secret=secret, ) app1.raiden.transport.stop() app1.raiden.transport.greenlet.get() # make sure to wait until the queue is created def has_initiator_events(): assert app0.raiden.wal, "raiden server must have been started" initiator_events = app0.raiden.wal.storage.get_events() return search_for_item(initiator_events, SendLockedTransfer, {}) assert wait_until(has_initiator_events, network_wait) # assert the specific queue is present chain_state0 = views.state_from_app(app0) queues0 = views.get_all_messagequeues(chain_state=chain_state0) assert [ (queue_id, queue) for queue_id, queue in queues0.items() if queue_id.recipient == app1.raiden.address and queue_id. canonical_identifier.channel_identifier == channel_identifier and queue ] # A ChannelClose event will be generated, this will be polled by both apps RaidenAPI(app0.raiden).channel_close(registry_address, token_address, app1.raiden.address) with block_offset_timeout(app0.raiden, "Could not get close event"): waiting.wait_for_close( app0.raiden, registry_address, token_address, [channel_identifier], app0.raiden.alarm.sleep_time, ) # assert all queues with this partner are gone or empty chain_state0 = views.state_from_app(app0) queues0 = views.get_all_messagequeues(chain_state=chain_state0) assert not [(queue_id, queue) for queue_id, queue in queues0.items() if queue_id.recipient == app1.raiden.address and queue] chain_state1 = views.state_from_app(app1) queues1 = views.get_all_messagequeues(chain_state=chain_state1) assert not [(queue_id, queue) for queue_id, queue in queues1.items() if queue_id.recipient == app0.raiden.address and queue]
def test_mediated_transfer_calls_pfs(raiden_chain: List[App], token_addresses: List[TokenAddress]): app0, app1, app2 = raiden_chain 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 ) assert token_network_address, "Fixture token_addresses don't have correspoding token_network" with patch("raiden.routing.query_paths", return_value=([], None)) as patched: app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=PaymentAmount(10), target=TargetAddress(app1.raiden.address), identifier=PaymentID(1), secret=Secret(b"1" * 32), ) assert not patched.called # Setup PFS config app0.raiden.config.pfs_config = PFSConfig( info=PFSInfo( url="mock-address", chain_id=app0.raiden.rpc_client.chain_id, token_network_registry_address=token_network_registry_address, user_deposit_address=factories.make_address(), payment_address=factories.make_address(), confirmed_block_number=chain_state.block_number, message="", operator="", version="", price=TokenAmount(0), ), maximum_fee=TokenAmount(100), iou_timeout=BlockTimeout(100), max_paths=5, ) app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=PaymentAmount(11), target=TargetAddress(app2.raiden.address), identifier=PaymentID(2), secret=Secret(b"2" * 32), ) assert patched.call_count == 1 # Mediator should not re-query PFS locked_transfer = factories.create( factories.LockedTransferProperties( amount=TokenAmount(5), initiator=factories.HOP1, target=TargetAddress(app2.raiden.address), sender=factories.HOP1, pkey=factories.HOP1_KEY, token=token_address, canonical_identifier=factories.make_canonical_identifier( token_network_address=token_network_address ), ) ) app0.raiden.on_messages([locked_transfer]) assert patched.call_count == 1
def transfer_and_assert_path( path: List[App], token_address: TokenAddress, amount: PaymentAmount, identifier: PaymentID, timeout: float = 10, ) -> SecretHash: """ Nice to read shortcut to make successful LockedTransfer. Note: This utility *does not enforce the path*, however it does check the provided path is used in totality. It's the responsability of the caller to ensure the path will be used. All nodes in `path` are synched. """ assert identifier is not None, "The identifier must be provided" secret, secrethash = make_secret_with_hash() first_app = path[0] token_network_registry_address = first_app.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state=views.state_from_app(first_app), token_network_registry_address=token_network_registry_address, token_address=token_address, ) assert token_network_address for app in path: assert isinstance(app.raiden.message_handler, WaitForMessage) msg = "The apps must be on the same token network registry" assert app.raiden.default_registry.address == token_network_registry_address, msg app_token_network_address = views.get_token_network_address_by_token_address( chain_state=views.state_from_app(app), token_network_registry_address=token_network_registry_address, token_address=token_address, ) msg = "The apps must be synchronized with the blockchain" assert token_network_address == app_token_network_address, msg pairs = zip(path[:-1], path[1:]) receiving = list() for from_app, to_app in pairs: from_channel_state = views.get_channelstate_by_token_network_and_partner( chain_state=views.state_from_app(from_app), token_network_address=token_network_address, partner_address=to_app.raiden.address, ) to_channel_state = views.get_channelstate_by_token_network_and_partner( chain_state=views.state_from_app(to_app), token_network_address=token_network_address, partner_address=from_app.raiden.address, ) msg = ( f"{to_checksum_address(from_app.raiden.address)} does not have a channel with " f"{to_checksum_address(to_app.raiden.address)} needed to transfer through the " f"path {[to_checksum_address(app.raiden.address) for app in path]}." ) assert from_channel_state, msg assert to_channel_state, msg msg = ( f"channel among {to_checksum_address(from_app.raiden.address)} and " f"{to_checksum_address(to_app.raiden.address)} must be open to be used for a " f"transfer") assert channel.get_status( from_channel_state) == ChannelState.STATE_OPENED, msg assert channel.get_status( to_channel_state) == ChannelState.STATE_OPENED, msg receiving.append((to_app, to_channel_state.identifier)) assert isinstance(app.raiden.message_handler, WaitForMessage) results = [ app.raiden.message_handler.wait_for_message( Unlock, { "channel_identifier": channel_identifier, "token_network_address": token_network_address, "payment_identifier": identifier, "secret": secret, }, ) for app, channel_identifier in receiving ] last_app = path[-1] payment_status = first_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=amount, target=TargetAddress(last_app.raiden.address), identifier=identifier, secret=secret, ) msg = (f"transfer from {to_checksum_address(first_app.raiden.address)} " f"to {to_checksum_address(last_app.raiden.address)} for amount " f"{amount} failed") exception = RuntimeError(msg + " due to Timeout") with watch_for_unlock_failures(*path): with Timeout(seconds=timeout, exception=exception): gevent.wait(results) assert payment_status.payment_done.get(), msg return secrethash
def on_transfer(self, transfer: EventPaymentReceivedSuccess) -> None: """ This handles the echo logic, as described in https://github.com/raiden-network/raiden/issues/651: - for transfers with an amount that satisfies `amount % 3 == 0`, it sends a transfer with an amount of `amount - 1` back to the initiator - for transfers with a "lucky number" amount `amount == 7` it does not send anything back immediately -- after having received "lucky number transfers" from 7 different addresses it sends a transfer with `amount = 49` to one randomly chosen one (from the 7 lucky addresses) - consecutive entries to the lucky lottery will receive the current pool size as the `echo_amount` - for all other transfers it sends a transfer with the same `amount` back to the initiator """ echo_amount = PaymentAmount(0) if transfer.amount % 3 == 0: log.info( "Received amount divisible by three", node=to_checksum_address(self.api.address), initiator=to_checksum_address(transfer.initiator), amount=transfer.amount, identifier=transfer.identifier, ) echo_amount = PaymentAmount(transfer.amount - 1) elif transfer.amount == 7: log.info( "Received lottery entry", node=to_checksum_address(self.api.address), initiator=to_checksum_address(transfer.initiator), amount=transfer.amount, identifier=transfer.identifier, poolsize=self.lottery_pool.qsize(), ) # obtain a local copy of the pool pool = self.lottery_pool.copy() tickets = [pool.get() for _ in range(pool.qsize())] assert pool.empty() del pool if any(ticket.initiator == transfer.initiator for ticket in tickets): assert transfer not in tickets log.debug( "Duplicate lottery entry", node=to_checksum_address(self.api.address), initiator=to_checksum_address(transfer.initiator), identifier=transfer.identifier, poolsize=len(tickets), ) # signal the poolsize to the participant echo_amount = PaymentAmount(len(tickets)) # payout elif len(tickets) == 6: log.info("Payout!") # reset the pool assert self.lottery_pool.qsize() == 6 self.lottery_pool = Queue() # add the new participant tickets.append(transfer) # choose the winner transfer = random.choice(tickets) echo_amount = PaymentAmount(49) else: self.lottery_pool.put(transfer) else: log.debug( "Received transfer", node=to_checksum_address(self.api.address), initiator=to_checksum_address(transfer.initiator), amount=transfer.amount, identifier=transfer.identifier, ) echo_amount = PaymentAmount(transfer.amount) if echo_amount: echo_identifier = PaymentID(transfer.identifier + echo_amount) log.debug( "Sending echo transfer", node=to_checksum_address(self.api.address), target=to_checksum_address(transfer.initiator), amount=echo_amount, original_identifier=transfer.identifier, echo_identifier=echo_identifier, token_address=to_checksum_address(self.token_address), num_handled_transfers=self.num_handled_transfers + 1, ) self.api.transfer( registry_address=self.api.raiden.default_registry.address, token_address=self.token_address, amount=echo_amount, target=TargetAddress(transfer.initiator), identifier=echo_identifier, ) self.num_handled_transfers += 1
def test_send_queued_messages_after_restart( # pylint: disable=unused-argument raiden_network: List[App], deposit: TokenAmount, token_addresses: List[TokenAddress], network_wait: float, ): """Test re-sending of undelivered messages on node restart""" 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) assert token_network_address number_of_transfers = 7 amount_per_transfer = PaymentAmount(1) total_transferred_amount = TokenAmount(amount_per_transfer * number_of_transfers) # Make sure none of the transfers will be sent before the restart transfers = [] for secret_seed in range(number_of_transfers): secret = make_secret(secret_seed) secrethash = sha256_secrethash(secret) transfers.append((create_default_identifier(), amount_per_transfer, secret, secrethash)) assert isinstance(app0.raiden.raiden_event_handler, HoldRaidenEventHandler) # for mypy app0.raiden.raiden_event_handler.hold( SendLockedTransfer, {"transfer": { "lock": { "secrethash": secrethash } }}) for identifier, amount, secret, _ in transfers: app0.raiden.mediated_transfer_async( token_network_address=token_network_address, amount=amount, target=TargetAddress(app1.raiden.address), identifier=identifier, secret=secret, ) app0.stop() # Restart the app. The pending transfers must be processed. new_transport = MatrixTransport( config=app0.raiden.config.transport, environment=app0.raiden.config.environment_type) raiden_event_handler = RaidenEventHandler() message_handler = MessageHandler() app0_restart = App( config=app0.config, rpc_client=app0.raiden.rpc_client, proxy_manager=app0.raiden.proxy_manager, query_start_block=BlockNumber(0), default_registry=app0.raiden.default_registry, default_secret_registry=app0.raiden.default_secret_registry, default_service_registry=app0.raiden.default_service_registry, default_one_to_n_address=app0.raiden.default_one_to_n_address, default_msc_address=app0.raiden.default_msc_address, transport=new_transport, raiden_event_handler=raiden_event_handler, message_handler=message_handler, routing_mode=RoutingMode.PRIVATE, ) del app0 app0_restart.start() # XXX: There is no synchronization among the app and the test, so it is # possible between `start` and the check below that some of the transfers # have completed, making it flaky. # # Make sure the transfers are in the queue and fail otherwise. chain_state = views.state_from_raiden(app0_restart.raiden) for _, _, _, secrethash in transfers: msg = "The secrethashes of the pending transfers must be in the queue after a restart." assert secrethash in chain_state.payment_mapping.secrethashes_to_task, msg with watch_for_unlock_failures(*raiden_network): exception = RuntimeError( "Timeout while waiting for balance update for app0") with gevent.Timeout(20, exception=exception): waiting.wait_for_payment_balance( raiden=app0_restart.raiden, token_network_registry_address=token_network_registry_address, token_address=token_address, partner_address=app1.raiden.address, target_address=app1.raiden.address, target_balance=total_transferred_amount, retry_timeout=network_wait, ) exception = RuntimeError( "Timeout while waiting for balance update for app1") with gevent.Timeout(20, exception=exception): waiting.wait_for_payment_balance( raiden=app1.raiden, token_network_registry_address=token_network_registry_address, token_address=token_address, partner_address=app0_restart.raiden.address, target_address=app1.raiden.address, target_balance=total_transferred_amount, retry_timeout=network_wait, ) assert_synced_channel_state( token_network_address, app0_restart, Balance(deposit - total_transferred_amount), [], app1, Balance(deposit + total_transferred_amount), [], ) new_transport.stop()