def test_receive_secrethashtransfer_unknown(raiden_network, 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_app(app0), app0.raiden.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=app0.raiden.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_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 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_settled_lock(token_addresses, raiden_network, deposit): """ Any transfer following a secret reveal must update the locksroot, so that an attacker cannot reuse a secret to double claim a lock. """ app0, app1 = raiden_network registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] amount = PaymentAmount(30) 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 address0 = app0.raiden.address address1 = app1.raiden.address deposit0 = deposit deposit1 = deposit token_proxy = app0.raiden.proxy_manager.token(token_address) initial_balance0 = token_proxy.balance_of(address0) initial_balance1 = token_proxy.balance_of(address1) identifier = 1 target = app1.raiden.address secret = Secret(sha3(target)) secrethash = sha256_secrethash(secret) secret_available = 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, ) secret_available.wait() # wait for the messages to be exchanged # Save the pending locks from the pending transfer, used to test the unlock channelstate_0_1 = get_channelstate(app0, app1, token_network_address) batch_unlock = channel.get_batch_unlock(channelstate_0_1.our_state) assert batch_unlock hold_event_handler.release_secretrequest_for(app1.raiden, secrethash) transfer( initiator_app=app0, target_app=app1, token_address=token_address, amount=amount, identifier=PaymentID(2), ) RaidenAPI(app1.raiden).channel_close(registry_address, token_address, app0.raiden.address) waiting.wait_for_settle( app1.raiden, app1.raiden.default_registry.address, token_address, [channelstate_0_1.identifier], app1.raiden.alarm.sleep_time, ) current_block = app0.raiden.rpc_client.block_number() netting_channel = app1.raiden.proxy_manager.payment_channel( canonical_identifier=channelstate_0_1.canonical_identifier ) # The transfer locksroot must not contain the unlocked lock, the # unlock must fail. with pytest.raises(RaidenUnrecoverableError): netting_channel.unlock( sender=channelstate_0_1.our_state.address, receiver=channelstate_0_1.partner_state.address, pending_locks=batch_unlock, given_block_identifier=current_block, ) expected_balance0 = initial_balance0 + deposit0 - amount * 2 expected_balance1 = initial_balance1 + deposit1 + amount * 2 assert token_proxy.balance_of(address0) == expected_balance0 assert token_proxy.balance_of(address1) == expected_balance1
def create_default_identifier() -> PaymentID: """ Generates a random identifier. """ return PaymentID(random.randint(0, constants.UINT64_MAX))
def test_locksroot_loading_during_channel_settle_handling( raiden_chain, deploy_client, token_addresses ): app0, app1 = raiden_chain token_network_registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] transfer( initiator_app=app0, target_app=app1, token_address=token_address, amount=PaymentAmount(10), identifier=PaymentID(1), transfer_state=TransferState.SECRET_NOT_REQUESTED, ) transfer( initiator_app=app1, target_app=app0, token_address=token_address, amount=PaymentAmount(7), identifier=PaymentID(2), transfer_state=TransferState.SECRET_NOT_REQUESTED, ) token_network_address = views.get_token_network_address_by_token_address( chain_state=views.state_from_raiden(app0.raiden), token_network_registry_address=token_network_registry_address, token_address=token_address, ) assert token_network_address channel_state = get_channelstate( app0=app0, app1=app1, token_network_address=token_network_address ) channel = app0.raiden.proxy_manager.payment_channel(channel_state.canonical_identifier) balance_proof = channel_state.partner_state.balance_proof assert balance_proof balance_proof = cast(BalanceProofSignedState, balance_proof) block_number = app0.raiden.rpc_client.block_number() closing_data = pack_signed_balance_proof( msg_type=MessageTypeId.BALANCE_PROOF, nonce=balance_proof.nonce, balance_hash=balance_proof.balance_hash, additional_hash=balance_proof.message_hash, canonical_identifier=balance_proof.canonical_identifier, partner_signature=balance_proof.signature, ) closing_signature = app0.raiden.signer.sign(data=closing_data) channel.close( nonce=balance_proof.nonce, balance_hash=balance_proof.balance_hash, additional_hash=balance_proof.message_hash, non_closing_signature=balance_proof.signature, closing_signature=closing_signature, block_identifier=block_number, ) close_block = app0.raiden.rpc_client.block_number() app0.stop() waiting.wait_for_settle( raiden=app1.raiden, token_network_registry_address=token_network_registry_address, token_address=token_address, channel_ids=[channel_state.canonical_identifier.channel_identifier], retry_timeout=1, ) contract_proxy, _ = deploy_rpc_test_contract(deploy_client, "RpcWithStorageTest") iterations = 1000 def send_transaction(): check_block = deploy_client.get_checking_block() startgas = contract_proxy.estimate_gas(check_block, "waste_storage", iterations) startgas = safe_gas_limit(startgas) transaction = contract_proxy.transact("waste_storage", startgas, iterations) return deploy_client.poll(transaction) for _ in range(10): send_transaction() # Wait until the target block has be prunned, it has to be larger than # pruning_history pruned_after_blocks = pruning_history * 1.5 waiting.wait_for_block( raiden=app1.raiden, block_number=close_block + pruned_after_blocks, retry_timeout=1 ) channel = app0.raiden.proxy_manager.payment_channel(channel_state.canonical_identifier) # make sure the block was pruned with pytest.raises(ValueError): channel.detail(block_identifier=close_block) # This must not raise when the settle event is being raised and the # locksroot is being recover (#3856) app0.start() assert wait_for_state_change( raiden=app0.raiden, item_type=ContractReceiveChannelSettled, attributes={"canonical_identifier": channel_state.canonical_identifier}, retry_timeout=1, )
def test_mediated_transfer_with_entire_deposit( raiden_network, number_of_nodes, token_addresses, deposit, network_wait ) -> None: app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state, token_network_registry_address, token_address ) # The test uses internal routing at the moment, that's why this is set like that. # However, the actual calculated fee is 3 instead of the 4 calculated here, therefore # the amounts are adjusted below fee1 = FeeAmount(int(deposit * INTERNAL_ROUTING_DEFAULT_FEE_PERC)) fee_margin1 = calculate_fee_margin(deposit, fee1) fee_difference = 1 secrethash = transfer_and_assert_path( path=raiden_network, token_address=token_address, amount=deposit - fee1 - fee_margin1, identifier=PaymentID(1), timeout=network_wait * number_of_nodes, ) with block_timeout_for_transfer_by_secrethash(app1.raiden, secrethash): wait_assert( func=assert_succeeding_transfer_invariants, token_network_address=token_network_address, app0=app0, balance0=0, pending_locks0=[], app1=app1, balance1=deposit * 2, pending_locks1=[], ) with block_timeout_for_transfer_by_secrethash(app2.raiden, secrethash): wait_assert( func=assert_succeeding_transfer_invariants, token_network_address=token_network_address, app0=app1, balance0=fee1 - fee_difference, pending_locks0=[], app1=app2, balance1=deposit * 2 - fee1 + fee_difference, pending_locks1=[], ) app2_capacity = 2 * deposit - fee1 fee2 = FeeAmount(int(round(app2_capacity * INTERNAL_ROUTING_DEFAULT_FEE_PERC))) fee_margin2 = calculate_fee_margin(app2_capacity, fee2) reverse_path = list(raiden_network[::-1]) transfer_and_assert_path( path=reverse_path, token_address=token_address, amount=app2_capacity - fee2 - fee_margin2, identifier=PaymentID(2), timeout=network_wait * number_of_nodes, ) with block_timeout_for_transfer_by_secrethash(app1.raiden, secrethash): wait_assert( func=assert_succeeding_transfer_invariants, token_network_address=token_network_address, app0=app0, balance0=2 * deposit - fee2 + fee_difference, pending_locks0=[], app1=app1, balance1=fee2 - fee_difference, pending_locks1=[], ) with block_timeout_for_transfer_by_secrethash(app2.raiden, secrethash): wait_assert( func=assert_succeeding_transfer_invariants, token_network_address=token_network_address, app0=app1, balance0=deposit * 2 - fee_difference, pending_locks0=[], app1=app2, balance1=fee_difference, pending_locks1=[], )
def test_refund_transfer(raiden_chain, number_of_nodes, token_addresses, deposit, network_wait, retry_timeout): """A failed transfer must send a refund back. TODO: - Unlock the token on refund #1091 - Clear the pending locks and update the locked amount #193 - Remove the refund message type #490""" # Topology: # # 0 -> 1 -> 2 # app0, app1, app2 = raiden_chain token_address = token_addresses[0] token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), token_network_registry_address, token_address) # make a transfer to test the path app0 -> app1 -> app2 identifier_path = PaymentID(1) amount_path = PaymentAmount(1) 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, 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 # Wait for lock lock expiration but make sure app0 never processes LockExpired with dont_handle_lock_expired_mock(app0): wait_for_block( raiden=app0.raiden, block_number=BlockNumber( channel.get_sender_expiration_threshold(lock.expiration) + 1), retry_timeout=retry_timeout, ) # make sure that app0 still has the payment task for the secrethash # https://github.com/raiden-network/raiden/issues/3183 assert secrethash in state_from_raiden( app0.raiden).payment_mapping.secrethashes_to_task # make sure that app1 sent a lock expired message for the secrethash send_lock_expired = raiden_events_search_for_item( app1.raiden, SendLockExpired, {"secrethash": secrethash}) assert send_lock_expired # make sure that app0 never got it state_changes = app0.raiden.wal.storage.get_statechanges_by_range( RANGE_ALL_STATE_CHANGES) assert not search_for_item(state_changes, ReceiveLockExpired, {"secrethash": secrethash}) # Out of the handicapped app0 transport. # Now wait till app0 receives and processes LockExpired receive_lock_expired = wait_for_state_change(app0.raiden, ReceiveLockExpired, {"secrethash": secrethash}, retry_timeout) # And also till app1 received the processed wait_for_state_change( app1.raiden, ReceiveProcessed, {"message_identifier": receive_lock_expired.message_identifier}, retry_timeout, ) # make sure app1 queue has cleared the SendLockExpired chain_state1 = views.state_from_app(app1) queues1 = views.get_all_messagequeues(chain_state=chain_state1) result = [(queue_id, queue) for queue_id, queue in queues1.items() if queue_id.recipient == app0.raiden.address and queue] assert not result # and now wait for 1 more block so that the payment task can be deleted wait_for_block( raiden=app0.raiden, block_number=app0.raiden.get_block_number() + 1, retry_timeout=retry_timeout, ) # and since the lock expired message has been sent and processed then the # payment task should have been deleted from both nodes # https://github.com/raiden-network/raiden/issues/3183 assert secrethash not in state_from_raiden( app0.raiden).payment_mapping.secrethashes_to_task assert secrethash not in state_from_raiden( app1.raiden).payment_mapping.secrethashes_to_task
def test_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_secret_revealed_on_chain( raiden_chain: List[RaidenService], 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_raiden(app0), app0.default_registry.address, token_address) assert token_network_address amount = PaymentAmount(10) identifier = PaymentID(1) target = TargetAddress(app2.address) secret, secrethash = factories.make_secret_with_hash() # Reveal the secret, but do not unlock it off-chain app1_hold_event_handler = app1.raiden_event_handler msg = "test apps must have HoldRaidenEventHandler set" assert isinstance(app1_hold_event_handler, HoldRaidenEventHandler), msg app1_hold_event_handler.hold_unlock_for(secrethash=secrethash) app0.mediated_transfer_async( token_network_address=token_network_address, amount=amount, target=target, identifier=identifier, secret=secret, route_states=[ create_route_state_for_route( apps=raiden_chain, token_address=token_address, fee_estimate=FeeAmount( round(INTERNAL_ROUTING_DEFAULT_FEE_PERC * amount)), ) ], ) with watch_for_unlock_failures(*raiden_chain), block_offset_timeout(app0): wait_for_state_change(app2, 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.rpc_client.blockhash_from_blocknumber( BLOCK_ID_LATEST), ) assert app2.wal, "test apps must be started by the fixture." current_state = views.state_from_raiden(app2) app2.raiden_event_handler.on_raiden_events(raiden=app2, chain_state=current_state, events=[channel_close_event]) settle_expiration = (app0.rpc_client.block_number() + settle_timeout + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS) app0.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, ContractReceiveSecretReveal, {"secrethash": secrethash}, retry_interval_initial, )
def test_receive_lockedtransfer_invalidnonce( raiden_network: List[RaidenService], number_of_nodes, deposit, token_addresses, reveal_timeout, network_wait, ): app0, app1, app2 = raiden_network token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_raiden(app0), app0.default_registry.address, token_address ) assert token_network_address channel0 = get_channelstate(app0, app1, token_network_address) amount = 10 payment_identifier = PaymentID(1) secrethash = transfer( initiator_app=app0, target_app=app2, token_address=token_address, amount=PaymentAmount(10), identifier=payment_identifier, timeout=network_wait * number_of_nodes, routes=[[app0.address, app1.address, app2.address]], ) repeated_nonce = Nonce(1) expiration = reveal_timeout * 2 mediated_transfer_message = LockedTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=make_message_identifier(), payment_identifier=payment_identifier, nonce=repeated_nonce, token_network_address=token_network_address, token=token_address, channel_identifier=channel0.identifier, transferred_amount=TokenAmount(amount), locked_amount=LockedAmount(amount), recipient=app1.address, locksroot=make_locksroot(), lock=Lock( amount=PaymentWithFeeAmount(amount), expiration=expiration, secrethash=UNIT_SECRETHASH ), target=TargetAddress(app2.address), initiator=InitiatorAddress(app0.address), signature=EMPTY_SIGNATURE, metadata=Metadata(routes=[RouteMetadata(route=[app1.address, app2.address])]), ) sign_and_inject(mediated_transfer_message, app0.signer, app1) with block_timeout_for_transfer_by_secrethash(app1, secrethash): wait_assert( assert_synced_channel_state, token_network_address, app0, deposit - amount, [], app1, deposit + amount, [], )
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=TokenAmount(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=TokenAmount(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)
def test_recovery_unhappy_case( raiden_network: List[RaidenService], restart_node, number_of_nodes, deposit, token_addresses, network_wait, retry_timeout, ): app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_raiden(app0) token_network_registry_address = app0.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state, token_network_registry_address, token_address) # make a few transfers from app0 to app2 amount = PaymentAmount(1) spent_amount = deposit - 2 for identifier in range(spent_amount): transfer( initiator_app=app0, target_app=app2, token_address=token_address, amount=amount, identifier=PaymentID(identifier), timeout=network_wait * number_of_nodes, routes=[[app0.address, app1.address, app2.address]], ) app0.stop() new_transport = MatrixTransport(config=app0.config.transport, environment=app0.config.environment_type) app0.stop() RaidenAPI(app1).channel_close(app1.default_registry.address, token_address, app0.address) channel01 = views.get_channelstate_for( views.state_from_raiden(app1), app1.default_registry.address, token_address, app0.address, ) assert channel01 waiting.wait_for_settle( app1, app1.default_registry.address, token_address, [channel01.identifier], retry_timeout, ) raiden_event_handler = RaidenEventHandler() message_handler = MessageHandler() app0_restart = RaidenService( config=app0.config, rpc_client=app0.rpc_client, proxy_manager=app0.proxy_manager, query_start_block=BlockNumber(0), raiden_bundle=RaidenBundle(app0.default_registry, app0.default_secret_registry), services_bundle=app0.default_services_bundle, transport=new_transport, raiden_event_handler=raiden_event_handler, message_handler=message_handler, routing_mode=RoutingMode.PRIVATE, ) del app0 # from here on the app0_restart should be used restart_node(app0_restart) wal = app0_restart.wal assert wal state_changes = wal.storage.get_statechanges_by_range( RANGE_ALL_STATE_CHANGES) assert search_for_item( state_changes, ContractReceiveChannelSettled, { "token_network_address": token_network_address, "channel_identifier": channel01.identifier, }, )
def test_batch_unlock( raiden_network: List[App], token_addresses: List[TokenAddress], secret_registry_address: SecretRegistryAddress, deposit: TokenAmount, ) -> None: """Tests that batch unlock is properly called. This test will start a single incomplete transfer, the secret will be revealed *on-chain*. The node that receives the tokens has to call unlock, the node that doesn't gain anything does nothing. """ alice_app, bob_app = raiden_network alice_address = alice_app.raiden.address bob_address = bob_app.raiden.address token_network_registry_address = alice_app.raiden.default_registry.address token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(alice_app), token_network_registry_address, token_address) assert token_network_address hold_event_handler = bob_app.raiden.raiden_event_handler assert isinstance(hold_event_handler, HoldRaidenEventHandler) # Take a snapshot early on alice_app.raiden.snapshot() canonical_identifier = get_channelstate( alice_app, bob_app, token_network_address).canonical_identifier assert is_channel_registered(alice_app, bob_app, canonical_identifier) assert is_channel_registered(bob_app, alice_app, canonical_identifier) token_proxy = alice_app.raiden.proxy_manager.token(token_address) alice_initial_balance = token_proxy.balance_of(alice_app.raiden.address) bob_initial_balance = token_proxy.balance_of(bob_app.raiden.address) # Take snapshot before transfer alice_app.raiden.snapshot() alice_to_bob_amount = 10 identifier = 1 secret = Secret(sha3(bob_address)) secrethash = sha256_secrethash(secret) secret_request_event = hold_event_handler.hold_secretrequest_for( secrethash=secrethash) alice_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=PaymentAmount(alice_to_bob_amount), target=TargetAddress(bob_address), identifier=PaymentID(identifier), secret=secret, ) secret_request_event.get() # wait for the messages to be exchanged alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_address) lock = channel.get_lock(alice_bob_channel_state.our_state, secrethash) assert lock # This is the current state of the protocol: # # A -> B LockedTransfer # B -> A SecretRequest # - protocol didn't continue assert_synced_channel_state(token_network_address, alice_app, Balance(deposit), [lock], bob_app, Balance(deposit), []) # Test WAL restore to return the latest channel state alice_app.raiden.snapshot() our_balance_proof = alice_bob_channel_state.our_state.balance_proof restored_channel_state = channel_state_until_state_change( raiden=alice_app.raiden, canonical_identifier=alice_bob_channel_state.canonical_identifier, state_change_identifier=HIGH_STATECHANGE_ULID, ) assert restored_channel_state our_restored_balance_proof = restored_channel_state.our_state.balance_proof assert our_balance_proof == our_restored_balance_proof # Close the channel before revealing the secret off-chain. This will leave # a pending lock in the channel which has to be unlocked on-chain. # # The token network will emit a ChannelClose event, this will be polled by # both apps and each must start a task for calling settle. RaidenAPI(bob_app.raiden).channel_close(token_network_registry_address, token_address, alice_app.raiden.address) # The secret has to be registered manually because Bob never learned the # secret. The test is holding the SecretRequest to ensure the off-chain # unlock will not happen and the channel is closed with a pending lock. # # Alternatives would be to hold the unlock messages, or to stop and restart # the apps after the channel is closed. secret_registry_proxy = alice_app.raiden.proxy_manager.secret_registry( secret_registry_address) secret_registry_proxy.register_secret(secret=secret) msg = ( "The lock must still be part of the node state for the test to proceed, " "otherwise there is not unlock to be done.") assert lock, msg msg = ( "The secret must be registered before the lock expires, in order for " "the unlock to happen on-chain. Otherwise the test will fail on the " "expected balances.") assert lock.expiration > alice_app.raiden.get_block_number(), msg assert lock.secrethash == sha256_secrethash(secret) waiting.wait_for_settle( alice_app.raiden, token_network_registry_address, token_address, [alice_bob_channel_state.identifier], alice_app.raiden.alarm.sleep_time, ) msg = "The channel_state must not have been cleared, one of the ends has pending locks to do." assert is_channel_registered(alice_app, bob_app, canonical_identifier), msg assert is_channel_registered(bob_app, alice_app, canonical_identifier), msg msg = ( "Timeout while waiting for the unlock to be mined. This may happen if " "transaction is rejected, not mined, or the node's alarm task is " "not running.") with gevent.Timeout(seconds=30, exception=AssertionError(msg)): # Wait for both nodes (Bob and Alice) to see the on-chain unlock wait_for_batch_unlock( app=alice_app, token_network_address=token_network_address, receiver=bob_address, sender=alice_address, ) wait_for_batch_unlock( app=bob_app, token_network_address=token_network_address, receiver=bob_address, sender=alice_address, ) msg = ( "The nodes have done the unlock, and both ends have seen it, now the " "channel must be cleared") assert not is_channel_registered(alice_app, bob_app, canonical_identifier), msg assert not is_channel_registered(bob_app, alice_app, canonical_identifier), msg alice_new_balance = alice_initial_balance + deposit - alice_to_bob_amount bob_new_balance = bob_initial_balance + deposit + alice_to_bob_amount msg = "Unexpected end balance after channel settlement with batch unlock." assert token_proxy.balance_of( alice_app.raiden.address) == alice_new_balance, msg assert token_proxy.balance_of( bob_app.raiden.address) == bob_new_balance, msg
def test_recovery_unhappy_case( raiden_network, number_of_nodes, deposit, token_addresses, network_wait, retry_timeout ): app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state, token_network_registry_address, token_address ) # make a few transfers from app0 to app2 amount = TokenAmount(1) spent_amount = deposit - 2 for identifier in range(spent_amount): transfer( initiator_app=app0, target_app=app2, token_address=token_address, amount=amount, identifier=PaymentID(identifier), timeout=network_wait * number_of_nodes, ) app0.raiden.stop() new_transport = MatrixTransport(app0.raiden.config["transport"]["matrix"]) app0.stop() RaidenAPI(app1.raiden).channel_close( app1.raiden.default_registry.address, token_address, app0.raiden.address ) channel01 = views.get_channelstate_for( views.state_from_app(app1), app1.raiden.default_registry.address, token_address, app0.raiden.address, ) waiting.wait_for_settle( app1.raiden, app1.raiden.default_registry.address, token_address, [channel01.identifier], retry_timeout, ) 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_one_to_n_address=app0.raiden.default_one_to_n_address, default_secret_registry=app0.raiden.default_secret_registry, default_service_registry=app0.raiden.default_service_registry, 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 # from here on the app0_restart should be used app0_restart.start() state_changes = app0_restart.raiden.wal.storage.get_statechanges_by_range( RANGE_ALL_STATE_CHANGES ) assert search_for_item( state_changes, ContractReceiveChannelSettled, { "token_network_address": token_network_address, "channel_identifier": channel01.identifier, }, )
def test_refund_messages(raiden_chain, token_addresses, deposit, network_wait): # The network has the following topology: # # App0 <---> App1 <---> App2 app0, app1, app2 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking token_address = token_addresses[0] token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), token_network_registry_address, token_address) # Exhaust the channel App1 <-> App2 (to force the refund transfer) # Here we make a single-hop transfer, no fees are charged so we should # send the whole deposit amount to drain the channel. transfer( initiator_app=app1, target_app=app2, token_address=token_address, amount=deposit, identifier=PaymentID(1), ) refund_amount = deposit // 2 refund_fees = calculate_fee_for_amount(refund_amount) fee_margin = calculate_fee_margin(refund_amount, refund_fees) refund_amount_with_fees = refund_amount + refund_fees + fee_margin identifier = 1 payment_status = app0.raiden.mediated_transfer_async( token_network_address, refund_amount, app2.raiden.address, identifier) msg = "Must fail, there are no routes available" assert isinstance(payment_status.payment_done.wait(), EventPaymentSentFailed), msg # The transfer from app0 to app2 failed, so the balances did change. # Since the refund is not unlocked both channels have the corresponding # amount locked (issue #1091) send_lockedtransfer = raiden_events_search_for_item( app0.raiden, SendLockedTransfer, {"transfer": { "lock": { "amount": refund_amount_with_fees } }}, ) assert send_lockedtransfer send_refundtransfer = raiden_events_search_for_item( app1.raiden, SendRefundTransfer, {}) assert send_refundtransfer with gevent.Timeout(network_wait): wait_assert( func=assert_synced_channel_state, token_network_address=token_network_address, app0=app0, balance0=deposit, pending_locks0=[send_lockedtransfer.transfer.lock], app1=app1, balance1=deposit, pending_locks1=[send_refundtransfer.transfer.lock], ) # This channel was exhausted to force the refund transfer except for the fees with gevent.Timeout(network_wait): wait_assert( func=assert_succeeding_transfer_invariants, token_network_address=token_network_address, app0=app1, balance0=0, pending_locks0=[], app1=app2, balance1=deposit * 2, pending_locks1=[], )
def make_receive_transfer_mediated( channel_state: NettingChannelState, privkey: bytes, nonce: Nonce, transferred_amount: TokenAmount, lock: HashTimeLockState, pending_locks: PendingLocksState = None, locked_amount: Optional[PaymentWithFeeAmount] = None, chain_id: Optional[ChainID] = None, ) -> LockedTransferSignedState: typecheck(lock, HashTimeLockState) signer = LocalSigner(privkey) address = signer.address if address not in (channel_state.our_state.address, channel_state.partner_state.address): raise ValueError("Private key does not match any of the participants.") if pending_locks is None: locks = make_empty_pending_locks_state() locks.locks.append(lock.encoded) else: assert bytes(lock.encoded) in pending_locks.locks locks = pending_locks if locked_amount is None: locked_amount = lock.amount assert locked_amount >= lock.amount locksroot = compute_locksroot(locks) payment_identifier = PaymentID(nonce) transfer_target = make_target_address() transfer_initiator = make_initiator_address() chain_id = chain_id or channel_state.chain_id transfer_metadata = Metadata(routes=[ RouteMetadata( route=[channel_state.our_state.address, Address(transfer_target)]) ]) mediated_transfer_msg = LockedTransfer( chain_id=chain_id, message_identifier=make_message_identifier(), payment_identifier=payment_identifier, nonce=nonce, token_network_address=channel_state.token_network_address, token=channel_state.token_address, channel_identifier=channel_state.identifier, transferred_amount=transferred_amount, locked_amount=TokenAmount(locked_amount), recipient=channel_state.partner_state.address, locksroot=locksroot, lock=Lock(amount=lock.amount, expiration=lock.expiration, secrethash=lock.secrethash), target=transfer_target, initiator=transfer_initiator, signature=EMPTY_SIGNATURE, fee=0, metadata=transfer_metadata, ) mediated_transfer_msg.sign(signer) receive_lockedtransfer = LockedTransferSignedState( payment_identifier=payment_identifier, token=channel_state.token_address, lock=lock, initiator=transfer_initiator, target=transfer_target, message_identifier=make_message_identifier(), balance_proof=balanceproof_from_envelope(mediated_transfer_msg), routes=[ route_metadata.route for route_metadata in transfer_metadata.routes ], ) return receive_lockedtransfer
def test_refund_transfer_after_2nd_hop(raiden_chain, number_of_nodes, token_addresses, deposit, network_wait): """Test the refund transfer sent due to failure after 2nd hop""" # Topology: # # 0 -> 1 -> 2 -> 3 # app0, app1, app2, app3 = raiden_chain token_address = token_addresses[0] token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), token_network_registry_address, token_address) # make a transfer to test the path app0 -> app1 -> app2 -> app3 identifier_path = PaymentID(1) amount_path = PaymentAmount(1) transfer( initiator_app=app0, target_app=app3, token_address=token_address, amount=amount_path, identifier=identifier_path, timeout=network_wait * number_of_nodes, ) # drain the channel app2 -> app3 identifier_drain = PaymentID(2) amount_drain = PaymentAmount(deposit * 8 // 10) transfer( initiator_app=app2, target_app=app3, token_address=token_address, amount=amount_drain, identifier=identifier_drain, timeout=network_wait, ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_address, app0, deposit - amount_path, [], app1, deposit + amount_path, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_address, app1, deposit - amount_path, [], app2, deposit + amount_path, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_address, app2, deposit - amount_path - amount_drain, [], app3, deposit + amount_path + amount_drain, [], ) # app0 -> app1 -> app2 > app3 is the only available path, but the channel # app2 -> app3 doesn't have capacity, so a refund will be sent on # app2 -> app1 -> app0 identifier_refund = PaymentID(3) amount_refund = PaymentAmount(50) fee = calculate_fee_for_amount(amount_refund) fee_margin = calculate_fee_margin(amount_refund, fee) amount_refund_with_fees = amount_refund + fee + fee_margin payment_status = app0.raiden.mediated_transfer_async( token_network_address, amount_refund, app3.raiden.address, identifier_refund) msg = "there is no path with capacity, the transfer must fail" assert isinstance(payment_status.payment_done.wait(), EventPaymentSentFailed), msg # Lock structures with the correct amount send_locked1 = raiden_events_search_for_item( app0.raiden, SendLockedTransfer, {"transfer": { "lock": { "amount": amount_refund_with_fees } }}, ) assert send_locked1 send_refund1 = raiden_events_search_for_item(app1.raiden, SendRefundTransfer, {}) assert send_refund1 lock1 = send_locked1.transfer.lock refund_lock1 = send_refund1.transfer.lock assert lock1.amount == refund_lock1.amount assert lock1.secrethash == refund_lock1.secrethash send_locked2 = raiden_events_search_for_item( app1.raiden, SendLockedTransfer, {"transfer": { "lock": { "amount": amount_refund_with_fees } }}, ) assert send_locked2 send_refund2 = raiden_events_search_for_item(app2.raiden, SendRefundTransfer, {}) assert send_refund2 lock2 = send_locked2.transfer.lock refund_lock2 = send_refund2.transfer.lock assert lock2.amount == refund_lock2.amount assert lock2.secrethash assert lock2.expiration # channels have the amount locked because of the refund message with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_address, app0, deposit - amount_path, [lock1], app1, deposit + amount_path, [refund_lock1], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_address, app1, deposit - amount_path, [lock2], app2, deposit + amount_path, [refund_lock2], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_address, app2, deposit - amount_path - amount_drain, [], app3, deposit + amount_path + amount_drain, [], )
cap_fees=True, imbalance_penalty=[(0, 0), (1000, 50)] # type: ignore ), no_fees, ], incoming_fee_schedules=[no_fees, no_fees, no_fees], expected_transferred_amounts=[amount + fee, amount + fee, amount + fee], ), ] case = cases[case_no] for i, fee_schedule in enumerate(case.get("fee_schedules", [])): if fee_schedule: set_fee_schedule(apps[i], apps[i + 1], fee_schedule) for i, fee_schedule in enumerate(case.get("incoming_fee_schedules", [])): if fee_schedule: set_fee_schedule(apps[i + 1], apps[i], fee_schedule) route_patch = patch("raiden.routing.get_best_routes", get_best_routes_with_fees) disable_max_mediation_fee_patch = patch( "raiden.transfer.mediated_transfer.initiator.MAX_MEDIATION_FEE_PERC", new=10000 ) with route_patch, disable_max_mediation_fee_patch: transfer_and_assert_path( path=raiden_network, token_address=token_address, amount=amount, identifier=PaymentID(2), ) assert_balances(case["expected_transferred_amounts"])
def test_mediator_task_view(): """Same as above for mediator tasks.""" secret1 = factories.make_secret(1) locked_amount1 = TokenAmount(11) payee_transfer = factories.create( factories.LockedTransferUnsignedStateProperties(secret=secret1)) payer_transfer = factories.create( factories.LockedTransferSignedStateProperties( secret=secret1, payment_identifier=PaymentID(1), locked_amount=locked_amount1)) secrethash1 = payee_transfer.lock.secrethash initiator = payee_transfer.initiator initiator_channel = factories.create( factories.NettingChannelStateProperties( partner_state=factories.NettingChannelEndStateProperties( address=initiator, balance=TokenAmount(100)))) route_state = RouteState( route=[payee_transfer.target], forward_channel_id=initiator_channel.canonical_identifier. channel_identifier, ) transfer_state1 = MediatorTransferState(secrethash=secrethash1, routes=[route_state]) # pylint: disable=E1101 transfer_state1.transfers_pair.append( MediationPairState( payer_transfer=payer_transfer, payee_transfer=payee_transfer, payee_address=payee_transfer.target, )) task1 = MediatorTask( token_network_address=factories.UNIT_TOKEN_NETWORK_ADDRESS, mediator_state=transfer_state1) secret2 = factories.make_secret(2) locked_amount2 = TokenAmount(13) transfer2 = factories.create( factories.LockedTransferSignedStateProperties( secret=secret2, payment_identifier=PaymentID(2), locked_amount=locked_amount2)) secrethash2 = transfer2.lock.secrethash transfer_state2 = MediatorTransferState(secrethash=secrethash2, routes=[route_state]) transfer_state2.waiting_transfer = WaitingTransferState(transfer=transfer2) task2 = MediatorTask( token_network_address=factories.UNIT_TOKEN_NETWORK_ADDRESS, mediator_state=transfer_state2) payment_mapping = { secrethash1: cast(TransferTask, task1), secrethash2: cast(TransferTask, task2), } view = transfer_tasks_view(payment_mapping) assert len(view) == 2 if view[0].get("payment_identifier") == "1": pending_transfer, waiting_transfer = view else: waiting_transfer, pending_transfer = view assert pending_transfer.get("role") == waiting_transfer.get( "role") == "mediator" assert pending_transfer.get("payment_identifier") == "1" assert waiting_transfer.get("payment_identifier") == "2" assert pending_transfer.get("locked_amount") == str(locked_amount1) assert waiting_transfer.get("locked_amount") == str(locked_amount2)
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 test_recovery_happy_case(raiden_network, restart_node, number_of_nodes, deposit, token_addresses, network_wait): app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state, token_network_registry_address, token_address) assert token_network_address # make a few transfers from app0 to app2 amount = PaymentAmount(1) spent_amount = deposit - 2 for identifier in range(spent_amount): transfer_and_assert_path( path=raiden_network, token_address=token_address, amount=amount, identifier=PaymentID(identifier), timeout=network_wait * number_of_nodes, ) app0.stop() waiting.wait_for_network_state(app1.raiden, app0.raiden.address, NetworkState.UNREACHABLE, network_wait) restart_node(app0) assert_synced_channel_state(token_network_address, app0, deposit - spent_amount, [], app1, deposit + spent_amount, []) assert_synced_channel_state(token_network_address, app1, deposit - spent_amount, [], app2, deposit + spent_amount, []) # wait for the nodes' healthcheck to update the network statuses waiting.wait_for_healthy(app0.raiden, app1.raiden.address, network_wait) waiting.wait_for_healthy(app1.raiden, app0.raiden.address, network_wait) transfer_and_assert_path( path=raiden_network[::-1], token_address=token_address, amount=amount, identifier=create_default_identifier(), timeout=network_wait * number_of_nodes, ) transfer_and_assert_path( path=raiden_network, token_address=token_address, amount=amount, identifier=create_default_identifier(), timeout=network_wait * number_of_nodes, ) assert_succeeding_transfer_invariants(token_network_address, app0, deposit - spent_amount, [], app1, deposit + spent_amount, []) assert_succeeding_transfer_invariants(token_network_address, app1, deposit - spent_amount, [], app2, deposit + spent_amount, [])
def test_automatic_dispute(raiden_network, deposit, token_addresses): app0, app1 = raiden_network registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address ) assert token_network_address channel0 = get_channelstate(app0, app1, token_network_address) token_proxy = app0.raiden.proxy_manager.token(channel0.token_address) initial_balance0 = token_proxy.balance_of(app0.raiden.address) initial_balance1 = token_proxy.balance_of(app1.raiden.address) amount0_1 = PaymentAmount(10) transfer( initiator_app=app0, target_app=app1, token_address=token_address, amount=amount0_1, identifier=PaymentID(1), ) amount1_1 = PaymentAmount(50) transfer( initiator_app=app1, target_app=app0, token_address=token_address, amount=amount1_1, identifier=PaymentID(2), ) amount0_2 = PaymentAmount(60) transfer( initiator_app=app0, target_app=app1, token_address=token_address, amount=amount0_2, identifier=PaymentID(3), ) # Alice can only provide one of Bob's transfer, so she is incentivized to # use the one with the largest transferred_amount. RaidenAPI(app0.raiden).channel_close(registry_address, token_address, app1.raiden.address) # Bob needs to provide a transfer otherwise its netted balance will be # wrong, so he is incentivised to use Alice's transfer with the largest # transferred_amount. # # This is done automatically # channel1.external_state.update_transfer( # alice_second_transfer, # ) waiting.wait_for_settle( app0.raiden, registry_address, token_address, [channel0.identifier], app0.raiden.alarm.sleep_time, ) # check that the channel is properly settled and that Bob's client # automatically called updateTransfer() to reflect the actual transactions assert token_proxy.balance_of(token_network_address) == 0 total0 = amount0_1 + amount0_2 total1 = amount1_1 expected_balance0 = initial_balance0 + deposit - total0 + total1 expected_balance1 = initial_balance1 + deposit + total0 - total1 assert token_proxy.balance_of(app0.raiden.address) == expected_balance0 assert token_proxy.balance_of(app1.raiden.address) == expected_balance1
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_payment_events_endpoints(api_server_test_instance: APIServer, raiden_network, token_addresses): app0, app1, app2 = raiden_network token_address0 = token_addresses[0] token_address2 = token_addresses[1] app0_address = app0.raiden.address app1_address = app1.raiden.address app2_address = app2.raiden.address 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 token1 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 token2 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_address2), 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 token1 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() # Payment 4: app1 also sends some tokens to app2 identifier4 = PaymentID(40) amount4 = PaymentAmount(5) secret4, secrethash4 = factories.make_secret_with_hash() request = grequests.post( api_url_for( app1_server, "token_target_paymentresource", token_address=to_checksum_address(token_address0), target_address=to_checksum_address(app2_address), ), json={ "amount": str(amount4), "identifier": str(identifier4), "secret": to_hex(secret4) }, ) 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( app1.raiden, identifier3, amount3, app1.raiden.alarm.sleep_time, secrethash3) msg = f"Unexpected transfer result: {str(result)}" assert result == TransferWaitResult.UNLOCKED, msg result = wait_for_received_transfer_result( app2.raiden, identifier4, amount4, app2.raiden.alarm.sleep_time, secrethash4) 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_address2), } event_sent_3 = { "event": "EventPaymentSentSuccess", "identifier": str(identifier3), "target": to_checksum_address(app1_address), "token_address": to_checksum_address(token_address0), } event_sent_4 = { "event": "EventPaymentSentSuccess", "identifier": str(identifier4), "target": to_checksum_address(app2_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_address2), } event_received_3 = { "event": "EventPaymentReceivedSuccess", "identifier": str(identifier3), "initiator": to_checksum_address(app0_address), "token_address": to_checksum_address(token_address0), } event_received_4 = { "event": "EventPaymentReceivedSuccess", "identifier": str(identifier4), "initiator": to_checksum_address(app1_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) assert must_have_event(json_response, event_sent_4) # 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) assert must_have_event(json_response, event_received_4) # 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) assert must_have_events(json_response, event_sent_4) # 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 must_have_events(json_response, event_received_4) # test endpoint of app2 without partner for token_address2 request = grequests.get( api_url_for(app2_server, "token_paymentresource", token_address=token_address2)) 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_address2, 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) == 1 assert must_have_event(json_response, event_sent_4) 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_address2, 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 must_have_events(json_response, event_received_4) 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_broadcast_messages_must_be_sent_before_protocol_messages_on_restarts( raiden_network, number_of_nodes, token_addresses, network_wait, user_deposit_address ): """ Raiden must broadcast the latest known balance proof on restarts. Regression test for: https://github.com/raiden-network/raiden/issues/3656. """ app0, app1 = raiden_network app0.config["services"]["monitoring_enabled"] = True # Send a transfer to make sure the state has a balance proof to broadcast token_address = token_addresses[0] amount = PaymentAmount(10) payment_id = PaymentID(23) transfer( initiator_app=app1, target_app=app0, token_address=token_address, amount=amount, identifier=payment_id, timeout=network_wait * number_of_nodes, ) app0.stop() transport = MatrixTransport(app0.config["transport"]["matrix"]) transport.send_async = Mock() # type: ignore transport._send_raw = Mock() # type: ignore old_start_transport = transport.start # Asserts the balance proofs are broadcasted before protocol messages def start_transport(*args, **kwargs): # Before restart the transport's broadcast queue should be initialized # There should be 3 messages in the queue: # - A `MonitorRequest` to the MS # - A `PFSCapacityUpdate` # - A `PFSFeeUpdate` queue_copy = transport._broadcast_queue.copy() queued_messages = list() for _ in range(len(transport._broadcast_queue)): queued_messages.append(queue_copy.get()) def num_matching_queued_messages(room: str, message_type: Type) -> int: return len( [ item for item in queued_messages if item[0] == room and type(item[1]) == message_type ] ) assert num_matching_queued_messages(MONITORING_BROADCASTING_ROOM, RequestMonitoring) == 1 assert num_matching_queued_messages(PATH_FINDING_BROADCASTING_ROOM, PFSFeeUpdate) == 1 assert num_matching_queued_messages(PATH_FINDING_BROADCASTING_ROOM, PFSCapacityUpdate) == 1 old_start_transport(*args, **kwargs) transport.start = start_transport # type: ignore 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_one_to_n_address=app0.raiden.default_one_to_n_address, default_secret_registry=app0.raiden.default_secret_registry, default_service_registry=app0.raiden.default_service_registry, default_msc_address=app0.raiden.default_msc_address, transport=transport, raiden_event_handler=RaidenEventHandler(), message_handler=MessageHandler(), routing_mode=RoutingMode.PFS, # not private mode, otherwise no PFS updates are queued user_deposit=app0.raiden.proxy_manager.user_deposit(user_deposit_address), ) app0_restart.start()
def from_dict(cls, data: Dict[str, Any]) -> "ActionCancelPayment": return cls( payment_identifier=PaymentID(int(data["payment_identifier"])))
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_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 from_dict(cls, data: Dict[str, Any]) -> 'ActionCancelPayment': return cls( payment_identifier=PaymentID(int(data['payment_identifier'])), )