def test_get_channel_list(raiden_network, token_addresses): app0, app1, app2 = raiden_network # pylint: disable=unbalanced-tuple-unpacking channel0 = channel(app0, app1, token_addresses[0]) channel1 = channel(app1, app0, token_addresses[0]) channel2 = channel(app0, app2, token_addresses[0]) api0 = RaidenAPI(app0.raiden) api1 = RaidenAPI(app1.raiden) api2 = RaidenAPI(app2.raiden) assert channel0, channel2 in api0.get_channel_list() assert channel0 in api0.get_channel_list(partner_address=app1.raiden.address) assert channel1 in api1.get_channel_list(token_address=token_addresses[0]) assert channel1 in api1.get_channel_list(token_addresses[0], app0.raiden.address) assert not api1.get_channel_list(partner_address=app2.raiden.address) assert not api1.get_channel_list( token_address=token_addresses[0], partner_address=app2.raiden.address, ) assert not api2.get_channel_list( token_address=app2.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_direct_transfer_to_offline_node(raiden_network, token_addresses): token_address = token_addresses[0] app0, app1 = raiden_network # 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 async_result = app0.raiden.direct_transfer_async( token_address, amount, target, identifier=1, ) assert async_result.wait(5) is None app1.raiden.start() assert async_result.wait(5) is True assert_mirror( channel(app0, app1, token_address), channel(app1, app0, token_address), )
def test_transfer(raiden_network, assets_addresses): app0, app1 = raiden_network # pylint: disable=unbalanced-tuple-unpacking channel0 = channel(app0, app1, assets_addresses[0]) channel1 = channel(app1, app0, assets_addresses[0]) contract_balance0 = channel0.contract_balance contract_balance1 = channel1.contract_balance # check agreement on addresses address0 = channel0.our_state.address address1 = channel1.our_state.address app0_asset = app0.raiden.managers_by_asset_address.keys()[0] app1_asset = app1.raiden.managers_by_asset_address.keys()[0] app0_partners = app0.raiden.managers_by_asset_address.values()[0].partneraddress_channel.keys() app1_partners = app1.raiden.managers_by_asset_address.values()[0].partneraddress_channel.keys() assert channel0.asset_address == channel1.asset_address assert app0_asset == app1_asset assert app1.raiden.address in app0_partners assert app0.raiden.address in app1_partners netting_address = channel0.external_state.netting_channel.address netting_channel = app0.raiden.chain.netting_channel(netting_address) # check balances of channel and contract are equal details0 = netting_channel.detail(address0) details1 = netting_channel.detail(address1) assert contract_balance0 == details0['our_balance'] assert contract_balance1 == details1['our_balance'] assert_synched_channels( channel0, contract_balance0, [], channel1, contract_balance1, [], ) amount = 10 direct_transfer = channel0.create_directtransfer( amount, 1 # TODO: fill in identifier ) app0.raiden.sign(direct_transfer) channel0.register_transfer(direct_transfer) channel1.register_transfer(direct_transfer) # check the contract is intact assert details0 == netting_channel.detail(address0) assert details1 == netting_channel.detail(address1) assert channel0.contract_balance == contract_balance0 assert channel1.contract_balance == contract_balance1 assert_synched_channels( channel0, contract_balance0 - amount, [], channel1, contract_balance1 + amount, [], )
def test_receive_direct_before_deposit(raiden_network): """Regression test that ensures we accept incoming direct transfers, even if we don't have any back channel balance. """ app0, app1, _ = raiden_network token_address = app0.raiden.default_registry.token_addresses()[0] channel_0_1 = channel(app0, app1, token_address) back_channel = channel(app1, app0, token_address) assert not channel_0_1.can_transfer assert not back_channel.can_transfer deposit_amount = 2 transfer_amount = 1 api0 = RaidenAPI(app0.raiden) api0.deposit(token_address, app1.raiden.address, deposit_amount) app0.raiden.chain.next_block() gevent.sleep(app0.raiden.alarm.wait_time) assert channel_0_1.can_transfer assert not back_channel.can_transfer assert back_channel.distributable == 0 api0.transfer_and_wait(token_address, transfer_amount, app1.raiden.address) gevent.sleep(app1.raiden.alarm.wait_time) assert back_channel.can_transfer assert back_channel.distributable == transfer_amount
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_secret_revealed(raiden_chain, deposit, settle_timeout, events_poll_timeout): app0, app1, app2 = raiden_chain token_address = app0.raiden.default_registry.token_addresses()[0] amount = 10 channel21 = channel(app2, app1, token_address) netting_channel = channel21.external_state.netting_channel identifier = 1 expiration = app2.raiden.get_block_number() + settle_timeout - 3 secret = pending_mediated_transfer( raiden_chain, token_address, amount, identifier, expiration, ) hashlock = sha3(secret) gevent.sleep(.1) # wait for the messages lock = channel21.our_state.get_lock_by_hashlock(hashlock) proof = channel21.our_state.compute_proof_for_lock(secret, lock) # the secret hasn't been revealed yet (through messages) assert len(channel21.our_state.hashlocks_to_pendinglocks) == 1 proofs = list(channel21.our_state.get_known_unlocks()) assert len(proofs) == 0 netting_channel.close(channel21.our_state.balance_proof) # reveal it through the blockchain (this needs to emit the SecretRevealed event) netting_channel.withdraw( app2.raiden.address, [proof], ) settle_expiration = app0.raiden.chain.block_number() + settle_timeout wait_until_block(app0.raiden.chain, settle_expiration) channel21.settle_event.wait(timeout=10) assert_synched_channels( channel(app1, app2, token_address), deposit - amount, [], channel(app2, app1, token_address), deposit + amount, [], ) assert_synched_channels( channel(app0, app1, token_address), deposit - amount, [], channel(app1, app2, token_address), deposit + 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_secret_revealed(deployed_network, deposit): app0, app1, app2 = deployed_network asset_address = app0.raiden.chain.default_registry.asset_addresses()[0] amount = 10 secret = pending_mediated_transfer(deployed_network, asset_address, amount) mediated_transfer = get_received_transfer( channel(app2, app1, asset_address), 0, ) merkle_proof = channel(app2, app1, asset_address).our_state.locked.get_proof(mediated_transfer) channel(app2, app1, asset_address).netting_contract.unlock( [(merkle_proof, mediated_transfer.lock, secret)], ) gevent.sleep(0.1) # let the task run assert_synched_channels( channel(app1, app2, asset_address), deposit - amount, [], channel(app2, app1, asset_address), deposit + amount, [], ) assert_synched_channels( channel(app0, app1, asset_address), deposit - amount, [], channel(app1, app2, asset_address), deposit + amount, [], )
def test_mediated_transfer_with_entire_deposit(raiden_network, token_addresses, deposit): alice_app, bob_app, charlie_app = raiden_network token_address = token_addresses[0] result = alice_app.raiden.mediated_transfer_async( token_address, deposit, charlie_app.raiden.address, identifier=1, ) channel_ab = channel(alice_app, bob_app, token_address) assert channel_ab.locked == deposit assert channel_ab.outstanding == 0 assert channel_ab.distributable == 0 assert result.wait(timeout=10) gevent.sleep(.1) # wait for charlie to sync result = charlie_app.raiden.mediated_transfer_async( token_address, deposit * 2, alice_app.raiden.address, identifier=1, ) assert result.wait(timeout=10)
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_token_swap(raiden_network, deposit, settle_timeout): app0, app1 = raiden_network maker_address = app0.raiden.address taker_address = app1.raiden.address maker_token, taker_token = list(app0.raiden.token_to_channelgraph.keys())[:2] maker_amount = 70 taker_amount = 30 identifier = 313 RaidenAPI(app1.raiden).expect_token_swap( identifier, maker_token, maker_amount, maker_address, taker_token, taker_amount, taker_address, ) async_result = RaidenAPI(app0.raiden).token_swap_async( identifier, maker_token, maker_amount, maker_address, taker_token, taker_amount, taker_address, ) assert async_result.wait() # wait for the taker to receive and process the messages gevent.sleep(0.5) assert_synched_channels( channel(app0, app1, maker_token), deposit - maker_amount, [], channel(app1, app0, maker_token), deposit + maker_amount, [], ) assert_synched_channels( channel(app0, app1, taker_token), deposit + taker_amount, [], channel(app1, app0, taker_token), deposit - taker_amount, [], )
def test_setup(raiden_network, deposit, token_addresses): app0, app1 = raiden_network # pylint: disable=unbalanced-tuple-unpacking tokens0 = list(app0.raiden.token_to_channelgraph.keys()) tokens1 = list(app1.raiden.token_to_channelgraph.keys()) assert len(tokens0) == 1 assert len(tokens1) == 1 assert tokens0 == tokens1 assert tokens0[0] == token_addresses[0] token_address = tokens0[0] channel0 = channel(app0, app1, token_address) channel1 = channel(app1, app0, token_address) assert channel0 and channel1 assert_synched_channels( channel0, deposit, [], channel1, deposit, [], )
def test_setup(raiden_network, deposit, assets_addresses): app0, app1 = raiden_network # pylint: disable=unbalanced-tuple-unpacking assets0 = app0.raiden.managers_by_asset_address.keys() assets1 = app1.raiden.managers_by_asset_address.keys() assert len(assets0) == 1 assert len(assets1) == 1 assert assets0 == assets1 assert assets0[0] == assets_addresses[0] asset_address = assets0[0] channel0 = channel(app0, app1, asset_address) channel1 = channel(app1, app0, asset_address) assert channel0 and channel1 assert_synched_channels( channel0, deposit, [], channel1, deposit, [], )
def test_deposit_updates_balance_immediately(raiden_chain, token_addresses): """Test that the balance of a channel gets updated by the deposit() call immediately and without having to wait for the `ContractReceiveBalance` message since the API needs to return the channel with the deposit balance updated""" app0, app1 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking api0 = RaidenAPI(app0.raiden) token_address = token_addresses[0] channel_0_1 = channel(app0, app1, token_address) old_balance = channel_0_1.contract_balance returned_channel = api0.deposit(token_address, app1.raiden.address, 10) assert returned_channel.contract_balance == old_balance + 10
def test_receive_mediatedtransfer_invalid_address(raiden_network, private_keys): alice_app = raiden_network[0] bob_app = raiden_network[1] graph = list(alice_app.raiden.token_to_channelgraph.values())[0] token_address = graph.token_address channel0 = channel(alice_app, bob_app, token_address) mt_helper = MediatedTransferTestHelper(raiden_network, graph) initiator_address = alice_app.raiden.address path = mt_helper.get_paths_of_length(initiator_address, 2) alice_address, bob_address, charlie_address = path amount = 10 result = alice_app.raiden.mediated_transfer_async( token_address, amount, charlie_address, identifier=1, ) assert result.wait(timeout=10) gevent.sleep(1.) # and now send one more mediated transfer with the same nonce, simulating # an out-of-order/resent message that arrives late lock = Lock(amount, 1, UNIT_HASHLOCK) identifier = create_default_identifier() mediated_transfer = MediatedTransfer( identifier=identifier, nonce=1, token=token_address, channel=channel0.channel_address, transferred_amount=amount, recipient=bob_address, locksroot=UNIT_HASHLOCK, lock=lock, target=charlie_address, initiator=initiator_address, fee=0 ) alice_key = PrivateKey(private_keys[0]) target_app = None for app in raiden_network: if app.raiden.address not in path: target_app = app break sign_and_send(mediated_transfer, alice_key, alice_address, target_app)
def test_receive_mediatedtransfer_outoforder(raiden_network, private_keys): alice_app = raiden_network[0] bob_app = raiden_network[1] charlie_app = raiden_network[2] graph = list(alice_app.raiden.token_to_channelgraph.values())[0] token_address = graph.token_address channel0 = channel( alice_app, bob_app, token_address, ) amount = 10 result = alice_app.raiden.mediated_transfer_async( token_address, amount, charlie_app.raiden.address, identifier=1, ) assert result.wait(timeout=10) lock = Lock(amount, 1, UNIT_HASHLOCK) identifier = create_default_identifier() mediated_transfer = MediatedTransfer( identifier=identifier, nonce=1, token=token_address, channel=channel0.channel_address, transferred_amount=amount, recipient=bob_app.raiden.address, locksroot=UNIT_HASHLOCK, lock=lock, target=charlie_app.raiden.address, initiator=alice_app.raiden.address, fee=0 ) alice_key = PrivateKey(private_keys[0]) # send the invalid mediated transfer from alice to bob with the same nonce sign_and_send( mediated_transfer, alice_key, alice_app.raiden.address, bob_app, )
def test_close_channel_lack_of_balance_proof( raiden_chain, reveal_timeout, settle_timeout, token_addresses): app0, app1 = raiden_chain token_address = token_addresses[0] channel01 = channel(app0, app1, token_address) secret = sha3(b'test_close_channel_lack_of_balance_proof') hashlock = sha3(secret) fee = 0 amount = 100 identifier = 1 expiration = app0.raiden.get_block_number() + reveal_timeout * 2 transfer = channel01.create_mediatedtransfer( app0.raiden.address, app1.raiden.address, fee, amount, identifier, expiration, hashlock, ) app0.raiden.sign(transfer) async_result = app0.raiden.send_async(app1.raiden.address, transfer) assert async_result.wait() reveal_secret = RevealSecret(secret) app0.raiden.sign(reveal_secret) async_result = app0.raiden.send_async(app1.raiden.address, reveal_secret) assert async_result.wait() wait_until = app0.raiden.get_block_number() + settle_timeout + 2 wait_until_block(app0.raiden.chain, wait_until) # required for tester blockchain: let the alarm task run gevent.sleep(1) assert channel01.state != CHANNEL_STATE_OPENED
def test_close_channel_lack_of_balance_proof(raiden_chain, reveal_timeout, settle_timeout, token_addresses): app0, app1 = raiden_chain token_address = token_addresses[0] channel01 = channel(app0, app1, token_address) secret = sha3('test_close_channel_lack_of_balance_proof') hashlock = sha3(secret) fee = 0 amount = 100 identifier = 1 expiration = app0.raiden.get_block_number() + reveal_timeout * 2 transfer = channel01.create_mediatedtransfer( app0.raiden.address, app1.raiden.address, fee, amount, identifier, expiration, hashlock, ) app0.raiden.sign(transfer) async_result = app0.raiden.send_async(app1.raiden.address, transfer) assert async_result.wait() reveal_secret = RevealSecret(secret) app0.raiden.sign(reveal_secret) async_result = app0.raiden.send_async(app1.raiden.address, reveal_secret) assert async_result.wait() wait_until = app0.raiden.get_block_number() + settle_timeout + 2 wait_until_block(app0.raiden.chain, wait_until) # required for tester blockchain: let the alarm task run gevent.sleep(1) assert channel01.state != CHANNEL_STATE_OPENED
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_receive_mediatedtransfer_invalid_address(raiden_chain, token_addresses): app0, app1, app2 = raiden_chain token_address = token_addresses[0] amount = 10 result = app0.raiden.mediated_transfer_async( token_address, amount, app2.raiden.address, identifier=1, ) assert result.wait(timeout=10) # and now send one more mediated transfer with the same nonce, simulating # an out-of-order/resent message that arrives late channel0 = channel(app0, app1, token_address) lock = Lock(amount, 1, UNIT_HASHLOCK) identifier = create_default_identifier() mediated_transfer = MediatedTransfer(identifier=identifier, nonce=1, token=token_address, channel=channel0.channel_address, transferred_amount=amount, recipient=app1.raiden.address, locksroot=UNIT_HASHLOCK, lock=lock, target=app2.raiden.address, initiator=app0.raiden.address, fee=0) sign_and_send( mediated_transfer, app0.raiden.private_key, app0.raiden.address, app1, )
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_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_transfer(raiden_network, token_addresses): app0, app1 = raiden_network # pylint: disable=unbalanced-tuple-unpacking channel0 = channel(app0, app1, token_addresses[0]) channel1 = channel(app1, app0, token_addresses[0]) contract_balance0 = channel0.contract_balance contract_balance1 = channel1.contract_balance app0_token = app0.raiden.token_to_channelgraph.keys()[0] app1_token = app1.raiden.token_to_channelgraph.keys()[0] graph0 = app0.raiden.token_to_channelgraph.values()[0] graph1 = app1.raiden.token_to_channelgraph.values()[0] app0_partners = graph0.partneraddress_to_channel.keys() app1_partners = graph1.partneraddress_to_channel.keys() assert channel0.token_address == channel1.token_address assert app0_token == app1_token assert app1.raiden.address in app0_partners assert app0.raiden.address in app1_partners netting_address = channel0.external_state.netting_channel.address netting_channel = app0.raiden.chain.netting_channel(netting_address) # check balances of channel and contract are equal details0 = netting_channel.detail() details1 = netting_channel.detail() assert contract_balance0 == details0['our_balance'] assert contract_balance1 == details1['our_balance'] assert_synched_channels( channel0, contract_balance0, [], channel1, contract_balance1, [], ) amount = 10 direct_transfer = channel0.create_directtransfer( amount, identifier=1, ) app0.raiden.sign(direct_transfer) channel0.register_transfer( app0.raiden.get_block_number(), direct_transfer, ) channel1.register_transfer( app1.raiden.get_block_number(), direct_transfer, ) # check the contract is intact assert details0 == netting_channel.detail() assert details1 == netting_channel.detail() assert channel0.contract_balance == contract_balance0 assert channel1.contract_balance == contract_balance1 assert_synched_channels( channel0, contract_balance0 - amount, [], channel1, contract_balance1 + amount, [], )
def test_start_end_attack(): """ An attacker can try to steal assets 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 it's know secret and reveal to close and settles the channel H-A2, without revealing the secret to H's raiden node. The intention is to make the hub transfer the asset but for him to be unable to require the asset A1. """ asset = sha3('test_two_attack')[:20] deposit = 100 amount = 30 apps = create_sequential_network(num_nodes=3, deposit=deposit, asset=asset) # The attacker creates a mediated transfer from it's account A1, to it's # account A2, throught the hub H secret = hidden_mediated_transfer(apps, asset, amount) attack_channel = channel(apps[2], apps[1], asset) attack_transfer = get_received_transfer(attack_channel, 0) attack_contract = attack_channel.nettingcontract_address hub_contract = channel(apps[1], apps[0], asset).nettingcontract_address # the attacker can create a merkle proof of the locked transfer merkle_proof = attack_channel.our_state.locked.get_proof(attack_transfer) # start the settle counter apps[2].raiden.chain.close( asset, attack_contract, apps[2].raiden.address, [attack_transfer], [], ) # wait until the last block to reveal the secret for _ in range(attack_transfer.lock.expiration - 1): apps[2].raiden.chain.next_block() # since the attacker knows the secret he can net the lock apps[2].raiden.chain.close( asset, attack_contract, apps[2].raiden.address, [attack_transfer], [(merkle_proof, attack_transfer.lock, secret)], ) # XXX: verify that the secret was publicized # at this point the hub might not know yet the secret, and won't be able to # claim the asset from the channel A1 - H # the attacker settle the contract apps[2].raiden.chain.next_block() apps[2].raiden.chain.settle(asset, attack_contract) # at this point the attack has the "stolen" funds attack_contract = apps[2].raiden.chain.asset_hashchannel[asset][attack_contract] assert attack_contract.participants[apps[2].raiden.address]['netted'] == deposit + amount assert attack_contract.participants[apps[1].raiden.address]['netted'] == deposit - amount # and the hub's channel A1-H doesn't hub_contract = apps[1].raiden.chain.asset_hashchannel[asset][hub_contract] assert hub_contract.participants[apps[0].raiden.address]['netted'] == deposit assert hub_contract.participants[apps[1].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, since for A2 to acquire the asset # it needs to make the secret public in the block chain we publish the # secret through an event and the Hub will be able to require it's funds apps[1].raiden.chain.next_block() # XXX: verify that the Hub has found the secret, close and settle the channel # the hub has acquired it's asset hub_contract = apps[1].raiden.chain.asset_hashchannel[asset][hub_contract] assert hub_contract.participants[apps[0].raiden.address]['netted'] == deposit + amount assert hub_contract.participants[apps[1].raiden.address]['netted'] == deposit - amount
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_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_regression_multiple_revealsecret(raiden_network, token_addresses): """ 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 hashlock 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, because the channel was already updated an exception was raised for an unknown secret. """ app0, app1 = raiden_network token = token_addresses[0] identifier = 1 secret = sha3(b'test_regression_multiple_revealsecret') hashlock = sha3(secret) expiration = app0.raiden.get_block_number() + 100 amount = 10 mediated_transfer = channel(app0, app1, token).create_mediatedtransfer( transfer_initiator=app0.raiden.address, transfer_target=app1.raiden.address, fee=0, amount=amount, identifier=identifier, expiration=expiration, hashlock=hashlock, ) app0.raiden.sign(mediated_transfer) message_data = mediated_transfer.encode() app1.raiden.protocol.receive(message_data) reveal_secret = RevealSecret(secret) app0.raiden.sign(reveal_secret) reveal_secret_data = reveal_secret.encode() secret = Secret( identifier=identifier, nonce=mediated_transfer.nonce + 1, channel=channel(app0, app1, token).channel_address, transferred_amount=amount, locksroot=EMPTY_MERKLE_ROOT, secret=secret, ) app0.raiden.sign(secret) secret_data = secret.encode() messages = [ secret_data, reveal_secret_data, ] wait = [ gevent.spawn_later( .1, app1.raiden.protocol.receive, data, ) for data in messages ] gevent.joinall(wait)
def test_start_end_attack(asset_address, raiden_chain, deposit): """ An attacker can try to steal assets 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 it's know secret and reveal to close and settles the channel H-A2, without revealing the secret to H's raiden node. The intention is to make the hub transfer the asset but for him to be unable to require the asset A1. """ amount = 30 asset = asset_address[0] app0, app1, app2 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking # The attacker creates a mediated transfer from it's account A1, to it's # account A2, throught the hub H secret = pending_mediated_transfer(raiden_chain, asset, amount) attack_channel = channel(app2, app1, asset) attack_transfer = get_received_transfer(attack_channel, 0) attack_contract = attack_channel.external_state.netting_channel.address hub_contract = channel(app1, app0, asset).external_state.netting_channel.address # the attacker can create a merkle proof of the locked transfer merkle_proof = attack_channel.our_state.locked.get_proof(attack_transfer) # start the settle counter attack_channel.netting_channel.close( app2.raiden.address, attack_transfer, None ) # wait until the last block to reveal the secret for _ in range(attack_transfer.lock.expiration - 1): app2.raiden.chain.next_block() # since the attacker knows the secret he can net the lock attack_channel.netting_channel.unlock( [(merkle_proof, attack_transfer.lock, secret)], ) # XXX: verify that the secret was publicized # at this point the hub might not know yet the secret, and won't be able to # claim the asset from the channel A1 - H # the attacker settle the contract app2.raiden.chain.next_block() attack_channel.netting_channel.settle(asset, attack_contract) # at this point the attack has the "stolen" funds attack_contract = app2.raiden.chain.asset_hashchannel[asset][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.asset_hashchannel[asset][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, since for A2 to acquire the asset # it needs to make the secret public in the block chain we publish the # secret through an event and the Hub will be able to require it's funds app1.raiden.chain.next_block() # XXX: verify that the Hub has found the secret, close and settle the channel # the hub has acquired it's asset hub_contract = app1.raiden.chain.asset_hashchannel[asset][hub_contract] assert hub_contract.participants[app0.raiden.address]['netted'] == deposit + amount assert hub_contract.participants[app1.raiden.address]['netted'] == deposit - amount
def test_fullnetwork(raiden_chain, token_addresses, deposit, settle_timeout, reveal_timeout): # 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) assert get_sent_transfer(channel_0_1, 0).transferred_amount == amount amount = int(deposit / 2.) mediated_transfer(app0, app2, token_address, amount) # 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), ) # Now let's query the WAL to see if the state changes were logged as expected app0_state_changes = [ change[1] for change in get_all_state_changes(app0.raiden.transaction_log) if not isinstance(change[1], Block) ] app0_events = [ event.event_object for event in get_all_state_events(app0.raiden.transaction_log) ] app1_state_changes = [ change[1] for change in get_all_state_changes(app1.raiden.transaction_log) if not isinstance(change[1], Block) ] app1_events = [ event.event_object for event in get_all_state_events(app1.raiden.transaction_log) ] app2_state_changes = [ change[1] for change in get_all_state_changes(app2.raiden.transaction_log) if not isinstance(change[1], Block) ] app2_events = [ event.event_object for event in get_all_state_events(app2.raiden.transaction_log) ] app3_state_changes = [ change[1] for change in get_all_state_changes(app3.raiden.transaction_log) if not isinstance(change[1], Block) ] app3_events = [ event.event_object for event in get_all_state_events(app3.raiden.transaction_log) ] # app1 received one direct transfers assert len(app1_state_changes) == 1 assert len(app1_events) == 1 # app0 initiates the direct transfer and mediated_transfer assert len(app0_state_changes) == 4 assert isinstance(app0_state_changes[1], ActionInitInitiator) assert app0_state_changes[1].our_address == app0.raiden.address assert app0_state_changes[1].transfer.amount == amount assert app0_state_changes[1].transfer.token == token_address assert app0_state_changes[1].transfer.initiator == app0.raiden.address assert app0_state_changes[1].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[1].transfer.expiration is None assert app0_state_changes[1].transfer.hashlock is None assert app0_state_changes[1].transfer.secret is None # We should have one available route assert len(app0_state_changes[1].routes.available_routes) == 1 assert len(app0_state_changes[1].routes.ignored_routes) == 0 assert len(app0_state_changes[1].routes.refunded_routes) == 0 assert len(app0_state_changes[1].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=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, ) assert taken_route in app0_state_changes[1].routes.available_routes assert not_taken_route not in app0_state_changes[1].routes.available_routes # app0 will also receive a secret request from the target assert isinstance(app0_state_changes[2], ReceiveSecretRequest) assert app0_state_changes[2].amount == amount assert app0_state_changes[2].sender == app2.raiden.address hashlock = app0_state_changes[2].hashlock # app0 will also receive a secret reveal from the immediate neighbour assert isinstance(app0_state_changes[3], ReceiveSecretReveal) assert app0_state_changes[3].sender == app3.raiden.address secret = app0_state_changes[3].secret assert sha3(secret) == hashlock assert len(app0_events) == 6 # Direct transfer assert isinstance(app0_events[0], EventTransferSentSuccess) # not checking the expiration and identifier assert isinstance(app0_events[1], SendMediatedTransfer) assert app0_events[1].token == token_address assert app0_events[1].amount == amount assert app0_events[1].hashlock == hashlock assert app0_events[1].initiator == app0.raiden.address assert app0_events[1].target == app2.raiden.address assert app0_events[1].receiver == app3.raiden.address # not checking the identifier assert isinstance(app0_events[2], SendRevealSecret) assert app0_events[2].secret == secret assert app0_events[2].token == token_address assert app0_events[2].receiver == app2.raiden.address assert app0_events[2].sender == app0.raiden.address # not checking the identifier assert isinstance(app0_events[3], SendBalanceProof) assert app0_events[3].token == token_address assert app0_events[3].channel_address == channel_0_3.channel_address assert app0_events[3].receiver == app3.raiden.address assert app0_events[3].secret == secret assert isinstance(app0_events[4], EventTransferSentSuccess) # EventUnlockSuccess, not checking the identifier assert isinstance(app0_events[5], EventUnlockSuccess) assert app0_events[5].hashlock == 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=deposit, 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=deposit, 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 # check app3 state events assert len(app3_events) == 4 assert isinstance(app3_events[0], SendMediatedTransfer) assert app3_events[0].token == token_address assert app3_events[0].amount == amount assert app3_events[0].hashlock == hashlock assert app3_events[0].initiator == app0.raiden.address assert app3_events[0].target == app2.raiden.address assert app3_events[0].receiver == app2.raiden.address assert isinstance(app3_events[1], SendRevealSecret) assert app3_events[1].secret == secret assert app3_events[1].token == token_address assert app3_events[1].receiver == app0.raiden.address assert app3_events[1].sender == app3.raiden.address assert isinstance(app3_events[2], SendBalanceProof) assert app3_events[2].token == token_address assert app3_events[2].channel_address == channel_3_2.channel_address assert app3_events[2].receiver == app2.raiden.address assert app3_events[2].secret == secret assert isinstance(app3_events[3], EventUnlockSuccess) # 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=deposit, 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 # check app2 state events assert len(app2_events) == 2 assert isinstance(app2_events[0], SendSecretRequest) assert app2_events[0].amount == amount assert app2_events[0].hashlock == hashlock assert app2_events[0].receiver == app0.raiden.address assert isinstance(app2_events[1], SendRevealSecret) assert app2_events[1].token == token_address assert app2_events[1].secret == secret assert app2_events[1].receiver == app3.raiden.address assert app2_events[1].sender == app2.raiden.address
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_transfer(raiden_network, token_addresses): app0, app1 = raiden_network # pylint: disable=unbalanced-tuple-unpacking channel0 = channel(app0, app1, token_addresses[0]) channel1 = channel(app1, app0, token_addresses[0]) contract_balance0 = channel0.contract_balance contract_balance1 = channel1.contract_balance app0_token = list(app0.raiden.token_to_channelgraph.keys())[0] app1_token = list(app1.raiden.token_to_channelgraph.keys())[0] graph0 = list(app0.raiden.token_to_channelgraph.values())[0] graph1 = list(app1.raiden.token_to_channelgraph.values())[0] app0_partners = list(graph0.partneraddress_to_channel.keys()) app1_partners = list(graph1.partneraddress_to_channel.keys()) assert channel0.token_address == channel1.token_address assert app0_token == app1_token assert app1.raiden.address in app0_partners assert app0.raiden.address in app1_partners netting_address = channel0.external_state.netting_channel.address netting_channel = app0.raiden.chain.netting_channel(netting_address) # check balances of channel and contract are equal details0 = netting_channel.detail() details1 = netting_channel.detail() assert contract_balance0 == details0['our_balance'] assert contract_balance1 == details1['our_balance'] assert_synched_channels( channel0, contract_balance0, [], channel1, contract_balance1, [], ) amount = 10 direct_transfer = channel0.create_directtransfer( amount, identifier=1, ) app0.raiden.sign(direct_transfer) channel0.register_transfer( app0.raiden.get_block_number(), direct_transfer, ) channel1.register_transfer( app1.raiden.get_block_number(), direct_transfer, ) # check the contract is intact assert details0 == netting_channel.detail() assert details1 == netting_channel.detail() assert channel0.contract_balance == contract_balance0 assert channel1.contract_balance == contract_balance1 assert_synched_channels( channel0, contract_balance0 - amount, [], channel1, contract_balance1 + amount, [], )
def test_receive_mediated_before_deposit(raiden_network, token_addresses): """Regression test that ensures we accept incoming mediated transfers, even if we don't have any back channel balance. """ app_alice, app_bob, app_charlie = raiden_network token_address = token_addresses[0] # path alice -> bob -> charlie alice_bob = channel(app_alice, app_bob, token_address) bob_alice = channel(app_bob, app_alice, token_address) bob_charlie = channel(app_bob, app_charlie, token_address) charlie_bob = channel(app_charlie, app_bob, token_address) # ensure alice charlie is mediated with pytest.raises(KeyError): channel(app_alice, app_charlie, token_address) assert not alice_bob.can_transfer assert not bob_charlie.can_transfer assert not bob_alice.can_transfer deposit_amount = 3 RaidenAPI(app_alice.raiden).deposit( token_address, app_bob.raiden.address, deposit_amount, ) RaidenAPI(app_bob.raiden).deposit( token_address, app_charlie.raiden.address, deposit_amount, ) # catch up with the Balance events for app in raiden_network: app.raiden.poll_blockchain_events() assert alice_bob.can_transfer assert bob_charlie.can_transfer assert not bob_alice.can_transfer assert alice_bob.distributable == deposit_amount assert bob_charlie.distributable == deposit_amount transfer_amount = 1 async_result = app_alice.raiden.mediated_transfer_async( token_address, transfer_amount, app_charlie.raiden.address, 1, ) assert async_result.wait(10) # give extra time for the intermediaries to process the secret messages and # withdraw the tokens gevent.sleep(1) assert alice_bob.distributable == deposit_amount - transfer_amount assert bob_charlie.distributable == deposit_amount - transfer_amount assert bob_alice.distributable == transfer_amount assert charlie_bob.distributable == transfer_amount assert alice_bob.can_transfer assert bob_alice.can_transfer assert charlie_bob.can_transfer
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_transfer(raiden_network, tokens_addresses): app0, app1 = raiden_network # pylint: disable=unbalanced-tuple-unpacking channel0 = channel(app0, app1, tokens_addresses[0]) channel1 = channel(app1, app0, tokens_addresses[0]) contract_balance0 = channel0.contract_balance contract_balance1 = channel1.contract_balance # check agreement on addresses address0 = channel0.our_state.address address1 = channel1.our_state.address app0_token = app0.raiden.managers_by_token_address.keys()[0] app1_token = app1.raiden.managers_by_token_address.keys()[0] app0_partners = app0.raiden.managers_by_token_address.values( )[0].partneraddress_channel.keys() app1_partners = app1.raiden.managers_by_token_address.values( )[0].partneraddress_channel.keys() assert channel0.token_address == channel1.token_address assert app0_token == app1_token assert app1.raiden.address in app0_partners assert app0.raiden.address in app1_partners netting_address = channel0.external_state.netting_channel.address netting_channel = app0.raiden.chain.netting_channel(netting_address) # check balances of channel and contract are equal details0 = netting_channel.detail(address0) details1 = netting_channel.detail(address1) assert contract_balance0 == details0['our_balance'] assert contract_balance1 == details1['our_balance'] assert_synched_channels( channel0, contract_balance0, [], channel1, contract_balance1, [], ) amount = 10 direct_transfer = channel0.create_directtransfer( amount, 1 # TODO: fill in identifier ) app0.raiden.sign(direct_transfer) channel0.register_transfer(direct_transfer) channel1.register_transfer(direct_transfer) # check the contract is intact assert details0 == netting_channel.detail(address0) assert details1 == netting_channel.detail(address1) assert channel0.contract_balance == contract_balance0 assert channel1.contract_balance == contract_balance1 assert_synched_channels( channel0, contract_balance0 - amount, [], channel1, contract_balance1 + amount, [], )
def test_cancel_transfer(raiden_chain, asset, deposit): app0, app1, app2 = 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, []) # 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 RefundTransfer 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 + RefundTransfer + a Ack for each app1_messages = mlogger.get_node_messages(pex(app1.raiden.address), only='sent') assert isinstance(app1_messages[-1], RefundTransfer)
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_start_end_attack(token_addresses, raiden_chain, deposit, reveal_timeout): """ 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 settles 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 # the attacker owns app0 and app2 and creates a transfer through app1 identifier = 1 expiration = reveal_timeout + 5 secret = pending_mediated_transfer(raiden_chain, token, amount, identifier, expiration) hashlock = sha3(secret) attack_channel = channel(app2, app1, token) attack_transfer = get_received_transfer(attack_channel, 0) attack_contract = attack_channel.external_state.netting_channel.address hub_contract = channel(app1, app0, token).external_state.netting_channel.address # the attacker can create a merkle proof of the locked transfer lock = attack_channel.our_state.balance_proof.get_lock_by_hashlock( hashlock) unlock_proof = attack_channel.our_state.balance_proof.compute_proof_for_lock( secret, lock) # start the settle counter attack_channel.netting_channel.close(attack_transfer) # 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.withdraw( [(unlock_proof, attack_transfer.lock, secret)], ) # XXX: verify that the secret was publicized # at this point the hub might not know yet the secret, and won't be able to # claim the token from the channel A1 - H # the attacker settle the contract app2.raiden.chain.next_block() attack_channel.netting_channel.settle(token, attack_contract) # at this point the attack 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, since for A2 to acquire the token # it needs to make the secret public in the block chain we publish the # secret through an event and the Hub will be able to require it's 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, 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) # 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 a proof for the pending transfer 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, token, secret) # a new transfer to update the hashlock direct_transfer(app0, app1, token, amount) 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(last_transfer) # check that the double unlock will fail with pytest.raises(Exception): back_channel.external_state.netting_channel.withdraw( [(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() 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, 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.balance_proof.get_lock_by_hashlock( hashlock) unlock_proof = back_channel.partner_state.balance_proof.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.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(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, identifier=1) # drain the channel app2 -> app3 amount23 = 80 direct_transfer(app2, app3, token, amount23, identifier=2) 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_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 get_all_state_changes(app0.raiden.transaction_log) if not isinstance(change[1], Block) ] app0_events = [ event[2] for event in get_all_state_events(app0.raiden.transaction_log) ] app1_state_changes = [ change[1] for change in get_all_state_changes(app1.raiden.transaction_log) if not isinstance(change[1], Block) ] app1_events = [ event[2] for event in get_all_state_events(app1.raiden.transaction_log) ] app2_state_changes = [ change[1] for change in get_all_state_changes(app2.raiden.transaction_log) if not isinstance(change[1], Block) ] app2_events = [ event[2] for event in get_all_state_events(app2.raiden.transaction_log) ] app3_state_changes = [ change[1] for change in get_all_state_changes(app3.raiden.transaction_log) if not isinstance(change[1], Block) ] app3_events = [ event[2] for event in get_all_state_events(app3.raiden.transaction_log) ] # app1 does not take part in the mediated transfer. assert len(app1_state_changes) == 0 assert len(app1_events) == 0 # 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 # check app0 state events assert len(app0_events) == 4 assert isinstance(app0_events[0], SendMediatedTransfer) assert app0_events[0].token == token_address assert app0_events[0].amount == amount assert app0_events[0].hashlock == hashlock assert app0_events[0].initiator == app0.raiden.address assert app0_events[0].target == app2.raiden.address assert app0_events[0].receiver == app3.raiden.address assert isinstance(app0_events[1], SendRevealSecret) assert app0_events[1].secret == secret assert app0_events[1].token == token_address assert app0_events[1].receiver == app2.raiden.address assert app0_events[1].sender == app0.raiden.address assert isinstance(app0_events[2], SendBalanceProof) assert app0_events[2].token == token_address assert app0_events[2].channel_address == channel_0_3.channel_address assert app0_events[2].receiver == app3.raiden.address assert app0_events[2].secret == secret assert isinstance(app0_events[3], EventTransferCompleted) assert app0_events[3].secret == secret assert app0_events[3].hashlock == 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 # check app3 state events assert len(app3_events) == 3 assert isinstance(app3_events[0], SendMediatedTransfer) assert app3_events[0].token == token_address assert app3_events[0].amount == amount assert app3_events[0].hashlock == hashlock assert app3_events[0].initiator == app0.raiden.address assert app3_events[0].target == app2.raiden.address assert app3_events[0].receiver == app2.raiden.address assert isinstance(app3_events[1], SendRevealSecret) assert app3_events[1].secret == secret assert app3_events[1].token == token_address assert app3_events[1].receiver == app0.raiden.address assert app3_events[1].sender == app3.raiden.address assert isinstance(app3_events[2], SendBalanceProof) assert app3_events[2].token == token_address assert app3_events[2].channel_address == channel_3_2.channel_address assert app3_events[2].receiver == app2.raiden.address assert app3_events[2].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 # check app2 state events assert len(app2_events) == 2 assert isinstance(app2_events[0], SendSecretRequest) assert app2_events[0].amount == amount assert app2_events[0].hashlock == hashlock assert app2_events[0].receiver == app0.raiden.address assert isinstance(app2_events[1], SendRevealSecret) assert app2_events[1].token == token_address assert app2_events[1].secret == secret assert app2_events[1].receiver == app3.raiden.address assert app2_events[1].sender == app2.raiden.address
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_start_end_attack(asset_address, raiden_chain, deposit): """ An attacker can try to steal assets 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 it's know secret and reveal to close and settles the channel H-A2, without revealing the secret to H's raiden node. The intention is to make the hub transfer the asset but for him to be unable to require the asset A1. """ amount = 30 asset = asset_address[0] app0, app1, app2 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking # the attacker owns app0 and app2 and creates a transfer throught app1 secret = pending_mediated_transfer( raiden_chain, asset, amount, 1 # TODO: fill in identifier ) hashlock = sha3(secret) attack_channel = channel(app2, app1, asset) attack_transfer = get_received_transfer(attack_channel, 0) attack_contract = attack_channel.external_state.netting_channel.address hub_contract = channel(app1, app0, asset).external_state.netting_channel.address # the attacker can create a merkle proof of the locked transfer lock = attack_channel.our_state.balance_proof.get_lock_by_hashlock(hashlock) unlock_proof = attack_channel.our_state.balance_proof.compute_proof_for_lock(secret, lock) # start the settle counter attack_channel.netting_channel.close( app2.raiden.address, attack_transfer, None ) # 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( [(unlock_proof, attack_transfer.lock, secret)], ) # XXX: verify that the secret was publicized # at this point the hub might not know yet the secret, and won't be able to # claim the asset from the channel A1 - H # the attacker settle the contract app2.raiden.chain.next_block() attack_channel.netting_channel.settle(asset, attack_contract) # at this point the attack has the "stolen" funds attack_contract = app2.raiden.chain.asset_hashchannel[asset][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.asset_hashchannel[asset][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, since for A2 to acquire the asset # it needs to make the secret public in the block chain we publish the # secret through an event and the Hub will be able to require it's funds app1.raiden.chain.next_block() # XXX: verify that the Hub has found the secret, close and settle the channel # the hub has acquired it's asset hub_contract = app1.raiden.chain.asset_hashchannel[asset][hub_contract] assert hub_contract.participants[app0.raiden.address]['netted'] == deposit + amount assert hub_contract.participants[app1.raiden.address]['netted'] == deposit - amount