def test_initiator_log_directransfer_success( raiden_chain, token_addresses, deposit): token_address = token_addresses[0] amount = int(deposit / 2.) identifier = 7 app0, app1 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking direct_transfer( app0, app1, token_address, amount, identifier, ) app0_events = get_all_state_events(app0.raiden.transaction_log) sucessful_transfers = [ event.event_object for event in app0_events if isinstance(event.event_object, EventTransferSentSuccess) ] assert sucessful_transfers[0] == EventTransferSentSuccess( identifier, amount, app1.raiden.address, )
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_initiator_log_directransfer_action( raiden_chain, token_addresses, deposit): """ The action that start a direct transfer must be logged in the WAL. """ token_address = token_addresses[0] amount = int(deposit / 2.) identifier = 13 app0, app1 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking direct_transfer( app0, app1, token_address, amount, identifier, ) app0_state_changes = get_all_state_changes(app0.raiden.transaction_log) direct_transfers = [ state_change for _, state_change in app0_state_changes if isinstance(state_change, ActionTransferDirect) ] assert direct_transfers[0] == ActionTransferDirect( identifier, amount, token_address, app1.raiden.address, )
def test_target_log_directransfer_message( raiden_chain, token_addresses, deposit): token_address = token_addresses[0] amount = int(deposit / 2.) identifier = 21 app0, app1 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking direct_transfer( app0, app1, token_address, amount, identifier, ) app1_state_changes = get_all_state_changes(app1.raiden.transaction_log) received_transfers = [ state_change for _, state_change in app1_state_changes if isinstance(state_change, ReceiveTransferDirect) ] assert received_transfers[0] == ReceiveTransferDirect( identifier, amount, token_address, app0.raiden.address, )
def test_fullnetwork(raiden_chain): app0, app1, app2 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking asset_address = app0.raiden.chain.default_registry.asset_addresses()[0] amount = 80 random.seed(0) direct_transfer(app0, app1, asset_address, amount) # Assert default identifier is generated correctly fchannel = channel(app0, app1, asset_address) last_transfer = get_sent_transfer(fchannel, 0) random.seed(0) assert_identifier_correct(app0, asset_address, app1.raiden.address, last_transfer.identifier) amount = 50 direct_transfer(app1, app2, asset_address, amount) amount = 30 random.seed(0) mediated_transfer( app1, app2, asset_address, amount ) # Assert default identifier is generated correctly fchannel = channel(app1, app2, asset_address) last_transfer = get_sent_transfer(fchannel, 1) random.seed(0) assert_identifier_correct(app1, asset_address, app2.raiden.address, last_transfer.identifier)
def test_api_channel_events(raiden_chain): app0, app1 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking token_address = app0.raiden.default_registry.token_addresses()[0] channel_0_1 = channel(app0, app1, token_address) amount = 30 direct_transfer( app0, app1, token_address, amount, identifier=1, ) results = RaidenAPI(app0.raiden).get_channel_events(channel_0_1.channel_address, 0) assert len(results) == 3 max_block = 0 for idx, result in enumerate(results): if result['block_number'] > max_block: max_block = result['block_number'] assert max_block != 0 if idx == 2: assert result['_event_type'] == 'EventTransferSentSuccess' assert result['amount'] == amount assert result['target'] == app1.raiden.address else: assert result['_event_type'] == b'ChannelNewBalance' assert max_block != 0 results = RaidenAPI(app0.raiden).get_channel_events( channel_0_1.channel_address, max_block + 1, max_block + 100 ) assert not results
def test_settled_lock(token_addresses, raiden_network, settle_timeout, reveal_timeout): """ Any transfer following a secret revealed must update the locksroot, so that an attacker cannot reuse a secret to double claim a lock. """ token = token_addresses[0] amount = 30 app0, app1, app2, _ = raiden_network # pylint: disable=unbalanced-tuple-unpacking address0 = app0.raiden.address address1 = app1.raiden.address forward_channel = channel(app0, app1, token) back_channel = channel(app1, app0, token) deposit0 = forward_channel.contract_balance deposit1 = back_channel.contract_balance token_contract = app0.raiden.chain.token(token) balance0 = token_contract.balance_of(address0) balance1 = token_contract.balance_of(address1) # A pending mediated transfer identifier = 1 expiration = app0.raiden.chain.block_number() + settle_timeout - reveal_timeout secret = pending_mediated_transfer( raiden_network, token, amount, identifier, expiration, ) hashlock = sha3(secret) # Get the proof to unlock the pending lock secret_transfer = get_received_transfer(back_channel, 0) lock = back_channel.partner_state.get_lock_by_hashlock(hashlock) unlock_proof = back_channel.partner_state.compute_proof_for_lock(secret, lock) # Update the hashlock claim_lock(raiden_network, identifier, token, secret) direct_transfer(app0, app1, token, amount, identifier=1) # The direct transfer locksroot must remove the unlocked lock and update # the transferred amount, the withdraw must fail. balance_proof = back_channel.partner_state.balance_proof back_channel.external_state.close(balance_proof) with pytest.raises(Exception): back_channel.external_state.netting_channel.withdraw( [(unlock_proof, secret_transfer.lock.as_bytes, secret)], ) settle_expiration = app2.raiden.chain.block_number() + settle_timeout wait_until_block(app2.raiden.chain, settle_expiration) back_channel.external_state.netting_channel.settle() assert token_contract.balance_of(address0) == balance0 + deposit0 - amount * 2 assert token_contract.balance_of(address1) == balance1 + deposit1 + amount * 2
def test_cancel_transfer(): deposit = 100 asset = sha3('test_cancel_transfer')[:20] # pylint: disable=unbalanced-tuple-unpacking app0, app1, app2 = create_sequential_network(num_nodes=3, deposit=deposit, asset=asset) messages = setup_messages_cb() mlogger = MessageLogger() assert_synched_channels( channel(app0, app1, asset), deposit, [], channel(app1, app0, asset), deposit, [] ) assert_synched_channels( channel(app1, app2, asset), deposit, [], channel(app2, app1, asset), deposit, [] ) # drain the channel app1 -> app2 amount = 80 direct_transfer(app1, app2, asset, amount) assert_synched_channels( channel(app0, app1, asset), deposit, [], channel(app1, app0, asset), deposit, [] ) assert_synched_channels( channel(app1, app2, asset), deposit - amount, [], channel(app2, app1, asset), deposit + amount, [] ) # app1 -> app2 is the only available path and doens't have resource, app1 # needs to send CancelTransfer to app0 transfer(app0, app2, asset, 50) assert_synched_channels( channel(app0, app1, asset), deposit, [], channel(app1, app0, asset), deposit, [] ) assert_synched_channels( channel(app1, app2, asset), deposit - amount, [], channel(app2, app1, asset), deposit + amount, [] ) assert len(messages) == 6 # DirectTransfer + MediatedTransfer + CancelTransfer + a Ack for each app1_messages = mlogger.get_node_messages(pex(app1.raiden.address), only='sent') assert isinstance(app1_messages[-1], CancelTransfer)
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 direct_transfer(app1, app2, token_network_identifier, 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_synched_channel_state( token_network_identifier, app0, deposit, [send_lockedtransfer.transfer.lock], app1, deposit, [send_refundtransfer.lock], ) # This channel was exhausted to force the refund transfer assert_synched_channel_state( token_network_identifier, app1, 0, [], app2, deposit * 2, [], )
def test_fullnetwork(deployed_network): app0, app1, app2 = deployed_network # pylint: disable=unbalanced-tuple-unpacking asset_address = app0.raiden.chain.default_registry.asset_addresses()[0] amount = 80 direct_transfer(app0, app1, asset_address, amount) amount = 50 direct_transfer(app1, app2, asset_address, amount) amount = 30 mediated_transfer(app1, app2, asset_address, amount)
def test_settled_lock(assets_addresses, raiden_network, settle_timeout): """ Any transfer following a secret revealed must update the locksroot, so that an attacker cannot reuse a secret to double claim a lock. """ asset = assets_addresses[0] amount = 30 app0, app1, app2, app3 = raiden_network # pylint: disable=unbalanced-tuple-unpacking # mediated transfer secret = pending_mediated_transfer(raiden_network, asset, amount) # get a proof for the pending transfer back_channel = channel(app1, app0, asset) secret_transfer = get_received_transfer(back_channel, 0) merkle_proof = back_channel.our_state.locked.get_proof(secret_transfer) # reveal the secret register_secret(raiden_network, asset, secret) # a new transfer to update the hashlock direct_transfer(app0, app1, asset, amount) forward_channel = channel(app0, app1, asset) last_transfer = get_sent_transfer(forward_channel, 1) # call close giving the secret for a transfer that has being revealed back_channel.external_state.netting_channel.close( app1.raiden.address, last_transfer, None ) # check that the double unlock will failed with pytest.raises(Exception): back_channel.external_state.netting_channel.unlock( app1.raiden.address, [(merkle_proof, secret_transfer.lock.as_bytes, secret)], ) # forward the block number to allow settle for _ in range(settle_timeout): app2.raiden.chain.next_block() back_channel.external_state.netting_channel.settle() participant0 = back_channel.external_state.netting_channel.contract.participants[app0.raiden.address] participant1 = back_channel.external_state.netting_channel.contract.participants[app1.raiden.address] assert participant0.netted == participant0.deposit - amount * 2 assert participant1.netted == participant1.deposit + amount * 2
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_settled_lock(): """ After a lock has it's secret revealed and a transfer happened, the lock cannot be used to net any value with the contract. """ deposit = 100 asset = sha3('test_settled_lock')[:20] amount = 30 # pylint: disable=unbalanced-tuple-unpacking apps = create_sequential_network(num_nodes=4, deposit=deposit, asset=asset) # mediated transfer with the secret revealed transfer(apps[0], apps[3], asset, amount) # create the latest transfer direct_transfer(apps[0], apps[1], asset, amount) secret = '' # need to get the secret attack_channel = channel(apps[2], apps[1], asset) secret_transfer = get_received_transfer(attack_channel, 0) last_transfer = get_received_transfer(attack_channel, 1) nettingcontract_address = attack_channel.nettingcontract_address # create a fake proof merkle_proof = attack_channel.our_state.locked.get_proof(secret_transfer) # call close giving the secret for a transfer that has being revealed apps[1].raiden.chain.close( asset, nettingcontract_address, apps[1].raiden.address, [last_transfer], [(merkle_proof, secret_transfer.lock, secret)], ) # forward the block number to allow settle for _ in range(NettingChannelContract.locked_time): apps[2].raiden.chain.next_block() apps[1].raiden.chain.settle(asset, nettingcontract_address)
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_settled_lock(token_addresses, raiden_network, settle_timeout, reveal_timeout): """ Any transfer following a secret revealed must update the locksroot, so that an attacker cannot reuse a secret to double claim a lock. """ token = token_addresses[0] amount = 30 app0, app1, app2, _ = raiden_network # pylint: disable=unbalanced-tuple-unpacking address0 = app0.raiden.address address1 = app1.raiden.address forward_channel = channel(app0, app1, token) back_channel = channel(app1, app0, token) deposit0 = forward_channel.contract_balance deposit1 = back_channel.contract_balance token_contract = app0.raiden.chain.token(token) balance0 = token_contract.balance_of(address0) balance1 = token_contract.balance_of(address1) # A pending mediated transfer identifier = 1 expiration = app0.raiden.chain.block_number( ) + settle_timeout - reveal_timeout secret = pending_mediated_transfer( raiden_network, token, amount, identifier, expiration, ) hashlock = sha3(secret) # Get the proof to unlock the pending lock secret_transfer = get_received_transfer(back_channel, 0) lock = back_channel.our_state.balance_proof.get_lock_by_hashlock(hashlock) unlock_proof = back_channel.our_state.balance_proof.compute_proof_for_lock( secret, lock) # Update the hashlock claim_lock(raiden_network, token, secret) direct_transfer(app0, app1, token, amount) # The direct transfer locksroot must remove the unlocked lock and update # the transferred amount, the withdraw must fail. balance_proof = back_channel.our_state.balance_proof.balance_proof back_channel.external_state.close(balance_proof) with pytest.raises(Exception): back_channel.external_state.netting_channel.withdraw( [(unlock_proof, secret_transfer.lock.as_bytes, secret)], ) settle_expiration = app2.raiden.chain.block_number() + settle_timeout wait_until_block(app2.raiden.chain, settle_expiration) back_channel.external_state.netting_channel.settle() assert token_contract.balance_of( address0) == balance0 + deposit0 - amount * 2 assert token_contract.balance_of( address1) == balance1 + deposit1 + amount * 2
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.sleep_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_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_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) token_proxy = app0.raiden.chain.token(channel0.token_address) initial_balance0 = token_proxy.balance_of(app0.raiden.address) initial_balance1 = token_proxy.balance_of(app1.raiden.address) amount0_1 = 10 direct_transfer( app0, app1, token_network_identifier, amount0_1, ) amount1_1 = 50 direct_transfer( app1, app0, token_network_identifier, amount1_1, ) amount0_2 = 60 direct_transfer( app0, app1, token_network_identifier, amount0_2, ) # 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_identifier) == 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_cancel_transfer(raiden_chain, token, deposit): app0, app1, app2, app3 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking messages = setup_messages_cb() mlogger = MessageLogger() assert_synched_channels( channel(app0, app1, token), deposit, [], channel(app1, app0, token), deposit, [] ) assert_synched_channels( channel(app1, app2, token), deposit, [], channel(app2, app1, token), deposit, [] ) assert_synched_channels( channel(app2, app3, token), deposit, [], channel(app3, app2, token), deposit, [] ) assert_synched_channels( channel(app0, app1, token), deposit, [], channel(app1, app0, token), deposit, [] ) # drain the channel app1 -> app2 amount12 = 50 direct_transfer(app1, app2, token, amount12) # drain the channel app2 -> app3 amount23 = 80 direct_transfer(app2, app3, token, amount23) assert_synched_channels( channel(app1, app2, token), deposit - amount12, [], channel(app2, app1, token), deposit + amount12, [] ) assert_synched_channels( channel(app2, app3, token), deposit - amount23, [], channel(app3, app2, token), deposit + amount23, [] ) # app1 -> app3 is the only available path but app2 -> app3 doesnt have # resources and needs to send a RefundTransfer down the path transfer(app0, app3, token, amount=50, identifier=1) assert_synched_channels( channel(app0, app1, token), deposit, [], channel(app1, app0, token), deposit, [] ) assert_synched_channels( channel(app1, app2, token), deposit - amount12, [], channel(app2, app1, token), deposit + amount12, [] ) assert_synched_channels( channel(app2, app3, token), deposit - amount23, [], channel(app3, app2, token), deposit + amount23, [] ) assert len(unique(messages)) == 12 # DT + DT + SMT + MT + RT + RT + ACKs app1_messages = mlogger.get_node_messages(pex(app1.raiden.address), only='sent') assert isinstance(app1_messages[-1], RefundTransfer) app2_messages = mlogger.get_node_messages(pex(app2.raiden.address), only='sent') assert isinstance(app2_messages[-1], RefundTransfer)
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.sleep_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_settled_lock(token_addresses, raiden_network, deposit): """ Any transfer following a secret revealed 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 = 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, ) secrethash = sha3(secret) # Compute the merkle proof for the pending transfer, and then unlock channelstate_0_1 = get_channelstate(app0, app1, token_network_identifier) lock = channel.get_lock(channelstate_0_1.our_state, secrethash) unlock_proof = channel.compute_proof_for_lock( channelstate_0_1.our_state, secret, lock, ) 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, ) # The direct transfer locksroot must not contain the unlocked lock, the # withdraw must fail. netting_channel = app1.raiden.chain.netting_channel( channelstate_0_1.identifier) with pytest.raises(Exception): netting_channel.withdraw( UnlockProofState(unlock_proof, lock.encoded, secret)) waiting.wait_for_settle( app1.raiden, app1.raiden.default_registry.address, token_address, [channelstate_0_1.identifier], app1.raiden.alarm.wait_time, ) 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_refund_transfer_after_2nd_hop( raiden_chain, number_of_nodes, token_addresses, deposit, network_wait, ): """Test the refund transfer sent due to failure after 2nd hop""" # Topology: # # 0 -> 1 -> 2 -> 3 # app0, app1, app2, app3 = raiden_chain token_address = token_addresses[0] payment_network_identifier = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), payment_network_identifier, token_address, ) # make a transfer to test the path app0 -> app1 -> app2 -> app3 identifier_path = 1 amount_path = 1 mediated_transfer( app0, app3, token_network_identifier, amount_path, identifier_path, timeout=network_wait * number_of_nodes, ) # drain the channel app2 -> app3 identifier_drain = 2 amount_drain = deposit * 8 // 10 direct_transfer( app2, app3, token_network_identifier, amount_drain, identifier_drain, timeout=network_wait, ) # wait for the nodes to sync gevent.sleep(0.2) assert_synched_channel_state( token_network_identifier, app0, deposit - amount_path, [], app1, deposit + amount_path, [], ) assert_synched_channel_state( token_network_identifier, app1, deposit - amount_path, [], app2, deposit + amount_path, [], ) assert_synched_channel_state( token_network_identifier, app2, deposit - amount_path - amount_drain, [], app3, deposit + amount_path + amount_drain, [], ) # app0 -> app1 -> app2 > app3 is the only available path, but the channel # app2 -> app3 doesn't have capacity, so a refund will be sent on # app2 -> app1 -> app0 identifier_refund = 3 amount_refund = 50 async_result = app0.raiden.mediated_transfer_async( token_network_identifier, amount_refund, app3.raiden.address, identifier_refund, ) assert async_result.wait() is False, 'there is no path with capacity, the transfer must fail' gevent.sleep(0.2) # Lock structures with the correct amount send_locked1 = raiden_events_must_contain_entry( app0.raiden, SendLockedTransfer, {'transfer': {'lock': {'amount': amount_refund}}}, ) assert send_locked1 send_refund1 = raiden_events_must_contain_entry(app1.raiden, SendRefundTransfer, {}) assert send_refund1 lock1 = send_locked1.transfer.lock refund_lock1 = send_refund1.lock assert lock1.amount == refund_lock1.amount assert lock1.secrethash == refund_lock1.secrethash send_locked2 = raiden_events_must_contain_entry( app1.raiden, SendLockedTransfer, {'transfer': {'lock': {'amount': amount_refund}}}, ) assert send_locked2 send_refund2 = raiden_events_must_contain_entry(app2.raiden, SendRefundTransfer, {}) assert send_refund2 lock2 = send_locked2.transfer.lock refund_lock2 = send_refund2.lock assert lock2.amount == refund_lock2.amount assert lock2.secrethash assert lock2.expiration # channels have the amount locked because of the refund message assert_synched_channel_state( token_network_identifier, app0, deposit - amount_path, [lockstate_from_lock(lock1)], app1, deposit + amount_path, [lockstate_from_lock(refund_lock1)], ) assert_synched_channel_state( token_network_identifier, app1, deposit - amount_path, [lockstate_from_lock(lock2)], app2, deposit + amount_path, [lockstate_from_lock(refund_lock2)], ) assert_synched_channel_state( token_network_identifier, app2, deposit - amount_path - amount_drain, [], app3, deposit + amount_path + amount_drain, [], )
def test_fullnetwork(raiden_chain, settle_timeout, reveal_timeout): app0, app1, app2, app3 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking token_address = app0.raiden.chain.default_registry.token_addresses()[0] channel_0_1 = channel(app0, app1, token_address) channel_1_2 = channel(app1, app2, token_address) channel_3_2 = channel(app3, app2, token_address) channel_0_3 = channel(app0, app3, token_address) amount = 80 direct_transfer(app0, app1, token_address, amount) last_transfer = get_sent_transfer(channel_0_1, 0) assert last_transfer.transferred_amount == 80 amount = 50 direct_transfer(app1, app2, token_address, amount) last_transfer = get_sent_transfer(channel_1_2, 0) assert last_transfer.transferred_amount == 50 amount = 30 mediated_transfer(app0, app2, token_address, amount) last_transfer = get_sent_transfer(channel_0_1, 0) assert isinstance(last_transfer, DirectTransfer) initiator_transfer = get_sent_transfer(channel_0_3, 0) mediator_transfer = get_sent_transfer(channel_3_2, 0) assert initiator_transfer.identifier == mediator_transfer.identifier assert initiator_transfer.lock.amount == amount assert mediator_transfer.lock.amount == amount # Now let's query the WAL to see if the state changes were logged as expected app0_state_changes = [ change[1] for change in app0.raiden.transaction_log.get_all_state_changes() if not isinstance(change[1], Block) ] app1_state_changes = [ change[1] for change in app1.raiden.transaction_log.get_all_state_changes() if not isinstance(change[1], Block) ] app2_state_changes = [ change[1] for change in app2.raiden.transaction_log.get_all_state_changes() if not isinstance(change[1], Block) ] app3_state_changes = [ change[1] for change in app3.raiden.transaction_log.get_all_state_changes() if not isinstance(change[1], Block) ] # app1 does not take part in the mediated transfer. assert app1_state_changes == list() # app0 initiates the mediated_transfer assert len(app0_state_changes) == 3 assert isinstance(app0_state_changes[0], ActionInitInitiator) assert app0_state_changes[0].our_address == app0.raiden.address assert app0_state_changes[0].transfer.amount == amount assert app0_state_changes[0].transfer.token == token_address assert app0_state_changes[0].transfer.initiator == app0.raiden.address assert app0_state_changes[0].transfer.target == app2.raiden.address # The ActionInitInitiator state change does not have the following fields populated. # They get populated via an event during the processing of the state change inside # this function: mediated_transfer.mediated_transfer.initiator.try_new_route() assert app0_state_changes[0].transfer.expiration is None assert app0_state_changes[0].transfer.hashlock is None assert app0_state_changes[0].transfer.secret is None # We should have two available routes assert len(app0_state_changes[0].routes.available_routes) == 2 assert len(app0_state_changes[0].routes.ignored_routes) == 0 assert len(app0_state_changes[0].routes.refunded_routes) == 0 assert len(app0_state_changes[0].routes.canceled_routes) == 0 # Of these 2 the state machine will in the future choose the one with the most # available balance not_taken_route = RouteState( state='opened', node_address=app1.raiden.address, channel_address=channel_0_1.channel_address, available_balance=1048496, settle_timeout=settle_timeout, reveal_timeout=reveal_timeout, closed_block=None, ) taken_route = RouteState( state='opened', node_address=app3.raiden.address, channel_address=channel_0_3.channel_address, available_balance=1048576, settle_timeout=settle_timeout, reveal_timeout=reveal_timeout, closed_block=None, ) assert taken_route in app0_state_changes[0].routes.available_routes assert not_taken_route in app0_state_changes[0].routes.available_routes # app0 will also receive a secret request from the target assert isinstance(app0_state_changes[1], ReceiveSecretRequest) assert app0_state_changes[1].amount == amount assert app0_state_changes[1].sender == app2.raiden.address hashlock = app0_state_changes[1].hashlock # app0 will also receive a secret reveal from the immediate neighbour assert isinstance(app0_state_changes[2], ReceiveSecretReveal) assert app0_state_changes[2].sender == app3.raiden.address secret = app0_state_changes[2].secret assert sha3(secret) == hashlock # app3 is the mediator assert isinstance(app3_state_changes[0], ActionInitMediator) assert app3_state_changes[0].our_address == app3.raiden.address # We should have only 1 available route from mediator to target from_route = RouteState( state='opened', node_address=app0.raiden.address, channel_address=channel_0_3.channel_address, available_balance=1048576, settle_timeout=settle_timeout, reveal_timeout=reveal_timeout, closed_block=None, ) to_route = RouteState( state='opened', node_address=app2.raiden.address, channel_address=channel_3_2.channel_address, available_balance=1048576, settle_timeout=settle_timeout, reveal_timeout=reveal_timeout, closed_block=None, ) assert app3_state_changes[0].from_route == from_route assert len(app3_state_changes[0].routes.available_routes) == 1 assert len(app3_state_changes[0].routes.ignored_routes) == 0 assert len(app3_state_changes[0].routes.refunded_routes) == 0 assert len(app3_state_changes[0].routes.canceled_routes) == 0 assert app3_state_changes[0].routes.available_routes[0] == to_route # check the from_transfer is correct assert app3_state_changes[0].from_transfer.amount == amount assert app3_state_changes[0].from_transfer.hashlock == hashlock assert app3_state_changes[0].from_transfer.token == token_address assert app3_state_changes[0].from_transfer.initiator == app0.raiden.address assert app3_state_changes[0].from_transfer.target == app2.raiden.address # The mediator should have also received a SecretReveal from the target assert isinstance(app3_state_changes[1], ReceiveSecretReveal) assert app3_state_changes[1].sender == app2.raiden.address assert app3_state_changes[1].secret == secret # If the mediator received any more it is from the initiator # TODO: Figure out why we may get two times the secret reveal from the initiator for state_change in app3_state_changes[2:]: assert isinstance(state_change, ReceiveSecretReveal) assert state_change.sender == app0.raiden.address assert state_change.secret == secret # app2 is the target of the mediated transfer assert len(app2_state_changes ) == 4 # We get 2 secret reveals from the mediator. WHY? assert isinstance(app2_state_changes[0], ActionInitTarget) assert app2_state_changes[0].our_address == app2.raiden.address # check the route the transfer came from from_route = RouteState( state='opened', node_address=app3.raiden.address, channel_address=channel_3_2.channel_address, available_balance=1048576, settle_timeout=settle_timeout, reveal_timeout=reveal_timeout, closed_block=None, ) assert app2_state_changes[0].from_route == from_route # check the from_transfer is correct assert app2_state_changes[0].from_transfer.amount == amount assert app2_state_changes[0].from_transfer.hashlock == hashlock assert app2_state_changes[0].from_transfer.token == token_address assert app2_state_changes[0].from_transfer.initiator == app0.raiden.address assert app2_state_changes[0].from_transfer.target == app2.raiden.address # We also get secret reveals from the initiator and the mediator. assert isinstance(app2_state_changes[1], ReceiveSecretReveal) assert app2_state_changes[1].sender == app0.raiden.address assert app2_state_changes[1].secret == secret # TODO: Figure out why we get two times the Secret Reveal from the mediator assert isinstance(app2_state_changes[2], ReceiveSecretReveal) assert app2_state_changes[2].sender == app3.raiden.address assert app2_state_changes[2].secret == secret assert isinstance(app2_state_changes[3], ReceiveSecretReveal) assert app2_state_changes[3].sender == app3.raiden.address assert app2_state_changes[3].secret == secret
def test_cancel_transfer(raiden_chain, token_addresses, deposit): """ 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 # pylint: disable=unbalanced-tuple-unpacking token = token_addresses[0] assert_synched_channels( channel(app0, app1, token), deposit, [], channel(app1, app0, token), deposit, [] ) assert_synched_channels( channel(app1, app2, token), deposit, [], channel(app2, app1, token), deposit, [] ) # make a transfer to test the path app0 -> app1 -> app2 identifier_path = 1 amount_path = 1 transfer(app0, app2, token, amount_path, identifier_path) # drain the channel app1 -> app2 identifier_drain = 2 amount_drain = int(deposit * 0.8) direct_transfer(app1, app2, token, amount_drain, identifier_drain) # wait for the nodes to sync gevent.sleep(0.2) assert_synched_channels( channel(app0, app1, token), deposit - amount_path, [], channel(app1, app0, token), deposit + amount_path, [] ) assert_synched_channels( channel(app1, app2, token), deposit - amount_path - amount_drain, [], channel(app2, app1, token), deposit + amount_path + amount_drain, [] ) # app0 -> app1 -> app2 is the only available path but the channel app1 -> # app2 doesnt have resources and needs to send a RefundTransfer down the # path identifier_refund = 3 amount_refund = 50 async_result = app0.raiden.mediated_transfer_async( token, 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 app0_messages = app0.raiden.protocol.transport.get_sent_messages(app0.raiden.address) mediated_message = list( message for message in app0_messages if isinstance(message, MediatedTransfer) and message.target == app2.raiden.address )[-1] assert mediated_message app1_messages = app1.raiden.protocol.transport.get_sent_messages(app1.raiden.address) refund_message = next( message for message in app1_messages if isinstance(message, RefundTransfer) and message.recipient == app0.raiden.address ) assert refund_message assert mediated_message.lock.amount == refund_message.lock.amount assert mediated_message.lock.hashlock == refund_message.lock.hashlock assert mediated_message.lock.expiration > refund_message.lock.expiration # Both channels have the amount locked because of the refund message assert_synched_channels( channel(app0, app1, token), deposit - amount_path, [refund_message.lock], channel(app1, app0, token), deposit + amount_path, [mediated_message.lock], ) assert_synched_channels( channel(app1, app2, token), deposit - amount_path - amount_drain, [], channel(app2, app1, token), deposit + amount_path + amount_drain, [] )
def test_fullnetwork( raiden_chain, token_addresses, deposit, settle_timeout, reveal_timeout): # pylint: disable=too-many-locals,too-many-statements # The network has the following topology: # # App0 <---> App1 # ^ ^ # | | # v v # App3 <---> App2 token_address = token_addresses[0] app0, app1, app2, app3 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking channel_0_1 = channel(app0, app1, token_address) channel_3_2 = channel(app3, app2, token_address) channel_0_3 = channel(app0, app3, token_address) # Exhaust the channel deposit (to force the mediated transfer to go backwards) amount = deposit direct_transfer(app0, app1, token_address, amount, identifier=1) assert get_sent_transfer(channel_0_1, 0).transferred_amount == amount amount = int(deposit / 2.) mediated_transfer( app0, app2, token_address, amount ) gevent.sleep(0.5) # This is the only possible path, the transfer must go backwards assert_path_mediated_transfer( get_sent_transfer(channel_0_3, 0), get_sent_transfer(channel_3_2, 0), ) app0_state_changes = [ change[1] for change in get_all_state_changes(app0.raiden.transaction_log) ] app0_events = [ event.event_object for event in get_all_state_events(app0.raiden.transaction_log) ] secret = None for event in app0_events: if isinstance(event, SendRevealSecret): secret = event.secret assert secret is not None hashlock = sha3(secret) # app0 initiates the direct transfer and mediated_transfer assert must_contain_entry(app0_state_changes, ActionInitInitiator, { 'our_address': app0.raiden.address, 'transfer': { 'amount': amount, 'token': token_address, 'initiator': app0.raiden.address, 'target': app2.raiden.address, 'expiration': None, 'hashlock': None, 'secret': None, } }) # Of these 2 the state machine will in the future choose the one with the most # available balance not_taken_route = RouteState( state='opened', node_address=app1.raiden.address, channel_address=channel_0_1.channel_address, available_balance=deposit, settle_timeout=settle_timeout, reveal_timeout=reveal_timeout, closed_block=None, ) taken_route = RouteState( state='opened', node_address=app3.raiden.address, channel_address=channel_0_3.channel_address, available_balance=deposit, settle_timeout=settle_timeout, reveal_timeout=reveal_timeout, closed_block=None, ) for state_change in app0_state_changes: if isinstance(state_change, ActionInitMediator): assert taken_route in state_change.routes.available_routes assert not_taken_route not in state_change.routes.available_routes # app1 received one direct transfers app1_state_changes = [ change[1] for change in get_all_state_changes(app1.raiden.transaction_log) ] assert must_contain_entry(app1_state_changes, ReceiveBalanceProof, {}) assert must_contain_entry(app1_state_changes, ReceiveTransferDirect, {}) app2_state_changes = [ change[1] for change in get_all_state_changes(app2.raiden.transaction_log) ] assert must_contain_entry(app2_state_changes, ActionInitTarget, { 'our_address': app2.raiden.address, 'from_route': { 'state': 'opened', 'node_address': app3.raiden.address, 'channel_address': channel_3_2.channel_address, 'available_balance': deposit, 'settle_timeout': settle_timeout, 'reveal_timeout': reveal_timeout, 'closed_block': None }, 'from_transfer': { 'amount': amount, 'hashlock': hashlock, 'token': token_address, 'initiator': app0.raiden.address, 'target': app2.raiden.address, } }) assert must_contain_entry(app2_state_changes, ReceiveSecretReveal, { 'sender': app0.raiden.address, 'secret': secret, }) assert must_contain_entry(app2_state_changes, ReceiveSecretReveal, { 'sender': app3.raiden.address, 'secret': secret, }) app2_events = [ event.event_object for event in get_all_state_events(app2.raiden.transaction_log) ] assert must_contain_entry(app2_events, SendSecretRequest, { 'amount': amount, 'hashlock': hashlock, 'receiver': app0.raiden.address, }) assert must_contain_entry(app2_events, SendRevealSecret, { 'token': token_address, 'secret': secret, 'receiver': app3.raiden.address, 'sender': app2.raiden.address, }) assert must_contain_entry(app0_state_changes, ReceiveSecretRequest, { 'amount': amount, 'sender': app2.raiden.address, 'hashlock': hashlock, }) assert must_contain_entry(app0_state_changes, ReceiveSecretReveal, { 'sender': app3.raiden.address, 'secret': secret, }) assert must_contain_entry(app0_events, EventTransferSentSuccess, {}) assert must_contain_entry(app0_events, SendMediatedTransfer, { 'token': token_address, 'amount': amount, 'hashlock': hashlock, 'initiator': app0.raiden.address, 'target': app2.raiden.address, 'receiver': app3.raiden.address, }) assert must_contain_entry(app0_events, SendRevealSecret, { 'secret': secret, 'token': token_address, 'receiver': app2.raiden.address, 'sender': app0.raiden.address, }) assert must_contain_entry(app0_events, SendBalanceProof, { 'token': token_address, 'channel_address': channel_0_3.channel_address, 'receiver': app3.raiden.address, 'secret': secret, }) assert must_contain_entry(app0_events, EventTransferSentSuccess, {}) assert must_contain_entry(app0_events, EventUnlockSuccess, { 'hashlock': hashlock, }) app3_state_changes = [ change[1] for change in get_all_state_changes(app3.raiden.transaction_log) ] assert must_contain_entry(app3_state_changes, ActionInitMediator, { 'our_address': app3.raiden.address, 'from_route': { 'state': 'opened', 'node_address': app0.raiden.address, 'channel_address': channel_0_3.channel_address, 'available_balance': deposit, 'settle_timeout': settle_timeout, 'reveal_timeout': reveal_timeout, 'closed_block': None, }, 'from_transfer': { 'amount': amount, 'hashlock': hashlock, 'token': token_address, 'initiator': app0.raiden.address, 'target': app2.raiden.address, } }) assert must_contain_entry(app3_state_changes, ReceiveSecretReveal, { 'sender': app2.raiden.address, 'secret': secret, }) assert must_contain_entry(app3_state_changes, ReceiveSecretReveal, { 'sender': app2.raiden.address, 'secret': secret, }) app3_events = [ event.event_object for event in get_all_state_events(app3.raiden.transaction_log) ] assert must_contain_entry(app3_events, SendMediatedTransfer, { 'token': token_address, 'amount': amount, 'hashlock': hashlock, 'initiator': app0.raiden.address, 'target': app2.raiden.address, 'receiver': app2.raiden.address, }) assert must_contain_entry(app3_events, SendRevealSecret, { 'secret': secret, 'token': token_address, 'receiver': app0.raiden.address, 'sender': app3.raiden.address, }) assert must_contain_entry(app3_events, SendBalanceProof, { 'token': token_address, 'channel_address': channel_3_2.channel_address, 'receiver': app2.raiden.address, 'secret': secret, }) assert must_contain_entry(app3_events, EventUnlockSuccess, {})
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_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) token_proxy = app0.raiden.chain.token(channel0.token_address) initial_balance0 = token_proxy.balance_of(app0.raiden.address) initial_balance1 = token_proxy.balance_of(app1.raiden.address) amount0_1 = 10 direct_transfer( app0, app1, token_network_identifier, amount0_1, ) amount1_1 = 50 direct_transfer( app1, app0, token_network_identifier, amount1_1, ) amount0_2 = 60 direct_transfer( app0, app1, token_network_identifier, amount0_2, ) # 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_identifier) == 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_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 = 30 token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) hold_event_handler = HoldOffChainSecretRequest() app1.raiden.raiden_event_handler = hold_event_handler 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) identifier = 1 target = app1.raiden.address secret = sha3(target) secrethash = sha3(secret) secret_available = hold_event_handler.hold_secretrequest_for( secrethash=secrethash) app0.raiden.start_mediated_transfer_with_secret( token_network_identifier, amount, target, identifier, secret, ) secret_available.wait() # wait for the messages to be exchanged # 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 hold_event_handler.release_secretrequest_for( app1.raiden, secrethash, ) direct_transfer( app0, app1, token_network_identifier, amount, identifier=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, ) 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_refund_transfer_after_2nd_hop( raiden_chain, number_of_nodes, token_addresses, deposit, network_wait, ): """Test the refund transfer sent due to failure after 2nd hop""" # Topology: # # 0 -> 1 -> 2 -> 3 # app0, app1, app2, app3 = raiden_chain token_address = token_addresses[0] payment_network_identifier = app0.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), payment_network_identifier, token_address, ) # make a transfer to test the path app0 -> app1 -> app2 -> app3 identifier_path = 1 amount_path = 1 mediated_transfer( app0, app3, token_network_identifier, amount_path, identifier_path, timeout=network_wait * number_of_nodes, ) # drain the channel app2 -> app3 identifier_drain = 2 amount_drain = deposit * 8 // 10 direct_transfer( app2, app3, token_network_identifier, amount_drain, identifier_drain, timeout=network_wait, ) # wait for the nodes to sync gevent.sleep(0.2) assert_synched_channel_state( token_network_identifier, app0, deposit - amount_path, [], app1, deposit + amount_path, [], ) assert_synched_channel_state( token_network_identifier, app1, deposit - amount_path, [], app2, deposit + amount_path, [], ) assert_synched_channel_state( token_network_identifier, app2, deposit - amount_path - amount_drain, [], app3, deposit + amount_path + amount_drain, [], ) # app0 -> app1 -> app2 > app3 is the only available path, but the channel # app2 -> app3 doesn't have capacity, so a refund will be sent on # app2 -> app1 -> app0 identifier_refund = 3 amount_refund = 50 async_result = app0.raiden.mediated_transfer_async( token_network_identifier, amount_refund, app3.raiden.address, identifier_refund, ) assert async_result.wait( ) is False, 'there is no path with capacity, the transfer must fail' gevent.sleep(0.2) # Lock structures with the correct amount send_locked1 = raiden_events_must_contain_entry( app0.raiden, SendLockedTransfer, {'transfer': { 'lock': { 'amount': amount_refund } }}, ) assert send_locked1 send_refund1 = raiden_events_must_contain_entry(app1.raiden, SendRefundTransfer, {}) assert send_refund1 lock1 = send_locked1.transfer.lock refund_lock1 = send_refund1.lock assert lock1.amount == refund_lock1.amount assert lock1.secrethash == refund_lock1.secrethash send_locked2 = raiden_events_must_contain_entry( app1.raiden, SendLockedTransfer, {'transfer': { 'lock': { 'amount': amount_refund } }}, ) assert send_locked2 send_refund2 = raiden_events_must_contain_entry(app2.raiden, SendRefundTransfer, {}) assert send_refund2 lock2 = send_locked2.transfer.lock refund_lock2 = send_refund2.lock assert lock2.amount == refund_lock2.amount assert lock2.secrethash assert lock2.expiration # channels have the amount locked because of the refund message assert_synched_channel_state( token_network_identifier, app0, deposit - amount_path, [lockstate_from_lock(lock1)], app1, deposit + amount_path, [lockstate_from_lock(refund_lock1)], ) assert_synched_channel_state( token_network_identifier, app1, deposit - amount_path, [lockstate_from_lock(lock2)], app2, deposit + amount_path, [lockstate_from_lock(refund_lock2)], ) assert_synched_channel_state( token_network_identifier, app2, deposit - amount_path - amount_drain, [], app3, deposit + amount_path + amount_drain, [], )
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_synched_channel_state( token_network_identifier, app0, deposit - amount_path, [], app1, deposit + amount_path, [], ) assert_synched_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_synched_channel_state( token_network_identifier, app0, deposit - amount_path, [lockstate_from_lock(lock)], app1, deposit + amount_path, [lockstate_from_lock(refund_lock)], ) assert_synched_channel_state( token_network_identifier, app1, deposit - amount_path - amount_drain, [], app2, deposit + amount_path + amount_drain, [], )
def test_refund_transfer(raiden_chain, 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] # make a transfer to test the path app0 -> app1 -> app2 identifier_path = 1 amount_path = 1 mediated_transfer( app0, app2, token_address, amount_path, identifier_path, timeout=network_wait, ) # drain the channel app1 -> app2 identifier_drain = 2 amount_drain = deposit * 8 // 10 direct_transfer( app1, app2, token_address, amount_drain, identifier_drain, timeout=network_wait, ) # wait for the nodes to sync gevent.sleep(0.2) assert_synched_channel_state( token_address, app0, deposit - amount_path, [], app1, deposit + amount_path, [], ) assert_synched_channel_state( token_address, app1, deposit - amount_path - amount_drain, [], app2, deposit + amount_path + amount_drain, [], ) # app0 -> app1 -> app2 is the only available path, but the channel app1 -> # app2 doesn't have capacity, so a refund will be sent on app1 -> app0 identifier_refund = 3 amount_refund = 50 async_result = app0.raiden.mediated_transfer_async( token_address, 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 app0_messages = app0.raiden.protocol.transport.get_sent_messages(app0.raiden.address) mediated_message = list( message for message in app0_messages if isinstance(message, MediatedTransfer) and message.target == app2.raiden.address )[-1] assert mediated_message app1_messages = app1.raiden.protocol.transport.get_sent_messages(app1.raiden.address) refund_message = next( message for message in app1_messages if isinstance(message, RefundTransfer) and message.recipient == app0.raiden.address ) assert refund_message assert mediated_message.lock.amount == refund_message.lock.amount assert mediated_message.lock.hashlock == refund_message.lock.hashlock assert mediated_message.lock.expiration > refund_message.lock.expiration # Both channels have the amount locked because of the refund message assert_synched_channel_state( token_address, app0, deposit - amount_path, [lockstate_from_lock(mediated_message.lock)], app1, deposit + amount_path, [lockstate_from_lock(refund_message.lock)], ) assert_synched_channel_state( token_address, app1, deposit - amount_path - amount_drain, [], app2, deposit + amount_path + amount_drain, [], )
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 direct_transfer(app1, app2, token_network_identifier, 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_synched_channel_state( token_network_identifier, app0, deposit, [send_lockedtransfer.transfer.lock], app1, deposit, [send_refundtransfer.lock], ) # This channel was exhausted to force the refund transfer assert_synched_channel_state( token_network_identifier, app1, 0, [], app2, deposit * 2, [], )
def test_cancel_transfer(raiden_chain, asset, deposit): app0, app1, app2, app3 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking messages = setup_messages_cb() mlogger = MessageLogger() assert_synched_channels( channel(app0, app1, asset), deposit, [], channel(app1, app0, asset), deposit, [] ) assert_synched_channels( channel(app1, app2, asset), deposit, [], channel(app2, app1, asset), deposit, [] ) assert_synched_channels( channel(app2, app3, asset), deposit, [], channel(app3, app2, asset), deposit, [] ) assert_synched_channels( channel(app0, app1, asset), deposit, [], channel(app1, app0, asset), deposit, [] ) # drain the channel app1 -> app2 amount12 = 50 direct_transfer(app1, app2, asset, amount12) # drain the channel app2 -> app3 amount23 = 80 direct_transfer(app2, app3, asset, amount23) assert_synched_channels( channel(app1, app2, asset), deposit - amount12, [], channel(app2, app1, asset), deposit + amount12, [] ) assert_synched_channels( channel(app2, app3, asset), deposit - amount23, [], channel(app3, app2, asset), deposit + amount23, [] ) # app1 -> app3 is the only available path but app2 -> app3 doesnt have # resources and needs to send a RefundTransfer down the path transfer(app0, app3, asset, 50) assert_synched_channels( channel(app0, app1, asset), deposit, [], channel(app1, app0, asset), deposit, [] ) assert_synched_channels( channel(app1, app2, asset), deposit - amount12, [], channel(app2, app1, asset), deposit + amount12, [] ) assert_synched_channels( channel(app2, app3, asset), deposit - amount23, [], channel(app3, app2, asset), deposit + amount23, [] ) assert len(messages) == 12 # DT + DT + SMT + MT + RT + RT + ACKs app1_messages = mlogger.get_node_messages(pex(app1.raiden.address), only='sent') assert isinstance(app1_messages[-1], RefundTransfer) app2_messages = mlogger.get_node_messages(pex(app2.raiden.address), only='sent') assert isinstance(app2_messages[-1], RefundTransfer)
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_synched_channel_state( token_network_identifier, app0, deposit - amount_path, [], app1, deposit + amount_path, [], ) assert_synched_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_synched_channel_state( token_network_identifier, app0, deposit - amount_path, [lockstate_from_lock(lock)], app1, deposit + amount_path, [lockstate_from_lock(refund_lock)], ) assert_synched_channel_state( token_network_identifier, app1, deposit - amount_path - amount_drain, [], app2, deposit + amount_path + amount_drain, [], )
def test_settled_lock(assets_addresses, raiden_network, settle_timeout, reveal_timeout): """ Any transfer following a secret revealed must update the locksroot, so that an attacker cannot reuse a secret to double claim a lock. """ asset = assets_addresses[0] amount = 30 app0, app1, app2, _ = raiden_network # pylint: disable=unbalanced-tuple-unpacking address0 = app0.raiden.address address1 = app1.raiden.address # mediated transfer identifier = 1 expiration = app0.raiden.chain.block_number() + settle_timeout - reveal_timeout secret = pending_mediated_transfer( raiden_network, asset, amount, identifier, expiration, ) hashlock = sha3(secret) # get a proof for the pending transfer back_channel = channel(app1, app0, asset) secret_transfer = get_received_transfer(back_channel, 0) lock = back_channel.our_state.balance_proof.get_lock_by_hashlock(hashlock) unlock_proof = back_channel.our_state.balance_proof.compute_proof_for_lock(secret, lock) # reveal the secret claim_lock(raiden_network, asset, secret) # a new transfer to update the hashlock direct_transfer(app0, app1, asset, amount) forward_channel = channel(app0, app1, asset) last_transfer = get_sent_transfer(forward_channel, 1) # call close giving the secret for a transfer that has being revealed back_channel.external_state.netting_channel.close( app1.raiden.address, last_transfer, None ) # check that the double unlock will failed with pytest.raises(Exception): back_channel.external_state.netting_channel.unlock( app1.raiden.address, [(unlock_proof, secret_transfer.lock.as_bytes, secret)], ) # forward the block number to allow settle settle_expiration = app2.raiden.chain.block_number() + settle_timeout wait_until_block(app2.raiden.chain, settle_expiration) back_channel.external_state.netting_channel.settle() participant0 = back_channel.external_state.netting_channel.contract.participants[address0] participant1 = back_channel.external_state.netting_channel.contract.participants[address1] assert participant0.netted == participant0.deposit - amount * 2 assert participant1.netted == participant1.deposit + amount * 2
def test_send_queued_messages( raiden_network, number_of_nodes, deposit, token_addresses, network_wait, skip_if_not_matrix, ): """Test re-sending of undelivered messages on node restart""" app0, app1 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) 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, ) # stop app1 - direct transfer must be left unconfirmed app1.stop() # make a few transfers from app0 to app2 amount = 1 spent_amount = 7 for _ in range(spent_amount): direct_transfer( app0, app1, token_network_identifier, amount, timeout=network_wait * number_of_nodes, ) # restart app0 app0.raiden.stop() new_transport = MatrixTransport( app0.raiden.config['transport']['matrix'], ) 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 app1.start() app0_restart.start() waiting.wait_for_healthy( app0_restart.raiden, app1.raiden.address, network_wait, ) waiting.wait_for_healthy( app1.raiden, app0_restart.raiden.address, network_wait, ) exception = RuntimeError('Timeout while waiting for new channel') with gevent.Timeout(5, exception=exception): waiting.wait_for_newchannel( app0_restart.raiden, payment_network_id, token_address, app1.raiden.address, network_wait, ) exception = RuntimeError( 'Timeout while waiting for balance update for app0') with gevent.Timeout(30, exception=exception): waiting.wait_for_payment_balance( app0_restart.raiden, payment_network_id, token_address, app1.raiden.address, app1.raiden.address, spent_amount, network_wait, ) waiting.wait_for_payment_balance( app1.raiden, payment_network_id, token_address, app0_restart.raiden.address, app1.raiden.address, spent_amount, network_wait, ) assert_synced_channel_state( token_network_identifier, app0_restart, deposit - spent_amount, [], app1, deposit + spent_amount, [], )