def test_channel_new(raiden_chain, retry_timeout, token_addresses): app0, app1 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] channelcount0 = views.total_token_network_channels( views.state_from_app(app0), registry_address, token_address, ) RaidenAPI(app0.raiden).channel_open( registry_address, token_address, app1.raiden.address, ) wait_both_channel_open(app0, app1, registry_address, token_address, retry_timeout) # The channel is created but without funds channelcount1 = views.total_token_network_channels( views.state_from_app(app0), registry_address, token_address, ) assert channelcount0 + 1 == channelcount1
def test_automatic_secret_registration(raiden_chain, token_addresses): app0, app1 = raiden_chain token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) amount = 100 identifier = 1 hold_event_handler = HoldOffChainSecretRequest() app1.raiden.raiden_event_handler = hold_event_handler target = app1.raiden.address secret = sha3(target) secrethash = sha3(secret) hold_event_handler.hold_secretrequest_for(secrethash=secrethash) app0.raiden.start_mediated_transfer_with_secret( token_network_identifier, amount, target, identifier, secret, ) gevent.sleep(1) # wait for the messages to be exchanged # Stop app0 to avoid sending the unlock app0.raiden.transport.stop() reveal_secret = RevealSecret( random.randint(0, UINT64_MAX), secret, ) app0.raiden.sign(reveal_secret) message_handler.MessageHandler().on_message(app1.raiden, reveal_secret) chain_state = views.state_from_app(app1) secrethash = sha3(secret) target_task = chain_state.payment_mapping.secrethashes_to_task[secrethash] lock_expiration = target_task.target_state.transfer.lock.expiration wait_until_block(app1.raiden.chain, lock_expiration) assert app1.raiden.default_secret_registry.check_registered(secrethash)
def test_udp_ping_pong_unreachable_node(raiden_network, skip_if_not_udp): app0, app1 = raiden_network app1.raiden.transport.stop_and_wait() ping_message = Ping(nonce=0) app0.raiden.sign(ping_message) ping_encoded = ping_message.encode() messageid = ('ping', ping_message.nonce, app1.raiden.address) async_result = app0.raiden.transport.maybe_sendraw_with_result( app1.raiden.address, ping_encoded, messageid, ) nat_keepalive_fail = ( app0.config['transport']['nat_keepalive_timeout'] * app0.config['transport']['nat_keepalive_retries'] * 2 # wait a bit longer to avoid races ) msg = "The message was dropped, it can't be acknowledged" assert async_result.wait(nat_keepalive_fail) is None, msg network_state = views.get_node_network_status( views.state_from_app(app0), app1.raiden.address, ) assert network_state is state.NODE_NETWORK_UNREACHABLE
def test_regression_unfiltered_routes( raiden_network, token_addresses, settle_timeout, deposit, ): """ The transfer should proceed without triggering an assert. Transfers failed in networks where two or more paths to the destination are possible but they share same node as a first hop. """ app0, app1, app2, app3, app4 = raiden_network token = token_addresses[0] registry_address = app0.raiden.default_registry.address # Topology: # # 0 -> 1 -> 2 -> 4 # | ^ # +--> 3 ---+ app_channels = [ (app0, app1), (app1, app2), (app1, app3), (app3, app4), (app2, app4), ] greenlets = [] for first_app, second_app in app_channels: greenlets.append(gevent.spawn( payment_channel_open_and_deposit, first_app, second_app, token, deposit, settle_timeout, )) gevent.wait(greenlets) wait_for_channels( app_channels, registry_address, [token], deposit, ) payment_network_identifier = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), payment_network_identifier, token, ) transfer = app0.raiden.mediated_transfer_async( token_network_identifier=token_network_identifier, amount=1, target=app4.raiden.address, identifier=1, ) assert transfer.wait()
def test_close_channel_lack_of_balance_proof(raiden_chain, deposit, token_addresses): app0, app1 = raiden_chain token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) token_proxy = app0.raiden.chain.token(token_address) initial_balance0 = token_proxy.balance_of(app0.raiden.address) initial_balance1 = token_proxy.balance_of(app1.raiden.address) amount = 100 identifier = 1 secret = pending_mediated_transfer( raiden_chain, token_network_identifier, amount, identifier, ) # Stop app0 to avoid sending the unlock app0.raiden.transport.stop_and_wait() reveal_secret = RevealSecret( random.randint(0, UINT64_MAX), secret, ) app0.raiden.sign(reveal_secret) message_handler.on_message(app1.raiden, reveal_secret) RaidenAPI(app0.raiden).channel_close( app0.raiden.default_registry.address, token_address, app1.raiden.address, ) channel_state = get_channelstate(app0, app1, token_network_identifier) waiting.wait_for_settle( app0.raiden, app0.raiden.default_registry.address, token_address, [channel_state.identifier], app0.raiden.alarm.sleep_time, ) # wait for the node to call batch unlock with gevent.Timeout(10): wait_for_batch_unlock( app0, token_network_identifier, channel_state.partner_state.address, channel_state.our_state.address, ) expected_balance0 = initial_balance0 + deposit - amount expected_balance1 = initial_balance1 + deposit + amount assert token_proxy.balance_of(app0.raiden.address) == expected_balance0 assert token_proxy.balance_of(app1.raiden.address) == expected_balance1
def direct_transfer( initiator_app, target_app, token_network_identifier, amount, identifier=None, timeout=5, ): """ Nice to read shortcut to make a DirectTransfer. """ channel_state = views.get_channelstate_by_token_network_and_partner( views.state_from_app(initiator_app), token_network_identifier, target_app.raiden.address, ) assert channel_state, 'there is not a direct channel' initiator_app.raiden.direct_transfer_async( token_network_identifier, amount, target_app.raiden.address, identifier, ) # direct transfers don't have confirmation gevent.sleep(timeout)
def test_failsfast_lockedtransfer_exceeding_distributable( raiden_network, token_addresses, deposit, ): app0, app1 = raiden_network token_address = token_addresses[0] payment_network_identifier = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), payment_network_identifier, token_address, ) result = app0.raiden.mediated_transfer_async( token_network_identifier, deposit * 2, app1.raiden.address, identifier=1, ) assert result.successful() assert result.get_nowait() is False assert_synced_channel_state( token_network_identifier, app0, deposit, [], app1, deposit, [], )
def get_channelstate(app0, app1, token_network_identifier) -> NettingChannelState: channel_state = views.get_channelstate_by_token_network_and_partner( views.state_from_app(app0), token_network_identifier, app1.raiden.address, ) return channel_state
def pending_mediated_transfer(app_chain, token_network_identifier, amount, identifier): """ Nice to read shortcut to make a LockedTransfer where the secret is _not_ revealed. While the secret is not revealed all apps will be synchronized, meaning they are all going to receive the LockedTransfer message. Returns: The secret used to generate the LockedTransfer """ # pylint: disable=too-many-locals if len(app_chain) < 2: raise ValueError('Cannot make a LockedTransfer with less than two apps') target = app_chain[-1].raiden.address # Generate a secret initiator_channel = views.get_channelstate_by_token_network_and_partner( views.state_from_app(app_chain[0]), token_network_identifier, app_chain[1].raiden.address, ) address = initiator_channel.identifier nonce_int = channel.get_next_nonce(initiator_channel.our_state) nonce_bytes = nonce_int.to_bytes(2, 'big') secret = sha3(address + nonce_bytes) initiator_app = app_chain[0] init_initiator_statechange = initiator_init( initiator_app.raiden, identifier, amount, secret, token_network_identifier, target, ) events = initiator_app.raiden.wal.log_and_dispatch( init_initiator_statechange, initiator_app.raiden.get_block_number(), ) send_transfermessage = must_contain_entry(events, SendLockedTransfer, {}) transfermessage = LockedTransfer.from_event(send_transfermessage) initiator_app.raiden.sign(transfermessage) for mediator_app in app_chain[1:-1]: mediator_init_statechange = mediator_init(mediator_app.raiden, transfermessage) events = mediator_app.raiden.wal.log_and_dispatch( mediator_init_statechange, mediator_app.raiden.get_block_number(), ) send_transfermessage = must_contain_entry(events, SendLockedTransfer, {}) transfermessage = LockedTransfer.from_event(send_transfermessage) mediator_app.raiden.sign(transfermessage) target_app = app_chain[-1] mediator_init_statechange = target_init(transfermessage) events = target_app.raiden.wal.log_and_dispatch( mediator_init_statechange, target_app.raiden.get_block_number(), ) return secret
def test_direct_transfer(raiden_network, token_addresses, deposit, network_wait): token_address = token_addresses[0] app0, app1 = raiden_network amount = 10 chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) direct_transfer( app0, app1, token_network_identifier, amount, timeout=network_wait, ) assert_synched_channel_state( token_network_identifier, app0, deposit - amount, [], app1, deposit + amount, [], )
def test_receive_directtransfer_invalidnonce(raiden_network, deposit, token_addresses): app0, app1 = raiden_network token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) channel0 = get_channelstate(app0, app1, token_network_identifier) transferred_amount = 10 same_payment_identifier = 1 message_identifier = random.randint(0, UINT64_MAX) event = channel.send_directtransfer( channel0, transferred_amount, message_identifier, same_payment_identifier, ) direct_transfer_message = DirectTransfer.from_event(event) sign_and_inject( direct_transfer_message, app0.raiden.private_key, app0.raiden.address, app1, ) # Send a *different* direct transfer with the *same nonce* invalid_transferred_amount = transferred_amount // 2 message_identifier = random.randint(0, UINT64_MAX) invalid_direct_transfer_message = DirectTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=message_identifier, payment_identifier=same_payment_identifier, nonce=1, token_network_address=token_network_identifier, token=token_address, channel_identifier=channel0.identifier, transferred_amount=invalid_transferred_amount, locked_amount=0, recipient=app1.raiden.address, locksroot=EMPTY_MERKLE_ROOT, ) sign_and_inject( invalid_direct_transfer_message, app0.raiden.private_key, app0.raiden.address, app1, ) assert_synched_channel_state( token_network_identifier, app0, deposit - transferred_amount, [], app1, deposit + transferred_amount, [], )
def test_mediated_transfer_events(raiden_network, number_of_nodes, token_addresses, network_wait): app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) amount = 10 mediated_transfer( app0, app2, token_network_identifier, amount, timeout=network_wait * number_of_nodes, ) def test_initiator_events(): initiator_blockevents = app0.raiden.wal.storage.get_events_by_identifier( from_identifier=0, to_identifier='latest', ) initiator_events = [blocknumber_event[1] for blocknumber_event in initiator_blockevents] return ( must_contain_entry(initiator_events, SendRevealSecret, {}) and must_contain_entry(initiator_events, EventUnlockSuccess, {}) ) assert wait_until(test_initiator_events, network_wait) def test_mediator_events(): mediator_blockevents = app1.raiden.wal.storage.get_events_by_identifier( from_identifier=0, to_identifier='latest', ) mediator_events = [blocknumber_event[1] for blocknumber_event in mediator_blockevents] return ( must_contain_entry(mediator_events, EventUnlockSuccess, {}) and must_contain_entry(mediator_events, EventUnlockClaimSuccess, {}) ) assert wait_until(test_mediator_events, network_wait) def test_target_events(): target_blockevents = app2.raiden.wal.storage.get_events_by_identifier( from_identifier=0, to_identifier='latest', ) target_events = [blocknumber_event[1] for blocknumber_event in target_blockevents] return ( must_contain_entry(target_events, SendSecretRequest, {}) and must_contain_entry(target_events, SendRevealSecret, {}) and must_contain_entry(target_events, EventUnlockClaimSuccess, {}) ) assert wait_until(test_target_events, network_wait)
def test_refund_messages(raiden_chain, token_addresses, deposit): # The network has the following topology: # # App0 <---> App1 <---> App2 app0, app1, app2 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking token_address = token_addresses[0] payment_network_identifier = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), payment_network_identifier, token_address, ) # Exhaust the channel App1 <-> App2 (to force the refund transfer) exhaust_amount = deposit mediated_transfer( initiator_app=app1, target_app=app2, token_network_identifier=token_network_identifier, amount=exhaust_amount, identifier=1, ) refund_amount = deposit // 2 identifier = 1 async_result = app0.raiden.mediated_transfer_async( token_network_identifier, refund_amount, app2.raiden.address, identifier, ) assert async_result.wait() is False, 'Must fail, there are no routes available' # 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_must_contain_entry( app0.raiden, SendLockedTransfer, {'transfer': {'lock': {'amount': refund_amount}}}, ) assert send_lockedtransfer send_refundtransfer = raiden_events_must_contain_entry(app1.raiden, SendRefundTransfer, {}) assert send_refundtransfer assert_synced_channel_state( token_network_identifier, app0, deposit, [send_lockedtransfer.transfer.lock], app1, deposit, [send_refundtransfer.transfer.lock], ) # This channel was exhausted to force the refund transfer assert_synced_channel_state( token_network_identifier, app1, 0, [], app2, deposit * 2, [], )
def test_receive_secrethashtransfer_unknown(raiden_network, token_addresses): app0 = raiden_network[0] token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) other_key = HOP1_KEY other_address = HOP1 channel_identifier = make_channel_identifier() amount = 10 refund_transfer_message = make_refund_transfer( payment_identifier=1, nonce=1, token_network_address=token_network_identifier, token=token_address, channel_identifier=channel_identifier, transferred_amount=amount, recipient=app0.raiden.address, locksroot=UNIT_SECRETHASH, amount=amount, secrethash=UNIT_SECRETHASH, ) sign_and_inject(refund_transfer_message, other_key, other_address, app0) secret = Secret( chain_id=UNIT_CHAIN_ID, message_identifier=random.randint(0, UINT64_MAX), payment_identifier=1, nonce=1, channel_identifier=channel_identifier, token_network_address=token_network_identifier, transferred_amount=amount, locked_amount=0, locksroot=UNIT_SECRETHASH, secret=UNIT_SECRET, ) sign_and_inject(secret, other_key, other_address, app0) secret_request_message = SecretRequest( message_identifier=random.randint(0, UINT64_MAX), payment_identifier=1, secrethash=UNIT_SECRETHASH, amount=1, expiration=refund_transfer_message.lock.expiration, ) sign_and_inject(secret_request_message, other_key, other_address, app0) reveal_secret_message = RevealSecret( message_identifier=random.randint(0, UINT64_MAX), secret=UNIT_SECRET, ) sign_and_inject(reveal_secret_message, other_key, other_address, app0)
def test_locked_transfer_secret_registered_onchain( raiden_network, token_addresses, secret_registry_address, ): app0 = raiden_network[0] token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) amount = 1 target = UNIT_TRANSFER_INITIATOR identifier = 1 transfer_secret = sha3(target + b'1') secret_registry_proxy = app0.raiden.chain.secret_registry( secret_registry_address, ) secret_registry_proxy.register_secret(transfer_secret) # Test that sending a transfer with a secret already registered on-chain fails with pytest.raises(RaidenUnrecoverableError): app0.raiden.start_mediated_transfer_with_secret( token_network_identifier, amount, target, identifier, transfer_secret, ) expiration = 9999 transfer = make_signed_transfer( amount, UNIT_TRANSFER_INITIATOR, app0.raiden.address, expiration, transfer_secret, ) message_handler = MessageHandler() message_handler.handle_message_lockedtransfer( app0.raiden, transfer, ) state_changes = app0.raiden.wal.storage.get_statechanges_by_identifier(0, 'latest') transfer_statechange_dispatched = ( must_contain_entry(state_changes, ActionInitMediator, {}) or must_contain_entry(state_changes, ActionInitTarget, {}) ) assert not transfer_statechange_dispatched
def test_settle_is_automatically_called(raiden_network, token_addresses, deposit): """Settle is automatically called by one of the nodes.""" app0, app1 = raiden_network registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) channel_identifier = get_channelstate(app0, app1, token_network_identifier).identifier # A ChannelClose event will be generated, this will be polled by both apps # and each must start a task for calling settle RaidenAPI(app1.raiden).channel_close( registry_address, token_address, app0.raiden.address, ) waiting.wait_for_settle( app0.raiden, registry_address, token_address, [channel_identifier], app0.raiden.alarm.sleep_time, ) assert_synched_channel_state( token_network_identifier, app0, deposit, [], app1, deposit, [], ) state_changes = app0.raiden.wal.storage.get_statechanges_by_identifier( from_identifier=0, to_identifier='latest', ) channel_state = get_channelstate(app0, app1, token_network_identifier) assert channel_state.close_transaction.finished_block_number assert channel_state.settle_transaction.finished_block_number assert must_contain_entry(state_changes, ContractReceiveChannelClosed, { 'token_network_identifier': token_network_identifier, 'channel_identifier': channel_identifier, 'closing_address': app1.raiden.address, 'closed_block_number': channel_state.close_transaction.finished_block_number, }) assert must_contain_entry(state_changes, ContractReceiveChannelSettled, { 'token_network_identifier': token_network_identifier, 'channel_identifier': channel_identifier, 'settle_block_number': channel_state.settle_transaction.finished_block_number, })
def test_received_directtransfer_closedchannel(raiden_network, token_addresses, deposit): app0, app1 = raiden_network token_address = token_addresses[0] registry_address = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), registry_address, token_address, ) channel0 = get_channelstate(app0, app1, token_network_identifier) RaidenAPI(app1.raiden).channel_close( registry_address, token_address, app0.raiden.address, ) wait_until_block( app0.raiden.chain, app0.raiden.chain.block_number() + 1, ) # Now receive one direct transfer for the closed channel message_identifier = random.randint(0, UINT64_MAX) direct_transfer_message = DirectTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=message_identifier, payment_identifier=1, nonce=1, token_network_address=token_network_identifier, token=token_address, channel_identifier=channel0.identifier, transferred_amount=10, locked_amount=0, recipient=app0.raiden.address, locksroot=EMPTY_MERKLE_ROOT, ) sign_and_inject( direct_transfer_message, app0.raiden.private_key, app0.raiden.address, app1, ) # The local state must not change since the channel is already closed assert_synched_channel_state( token_network_identifier, app0, deposit, [], app1, deposit, [], )
def test_receive_lockedtransfer_invalidrecipient( raiden_network, token_addresses, reveal_timeout, deposit, ): app0, app1 = raiden_network token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) channel0 = get_channelstate(app0, app1, token_network_identifier) payment_identifier = 1 invalid_recipient = make_address() lock_amount = 10 expiration = reveal_timeout * 2 mediated_transfer_message = LockedTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=random.randint(0, UINT64_MAX), payment_identifier=payment_identifier, nonce=1, token_network_address=token_network_identifier, token=token_address, channel_identifier=channel0.identifier, transferred_amount=0, locked_amount=lock_amount, recipient=invalid_recipient, locksroot=UNIT_SECRETHASH, lock=Lock(lock_amount, expiration, UNIT_SECRETHASH), target=app1.raiden.address, initiator=app0.raiden.address, fee=0, ) sign_and_inject( mediated_transfer_message, app0.raiden.private_key, app0.raiden.address, app1, ) assert_synced_channel_state( token_network_identifier, app0, deposit, [], app1, deposit, [], )
def test_mediated_transfer_with_entire_deposit( raiden_network, number_of_nodes, token_addresses, deposit, network_wait, public_and_private_rooms, ): app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) mediated_transfer( app0, app2, token_network_identifier, deposit, timeout=network_wait * number_of_nodes, ) mediated_transfer( app2, app0, token_network_identifier, deposit * 2, timeout=network_wait * number_of_nodes, ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit * 2, [], app1, 0, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit * 2, [], app2, 0, [], )
def test_receive_directtransfer_invalidlocksroot(raiden_network, token_addresses): app0, app1 = raiden_network token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) channel0 = get_channelstate(app0, app1, token_network_identifier) balance0 = channel.get_balance(channel0.our_state, channel0.partner_state) balance1 = channel.get_balance(channel0.partner_state, channel0.our_state) payment_identifier = 1 invalid_locksroot = UNIT_SECRETHASH channel_identifier = channel0.identifier message_identifier = random.randint(0, UINT64_MAX) direct_transfer_message = DirectTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=message_identifier, payment_identifier=payment_identifier, nonce=1, token_network_address=token_network_identifier, token=token_address, channel_identifier=channel_identifier, transferred_amount=0, locked_amount=0, recipient=app1.raiden.address, locksroot=invalid_locksroot, ) sign_and_inject( direct_transfer_message, app0.raiden.private_key, app0.raiden.address, app1, ) assert_synched_channel_state( token_network_identifier, app0, balance0, [], app1, balance1, [], )
def test_log_directransfer(raiden_chain, token_addresses, deposit): """The action that starts a direct transfer must be logged in the WAL.""" app0, app1 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) amount = int(deposit / 2.) payment_identifier = 13 direct_transfer( app0, app1, token_network_identifier, amount, payment_identifier, ) app0_state_changes = app0.raiden.wal.storage.get_statechanges_by_identifier( from_identifier=0, to_identifier='latest', ) assert must_contain_entry(app0_state_changes, ActionTransferDirect, { 'token_network_identifier': token_network_identifier, 'amount': amount, 'receiver_address': app1.raiden.address, }) app1_state_changes = app1.raiden.wal.storage.get_statechanges_by_identifier( from_identifier=0, to_identifier='latest', ) assert must_contain_entry(app1_state_changes, ReceiveTransferDirect, { 'token_network_identifier': token_network_identifier, 'payment_identifier': payment_identifier, 'balance_proof': { 'transferred_amount': amount, 'sender': app0.raiden.address, }, })
def test_regression_revealsecret_after_secret(raiden_network, token_addresses, transport_protocol): """ A RevealSecret message received after a Secret message must be cleanly handled. """ app0, app1, app2 = raiden_network token = token_addresses[0] identifier = 1 payment_network_identifier = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), payment_network_identifier, token, ) transfer = app0.raiden.mediated_transfer_async( token_network_identifier, amount=1, target=app2.raiden.address, identifier=identifier, ) assert transfer.wait() event = must_contain_entry( app1.raiden.wal.storage.get_events(), SendSecretReveal, {}, ) assert event message_identifier = random.randint(0, UINT64_MAX) reveal_secret = RevealSecret( message_identifier, event.secret, ) app2.raiden.sign(reveal_secret) if transport_protocol is TransportProtocol.UDP: reveal_data = reveal_secret.encode() host_port = None app1.raiden.transport.receive(reveal_data, host_port) elif transport_protocol is TransportProtocol.MATRIX: app1.raiden.transport._receive_message(reveal_secret) # pylint: disable=protected-access else: raise TypeError('Unknown TransportProtocol')
def test_failfast_lockedtransfer_nochannel(raiden_network, token_addresses): """When the node has no channels it should fail without raising exceptions.""" token_address = token_addresses[0] app0, app1 = raiden_network amount = 10 payment_network_identifier = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), payment_network_identifier, token_address, ) async_result = app0.raiden.mediated_transfer_async( token_network_identifier, amount, app1.raiden.address, identifier=1, ) assert async_result.wait() is False
def test_mediated_transfer_messages_out_of_order( raiden_network, number_of_nodes, deposit, token_addresses, network_wait, skip_if_not_matrix, ): app0, app1, app2 = raiden_network _patch_transport(app1.raiden.transport) _patch_transport(app2.raiden.transport) token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) amount = 10 mediated_transfer( app0, app2, token_network_identifier, amount, timeout=network_wait * number_of_nodes, ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit - amount, [], app1, deposit + amount, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit - amount, [], app2, deposit + amount, [], )
def test_receive_directtransfer_invalidtoken(raiden_network, deposit, token_addresses): app0, app1 = raiden_network token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) channel0 = get_channelstate(app0, app1, token_network_identifier) message_identifier = random.randint(0, UINT64_MAX) payment_identifier = 1 invalid_token_address = make_address() channel_identifier = channel0.identifier direct_transfer_message = DirectTransfer( chain_id=UNIT_CHAIN_ID, message_identifier=message_identifier, payment_identifier=payment_identifier, nonce=1, token_network_address=token_network_identifier, token=invalid_token_address, channel_identifier=channel_identifier, transferred_amount=0, locked_amount=0, recipient=app1.raiden.address, locksroot=EMPTY_MERKLE_ROOT, ) sign_and_inject( direct_transfer_message, app0.raiden.private_key, app0.raiden.address, app1, ) assert_synched_channel_state( token_network_identifier, app0, deposit, [], app1, deposit, [], )
def transfer(initiator_app, target_app, token, amount, identifier): """ Nice to read shortcut to make a transfer. The transfer is either a DirectTransfer or a LockedTransfer, in both cases all apps are synched, in the case of a LockedTransfer the secret will be revealed. """ payment_network_identifier = initiator_app.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(initiator_app), payment_network_identifier, token, ) async_result = initiator_app.raiden.mediated_transfer_async( token_network_identifier, amount, target_app.raiden.address, identifier, ) assert async_result.wait()
def test_udp_ping_pong(raiden_network, skip_if_not_udp): app0, app1 = raiden_network ping_message = Ping(nonce=0) app0.raiden.sign(ping_message) ping_encoded = ping_message.encode() messageid = ('ping', ping_message.nonce, app1.raiden.address) async_result = app0.raiden.transport.maybe_sendraw_with_result( app1.raiden.address, ping_encoded, messageid, ) assert async_result.wait(2), 'The message was not processed' network_state = views.get_node_network_status( views.state_from_app(app0), app1.raiden.address, ) assert network_state is state.NODE_NETWORK_REACHABLE
def test_initiator_log_directransfer_success(raiden_chain, token_addresses, deposit): app0, app1 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking token_address = token_addresses[0] amount = int(deposit / 2.) identifier = 7 token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) direct_transfer( app0, app1, token_network_identifier, amount, identifier, ) app0_events = app0.raiden.wal.storage.get_events_by_identifier( from_identifier=0, to_identifier='latest', ) app0_all_events = [event for _, event in app0_events] assert must_contain_entry(app0_all_events, EventTransferSentSuccess, { 'identifier': identifier, 'amount': amount, 'target': app1.raiden.address, }) app1_state_events = app1.raiden.wal.storage.get_events_by_identifier( from_identifier=0, to_identifier='latest', ) app1_all_events = [event for _, event in app1_state_events] assert must_contain_entry(app1_all_events, EventTransferReceivedSuccess, { 'identifier': identifier, 'amount': amount, 'initiator': app0.raiden.address, })
def test_direct_transfer_to_offline_node(raiden_network, token_addresses, deposit): app0, app1 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) # Wait until the initialization of the node is complete and then stop it gevent.wait([app1.raiden.start_event]) app1.raiden.stop() amount = 10 target = app1.raiden.address payment_identifier = 13 app0.raiden.direct_transfer_async( token_network_identifier, amount, target, identifier=payment_identifier, ) app1.raiden.start() exception = ValueError('Waiting for transfer received success in the WAL timed out') with gevent.Timeout(seconds=5, exception=exception): wait_for_transfer_success( app1.raiden, payment_identifier, amount, app1.raiden.alarm.sleep_time, ) no_outstanding_locks = [] assert_synched_channel_state( token_network_identifier, app0, deposit - amount, no_outstanding_locks, app1, deposit + amount, no_outstanding_locks, )
def test_mediated_transfer( raiden_network, number_of_nodes, deposit, token_addresses, network_wait, ): app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) amount = 10 mediated_transfer( app0, app2, token_network_identifier, amount, timeout=network_wait * number_of_nodes, ) wait_assert( token_network_identifier, app0, deposit - amount, [], app1, deposit + amount, [], func=assert_synched_channel_state, timeout=network_wait, ) wait_assert( token_network_identifier, app1, deposit - amount, [], app2, deposit + amount, [], func=assert_synched_channel_state, timeout=network_wait, )
def test_batch_unlock(raiden_network, token_addresses, secret_registry_address, deposit): """Batch unlock can be called after the channel is settled.""" alice_app, bob_app = raiden_network registry_address = alice_app.raiden.default_registry.address token_address = token_addresses[0] token_proxy = alice_app.raiden.chain.token(token_address) token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(alice_app), alice_app.raiden.default_registry.address, token_address, ) alice_initial_balance = token_proxy.balance_of(alice_app.raiden.address) bob_initial_balance = token_proxy.balance_of(bob_app.raiden.address) alice_to_bob_amount = 10 identifier = 1 secret = pending_mediated_transfer( raiden_network, token_network_identifier, alice_to_bob_amount, identifier, ) secrethash = sha3(secret) secret_registry_proxy = alice_app.raiden.chain.secret_registry( secret_registry_address, ) secret_registry_proxy.register_secret(secret) alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_identifier) lock = channel.get_lock(alice_bob_channel_state.our_state, secrethash) assert lock assert lock.expiration > alice_app.raiden.get_block_number() assert lock.secrethash == sha3(secret) # This is the current state of the protocol: # # A -> B LockedTransfer # B -> A SecretRequest # - protocol didn't continue assert_synched_channel_state( token_network_identifier, alice_app, deposit, [lock], bob_app, deposit, [], ) # A ChannelClose event will be generated, this will be polled by both apps # and each must start a task for calling settle RaidenAPI(bob_app.raiden).channel_close( registry_address, token_address, alice_app.raiden.address, ) waiting.wait_for_settle( alice_app.raiden, registry_address, token_address, [alice_bob_channel_state.identifier], alice_app.raiden.alarm.wait_time, ) # wait for the node to call batch unlock with gevent.Timeout(10): wait_for_batch_unlock( alice_app, alice_bob_channel_state.identifier, token_network_identifier, ) alice_new_balance = alice_initial_balance + deposit - alice_to_bob_amount bob_new_balance = bob_initial_balance + deposit + alice_to_bob_amount assert token_proxy.balance_of(alice_app.raiden.address) == alice_new_balance assert token_proxy.balance_of(bob_app.raiden.address) == bob_new_balance
def test_refund_transfer(raiden_chain, number_of_nodes, token_addresses, deposit, network_wait): """A failed transfer must send a refund back. TODO: - Unlock the token on refund #1091 - Clear the merkletree and update the locked amount #193 - Remove the refund message type #490""" # Topology: # # 0 -> 1 -> 2 # app0, app1, app2 = raiden_chain token_address = token_addresses[0] payment_network_identifier = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), payment_network_identifier, token_address, ) # make a transfer to test the path app0 -> app1 -> app2 identifier_path = 1 amount_path = 1 mediated_transfer( app0, app2, token_network_identifier, amount_path, identifier_path, timeout=network_wait * number_of_nodes, ) # drain the channel app1 -> app2 identifier_drain = 2 amount_drain = deposit * 8 // 10 direct_transfer( app1, app2, token_network_identifier, amount_drain, identifier_drain, timeout=network_wait, ) # wait for the nodes to sync gevent.sleep(0.2) assert_synced_channel_state( token_network_identifier, app0, deposit - amount_path, [], app1, deposit + amount_path, [], ) assert_synced_channel_state( token_network_identifier, app1, deposit - amount_path - amount_drain, [], app2, deposit + amount_path + amount_drain, [], ) # app0 -> app1 -> app2 is the only available path, but the channel app1 -> # app2 doesn't have capacity, so a refund will be sent on app1 -> app0 identifier_refund = 3 amount_refund = 50 async_result = app0.raiden.mediated_transfer_async( token_network_identifier, amount_refund, app2.raiden.address, identifier_refund, ) assert async_result.wait( ) is False, 'there is no path with capacity, the transfer must fail' gevent.sleep(0.2) # A lock structure with the correct amount send_locked = raiden_events_must_contain_entry( app0.raiden, SendLockedTransfer, {'transfer': { 'lock': { 'amount': amount_refund } }}, ) assert send_locked send_refund = raiden_events_must_contain_entry(app1.raiden, SendRefundTransfer, {}) assert send_refund lock = send_locked.transfer.lock refund_lock = send_refund.lock assert lock.amount == refund_lock.amount assert lock.secrethash assert lock.expiration # Both channels have the amount locked because of the refund message assert_synced_channel_state( token_network_identifier, app0, deposit - amount_path, [lockstate_from_lock(lock)], app1, deposit + amount_path, [lockstate_from_lock(refund_lock)], ) assert_synced_channel_state( token_network_identifier, app1, deposit - amount_path - amount_drain, [], app2, deposit + amount_path + amount_drain, [], )
def test_recovery_happy_case( raiden_network, number_of_nodes, deposit, token_addresses, network_wait, skip_if_not_udp, ): app0, app1, app2 = raiden_network token_address = token_addresses[0] app2_wait_for = WaitForMessage() app2.raiden.message_handler = app2_wait_for chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) # make a few transfers from app0 to app2 amount = 1 spent_amount = deposit - 2 for _ in range(spent_amount): mediated_transfer( app0, app2, token_network_identifier, amount, timeout=network_wait * number_of_nodes, ) app0.raiden.stop() host_port = ( app0.raiden.config['transport']['udp']['host'], app0.raiden.config['transport']['udp']['port'], ) socket = server._udp_socket(host_port) new_transport = UDPTransport( app0.raiden.address, app0.discovery, socket, app0.raiden.transport.throttle_policy, app0.raiden.config['transport']['udp'], ) raiden_event_handler = RaidenEventHandler() message_handler = MessageHandler() app0_restart = App( config=app0.config, chain=app0.raiden.chain, query_start_block=0, default_registry=app0.raiden.default_registry, default_secret_registry=app0.raiden.default_secret_registry, transport=new_transport, raiden_event_handler=raiden_event_handler, message_handler=message_handler, discovery=app0.raiden.discovery, ) app0.stop() del app0 # from here on the app0_restart should be used app0_restart.start() assert_synced_channel_state( token_network_identifier, app0_restart, deposit - spent_amount, [], app1, deposit + spent_amount, [], ) assert_synced_channel_state( token_network_identifier, app1, deposit - spent_amount, [], app2, deposit + spent_amount, [], ) # wait for the nodes' healthcheck to update the network statuses waiting.wait_for_healthy( app0_restart.raiden, app1.raiden.address, network_wait, ) waiting.wait_for_healthy( app1.raiden, app0_restart.raiden.address, network_wait, ) identifier = create_default_identifier() wait_for_payment = app2_wait_for.wait_for_message( Unlock, {'payment_identifier': identifier}) mediated_transfer( app2, app0_restart, token_network_identifier, amount, timeout=network_wait * number_of_nodes * 2, ) mediated_transfer( initiator_app=app0_restart, target_app=app2, token_network_identifier=token_network_identifier, amount=amount, identifier=identifier, timeout=network_wait * number_of_nodes * 2, ) assert_synced_channel_state( token_network_identifier, app0_restart, deposit - spent_amount, [], app1, deposit + spent_amount, [], ) wait_for_payment.wait() assert_synced_channel_state( token_network_identifier, app1, deposit - spent_amount, [], app2, deposit + spent_amount, [], )
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_channel_lifecycle(raiden_network, token_addresses, deposit, transport_config): node1, node2 = raiden_network token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(node1), node1.raiden.default_registry.address, token_address, ) api1 = RaidenAPI(node1.raiden) api2 = RaidenAPI(node2.raiden) registry_address = node1.raiden.default_registry.address if transport_config.protocol == TransportProtocol.UDP: # nodes don't have a channel, so they are not healthchecking assert api1.get_node_network_state( api2.address) == NODE_NETWORK_UNKNOWN assert api2.get_node_network_state( api1.address) == NODE_NETWORK_UNKNOWN elif transport_config.protocol == TransportProtocol.MATRIX: # with Matrix nodes do not need a health check to know each others reachability assert api1.get_node_network_state( api2.address) == NODE_NETWORK_UNREACHABLE assert api2.get_node_network_state( api1.address) == NODE_NETWORK_UNREACHABLE assert not api1.get_channel_list(registry_address, token_address, api2.address) # open is a synchronous api api1.channel_open(node1.raiden.default_registry.address, token_address, api2.address) channels = api1.get_channel_list(registry_address, token_address, api2.address) assert len(channels) == 1 channel12 = get_channelstate(node1, node2, token_network_identifier) assert channel.get_status(channel12) == CHANNEL_STATE_OPENED event_list1 = api1.get_channel_events( channel12.identifier, channel12.open_transaction.finished_block_number, ) assert event_list1 == [] token_events = api1.get_token_network_events( token_address, channel12.open_transaction.finished_block_number, ) assert token_events[0]['event'] == EVENT_CHANNEL_NEW registry_address = api1.raiden.default_registry.address # Load the new state with the deposit api1.set_total_channel_deposit( registry_address, token_address, api2.address, deposit, ) # let's make sure it's idempotent api1.set_total_channel_deposit( registry_address, token_address, api2.address, deposit, ) channel12 = get_channelstate(node1, node2, token_network_identifier) assert channel.get_status(channel12) == CHANNEL_STATE_OPENED assert channel.get_balance(channel12.our_state, channel12.partner_state) == deposit assert channel12.our_state.contract_balance == deposit assert api1.get_channel_list(registry_address, token_address, api2.address) == [channel12] # there is a channel open, they must be healthchecking each other assert api1.get_node_network_state(api2.address) == NODE_NETWORK_REACHABLE assert api2.get_node_network_state(api1.address) == NODE_NETWORK_REACHABLE event_list2 = api1.get_channel_events( channel12.identifier, channel12.open_transaction.finished_block_number, ) assert any( (event['event'] == EVENT_CHANNEL_NEW_BALANCE and is_same_address( event['args']['registry_address'], to_normalized_address(registry_address), ) and is_same_address( event['args']['participant'], to_normalized_address(api1.address), )) for event in event_list2) api1.channel_close(registry_address, token_address, api2.address) # Load the new state with the channel closed channel12 = get_channelstate(node1, node2, token_network_identifier) event_list3 = api1.get_channel_events( channel12.identifier, channel12.open_transaction.finished_block_number, ) assert len(event_list3) > len(event_list2) assert any((event['event'] == EVENT_CHANNEL_CLOSED and is_same_address( event['args']['registry_address'], to_normalized_address(registry_address), ) and is_same_address( event['args']['closing_address'], to_normalized_address(api1.address), )) for event in event_list3) assert channel.get_status(channel12) == CHANNEL_STATE_CLOSED settlement_block = ( channel12.close_transaction.finished_block_number + channel12.settle_timeout + 10 # arbitrary number of additional blocks, used to wait for the settle() call ) wait_until_block(node1.raiden.chain, settlement_block) # Load the new state with the channel settled channel12 = get_channelstate(node1, node2, token_network_identifier) assert channel.get_status(channel12) == CHANNEL_STATE_SETTLED
def run_test_mediated_transfer_with_node_consuming_more_than_allocated_fee( raiden_network, number_of_nodes, deposit, token_addresses, network_wait, ): app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) fee = 5 amount = 10 app1_app2_channel_state = views.get_channelstate_by_token_network_and_partner( chain_state=views.state_from_raiden(app1.raiden), token_network_id=token_network_identifier, partner_address=app2.raiden.address, ) # Let app1 consume all of the allocated mediation fee action_set_fee = ActionChannelSetFee( canonical_identifier=app1_app2_channel_state.canonical_identifier, mediation_fee=fee * 2, ) app1.raiden.handle_state_change(state_change=action_set_fee, ) secret = factories.make_secret(0) secrethash = sha3(secret) wait_message_handler = WaitForMessage() app0.raiden.message_handler = wait_message_handler secret_request_received = wait_message_handler.wait_for_message( SecretRequest, {'secrethash': secrethash}, ) app0.raiden.start_mediated_transfer_with_secret( token_network_identifier=token_network_identifier, amount=amount, fee=fee, target=app2.raiden.address, identifier=1, secret=secret, ) app0_app1_channel_state = views.get_channelstate_by_token_network_and_partner( chain_state=views.state_from_raiden(app0.raiden), token_network_id=token_network_identifier, partner_address=app1.raiden.address, ) msg = 'App0 should have the transfer in secrethashes_to_lockedlocks' assert secrethash in app0_app1_channel_state.our_state.secrethashes_to_lockedlocks, msg msg = 'App0 should have locked the amount + fee' lock_amount = app0_app1_channel_state.our_state.secrethashes_to_lockedlocks[ secrethash].amount assert lock_amount == amount + fee, msg secret_request_received.wait() app0_chain_state = views.state_from_app(app0) initiator_task = app0_chain_state.payment_mapping.secrethashes_to_task[ secrethash] msg = 'App0 should have never revealed the secret' assert initiator_task.manager_state.initiator_transfers[ secrethash].revealsecret is None
def run_test_mediated_transfer_messages_out_of_order( raiden_network, deposit, token_addresses, network_wait, ): """Raiden must properly handle repeated locked transfer messages.""" app0, app1, app2 = raiden_network app1_wait_for_message = WaitForMessage() app2_wait_for_message = WaitForMessage() app1.raiden.message_handler = app1_wait_for_message app2.raiden.message_handler = app2_wait_for_message secret = factories.make_secret(0) secrethash = sha3(secret) # Save the messages, these will be processed again app1_mediatedtransfer = app1_wait_for_message.wait_for_message( LockedTransfer, {'lock': { 'secrethash': secrethash }}, ) app2_mediatedtransfer = app2_wait_for_message.wait_for_message( LockedTransfer, {'lock': { 'secrethash': secrethash }}, ) # Wait until the node receives a reveal secret to redispatch the locked # transfer message app1_revealsecret = app1_wait_for_message.wait_for_message( RevealSecret, {'secret': secret}, ) app2_revealsecret = app2_wait_for_message.wait_for_message( RevealSecret, {'secret': secret}, ) token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) amount = 10 identifier = 1 transfer_received = app0.raiden.start_mediated_transfer_with_secret( token_network_identifier=token_network_identifier, amount=amount, fee=0, target=app2.raiden.address, identifier=identifier, secret=secret, ) # - Wait until reveal secret is received to replay the message # - The secret is revealed backwards, app2 should be first # - The locked transfer is sent before the secret reveal, so the mediated # transfers async results must be set and `get_nowait` can be used app2_revealsecret.get(timeout=network_wait) mediated_transfer_msg = app2_mediatedtransfer.get_nowait() app2.raiden.message_handler.handle_message_lockedtransfer( app2.raiden, mediated_transfer_msg, ) app1_revealsecret.get(timeout=network_wait) app1.raiden.message_handler.handle_message_lockedtransfer( app1.raiden, app1_mediatedtransfer.get_nowait(), ) transfer_received.payment_done.wait() with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit - amount, [], app1, deposit + amount, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit - amount, [], app2, deposit + amount, [], )
def test_batch_unlock_after_restart(raiden_network, token_addresses, deposit): """Simulate the case where: - A sends B a transfer - B sends A a transfer - Secrets were never revealed - B closes channel - A crashes - Wait for settle - Wait for unlock from B - Restart A At this point, the current unlock logic will try to unlock iff the node gains from unlocking. Which means that the node will try to unlock either side. In the above scenario, each node will unlock its side. This test makes sure that we do NOT invalidate A's unlock transaction based on the ContractReceiveChannelBatchUnlock caused by B's unlock. """ alice_app, bob_app = raiden_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( chain_state=views.state_from_app(alice_app), token_network_registry_address=alice_app.raiden.default_registry. address, token_address=token_address, ) assert token_network_address timeout = 10 token_network = views.get_token_network_by_address( chain_state=views.state_from_app(alice_app), token_network_address=token_network_address) assert token_network channel_identifier = get_channelstate(alice_app, bob_app, token_network_address).identifier assert (channel_identifier in token_network.partneraddresses_to_channelidentifiers[ bob_app.raiden.address]) alice_to_bob_amount = 10 identifier = 1 alice_transfer_secret = Secret(sha3(alice_app.raiden.address)) alice_transfer_secrethash = sha256_secrethash(alice_transfer_secret) bob_transfer_secret = Secret(sha3(bob_app.raiden.address)) bob_transfer_secrethash = sha256_secrethash(bob_transfer_secret) alice_transfer_hold = bob_app.raiden.raiden_event_handler.hold_secretrequest_for( secrethash=alice_transfer_secrethash) bob_transfer_hold = alice_app.raiden.raiden_event_handler.hold_secretrequest_for( secrethash=bob_transfer_secrethash) alice_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=alice_to_bob_amount, target=bob_app.raiden.address, identifier=identifier, secret=alice_transfer_secret, ) bob_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=alice_to_bob_amount, target=alice_app.raiden.address, identifier=identifier + 1, secret=bob_transfer_secret, ) alice_transfer_hold.wait(timeout=timeout) bob_transfer_hold.wait(timeout=timeout) alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_address) alice_lock = channel.get_lock(alice_bob_channel_state.our_state, alice_transfer_secrethash) bob_lock = channel.get_lock(alice_bob_channel_state.partner_state, bob_transfer_secrethash) assert alice_lock assert bob_lock # This is the current state of protocol: # # A -> B LockedTransfer # - protocol didn't continue assert_synced_channel_state( token_network_address=token_network_address, app0=alice_app, balance0=deposit, pending_locks0=[alice_lock], app1=bob_app, balance1=deposit, pending_locks1=[bob_lock], ) # A ChannelClose event will be generated, this will be polled by both apps # and each must start a task for calling settle RaidenAPI(bob_app.raiden).channel_close( registry_address=registry_address, token_address=token_address, partner_address=alice_app.raiden.address, ) # wait for the close transaction to be mined, this is necessary to compute # the timeout for the settle with gevent.Timeout(timeout): waiting.wait_for_close( raiden=alice_app.raiden, token_network_registry_address=registry_address, token_address=token_address, channel_ids=[alice_bob_channel_state.identifier], retry_timeout=alice_app.raiden.alarm.sleep_time, ) channel_closed = raiden_state_changes_search_for_item( bob_app.raiden, ContractReceiveChannelClosed, { "canonical_identifier": { "token_network_address": token_network_address, "channel_identifier": alice_bob_channel_state.identifier, } }, ) assert isinstance(channel_closed, ContractReceiveChannelClosed) settle_max_wait_block = BlockNumber( channel_closed.block_number + alice_bob_channel_state.settle_timeout * 2) settle_timeout = BlockTimeout( RuntimeError("settle did not happen"), bob_app.raiden, settle_max_wait_block, alice_app.raiden.alarm.sleep_time, ) with settle_timeout: waiting.wait_for_settle( raiden=alice_app.raiden, token_network_registry_address=registry_address, token_address=token_address, channel_ids=[alice_bob_channel_state.identifier], retry_timeout=alice_app.raiden.alarm.sleep_time, ) with gevent.Timeout(timeout): wait_for_batch_unlock( app=bob_app, token_network_address=token_network_address, receiver=alice_bob_channel_state.partner_state.address, sender=alice_bob_channel_state.our_state.address, ) alice_app.start() with gevent.Timeout(timeout): wait_for_batch_unlock( app=alice_app, token_network_address=token_network_address, receiver=alice_bob_channel_state.partner_state.address, sender=alice_bob_channel_state.our_state.address, )
def test_received_lockedtransfer_closedchannel( raiden_network, reveal_timeout, token_addresses, deposit, ): app0, app1 = raiden_network registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) channel0 = get_channelstate(app0, app1, token_network_identifier) RaidenAPI(app1.raiden).channel_close( registry_address, token_address, app0.raiden.address, ) wait_until_block( app0.raiden.chain, app0.raiden.chain.block_number() + 1, ) # Now receive one mediated transfer for the closed channel lock_amount = 10 payment_identifier = 1 expiration = reveal_timeout * 2 mediated_transfer_message = LockedTransfer( message_identifier=random.randint(0, UINT64_MAX), payment_identifier=payment_identifier, nonce=1, token_network_address=token_network_identifier, token=token_address, channel=channel0.identifier, transferred_amount=0, locked_amount=lock_amount, recipient=app1.raiden.address, locksroot=UNIT_SECRETHASH, lock=Lock(lock_amount, expiration, UNIT_SECRETHASH), target=app1.raiden.address, initiator=app0.raiden.address, fee=0, ) sign_and_inject( mediated_transfer_message, app0.raiden.private_key, app0.raiden.address, app1, ) # The local state must not change since the channel is already closed assert_synched_channel_state( token_network_identifier, app0, deposit, [], app1, deposit, [], )
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_settle_is_automatically_called(raiden_network, token_addresses): """Settle is automatically called by one of the nodes.""" 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 token_network = views.get_token_network_by_address( views.state_from_app(app0), 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]) # A ChannelClose event will be generated, this will be polled by both apps # and each must start a task for calling settle RaidenAPI(app1.raiden).channel_close(registry_address, token_address, app0.raiden.address) waiting.wait_for_close( app0.raiden, registry_address, token_address, [channel_identifier], app0.raiden.alarm.sleep_time, ) channel_state = views.get_channelstate_for( views.state_from_raiden(app0.raiden), registry_address, token_address, app1.raiden.address) assert channel_state assert channel_state.close_transaction assert channel_state.close_transaction.finished_block_number waiting.wait_for_settle( app0.raiden, registry_address, token_address, [channel_identifier], app0.raiden.alarm.sleep_time, ) token_network = views.get_token_network_by_address( views.state_from_app(app0), token_network_address) assert token_network assert (channel_identifier not in token_network.partneraddresses_to_channelidentifiers[ app1.raiden.address]) state_changes = app0.raiden.wal.storage.get_statechanges_by_range( RANGE_ALL_STATE_CHANGES) assert search_for_item( state_changes, ContractReceiveChannelClosed, { "token_network_address": token_network_address, "channel_identifier": channel_identifier, "transaction_from": app1.raiden.address, "block_number": channel_state.close_transaction.finished_block_number, }, ) assert search_for_item( state_changes, ContractReceiveChannelSettled, { "token_network_address": token_network_address, "channel_identifier": channel_identifier }, )
def test_start_end_attack(token_addresses, raiden_chain, deposit): """ An attacker can try to steal tokens from a hub or the last node in a path. The attacker needs to use two addresses (A1 and A2) and connect both to the hub H. Once connected a mediated transfer is initialized from A1 to A2 through H. Once the node A2 receives the mediated transfer the attacker uses the known secret and reveal to close and settle the channel H-A2, without revealing the secret to H's raiden node. The intention is to make the hub transfer the token but for him to be unable to require the token A1.""" amount = 30 token = token_addresses[0] app0, app1, app2 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token) assert token_network_address hold_event_handler = app2.raiden.raiden_event_handler # the attacker owns app0 and app2 and creates a transfer through app1 identifier = 1 target = app2.raiden.address secret = Secret(sha3(target)) secrethash = sha256_secrethash(secret) hold_event_handler.hold_secretrequest_for(secrethash=secrethash) app0.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=amount, target=target, identifier=identifier, secret=secret, ) attack_channel = get_channelstate(app2, app1, token_network_address) attack_transfer = None # TODO attack_contract = attack_channel.external_state.netting_channel.address # type: ignore hub_contract = get_channelstate( # type: ignore app1, app0, token_network_address).external_state.netting_channel.address # start the settle counter attack_balance_proof = attack_transfer.to_balanceproof() # type: ignore attack_channel.netting_channel.channel_close( attack_balance_proof) # type: ignore # wait until the last block to reveal the secret, hopefully we are not # missing a block during the test assert attack_transfer app2.raiden.rpc_client.wait_until_block( target_block_number=attack_transfer.lock.expiration - 1) # since the attacker knows the secret he can net the lock # <the commented code below is left for documentation purposes> # attack_channel.netting_channel.unlock( # UnlockProofState(unlock_proof, attack_transfer.lock, secret) # ) # XXX: verify that the secret was publicized # at this point the hub might not know the secret yet, and won't be able to # claim the token from the channel A1 - H # the attacker settles the contract app2.raiden.rpc_client.wait_until_block( target_block_number=app2.raiden.rpc_client.block_number() + 1) attack_channel.netting_channel.settle(token, attack_contract) # at this point the attacker has the "stolen" funds attack_contract = app2.raiden.proxy_manager.token_hashchannel[token][ attack_contract] assert attack_contract.participants[ app2.raiden.address]["netted"] == deposit + amount assert attack_contract.participants[ app1.raiden.address]["netted"] == deposit - amount # and the hub's channel A1-H doesn't hub_contract = app1.raiden.proxy_manager.token_hashchannel[token][ hub_contract] assert hub_contract.participants[app0.raiden.address]["netted"] == deposit assert hub_contract.participants[app1.raiden.address]["netted"] == deposit # to mitigate the attack the Hub _needs_ to use a lower expiration for the # locked transfer between H-A2 than A1-H. For A2 to acquire the token # it needs to make the secret public in the blockchain so it publishes the # secret through an event and the Hub is able to require its funds app1.raiden.rpc_client.wait_until_block( target_block_number=app1.raiden.rpc_client.block_number() + 1) # XXX: verify that the Hub has found the secret, close and settle the channel # the hub has acquired its token hub_contract = app1.raiden.proxy_manager.token_hashchannel[token][ hub_contract] assert hub_contract.participants[ app0.raiden.address]["netted"] == deposit + amount assert hub_contract.participants[ app1.raiden.address]["netted"] == deposit - amount
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 test_channel_withdraw_expired(raiden_network, number_of_nodes, token_addresses, deposit, network_wait, retry_timeout): """ Tests withdraw expiration. """ alice_app, bob_app = raiden_network token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(alice_app), alice_app.raiden.default_registry.address, token_address) assert token_network_address # Prevent withdraw confirmation from being sent send_withdraw_confirmation_event = alice_app.raiden.raiden_event_handler.hold( SendWithdrawConfirmation, {}) alice_to_bob_amount = 10 total_withdraw = deposit + alice_to_bob_amount wait_for_withdraw_expired_message = alice_app.raiden.message_handler.wait_for_message( WithdrawExpired, {"total_withdraw": total_withdraw}) identifier = 1 target = bob_app.raiden.address secret = sha3(target) payment_status = alice_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=alice_to_bob_amount, target=target, identifier=identifier, secret=secret, ) wait_for_unlock = bob_app.raiden.message_handler.wait_for_message( Unlock, {"payment_identifier": identifier}) timeout = network_wait * number_of_nodes with Timeout(seconds=timeout): wait_for_unlock.get() msg = ( f"transfer from {to_checksum_address(alice_app.raiden.address)} " f"to {to_checksum_address(bob_app.raiden.address)} failed.") assert payment_status.payment_done.get(), msg bob_alice_channel_state = get_channelstate(bob_app, alice_app, token_network_address) bob_app.raiden.withdraw( canonical_identifier=bob_alice_channel_state.canonical_identifier, total_withdraw=total_withdraw, ) with Timeout(seconds=timeout): send_withdraw_confirmation_event.wait() # Make sure proper withdraw state is set in both channel states bob_alice_channel_state = get_channelstate(bob_app, alice_app, token_network_address) assert bob_alice_channel_state.our_total_withdraw == total_withdraw assert bob_alice_channel_state.our_state.withdraws_pending.get( total_withdraw) is not None alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_address) assert alice_bob_channel_state.partner_total_withdraw == total_withdraw assert alice_bob_channel_state.partner_state.withdraws_pending.get( total_withdraw) is not None withdraw_expiration = bob_alice_channel_state.our_state.withdraws_pending[ total_withdraw].expiration expiration_threshold = channel.get_sender_expiration_threshold( withdraw_expiration) waiting.wait_for_block( raiden=bob_app.raiden, block_number=BlockNumber(expiration_threshold + 1), retry_timeout=retry_timeout, ) bob_alice_channel_state = get_channelstate(bob_app, alice_app, token_network_address) assert bob_alice_channel_state.our_total_withdraw == 0 assert bob_alice_channel_state.our_state.withdraws_pending.get( total_withdraw) is None with Timeout(seconds=timeout): wait_for_withdraw_expired_message.wait() alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_address) assert alice_bob_channel_state.partner_total_withdraw == 0 assert alice_bob_channel_state.partner_state.withdraws_pending.get( total_withdraw) is None
def run_test_batch_unlock( raiden_network, token_addresses, secret_registry_address, deposit, blockchain_type ): """Batch unlock can be called after the channel is settled.""" alice_app, bob_app = raiden_network registry_address = alice_app.raiden.default_registry.address token_address = token_addresses[0] token_proxy = alice_app.raiden.chain.token(token_address) token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(alice_app), alice_app.raiden.default_registry.address, token_address ) hold_event_handler = bob_app.raiden.raiden_event_handler # Take a snapshot early on alice_app.raiden.wal.snapshot() token_network = views.get_token_network_by_identifier( views.state_from_app(alice_app), token_network_identifier ) channel_identifier = get_channelstate(alice_app, bob_app, token_network_identifier).identifier assert ( channel_identifier in token_network.partneraddresses_to_channelidentifiers[bob_app.raiden.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.wal.snapshot() alice_to_bob_amount = 10 identifier = 1 target = bob_app.raiden.address secret = sha3(target) secrethash = sha3(secret) secret_request_event = hold_event_handler.hold_secretrequest_for(secrethash=secrethash) alice_app.raiden.start_mediated_transfer_with_secret( token_network_identifier=token_network_identifier, amount=alice_to_bob_amount, fee=0, target=target, identifier=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_identifier) lock = channel.get_lock(alice_bob_channel_state.our_state, secrethash) # This is the current state of the protocol: # # A -> B LockedTransfer # B -> A SecretRequest # - protocol didn't continue assert_synced_channel_state( token_network_identifier, alice_app, deposit, [lock], bob_app, deposit, [] ) # Take a snapshot early on alice_app.raiden.wal.snapshot() our_balance_proof = alice_bob_channel_state.our_state.balance_proof # Test WAL restore to return the latest channel state restored_channel_state = channel_state_until_state_change( raiden=alice_app.raiden, canonical_identifier=alice_bob_channel_state.canonical_identifier, state_change_identifier="latest", ) our_restored_balance_proof = restored_channel_state.our_state.balance_proof assert our_balance_proof == our_restored_balance_proof # A ChannelClose event will be generated, this will be polled by both apps # and each must start a task for calling settle RaidenAPI(bob_app.raiden).channel_close( registry_address, token_address, alice_app.raiden.address ) secret_registry_proxy = alice_app.raiden.chain.secret_registry(secret_registry_address) secret_registry_proxy.register_secret(secret=secret) assert lock, "the lock must still be part of the node state" msg = "the secret must be registered before the lock expires" assert lock.expiration > alice_app.raiden.get_block_number(), msg assert lock.secrethash == sha3(secret) waiting.wait_for_settle( alice_app.raiden, registry_address, token_address, [alice_bob_channel_state.identifier], alice_app.raiden.alarm.sleep_time, ) token_network = views.get_token_network_by_identifier( views.state_from_app(bob_app), token_network_identifier ) assert ( channel_identifier in token_network.partneraddresses_to_channelidentifiers[alice_app.raiden.address] ) # Wait for both nodes to call batch unlock timeout = 30 if blockchain_type == "parity" else 10 with gevent.Timeout(timeout): wait_for_batch_unlock( app=bob_app, token_network_id=token_network_identifier, participant=alice_bob_channel_state.partner_state.address, partner=alice_bob_channel_state.our_state.address, ) token_network = views.get_token_network_by_identifier( views.state_from_app(bob_app), token_network_identifier ) assert ( channel_identifier not in token_network.partneraddresses_to_channelidentifiers[alice_app.raiden.address] ) alice_new_balance = alice_initial_balance + deposit - alice_to_bob_amount bob_new_balance = bob_initial_balance + deposit + alice_to_bob_amount assert token_proxy.balance_of(alice_app.raiden.address) == alice_new_balance assert token_proxy.balance_of(bob_app.raiden.address) == bob_new_balance
def test_receive_lockedtransfer_invalidnonce( raiden_network, deposit, token_addresses, reveal_timeout, network_wait, ): app0, app1, app2 = raiden_network token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) channel0 = get_channelstate(app0, app1, token_network_identifier) amount = 10 mediated_transfer( app0, app2, token_network_identifier, amount, timeout=network_wait, ) amount = 10 payment_identifier = 1 repeated_nonce = 1 expiration = reveal_timeout * 2 mediated_transfer_message = LockedTransfer( message_identifier=random.randint(0, UINT64_MAX), payment_identifier=payment_identifier, nonce=repeated_nonce, token_network_address=token_network_identifier, token=token_address, channel=channel0.identifier, transferred_amount=amount, locked_amount=amount, recipient=app1.raiden.address, locksroot=UNIT_SECRETHASH, lock=Lock(amount, expiration, UNIT_SECRETHASH), target=app2.raiden.address, initiator=app0.raiden.address, fee=0, ) sign_and_inject( mediated_transfer_message, app0.raiden.private_key, app0.raiden.address, app1, ) assert_synched_channel_state( token_network_identifier, app0, deposit - amount, [], app1, deposit + amount, [], )
def test_channel_lifecycle(raiden_network, token_addresses, deposit, transport_config): node1, node2 = raiden_network token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(node1), node1.raiden.default_registry.address, token_address, ) api1 = RaidenAPI(node1.raiden) api2 = RaidenAPI(node2.raiden) registry_address = node1.raiden.default_registry.address # nodes don't have a channel, so they are not healthchecking assert api1.get_node_network_state(api2.address) == NODE_NETWORK_UNKNOWN assert api2.get_node_network_state(api1.address) == NODE_NETWORK_UNKNOWN assert not api1.get_channel_list(registry_address, token_address, api2.address) # Make sure invalid arguments to get_channel_list are caught with pytest.raises(UnknownTokenAddress): api1.get_channel_list( registry_address=registry_address, token_address=None, partner_address=api2.address, ) # open is a synchronous api api1.channel_open(node1.raiden.default_registry.address, token_address, api2.address) channels = api1.get_channel_list(registry_address, token_address, api2.address) assert len(channels) == 1 channel12 = get_channelstate(node1, node2, token_network_identifier) assert channel.get_status(channel12) == CHANNEL_STATE_OPENED event_list1 = api1.get_blockchain_events_channel( token_address, channel12.partner_state.address, ) assert any((event['event'] == ChannelEvent.OPENED and is_same_address( event['args']['participant1'], to_normalized_address(api1.address), ) and is_same_address( event['args']['participant2'], to_normalized_address(api2.address), )) for event in event_list1) token_events = api1.get_blockchain_events_token_network(token_address, ) assert token_events[0]['event'] == ChannelEvent.OPENED registry_address = api1.raiden.default_registry.address # Load the new state with the deposit api1.set_total_channel_deposit( registry_address, token_address, api2.address, deposit, ) # let's make sure it's idempotent. Same deposit should raise deposit mismatch limit with pytest.raises(DepositMismatch): api1.set_total_channel_deposit( registry_address, token_address, api2.address, deposit, ) channel12 = get_channelstate(node1, node2, token_network_identifier) assert channel.get_status(channel12) == CHANNEL_STATE_OPENED assert channel.get_balance(channel12.our_state, channel12.partner_state) == deposit assert channel12.our_state.contract_balance == deposit assert api1.get_channel_list(registry_address, token_address, api2.address) == [channel12] # there is a channel open, they must be healthchecking each other assert api1.get_node_network_state(api2.address) == NODE_NETWORK_REACHABLE assert api2.get_node_network_state(api1.address) == NODE_NETWORK_REACHABLE event_list2 = api1.get_blockchain_events_channel( token_address, channel12.partner_state.address, ) assert any((event['event'] == ChannelEvent.DEPOSIT and is_same_address( event['args']['participant'], to_normalized_address(api1.address), ) and event['args']['total_deposit'] == deposit) for event in event_list2) api1.channel_close(registry_address, token_address, api2.address) # Load the new state with the channel closed channel12 = get_channelstate(node1, node2, token_network_identifier) event_list3 = api1.get_blockchain_events_channel( token_address, channel12.partner_state.address, ) assert len(event_list3) > len(event_list2) assert any((event['event'] == ChannelEvent.CLOSED and is_same_address( event['args']['closing_participant'], to_normalized_address(api1.address), )) for event in event_list3) assert channel.get_status(channel12) == CHANNEL_STATE_CLOSED settlement_block = ( channel12.close_transaction.finished_block_number + channel12.settle_timeout + 10 # arbitrary number of additional blocks, used to wait for the settle() call ) wait_until_block(node1.raiden.chain, settlement_block) state_changes = node1.raiden.wal.storage.get_statechanges_by_identifier( from_identifier=0, to_identifier='latest', ) assert must_contain_entry( state_changes, ContractReceiveChannelSettled, { 'token_network_identifier': token_network_identifier, 'channel_identifier': channel12.identifier, })
def run_test_locked_transfer_secret_registered_onchain( raiden_network, token_addresses, secret_registry_address, retry_timeout, ): app0 = raiden_network[0] token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) amount = 1 target = factories.UNIT_TRANSFER_INITIATOR identifier = 1 transfer_secret = sha3(target + b'1') secret_registry_proxy = app0.raiden.chain.secret_registry( secret_registry_address, ) secret_registry_proxy.register_secret(secret=transfer_secret, given_block_identifier='latest') # Wait until our node has processed the block that the secret registration was mined at block_number = app0.raiden.get_block_number() wait_for_block( raiden=app0.raiden, block_number=block_number + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS, retry_timeout=retry_timeout, ) # Test that sending a transfer with a secret already registered on-chain fails with pytest.raises(RaidenUnrecoverableError): app0.raiden.start_mediated_transfer_with_secret( token_network_identifier=token_network_identifier, amount=amount, fee=0, target=target, identifier=identifier, secret=transfer_secret, ) # Test that receiving a transfer with a secret already registered on chain fails expiration = 9999 locked_transfer = factories.make_signed_transfer( amount, factories.UNIT_TRANSFER_INITIATOR, app0.raiden.address, expiration, transfer_secret, ) message_handler = MessageHandler() message_handler.handle_message_lockedtransfer( app0.raiden, locked_transfer, ) state_changes = app0.raiden.wal.storage.get_statechanges_by_identifier( 0, 'latest') transfer_statechange_dispatched = ( search_for_item(state_changes, ActionInitMediator, {}) or search_for_item(state_changes, ActionInitTarget, {})) assert not transfer_statechange_dispatched
def run_test_mediated_transfer_with_allocated_fee( raiden_network, number_of_nodes, deposit, token_addresses, network_wait, ): app0, app1, app2, app3 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) fee = 5 amount = 10 transfer( initiator_app=app0, target_app=app3, token_address=token_address, amount=amount, identifier=1, fee=fee, timeout=network_wait * number_of_nodes, ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit - amount - fee, [], app1, deposit + amount + fee, [], ) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit - amount - fee, [], app2, deposit + amount + fee, [], ) app1_app2_channel_state = views.get_channelstate_by_token_network_and_partner( chain_state=views.state_from_raiden(app1.raiden), token_network_id=token_network_identifier, partner_address=app2.raiden.address, ) # Let app1 consume all of the allocated mediation fee action_set_fee = ActionChannelSetFee( canonical_identifier=app1_app2_channel_state.canonical_identifier, mediation_fee=fee, ) app1.raiden.handle_state_change(state_change=action_set_fee, ) transfer( initiator_app=app0, target_app=app3, token_address=token_address, amount=amount, identifier=2, fee=fee, timeout=network_wait * number_of_nodes, ) # The fees have been consumed exclusively by app1 with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app0, deposit - 2 * (amount + fee), [], app1, deposit + 2 * (amount + fee), [], ) # app2's poor soul gets no mediation fees on the second transfer. # Only the first transfer had a fee which was paid to app2 though # app2 doesn't set its fee but it would still receive the complete # locked amount = transfer amount + fee. # However app1 received from app0 two transfers # which it sent to app2. The first transfer # to app2 included the fee as it did not deduct # any fee (the channel's fee was 0). # The second transfer's fee was deducted by # app1 (provided we've set the fee of the channel) with gevent.Timeout(network_wait): wait_assert( assert_synced_channel_state, token_network_identifier, app1, deposit - (amount * 2) - fee, [], app2, deposit + (amount * 2) + fee, [], )
def test_receive_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_regression_multiple_revealsecret(raiden_network, token_addresses, transport_config): """ Multiple RevealSecret messages arriving at the same time must be handled properly. Secret handling followed these steps: The Secret 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 Secret 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_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token, ) channelstate_0_1 = get_channelstate(app0, app1, token_network_identifier) payment_identifier = 1 secret = sha3(b'test_regression_multiple_revealsecret') secrethash = sha3(secret) expiration = app0.raiden.get_block_number() + 100 lock_amount = 10 lock = Lock( lock_amount, expiration, secrethash, ) nonce = 1 transferred_amount = 0 mediated_transfer = LockedTransfer( random.randint(0, UINT64_MAX), payment_identifier, nonce, app0.raiden.default_registry.address, token, channelstate_0_1.identifier, transferred_amount, lock_amount, app1.raiden.address, lock.secrethash, lock, app1.raiden.address, app0.raiden.address, ) app0.raiden.sign(mediated_transfer) if transport_config.protocol is TransportProtocol.UDP: message_data = mediated_transfer.encode() app1.raiden.transport.receive(message_data) elif transport_config.protocol is TransportProtocol.MATRIX: app1.raiden.transport._receive_message(mediated_transfer) else: raise TypeError('Unknown TransportProtocol') reveal_secret = RevealSecret( random.randint(0, UINT64_MAX), secret, ) app0.raiden.sign(reveal_secret) token_network_identifier = channelstate_0_1.token_network_identifier secret = Secret( message_identifier=random.randint(0, UINT64_MAX), payment_identifier=payment_identifier, nonce=mediated_transfer.nonce + 1, token_network_address=token_network_identifier, channel=channelstate_0_1.identifier, transferred_amount=lock_amount, locked_amount=0, locksroot=EMPTY_MERKLE_ROOT, secret=secret, ) app0.raiden.sign(secret) if transport_config.protocol is TransportProtocol.UDP: messages = [ secret.encode(), reveal_secret.encode(), ] receive_method = app1.raiden.transport.receive elif transport_config.protocol is TransportProtocol.MATRIX: messages = [ secret, reveal_secret, ] receive_method = app1.raiden.transport._receive_message else: raise TypeError('Unknown TransportProtocol') wait = [ gevent.spawn_later( .1, receive_method, data, ) for data in messages ] gevent.joinall(wait)
def test_regression_unfiltered_routes( raiden_network, token_addresses, settle_timeout, deposit, ): """ The transfer should proceed without triggering an assert. Transfers failed in networks where two or more paths to the destination are possible but they share same node as a first hop. """ app0, app1, app2, app3, app4 = raiden_network token = token_addresses[0] registry_address = app0.raiden.default_registry.address # Topology: # # 0 -> 1 -> 2 -> 4 # | ^ # +--> 3 ---+ app_channels = [ (app0, app1), (app1, app2), (app1, app3), (app3, app4), (app2, app4), ] greenlets = [] for first_app, second_app in app_channels: greenlets.append( gevent.spawn( payment_channel_open_and_deposit, first_app, second_app, token, deposit, settle_timeout, )) gevent.wait(greenlets) wait_for_channels( app_channels, registry_address, [token], deposit, ) payment_network_identifier = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), payment_network_identifier, token, ) transfer = app0.raiden.mediated_transfer_async( token_network_identifier=token_network_identifier, amount=1, target=app4.raiden.address, identifier=1, ) assert transfer.wait()
def pending_mediated_transfer(app_chain, token_network_identifier, amount, identifier): """ Nice to read shortcut to make a LockedTransfer where the secret is _not_ revealed. While the secret is not revealed all apps will be synchronized, meaning they are all going to receive the LockedTransfer message. Returns: The secret used to generate the LockedTransfer """ # pylint: disable=too-many-locals if len(app_chain) < 2: raise ValueError( 'Cannot make a LockedTransfer with less than two apps') target = app_chain[-1].raiden.address # Generate a secret initiator_channel = views.get_channelstate_by_token_network_and_partner( views.state_from_app(app_chain[0]), token_network_identifier, app_chain[1].raiden.address, ) nonce_int = channel.get_next_nonce(initiator_channel.our_state) nonce_bytes = nonce_int.to_bytes(2, 'big') secret = sha3(target + nonce_bytes) initiator_app = app_chain[0] init_initiator_statechange = initiator_init( initiator_app.raiden, identifier, amount, secret, token_network_identifier, target, ) init_initiator_identifier = init_initiator_statechange.transfer.payment_identifier initiator_app.raiden.identifier_to_results[ init_initiator_identifier] = AsyncResult() events = initiator_app.raiden.wal.log_and_dispatch( init_initiator_statechange, ) send_transfermessage = must_contain_entry(events, SendLockedTransfer, {}) transfermessage = LockedTransfer.from_event(send_transfermessage) initiator_app.raiden.sign(transfermessage) for mediator_app in app_chain[1:-1]: mediator_init_statechange = mediator_init(mediator_app.raiden, transfermessage) mediator_init_identifier = init_initiator_statechange.transfer.payment_identifier mediator_app.raiden.identifier_to_results[ mediator_init_identifier] = AsyncResult() events = mediator_app.raiden.wal.log_and_dispatch( mediator_init_statechange, ) send_transfermessage = must_contain_entry(events, SendLockedTransfer, {}) transfermessage = LockedTransfer.from_event(send_transfermessage) mediator_app.raiden.sign(transfermessage) target_app = app_chain[-1] mediator_init_statechange = target_init(transfermessage) events = target_app.raiden.wal.log_and_dispatch( mediator_init_statechange, ) return secret
def test_stress( request, raiden_network, deposit, retry_timeout, token_addresses, port_generator, skip_if_not_udp, # pylint: disable=unused-argument ): config_converter = LogLevelConfigType() logging_levels = config_converter.convert( value=request.config.option.log_config or '', param=None, ctx=None, ) _trimmed_logging(logging_levels) token_address = token_addresses[0] rest_apis = start_apiserver_for_network(raiden_network, port_generator) identifier_generator = count() token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(raiden_network[0]), raiden_network[0].raiden.default_registry.address, token_address, ) for _ in range(2): assert_channels( raiden_network, token_network_identifier, deposit, ) stress_send_serial_transfers( rest_apis, token_address, identifier_generator, deposit, ) raiden_network, rest_apis = restart_network_and_apiservers( raiden_network, rest_apis, port_generator, retry_timeout, ) assert_channels( raiden_network, token_network_identifier, deposit, ) stress_send_parallel_transfers( rest_apis, token_address, identifier_generator, deposit, ) raiden_network, rest_apis = restart_network_and_apiservers( raiden_network, rest_apis, port_generator, retry_timeout, ) assert_channels( raiden_network, token_network_identifier, deposit, ) stress_send_and_receive_parallel_transfers( rest_apis, token_address, identifier_generator, deposit, ) raiden_network, rest_apis = restart_network_and_apiservers( raiden_network, rest_apis, port_generator, retry_timeout, ) restart_network(raiden_network, retry_timeout)
def test_query_events(raiden_chain, token_addresses, deposit, settle_timeout, events_poll_timeout): app0, app1 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), registry_address, token_address, ) manager0 = app0.raiden.default_registry.manager_by_token(token_address) channelcount0 = views.total_token_network_channels( views.state_from_app(app0), registry_address, token_address, ) events = get_all_registry_events( app0.raiden.chain, registry_address, events=ALL_EVENTS, from_block=0, to_block='latest', ) assert len(events) == 1 assert events[0]['event'] == EVENT_TOKEN_ADDED assert event_dicts_are_equal(events[0]['args'], { 'registry_address': to_normalized_address(registry_address), 'channel_manager_address': to_normalized_address(manager0.address), 'token_address': to_normalized_address(token_address), 'block_number': 'ignore', }) events = get_all_registry_events( app0.raiden.chain, app0.raiden.default_registry.address, events=ALL_EVENTS, from_block=999999998, to_block=999999999, ) assert not events channel_address = RaidenAPI(app0.raiden).channel_open( registry_address, token_address, app1.raiden.address, ) gevent.sleep(events_poll_timeout * 2) events = get_all_channel_manager_events( app0.raiden.chain, manager0.address, events=ALL_EVENTS, from_block=0, to_block='latest', ) assert len(events) == 1 assert events[0]['event'] == EVENT_CHANNEL_NEW assert event_dicts_are_equal(events[0]['args'], { 'registry_address': to_normalized_address(registry_address), 'settle_timeout': settle_timeout, 'netting_channel': to_normalized_address(channel_address), 'participant1': to_normalized_address(app0.raiden.address), 'participant2': to_normalized_address(app1.raiden.address), 'block_number': 'ignore', }) events = get_all_channel_manager_events( app0.raiden.chain, manager0.address, events=ALL_EVENTS, from_block=999999998, to_block=999999999, ) assert not events # channel is created but not opened and without funds channelcount1 = views.total_token_network_channels( views.state_from_app(app0), registry_address, token_address, ) assert channelcount0 + 1 == channelcount1 assert_synched_channel_state( token_network_identifier, app0, 0, [], app1, 0, [], ) RaidenAPI(app0.raiden).channel_deposit( registry_address, token_address, app1.raiden.address, deposit, ) gevent.sleep(events_poll_timeout * 2) all_netting_channel_events = get_all_netting_channel_events( app0.raiden.chain, channel_address, from_block=0, to_block='latest', ) events = get_all_netting_channel_events( app0.raiden.chain, channel_address, events=[CONTRACT_MANAGER.get_event_id(EVENT_CHANNEL_NEW_BALANCE)], ) assert len(all_netting_channel_events) == 1 assert len(events) == 1 assert events[0]['event'] == EVENT_CHANNEL_NEW_BALANCE new_balance_event = { 'registry_address': to_normalized_address(registry_address), 'token_address': to_normalized_address(token_address), 'participant': to_normalized_address(app0.raiden.address), 'balance': deposit, 'block_number': 'ignore', } assert event_dicts_are_equal(all_netting_channel_events[-1]['args'], new_balance_event) assert event_dicts_are_equal(events[0]['args'], new_balance_event) RaidenAPI(app0.raiden).channel_close( registry_address, token_address, app1.raiden.address, ) gevent.sleep(events_poll_timeout * 2) all_netting_channel_events = get_all_netting_channel_events( app0.raiden.chain, netting_channel_address=channel_address, from_block=0, to_block='latest', ) events = get_all_netting_channel_events( app0.raiden.chain, channel_address, events=[CONTRACT_MANAGER.get_event_id(EVENT_CHANNEL_CLOSED)], ) assert len(all_netting_channel_events) == 2 assert len(events) == 1 assert events[0]['event'] == EVENT_CHANNEL_CLOSED closed_event = { 'registry_address': to_normalized_address(registry_address), 'closing_address': to_normalized_address(app0.raiden.address), 'block_number': 'ignore', } assert event_dicts_are_equal(all_netting_channel_events[-1]['args'], closed_event) assert event_dicts_are_equal(events[0]['args'], closed_event) settle_expiration = app0.raiden.chain.block_number() + settle_timeout + 5 wait_until_block(app0.raiden.chain, settle_expiration) all_netting_channel_events = get_all_netting_channel_events( app0.raiden.chain, netting_channel_address=channel_address, from_block=0, to_block='latest', ) events = get_all_netting_channel_events( app0.raiden.chain, channel_address, events=[CONTRACT_MANAGER.get_event_id(EVENT_CHANNEL_SETTLED)], ) assert len(all_netting_channel_events) == 3 assert len(events) == 1 assert events[0]['event'] == EVENT_CHANNEL_SETTLED settled_event = { 'registry_address': to_normalized_address(registry_address), 'block_number': 'ignore', } assert event_dicts_are_equal(all_netting_channel_events[-1]['args'], settled_event) assert event_dicts_are_equal(events[0]['args'], settled_event)
def test_raidenapi_channel_lifecycle(raiden_network, token_addresses, deposit, retry_timeout, settle_timeout_max): """Uses RaidenAPI to go through a complete channel lifecycle.""" node1, node2 = raiden_network token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(node1), node1.raiden.default_registry.address, token_address) assert token_network_address api1 = RaidenAPI(node1.raiden) api2 = RaidenAPI(node2.raiden) registry_address = node1.raiden.default_registry.address # nodes don't have a channel, so they are not healthchecking assert api1.get_node_network_state(api2.address) == NetworkState.UNKNOWN assert api2.get_node_network_state(api1.address) == NetworkState.UNKNOWN assert not api1.get_channel_list(registry_address, token_address, api2.address) # Make sure invalid arguments to get_channel_list are caught with pytest.raises(UnknownTokenAddress): api1.get_channel_list(registry_address=registry_address, token_address=None, partner_address=api2.address) address_for_lowest_settle_timeout = make_address() lowest_valid_settle_timeout = node1.raiden.config.reveal_timeout * 2 # Make sure a small settle timeout is not accepted when opening a channel with pytest.raises(InvalidSettleTimeout): api1.channel_open( registry_address=node1.raiden.default_registry.address, token_address=token_address, partner_address=address_for_lowest_settle_timeout, settle_timeout=lowest_valid_settle_timeout - 1, ) # Make sure the smallest settle timeout is accepted api1.channel_open( registry_address=node1.raiden.default_registry.address, token_address=token_address, partner_address=address_for_lowest_settle_timeout, settle_timeout=lowest_valid_settle_timeout, ) address_for_highest_settle_timeout = make_address() highest_valid_settle_timeout = settle_timeout_max # Make sure a large settle timeout is not accepted when opening a channel with pytest.raises(InvalidSettleTimeout): api1.channel_open( registry_address=node1.raiden.default_registry.address, token_address=token_address, partner_address=address_for_highest_settle_timeout, settle_timeout=highest_valid_settle_timeout + 1, ) # Make sure the highest settle timeout is accepted api1.channel_open( registry_address=node1.raiden.default_registry.address, token_address=token_address, partner_address=address_for_highest_settle_timeout, settle_timeout=highest_valid_settle_timeout, ) # open is a synchronous api api1.channel_open(node1.raiden.default_registry.address, token_address, api2.address) channels = api1.get_channel_list(registry_address, token_address, api2.address) assert len(channels) == 1 channel12 = get_channelstate(node1, node2, token_network_address) assert channel.get_status(channel12) == ChannelState.STATE_OPENED registry_address = api1.raiden.default_registry.address # Check that giving a 0 total deposit is not accepted with pytest.raises(DepositMismatch): api1.set_total_channel_deposit( registry_address=registry_address, token_address=token_address, partner_address=api2.address, total_deposit=TokenAmount(0), ) # Load the new state with the deposit api1.set_total_channel_deposit( registry_address=registry_address, token_address=token_address, partner_address=api2.address, total_deposit=deposit, ) # let's make sure it's idempotent. Same deposit should raise deposit mismatch limit with pytest.raises(DepositMismatch): api1.set_total_channel_deposit(registry_address, token_address, api2.address, deposit) channel12 = get_channelstate(node1, node2, token_network_address) assert channel.get_status(channel12) == ChannelState.STATE_OPENED assert channel.get_balance(channel12.our_state, channel12.partner_state) == deposit assert channel12.our_state.contract_balance == deposit assert api1.get_channel_list(registry_address, token_address, api2.address) == [channel12] # there is a channel open, they must be healthchecking each other assert api1.get_node_network_state(api2.address) == NetworkState.REACHABLE assert api2.get_node_network_state(api1.address) == NetworkState.REACHABLE api1.channel_close(registry_address, token_address, api2.address) # Load the new state with the channel closed channel12 = get_channelstate(node1, node2, token_network_address) assert channel.get_status(channel12) == ChannelState.STATE_CLOSED with pytest.raises(UnexpectedChannelState): api1.set_total_channel_deposit(registry_address, token_address, api2.address, deposit + 100) assert wait_for_state_change( node1.raiden, ContractReceiveChannelSettled, { "canonical_identifier": { "token_network_address": token_network_address, "channel_identifier": channel12.identifier, } }, retry_timeout, )
def test_start_end_attack(token_addresses, raiden_chain, deposit): """ An attacker can try to steal tokens from a hub or the last node in a path. The attacker needs to use two addresses (A1 and A2) and connect both to the hub H. Once connected a mediated transfer is initialized from A1 to A2 through H. Once the node A2 receives the mediated transfer the attacker uses the known secret and reveal to close and settle the channel H-A2, without revealing the secret to H's raiden node. The intention is to make the hub transfer the token but for him to be unable to require the token A1.""" amount = 30 token = token_addresses[0] app0, app1, app2 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token, ) # the attacker owns app0 and app2 and creates a transfer through app1 identifier = 1 secret = pending_mediated_transfer( raiden_chain, token_network_identifier, amount, identifier, ) secrethash = sha3(secret) attack_channel = get_channelstate(app2, app1, token_network_identifier) attack_transfer = None # TODO attack_contract = attack_channel.external_state.netting_channel.address hub_contract = ( get_channelstate(app1, app0, token_network_identifier) .external_state .netting_channel.address ) # the attacker can create a merkle proof of the locked transfer lock = attack_channel.partner_state.get_lock_by_secrethash(secrethash) unlock_proof = attack_channel.partner_state.compute_proof_for_lock(secret, lock) # start the settle counter attack_balance_proof = attack_transfer.to_balanceproof() attack_channel.netting_channel.channel_close(attack_balance_proof) # wait until the last block to reveal the secret, hopefully we are not # missing a block during the test wait_until_block(app2.raiden.chain, attack_transfer.lock.expiration - 1) # since the attacker knows the secret he can net the lock attack_channel.netting_channel.unlock( UnlockProofState(unlock_proof, attack_transfer.lock, secret), ) # XXX: verify that the secret was publicized # at this point the hub might not know the secret yet, and won't be able to # claim the token from the channel A1 - H # the attacker settles the contract app2.raiden.chain.next_block() attack_channel.netting_channel.settle(token, attack_contract) # at this point the attacker has the "stolen" funds attack_contract = app2.raiden.chain.token_hashchannel[token][attack_contract] assert attack_contract.participants[app2.raiden.address]['netted'] == deposit + amount assert attack_contract.participants[app1.raiden.address]['netted'] == deposit - amount # and the hub's channel A1-H doesn't hub_contract = app1.raiden.chain.token_hashchannel[token][hub_contract] assert hub_contract.participants[app0.raiden.address]['netted'] == deposit assert hub_contract.participants[app1.raiden.address]['netted'] == deposit # to mitigate the attack the Hub _needs_ to use a lower expiration for the # locked transfer between H-A2 than A1-H. For A2 to acquire the token # it needs to make the secret public in the blockchain so it publishes the # secret through an event and the Hub is able to require its funds app1.raiden.chain.next_block() # XXX: verify that the Hub has found the secret, close and settle the channel # the hub has acquired its token hub_contract = app1.raiden.chain.token_hashchannel[token][hub_contract] assert hub_contract.participants[app0.raiden.address]['netted'] == deposit + amount assert hub_contract.participants[app1.raiden.address]['netted'] == deposit - amount
def test_settled_lock(token_addresses, raiden_network, deposit): """ Any transfer following a secret revealed must update the locksroot, so hat 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 = 30 token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) address0 = app0.raiden.address address1 = app1.raiden.address deposit0 = deposit deposit1 = deposit token_proxy = app0.raiden.chain.token(token_address) initial_balance0 = token_proxy.balance_of(address0) initial_balance1 = token_proxy.balance_of(address1) # Using a pending mediated transfer because this allows us to compute the # merkle proof identifier = 1 secret = pending_mediated_transfer( raiden_network, token_network_identifier, amount, identifier, ) # Save the merkle tree leaves from the pending transfer, used to test the unlock channelstate_0_1 = get_channelstate(app0, app1, token_network_identifier) batch_unlock = channel.get_batch_unlock(channelstate_0_1.our_state) assert batch_unlock claim_lock(raiden_network, identifier, token_network_identifier, secret) # Make a new transfer direct_transfer(app0, app1, token_network_identifier, amount, identifier=1) 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.wait_time, ) netting_channel = app1.raiden.chain.payment_channel( token_network_identifier, channelstate_0_1.identifier, ) # The direct transfer locksroot must not contain the unlocked lock, the # unlock must fail. with pytest.raises(Exception): netting_channel.unlock( channelstate_0_1.partner_state.address, batch_unlock, ) 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 test_lock_expiry(raiden_network, token_addresses, deposit): """Test lock expiry and removal.""" alice_app, bob_app = raiden_network token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(alice_app), alice_app.raiden.default_registry.address, token_address) assert token_network_address hold_event_handler = bob_app.raiden.raiden_event_handler wait_message_handler = bob_app.raiden.message_handler token_network = views.get_token_network_by_address( views.state_from_app(alice_app), token_network_address) assert token_network channel_state = get_channelstate(alice_app, bob_app, token_network_address) channel_identifier = channel_state.identifier assert (channel_identifier in token_network.partneraddresses_to_channelidentifiers[ bob_app.raiden.address]) alice_to_bob_amount = 10 identifier = 1 target = bob_app.raiden.address transfer_1_secret = factories.make_secret(0) transfer_1_secrethash = sha256_secrethash(transfer_1_secret) transfer_2_secret = factories.make_secret(1) transfer_2_secrethash = sha256_secrethash(transfer_2_secret) hold_event_handler.hold_secretrequest_for(secrethash=transfer_1_secrethash) transfer1_received = wait_message_handler.wait_for_message( LockedTransfer, {"lock": { "secrethash": transfer_1_secrethash }}) transfer2_received = wait_message_handler.wait_for_message( LockedTransfer, {"lock": { "secrethash": transfer_2_secrethash }}) remove_expired_lock_received = wait_message_handler.wait_for_message( LockExpired, {"secrethash": transfer_1_secrethash}) alice_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=alice_to_bob_amount, target=target, identifier=identifier, secret=transfer_1_secret, ) transfer1_received.wait() alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_address) lock = channel.get_lock(alice_bob_channel_state.our_state, transfer_1_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, deposit, [lock], bob_app, deposit, []) # Verify lock is registered in both channel states alice_channel_state = get_channelstate(alice_app, bob_app, token_network_address) assert transfer_1_secrethash in alice_channel_state.our_state.secrethashes_to_lockedlocks bob_channel_state = get_channelstate(bob_app, alice_app, token_network_address) assert transfer_1_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks alice_chain_state = views.state_from_raiden(alice_app.raiden) assert transfer_1_secrethash in alice_chain_state.payment_mapping.secrethashes_to_task remove_expired_lock_received.wait() alice_channel_state = get_channelstate(alice_app, bob_app, token_network_address) assert transfer_1_secrethash not in alice_channel_state.our_state.secrethashes_to_lockedlocks # Verify Bob received the message and processed the LockExpired message bob_channel_state = get_channelstate(bob_app, alice_app, token_network_address) assert transfer_1_secrethash not in bob_channel_state.partner_state.secrethashes_to_lockedlocks alice_chain_state = views.state_from_raiden(alice_app.raiden) assert transfer_1_secrethash not in alice_chain_state.payment_mapping.secrethashes_to_task # Make another transfer alice_to_bob_amount = 10 identifier = 2 hold_event_handler.hold_secretrequest_for(secrethash=transfer_2_secrethash) alice_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=alice_to_bob_amount, target=target, identifier=identifier, secret=transfer_2_secret, ) transfer2_received.wait() # Make sure the other transfer still exists alice_chain_state = views.state_from_raiden(alice_app.raiden) assert transfer_2_secrethash in alice_chain_state.payment_mapping.secrethashes_to_task bob_channel_state = get_channelstate(bob_app, alice_app, token_network_address) assert transfer_2_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks
def test_recovery_unhappy_case( raiden_network, number_of_nodes, deposit, token_addresses, network_wait, skip_if_not_udp, retry_timeout, ): app0, app1, app2 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) payment_network_id = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state, payment_network_id, token_address, ) # make a few transfers from app0 to app2 amount = 1 spent_amount = deposit - 2 for _ in range(spent_amount): mediated_transfer( app0, app2, token_network_identifier, amount, timeout=network_wait * number_of_nodes, ) app0.raiden.stop() host_port = ( app0.raiden.config['transport']['udp']['host'], app0.raiden.config['transport']['udp']['port'], ) socket = server._udp_socket(host_port) new_transport = UDPTransport( app0.raiden.address, app0.discovery, socket, app0.raiden.transport.throttle_policy, app0.raiden.config['transport']['udp'], ) 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, chain=app0.raiden.chain, query_start_block=0, default_registry=app0.raiden.default_registry, default_secret_registry=app0.raiden.default_secret_registry, transport=new_transport, raiden_event_handler=raiden_event_handler, message_handler=message_handler, discovery=app0.raiden.discovery, ) del app0 # from here on the app0_restart should be used app0_restart.start() state_changes = app0_restart.raiden.wal.storage.get_statechanges_by_identifier( from_identifier=0, to_identifier='latest', ) assert search_for_item( state_changes, ContractReceiveChannelSettled, { 'token_network_identifier': token_network_identifier, 'channel_identifier': channel01.identifier, })