def test_get_state_change_with_transfer_by_secrethash(): serializer = JSONSerializer() storage = SerializedSQLiteStorage(":memory:", serializer) mediator_secret, mediator_secrethash = factories.make_secret_with_hash() channels = factories.mediator_make_channel_pair() mediator_transfer = factories.create( factories.LockedTransferSignedStateProperties( secret=mediator_secret, target=channels.partner_address(1), initiator=channels.partner_address(0), ) ) mediator_state_change = factories.mediator_make_init_action(channels, mediator_transfer) target_secret, target_secrethash = factories.make_secret_with_hash() from_channel = factories.create( factories.NettingChannelStateProperties( partner_state=factories.NettingChannelEndStateProperties( balance=100, address=factories.make_address() ) ) ) target_transfer = factories.create( factories.LockedTransferSignedStateProperties( secret=target_secret, target=channels.our_address(0), initiator=channels.partner_address(1), ) ) target_state_change = ActionInitTarget( from_hop=HopState( node_address=from_channel.partner_state.address, channel_identifier=from_channel.canonical_identifier.channel_identifier, ), transfer=target_transfer, balance_proof=target_transfer.balance_proof, sender=target_transfer.balance_proof.sender, # pylint: disable=no-member ) assert storage.count_state_changes() == 0 storage.write_state_changes([mediator_state_change, target_state_change]) assert storage.count_state_changes() == 2 restored = get_state_change_with_transfer_by_secrethash(storage, mediator_secrethash) assert isinstance(restored.data, ActionInitMediator) assert restored.data.from_transfer == mediator_transfer restored = get_state_change_with_transfer_by_secrethash(storage, target_secrethash) assert isinstance(restored.data, ActionInitTarget) assert restored.data.transfer == target_transfer
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_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 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) 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 = 10 identifier = 42 secret, secrethash = factories.make_secret_with_hash() assert api1.transfer_and_wait( registry_address=registry_address, token_address=token_address, amount=amount, target=api2.address, identifier=identifier, secret=secret, transfer_timeout=10, ) exception = ValueError( "Waiting for transfer received success in the WAL timed out") with gevent.Timeout(seconds=5, exception=exception): 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 _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 _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_api_payments_with_secret_and_hash(api_server_test_instance: APIServer, raiden_network: List[RaidenService], token_addresses): _, app1 = raiden_network token_address = token_addresses[0] target_address = app1.address secret, secret_hash = factories.make_secret_with_hash() our_address = api_server_test_instance.rest_api.raiden_api.address payment = { "initiator_address": to_checksum_address(our_address), "target_address": to_checksum_address(target_address), "token_address": to_checksum_address(token_address), "amount": DEFAULT_AMOUNT, "identifier": DEFAULT_ID, } request = grequests.post( api_url_for( api_server_test_instance, "token_target_paymentresource", token_address=to_checksum_address(token_address), target_address=to_checksum_address(target_address), ), json={ "amount": DEFAULT_AMOUNT, "identifier": DEFAULT_ID, "secret": to_hex(secret), "secret_hash": to_hex(secret_hash), }, ) with watch_for_unlock_failures(*raiden_network): response = request.send().response assert_proper_response(response) json_response = get_json_response(response) assert_payment_secret_and_hash(json_response, payment) assert to_hex(secret) == json_response["secret"] assert to_hex(secret_hash) == json_response["secret_hash"]
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_payment_events_endpoints(api_server_test_instance: APIServer, raiden_network: List[RaidenService], token_addresses): app0, app1, app2 = raiden_network token_address0 = token_addresses[0] token_address1 = token_addresses[1] app0_server = api_server_test_instance app1_server = prepare_api_server(app1) app2_server = prepare_api_server(app2) # Payment 1: app0 is sending tokens of token0 to app1 identifier1 = PaymentID(10) amount1 = PaymentAmount(10) secret1, secrethash1 = factories.make_secret_with_hash() request = grequests.post( api_url_for( app0_server, "token_target_paymentresource", token_address=to_checksum_address(token_address0), target_address=to_checksum_address(app1.address), ), json={ "amount": str(amount1), "identifier": str(identifier1), "secret": to_hex(secret1) }, ) request.send() # Payment 2: app0 is sending some tokens of token1 to app2 identifier2 = PaymentID(20) amount2 = PaymentAmount(10) secret2, secrethash2 = factories.make_secret_with_hash() request = grequests.post( api_url_for( app0_server, "token_target_paymentresource", token_address=to_checksum_address(token_address1), target_address=to_checksum_address(app2.address), ), json={ "amount": str(amount2), "identifier": str(identifier2), "secret": to_hex(secret2) }, ) request.send() # Payment 3: app0 is sending some tokens of token0 to app2 identifier3 = PaymentID(30) amount3 = PaymentAmount(17) secret3, secrethash3 = factories.make_secret_with_hash() request = grequests.post( api_url_for( app0_server, "token_target_paymentresource", token_address=to_checksum_address(token_address0), target_address=to_checksum_address(app1.address), ), json={ "amount": str(amount3), "identifier": str(identifier3), "secret": to_hex(secret3) }, ) request.send() timeout = block_offset_timeout( app2, "Waiting for transfer received success in the WAL timed out") with watch_for_unlock_failures(*raiden_network), timeout: result = wait_for_received_transfer_result(app1, identifier1, amount1, app1.alarm.sleep_time, secrethash1) msg = f"Unexpected transfer result: {str(result)}" assert result == TransferWaitResult.UNLOCKED, msg result = wait_for_received_transfer_result(app2, identifier2, amount2, app2.alarm.sleep_time, secrethash2) msg = f"Unexpected transfer result: {str(result)}" assert result == TransferWaitResult.UNLOCKED, msg result = wait_for_received_transfer_result(app1, identifier3, amount3, app1.alarm.sleep_time, secrethash3) msg = f"Unexpected transfer result: {str(result)}" assert result == TransferWaitResult.UNLOCKED, msg # predefine events for later use in assertions event_sent_1 = { "event": "EventPaymentSentSuccess", "identifier": str(identifier1), "target": to_checksum_address(app1.address), "token_address": to_checksum_address(token_address0), } event_sent_2 = { "event": "EventPaymentSentSuccess", "identifier": str(identifier2), "target": to_checksum_address(app2.address), "token_address": to_checksum_address(token_address1), } event_sent_3 = { "event": "EventPaymentSentSuccess", "identifier": str(identifier3), "target": to_checksum_address(app1.address), "token_address": to_checksum_address(token_address0), } event_received_1 = { "event": "EventPaymentReceivedSuccess", "identifier": str(identifier1), "initiator": to_checksum_address(app0.address), "token_address": to_checksum_address(token_address0), } event_received_2 = { "event": "EventPaymentReceivedSuccess", "identifier": str(identifier2), "initiator": to_checksum_address(app0.address), "token_address": to_checksum_address(token_address1), } event_received_3 = { "event": "EventPaymentReceivedSuccess", "identifier": str(identifier3), "initiator": to_checksum_address(app0.address), "token_address": to_checksum_address(token_address0), } # test app0 endpoint without (partner and token) for sender request = grequests.get(api_url_for(app0_server, "paymentresource")) with watch_for_unlock_failures(*raiden_network): response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_event(json_response, event_sent_1) assert must_have_event(json_response, event_sent_2) assert must_have_event(json_response, event_sent_3) # test endpoint without (partner and token) for target1 request = grequests.get(api_url_for(app1_server, "paymentresource")) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_event(json_response, event_received_1) assert must_have_event(json_response, event_received_3) # test endpoint without (partner and token) for target2 request = grequests.get(api_url_for(app2_server, "paymentresource")) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_event(json_response, event_received_2) # test endpoint without partner for app0 request = grequests.get( api_url_for(app0_server, "token_paymentresource", token_address=token_address0)) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_event(json_response, event_sent_1) assert must_have_event(json_response, event_sent_3) # test endpoint without partner for app0 but with limit/offset to get only first request = grequests.get( api_url_for( app0_server, "token_paymentresource", token_address=token_address0, limit=1, offset=0, )) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_event(json_response, event_sent_1) assert len(json_response) == 1 # test endpoint without partner for app0 but with limit/offset # to get only second transfer of token_address request = grequests.get( api_url_for( app0_server, "token_paymentresource", token_address=token_address0, limit=1, offset=1, )) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) # this should return only payment 3, since payment 1 is offset # and payment 2 is of another token address assert len(json_response) == 1 assert must_have_event(json_response, event_sent_3) # test endpoint of app1 without partner for token_address request = grequests.get( api_url_for(app1_server, "token_paymentresource", token_address=token_address0)) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_events(json_response, event_received_1) assert must_have_events(json_response, event_received_3) # test endpoint of app2 without partner for token_address request = grequests.get( api_url_for(app2_server, "token_paymentresource", token_address=token_address0)) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert len(json_response) == 0 # test endpoint of app2 without partner for token_address2 request = grequests.get( api_url_for(app2_server, "token_paymentresource", token_address=token_address1)) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_events(json_response, event_received_2) # test endpoint for token_address0 and partner for app0 request = grequests.get( api_url_for( app0_server, "token_target_paymentresource", token_address=token_address0, target_address=app1.address, )) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert len(json_response) == 2 assert must_have_event(json_response, event_sent_1) assert must_have_event(json_response, event_sent_3) request = grequests.get( api_url_for( app0_server, "token_target_paymentresource", token_address=token_address1, target_address=app2.address, )) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert len(json_response) == 1 assert must_have_event(json_response, event_sent_2) # test endpoint for token_address0 and partner for app1. Check both partners # to see that filtering works correctly request = grequests.get( api_url_for( app1_server, "token_target_paymentresource", token_address=token_address0, target_address=app2.address, )) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert len(json_response) == 0 request = grequests.get( api_url_for( app1_server, "token_target_paymentresource", token_address=token_address0, target_address=app0.address, )) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert len(json_response) == 2 assert must_have_event(json_response, event_received_1) assert must_have_event(json_response, event_received_3) # test app1 checking payments to himself request = grequests.get( api_url_for( app1_server, "token_target_paymentresource", token_address=token_address0, target_address=app1.address, )) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert len(json_response) == 0 # test endpoint for token and partner for app2 request = grequests.get( api_url_for( app2_server, "token_target_paymentresource", token_address=token_address0, target_address=app0.address, )) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) # Since app2 has no payment with app0 in token_address assert len(json_response) == 0 # test endpoint for token2 and partner for app2 request = grequests.get( api_url_for( app2_server, "token_target_paymentresource", token_address=token_address1, target_address=app0.address, )) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) # app2 has one payment with app0 in token_address2 assert len(json_response) == 1 assert must_have_events(json_response, event_received_2) request = grequests.get( api_url_for( app2_server, "token_target_paymentresource", token_address=token_address0, target_address=app1.address, )) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert not must_have_event(json_response, event_received_2) # also add a test for filtering by wrong token address request = grequests.get( api_url_for( app2_server, "token_target_paymentresource", token_address=app1.address, target_address=app1.address, )) response = request.send().response assert_proper_response(response, HTTPStatus.BAD_REQUEST) app1_server.stop() app2_server.stop()
def test_echo_node_response(token_addresses, raiden_chain, retry_timeout): app0, app1, echo_app = raiden_chain token_address = token_addresses[0] registry_address = echo_app.raiden.default_registry.address echo_api = RaidenAPI(echo_app.raiden) echo_node = EchoNode(echo_api, token_address) message_handler = WaitForMessage() echo_app.raiden.message_handler = message_handler echo_node.ready.wait(timeout=30) assert echo_node.ready.is_set() transfer_timeout = 10 wait_for = list() for num, app in enumerate([app0, app1]): amount = PaymentAmount(1 + num) identifier = PaymentID(10 ** (num + 1)) secret, secrethash = make_secret_with_hash() payment_status = RaidenAPI(app.raiden).transfer_async( registry_address=registry_address, token_address=token_address, amount=amount, target=echo_app.raiden.address, identifier=identifier, secret=secret, secrethash=secrethash, ) wait = message_handler.wait_for_message(Unlock, {"secret": secret}) wait_for.append((wait, app.raiden.address, amount, identifier)) msg = ( f"Transfer {identifier} from " f"{to_checksum_address(app.raiden.address)} to " f"{to_checksum_address(echo_app.raiden.address)} timed out after " f"{transfer_timeout}" ) timeout = gevent.Timeout(transfer_timeout, exception=RuntimeError(msg)) with watch_for_unlock_failures(*raiden_chain), timeout: payment_status.payment_done.wait() echo_identifier = PaymentID(identifier + amount) msg = ( f"Response transfer {echo_identifier} from echo node " f"{to_checksum_address(echo_app.raiden.address)} to " f"{to_checksum_address(app.raiden.address)} timed out after " f"{transfer_timeout}" ) with gevent.Timeout(transfer_timeout, exception=RuntimeError(msg)): result = wait_for_received_transfer_result( raiden=app.raiden, payment_identifier=echo_identifier, amount=amount, retry_timeout=retry_timeout, secrethash=secrethash, ) assert result == TransferWaitResult.UNLOCKED for wait, sender, amount, ident in wait_for: wait.wait() assert search_for_item( echo_app.raiden.wal.storage.get_events(), EventPaymentReceivedSuccess, { "amount": amount, "identifier": ident, "initiator": sender, "token_network_registry_address": registry_address, }, ) echo_node.stop()
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_secret_revealed_on_chain(raiden_chain, deposit, settle_timeout, token_addresses, retry_interval_initial): """ A node must reveal the secret on-chain if it's known and the channel is closed. """ app0, app1, app2 = 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 amount = 10 identifier = 1 target = app2.raiden.address secret, secrethash = factories.make_secret_with_hash() # Reveal the secret, but do not unlock it off-chain app1_hold_event_handler = app1.raiden.raiden_event_handler app1_hold_event_handler.hold_unlock_for(secrethash=secrethash) app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=amount, target=target, identifier=identifier, secret=secret, ) with watch_for_unlock_failures(*raiden_chain), block_offset_timeout( app0.raiden): wait_for_state_change(app2.raiden, ReceiveSecretReveal, {"secrethash": secrethash}, retry_interval_initial) channel_state2_1 = get_channelstate(app2, app1, token_network_address) pending_lock = channel_state2_1.partner_state.secrethashes_to_unlockedlocks.get( secrethash) msg = "The lock must be registered in unlocked locks since the secret is known" assert pending_lock is not None, msg # The channels are out-of-sync. app1 has sent the unlock, however we are # intercepting it and app2 has not received the updated balance proof # Close the channel. This must register the secret on chain balance_proof = channel_state2_1.partner_state.balance_proof assert isinstance(balance_proof, BalanceProofSignedState) channel_close_event = ContractSendChannelClose( canonical_identifier=channel_state2_1.canonical_identifier, balance_proof=balance_proof, triggered_by_block_hash=app0.raiden.rpc_client. blockhash_from_blocknumber(BLOCK_ID_LATEST), ) current_state = app2.raiden.wal.state_manager.current_state app2.raiden.raiden_event_handler.on_raiden_events( raiden=app2.raiden, chain_state=current_state, events=[channel_close_event]) settle_expiration = (app0.raiden.rpc_client.block_number() + settle_timeout + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS) app0.raiden.proxy_manager.client.wait_until_block( target_block_number=settle_expiration) # TODO: # - assert on the transferred amounts on-chain (for settle and unlock) # The channel app0-app1 should continue with the protocol off-chain, once # the secret is released on-chain by app2 assert_synced_channel_state(token_network_address, app0, deposit - amount, [], app1, deposit + amount, []) with watch_for_unlock_failures(*raiden_chain), gevent.Timeout(10): wait_for_state_change( app2.raiden, ContractReceiveSecretReveal, {"secrethash": secrethash}, retry_interval_initial, )
def test_payment_events_endpoints(api_server_test_instance: APIServer, raiden_network, token_addresses): app0, app1, app2 = raiden_network amount1 = PaymentAmount(10) identifier1 = PaymentID(42) secret1, secrethash1 = factories.make_secret_with_hash() token_address = token_addresses[0] app0_address = app0.raiden.address target1_address = app1.raiden.address target2_address = app2.raiden.address app1_server = prepare_api_server(app1) app2_server = prepare_api_server(app2) # app0 is sending tokens to target 1 request = grequests.post( api_url_for( api_server_test_instance, "token_target_paymentresource", token_address=to_checksum_address(token_address), target_address=to_checksum_address(target1_address), ), json={ "amount": str(amount1), "identifier": str(identifier1), "secret": to_hex(secret1) }, ) request.send() # app0 is sending some tokens to target 2 identifier2 = PaymentID(43) amount2 = PaymentAmount(10) secret2, secrethash2 = factories.make_secret_with_hash() request = grequests.post( api_url_for( api_server_test_instance, "token_target_paymentresource", token_address=to_checksum_address(token_address), target_address=to_checksum_address(target2_address), ), json={ "amount": str(amount2), "identifier": str(identifier2), "secret": to_hex(secret2) }, ) request.send() # target1 also sends some tokens to target 2 identifier3 = PaymentID(44) amount3 = PaymentAmount(5) secret3, secrethash3 = factories.make_secret_with_hash() request = grequests.post( api_url_for( app1_server, "token_target_paymentresource", token_address=to_checksum_address(token_address), target_address=to_checksum_address(target2_address), ), json={ "amount": str(amount3), "identifier": str(identifier3), "secret": to_hex(secret3) }, ) request.send() timeout = block_offset_timeout( app2.raiden, "Waiting for transfer received success in the WAL timed out") with watch_for_unlock_failures(*raiden_network), timeout: result = wait_for_received_transfer_result( app1.raiden, identifier1, amount1, app1.raiden.alarm.sleep_time, secrethash1) msg = f"Unexpected transfer result: {str(result)}" assert result == TransferWaitResult.UNLOCKED, msg result = wait_for_received_transfer_result( app2.raiden, identifier2, amount2, app2.raiden.alarm.sleep_time, secrethash2) msg = f"Unexpected transfer result: {str(result)}" assert result == TransferWaitResult.UNLOCKED, msg result = wait_for_received_transfer_result( app2.raiden, identifier3, amount3, app2.raiden.alarm.sleep_time, secrethash3) msg = f"Unexpected transfer result: {str(result)}" assert result == TransferWaitResult.UNLOCKED, msg # test endpoint without (partner and token) for sender request = grequests.get( api_url_for(api_server_test_instance, "paymentresource")) with watch_for_unlock_failures(*raiden_network): response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_event( json_response, { "event": "EventPaymentSentSuccess", "identifier": str(identifier1), "target": to_checksum_address(target1_address), "token_address": to_checksum_address(token_address), }, ) assert must_have_event( json_response, { "event": "EventPaymentSentSuccess", "identifier": str(identifier2), "target": to_checksum_address(target2_address), "token_address": to_checksum_address(token_address), }, ) # test endpoint without (partner and token) for target1 request = grequests.get(api_url_for(app1_server, "paymentresource")) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_event( json_response, { "event": "EventPaymentReceivedSuccess", "identifier": str(identifier1), "token_address": to_checksum_address(token_address), }, ) assert must_have_event( json_response, { "event": "EventPaymentSentSuccess", "identifier": str(identifier3), "token_address": to_checksum_address(token_address), }, ) # test endpoint without (partner and token) for target2 request = grequests.get(api_url_for(app2_server, "paymentresource")) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_event( json_response, { "event": "EventPaymentReceivedSuccess", "identifier": str(identifier2), "token_address": to_checksum_address(token_address), }, ) assert must_have_event( json_response, { "event": "EventPaymentReceivedSuccess", "identifier": str(identifier3), "token_address": to_checksum_address(token_address), }, ) # test endpoint without partner for app0 request = grequests.get( api_url_for(api_server_test_instance, "token_paymentresource", token_address=token_address)) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_event( json_response, { "event": "EventPaymentSentSuccess", "identifier": str(identifier1), "target": to_checksum_address(target1_address), "token_address": to_checksum_address(token_address), }, ) assert must_have_event( json_response, { "event": "EventPaymentSentSuccess", "identifier": str(identifier2), "target": to_checksum_address(target2_address), "token_address": to_checksum_address(token_address), }, ) # test endpoint without partner for app0 but with limit/offset to get only first request = grequests.get( api_url_for( api_server_test_instance, "token_paymentresource", token_address=token_address, limit=1, offset=0, )) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_event( json_response, { "event": "EventPaymentSentSuccess", "identifier": str(identifier1), "target": to_checksum_address(target1_address), "token_address": to_checksum_address(token_address), }, ) assert len(json_response) == 1 # test endpoint without partner for app0 but with limit/offset to get only second request = grequests.get( api_url_for( api_server_test_instance, "token_paymentresource", token_address=token_address, limit=1, offset=1, )) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_event( json_response, { "event": "EventPaymentSentSuccess", "identifier": str(identifier2), "target": to_checksum_address(target2_address), "token_address": to_checksum_address(token_address), }, ) # test endpoint without partner for target1 request = grequests.get( api_url_for(app1_server, "token_paymentresource", token_address=token_address)) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_events( json_response, { "event": "EventPaymentReceivedSuccess", "identifier": str(identifier1), "token_address": to_checksum_address(token_address), }, { "event": "EventPaymentSentSuccess", "identifier": str(identifier3), "target": to_checksum_address(target2_address), "token_address": to_checksum_address(token_address), }, ) # test endpoint without partner for target2 request = grequests.get( api_url_for(app2_server, "token_paymentresource", token_address=token_address)) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_events( json_response, { "event": "EventPaymentReceivedSuccess", "identifier": str(identifier2), "token_address": to_checksum_address(token_address), }, { "event": "EventPaymentReceivedSuccess", "identifier": str(identifier3), "token_address": to_checksum_address(token_address), }, ) # test endpoint for token and partner for app0 request = grequests.get( api_url_for( api_server_test_instance, "token_target_paymentresource", token_address=token_address, target_address=target1_address, )) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_event( json_response, { "event": "EventPaymentSentSuccess", "identifier": str(identifier1), "target": to_checksum_address(target1_address), "token_address": to_checksum_address(token_address), }, ) assert not must_have_event( json_response, { "event": "EventPaymentSentSuccess", "identifier": str(identifier2), "target": to_checksum_address(target2_address), "token_address": to_checksum_address(token_address), }, ) # test endpoint for token and partner for target1. Check both partners # to see that filtering works correctly request = grequests.get( api_url_for( app1_server, "token_target_paymentresource", token_address=token_address, target_address=target2_address, )) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_event( json_response, { "event": "EventPaymentSentSuccess", "identifier": str(identifier3), "target": to_checksum_address(target2_address), "token_address": to_checksum_address(token_address), }, ) assert not must_have_event( response, { "event": "EventPaymentReceivedSuccess", "identifier": str(identifier1), "token_address": to_checksum_address(token_address), }, ) request = grequests.get( api_url_for( app1_server, "token_target_paymentresource", token_address=token_address, target_address=target1_address, )) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert len(json_response) == 0 # test endpoint for token and partner for target2 request = grequests.get( api_url_for( app2_server, "token_target_paymentresource", token_address=token_address, target_address=app0_address, )) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_events( json_response, { "event": "EventPaymentReceivedSuccess", "identifier": str(identifier2), "token_address": to_checksum_address(token_address), }, ) assert not must_have_event( json_response, { "event": "EventPaymentReceivedSuccess", "identifier": str(identifier1), "token_address": to_checksum_address(token_address), }, ) assert not must_have_event( json_response, { "event": "EventPaymentReceivedSuccess", "identifier": str(identifier3), "token_address": to_checksum_address(token_address), }, ) request = grequests.get( api_url_for( app2_server, "token_target_paymentresource", token_address=token_address, target_address=target1_address, )) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert must_have_events( json_response, { "event": "EventPaymentReceivedSuccess", "identifier": str(identifier3), "token_address": to_checksum_address(token_address), }, ) assert not must_have_event( json_response, { "event": "EventPaymentReceivedSuccess", "identifier": str(identifier2), "token_address": to_checksum_address(token_address), }, ) assert not must_have_event( json_response, { "event": "EventPaymentReceivedSuccess", "identifier": str(identifier1), "token_address": to_checksum_address(token_address), }, ) # also add a test for filtering by wrong token address request = grequests.get( api_url_for( app2_server, "token_target_paymentresource", token_address=target1_address, target_address=target1_address, )) response = request.send().response assert_proper_response(response, HTTPStatus.OK) json_response = get_json_response(response) assert len(json_response) == 0 app1_server.stop() app2_server.stop()
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 test_start_end_attack( token_addresses: List[TokenAddress], raiden_chain: List[App], deposit: List[App] ) -> None: """ An attacker can try to steal tokens from a hub or the last node in a path. The attacker needs to use two addresses (A1 and A2) and connect both to the hub H. Once connected a mediated transfer is initialized from A1 to A2 through H. Once the node A2 receives the mediated transfer the attacker uses the known secret and reveal to close and settle the channel H-A2, without revealing the secret to H's raiden node. The intention is to make the hub transfer the token but for him to be unable to require the token A1.""" token = token_addresses[0] app0, app1, app2 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking 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 hold_event_handler = app2.raiden.raiden_event_handler msg = "hold event handler necessary to control messages" assert isinstance(hold_event_handler, HoldRaidenEventHandler), msg # the attacker owns app0 and app2 and creates a transfer through app1 amount = PaymentAmount(30) identifier = PaymentID(1) target = TargetAddress(app2.raiden.address) secret, secrethash = factories.make_secret_with_hash() hold_event_handler.hold_secretrequest_for(secrethash=secrethash) app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=amount, target=target, identifier=identifier, secret=secret, ) attack_channel = get_channelstate(app2, app1, token_network_address) attack_transfer = None # TODO attack_contract = attack_channel.external_state.netting_channel.address # type: ignore hub_contract = get_channelstate( # type: ignore app1, app0, token_network_address ).external_state.netting_channel.address # start the settle counter attack_balance_proof = attack_transfer.to_balanceproof() # type: ignore attack_channel.netting_channel.channel_close(attack_balance_proof) # type: ignore # wait until the last block to reveal the secret, hopefully we are not # missing a block during the test assert attack_transfer app2.raiden.rpc_client.wait_until_block( target_block_number=attack_transfer.lock.expiration - 1 ) # since the attacker knows the secret he can net the lock # <the commented code below is left for documentation purposes> # attack_channel.netting_channel.unlock( # UnlockProofState(unlock_proof, attack_transfer.lock, secret) # ) # XXX: verify that the secret was publicized # at this point the hub might not know the secret yet, and won't be able to # claim the token from the channel A1 - H # the attacker settles the contract app2.raiden.rpc_client.wait_until_block( target_block_number=app2.raiden.rpc_client.block_number() + 1 ) attack_channel.netting_channel.settle(token, attack_contract) # at this point the attacker has the "stolen" funds attack_contract = app2.raiden.proxy_manager.token_hashchannel[token][attack_contract] assert attack_contract.participants[app2.raiden.address]["netted"] == deposit + amount assert attack_contract.participants[app1.raiden.address]["netted"] == deposit - amount # and the hub's channel A1-H doesn't hub_contract = app1.raiden.proxy_manager.token_hashchannel[token][hub_contract] assert hub_contract.participants[app0.raiden.address]["netted"] == deposit assert hub_contract.participants[app1.raiden.address]["netted"] == deposit # to mitigate the attack the Hub _needs_ to use a lower expiration for the # locked transfer between H-A2 than A1-H. For A2 to acquire the token # it needs to make the secret public in the blockchain so it publishes the # secret through an event and the Hub is able to require its funds app1.raiden.rpc_client.wait_until_block( target_block_number=app1.raiden.rpc_client.block_number() + 1 ) # XXX: verify that the Hub has found the secret, close and settle the channel # the hub has acquired its token hub_contract = app1.raiden.proxy_manager.token_hashchannel[token][hub_contract] assert hub_contract.participants[app0.raiden.address]["netted"] == deposit + amount assert hub_contract.participants[app1.raiden.address]["netted"] == deposit - amount