def test_close_channel_lack_of_balance_proof(raiden_chain, deposit, token_addresses): app0, app1 = raiden_chain token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) token_proxy = app0.raiden.chain.token(token_address) initial_balance0 = token_proxy.balance_of(app0.raiden.address) initial_balance1 = token_proxy.balance_of(app1.raiden.address) amount = 100 identifier = 1 secret = pending_mediated_transfer( raiden_chain, token_network_identifier, amount, identifier, ) # Stop app0 to avoid sending the unlock app0.raiden.transport.stop_and_wait() reveal_secret = RevealSecret( random.randint(0, UINT64_MAX), secret, ) app0.raiden.sign(reveal_secret) message_handler.on_message(app1.raiden, reveal_secret) RaidenAPI(app0.raiden).channel_close( app0.raiden.default_registry.address, token_address, app1.raiden.address, ) channel_state = get_channelstate(app0, app1, token_network_identifier) waiting.wait_for_settle( app0.raiden, app0.raiden.default_registry.address, token_address, [channel_state.identifier], app0.raiden.alarm.sleep_time, ) # wait for the node to call batch unlock with gevent.Timeout(10): wait_for_batch_unlock( app0, token_network_identifier, channel_state.partner_state.address, channel_state.our_state.address, ) expected_balance0 = initial_balance0 + deposit - amount expected_balance1 = initial_balance1 + deposit + amount assert token_proxy.balance_of(app0.raiden.address) == expected_balance0 assert token_proxy.balance_of(app1.raiden.address) == expected_balance1
def 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_secret_revealed(raiden_chain, deposit, settle_timeout, events_poll_timeout): app0, app1, app2 = raiden_chain asset_address = app0.raiden.chain.default_registry.asset_addresses()[0] amount = 10 channel21 = channel(app2, app1, asset_address) netting_channel = channel21.external_state.netting_channel secret = pending_mediated_transfer( raiden_chain, asset_address, amount, 1 # TODO: fill in identifier ) hashlock = sha3(secret) gevent.sleep(.1) # wait for the messages balance_proof = channel21.our_state.balance_proof lock = balance_proof.get_lock_by_hashlock(hashlock) proof = balance_proof.compute_proof_for_lock(secret, lock) # the secret hasn't been revealed yet (through messages) assert len(balance_proof.hashlock_pendinglocks) == 1 proofs = list(balance_proof.get_known_unlocks()) assert len(proofs) == 0 netting_channel.close(app2.raiden.address, balance_proof.transfer, None) # reveal it through the blockchain (this needs to emit the SecretRevealed event) netting_channel.unlock( 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, 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_secret_revealed(raiden_chain, deposit, settle_timeout, token_addresses): app0, app1, app2 = raiden_chain registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) amount = 10 identifier = 1 secret = pending_mediated_transfer( raiden_chain, token_network_identifier, amount, identifier, ) secrethash = sha3(secret) gevent.sleep(.1) # wait for the messages channel_state2_1 = get_channelstate(app2, app1, token_network_identifier) # the secret hasn't been revealed yet (through messages) assert len(channel_state2_1.our_state.secrethashes_to_lockedlocks) == 1 proofs = list(channel.get_known_unlocks(channel_state2_1.our_state)) assert not proofs channel.register_secret(channel_state2_1, secret, secrethash) # Close the channel netting_channel_proxy = app2.raiden.chain.netting_channel(channel_state2_1.identifier) netting_channel_proxy.channel_close( registry_address, channel_state2_1.partner_state.balance_proof, ) # Reveal the secret through the blockchain (this needs to emit the # SecretRevealed event) for unlock_proof in channel.get_known_unlocks(channel_state2_1.partner_state): netting_channel_proxy.unlock(unlock_proof) settle_expiration = app0.raiden.chain.block_number() + settle_timeout wait_until_block(app0.raiden.chain, settle_expiration) assert_synched_channel_state( token_address, app1, deposit - amount, [], app2, deposit + amount, [], ) assert_synched_channel_state( token_address, app0, deposit - amount, [], app1, 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) # 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_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) 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[ 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(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_secret_revealed(raiden_chain, deposit, settle_timeout, token_addresses): app0, app1, app2 = raiden_chain registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) amount = 10 identifier = 1 secret = pending_mediated_transfer( raiden_chain, token_network_identifier, amount, identifier, ) secrethash = sha3(secret) gevent.sleep(.1) # wait for the messages # The secret hasn't been revealed yet channel_state2_1 = get_channelstate(app2, app1, token_network_identifier) assert len(channel_state2_1.our_state.secrethashes_to_lockedlocks) == 1 channel.register_secret(channel_state2_1, secret, secrethash) # Close the channel # This needs to register the secrets on chain netting_channel_proxy = app2.raiden.chain.payment_channel( token_network_identifier, channel_state2_1.identifier, ) netting_channel_proxy.channel_close( registry_address, channel_state2_1.partner_state.balance_proof, ) settle_expiration = app0.raiden.chain.block_number() + settle_timeout wait_until_block(app0.raiden.chain, settle_expiration) assert_synced_channel_state( token_address, app1, deposit - amount, [], app2, deposit + amount, [], ) assert_synced_channel_state( token_address, app0, deposit - amount, [], app1, 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_close_channel_lack_of_balance_proof(raiden_chain, deposit, token_addresses): app0, app1 = raiden_chain token_address = token_addresses[0] token_proxy = app0.raiden.chain.token(token_address) initial_balance0 = token_proxy.balance_of(app0.raiden.address) initial_balance1 = token_proxy.balance_of(app1.raiden.address) amount = 100 identifier = 1 secret = pending_mediated_transfer( raiden_chain, token_address, amount, identifier, ) # Stop app0 to avoid sending the unlock app0.raiden.protocol.stop_and_wait() reveal_secret = RevealSecret( random.randint(0, UINT64_MAX), secret, ) app0.raiden.sign(reveal_secret) udp_message_handler.on_udp_message(app1.raiden, reveal_secret) channel_state = get_channelstate(app0, app1, token_address) waiting.wait_for_settle( app0.raiden, app0.raiden.default_registry.address, token_address, [channel_state.identifier], app0.raiden.alarm.wait_time, ) expected_balance0 = initial_balance0 + deposit - amount expected_balance1 = initial_balance1 + deposit + amount assert token_proxy.balance_of(app0.raiden.address) == expected_balance0 assert token_proxy.balance_of(app1.raiden.address) == expected_balance1
def 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_automatic_secret_registration(raiden_chain, deposit, token_addresses, reveal_timeout): app0, app1 = raiden_chain token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) amount = 100 identifier = 1 secret = pending_mediated_transfer( raiden_chain, token_network_identifier, amount, identifier, ) # Stop app0 to avoid sending the unlock app0.raiden.transport.stop() reveal_secret = RevealSecret( random.randint(0, UINT64_MAX), secret, ) app0.raiden.sign(reveal_secret) message_handler.on_message(app1.raiden, reveal_secret) chain_state = views.state_from_app(app1) secrethash = sha3(secret) target_task = chain_state.payment_mapping.secrethashes_to_task[secrethash] lock_expiration = target_task.target_state.transfer.lock.expiration wait_until_block(app1.raiden.chain, lock_expiration) assert app1.raiden.default_secret_registry.check_registered(secrethash)
def test_settled_lock(token_addresses, raiden_network, deposit): """ Any transfer following a secret revealed must update the locksroot, so hat an attacker cannot reuse a secret to double claim a lock.""" app0, app1 = raiden_network registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] amount = 30 token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) address0 = app0.raiden.address address1 = app1.raiden.address deposit0 = deposit deposit1 = deposit token_proxy = app0.raiden.chain.token(token_address) initial_balance0 = token_proxy.balance_of(address0) initial_balance1 = token_proxy.balance_of(address1) # Using a pending mediated transfer because this allows us to compute the # merkle proof identifier = 1 secret = pending_mediated_transfer( raiden_network, token_network_identifier, amount, identifier, ) # Save the merkle tree leaves from the pending transfer, used to test the unlock channelstate_0_1 = get_channelstate(app0, app1, token_network_identifier) batch_unlock = channel.get_batch_unlock(channelstate_0_1.our_state) assert batch_unlock claim_lock(raiden_network, identifier, token_network_identifier, secret) # Make a new transfer direct_transfer(app0, app1, token_network_identifier, amount, identifier=1) RaidenAPI(app1.raiden).channel_close( registry_address, token_address, app0.raiden.address, ) waiting.wait_for_settle( app1.raiden, app1.raiden.default_registry.address, token_address, [channelstate_0_1.identifier], app1.raiden.alarm.sleep_time, ) netting_channel = app1.raiden.chain.payment_channel( token_network_identifier, channelstate_0_1.identifier, ) # The direct transfer locksroot must not contain the unlocked lock, the # unlock must fail. with pytest.raises(Exception): netting_channel.unlock( channelstate_0_1.partner_state.address, batch_unlock, ) expected_balance0 = initial_balance0 + deposit0 - amount * 2 expected_balance1 = initial_balance1 + deposit1 + amount * 2 assert token_proxy.balance_of(address0) == expected_balance0 assert token_proxy.balance_of(address1) == expected_balance1
def test_batch_unlock(raiden_network, token_addresses, secret_registry_address, deposit): """Batch unlock can be called after the channel is settled.""" alice_app, bob_app = raiden_network registry_address = alice_app.raiden.default_registry.address token_address = token_addresses[0] token_proxy = alice_app.raiden.chain.token(token_address) token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(alice_app), alice_app.raiden.default_registry.address, token_address, ) token_network = views.get_token_network_by_identifier( views.state_from_app(alice_app), token_network_identifier, ) channel_identifier = get_channelstate(alice_app, bob_app, token_network_identifier).identifier assert channel_identifier in token_network.partneraddresses_to_channels[ bob_app.raiden.address] alice_initial_balance = token_proxy.balance_of(alice_app.raiden.address) bob_initial_balance = token_proxy.balance_of(bob_app.raiden.address) alice_to_bob_amount = 10 identifier = 1 secret = pending_mediated_transfer( raiden_network, token_network_identifier, alice_to_bob_amount, identifier, ) secrethash = sha3(secret) alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_identifier) lock = channel.get_lock(alice_bob_channel_state.our_state, secrethash) # This is the current state of the protocol: # # A -> B LockedTransfer # B -> A SecretRequest # - protocol didn't continue assert_synced_channel_state( token_network_identifier, alice_app, deposit, [lock], bob_app, deposit, [], ) # A ChannelClose event will be generated, this will be polled by both apps # and each must start a task for calling settle RaidenAPI(bob_app.raiden).channel_close( registry_address, token_address, alice_app.raiden.address, ) secret_registry_proxy = alice_app.raiden.chain.secret_registry( secret_registry_address, ) secret_registry_proxy.register_secret(secret) assert lock, 'the lock must still be part of the node state' msg = 'the secret must be registered before the lock expires' assert lock.expiration > alice_app.raiden.get_block_number(), msg assert lock.secrethash == sha3(secret) waiting.wait_for_settle( alice_app.raiden, registry_address, token_address, [alice_bob_channel_state.identifier], alice_app.raiden.alarm.sleep_time, ) token_network = views.get_token_network_by_identifier( views.state_from_app(bob_app), token_network_identifier, ) assert channel_identifier in token_network.partneraddresses_to_channels[ alice_app.raiden.address] # wait for the node to call batch unlock with gevent.Timeout(10): wait_for_batch_unlock( bob_app, token_network_identifier, alice_bob_channel_state.partner_state.address, alice_bob_channel_state.our_state.address, ) token_network = views.get_token_network_by_identifier( views.state_from_app(bob_app), token_network_identifier, ) assert channel_identifier not in token_network.partneraddresses_to_channels[ alice_app.raiden.address] alice_new_balance = alice_initial_balance + deposit - alice_to_bob_amount bob_new_balance = bob_initial_balance + deposit + alice_to_bob_amount assert token_proxy.balance_of( alice_app.raiden.address) == alice_new_balance assert token_proxy.balance_of(bob_app.raiden.address) == bob_new_balance
def test_lock_expiry(raiden_network, token_addresses, secret_registry_address, deposit): """Test lock expiry and removal.""" alice_app, bob_app = raiden_network token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(alice_app), alice_app.raiden.default_registry.address, token_address, ) token_network = views.get_token_network_by_identifier( views.state_from_app(alice_app), token_network_identifier, ) channel_state = get_channelstate(alice_app, bob_app, token_network_identifier) channel_identifier = channel_state.identifier assert channel_identifier in token_network.partneraddresses_to_channels[ bob_app.raiden.address] alice_to_bob_amount = 10 identifier = 1 transfer_1_secret = pending_mediated_transfer( raiden_network, token_network_identifier, alice_to_bob_amount, identifier, ) transfer_1_secrethash = sha3(transfer_1_secret) alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_identifier) lock = channel.get_lock(alice_bob_channel_state.our_state, transfer_1_secrethash) # This is the current state of the protocol: # # A -> B LockedTransfer # B -> A SecretRequest # - protocol didn't continue assert_synced_channel_state( token_network_identifier, alice_app, deposit, [lock], bob_app, deposit, [], ) # Verify lock is registered in both channel states alice_channel_state = get_channelstate(alice_app, bob_app, token_network_identifier) assert transfer_1_secrethash in alice_channel_state.our_state.secrethashes_to_lockedlocks bob_channel_state = get_channelstate(bob_app, alice_app, token_network_identifier) assert transfer_1_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks alice_chain_state = views.state_from_raiden(alice_app.raiden) assert transfer_1_secrethash in alice_chain_state.payment_mapping.secrethashes_to_task # Wait for the expiration to trigger with some additional buffer # time for processing (+2) blocks. waiting.wait_for_block( alice_app.raiden, lock.expiration + (DEFAULT_NUMBER_OF_CONFIRMATIONS_BLOCK + 2), DEFAULT_RETRY_TIMEOUT, ) alice_channel_state = get_channelstate(alice_app, bob_app, token_network_identifier) assert transfer_1_secrethash not in alice_channel_state.our_state.secrethashes_to_lockedlocks # Verify Bob received the message and processed the LockExpired message bob_channel_state = get_channelstate(bob_app, alice_app, token_network_identifier) assert transfer_1_secrethash not in bob_channel_state.partner_state.secrethashes_to_lockedlocks alice_chain_state = views.state_from_raiden(alice_app.raiden) assert transfer_1_secrethash not in alice_chain_state.payment_mapping.secrethashes_to_task # Make another transfer alice_to_bob_amount = 10 identifier = 2 transfer_2_secret = pending_mediated_transfer( raiden_network, token_network_identifier, alice_to_bob_amount, identifier, ) transfer_2_secrethash = sha3(transfer_2_secret) # Make sure the other transfer still exists alice_chain_state = views.state_from_raiden(alice_app.raiden) assert transfer_2_secrethash in alice_chain_state.payment_mapping.secrethashes_to_task bob_channel_state = get_channelstate(bob_app, alice_app, token_network_identifier) assert transfer_2_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks
def test_unlock(raiden_network, token_addresses, deposit): """Unlock can be called on a closed channel.""" alice_app, bob_app = raiden_network registry_address = alice_app.raiden.default_registry.address token_address = token_addresses[0] token_proxy = alice_app.raiden.chain.token(token_address) token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(alice_app), alice_app.raiden.default_registry.address, token_address, ) alice_initial_balance = token_proxy.balance_of(alice_app.raiden.address) bob_initial_balance = token_proxy.balance_of(bob_app.raiden.address) alice_to_bob_amount = 10 identifier = 1 secret = pending_mediated_transfer( raiden_network, token_network_identifier, alice_to_bob_amount, identifier, ) secrethash = sha3(secret) # This is the current state of the protocol: # # A -> B LockedTransfer # B -> A SecretRequest # - protocol didn't continue alice_bob_channel = get_channelstate(alice_app, bob_app, token_network_identifier) bob_alice_channel = get_channelstate(bob_app, alice_app, token_network_identifier) lock = channel.get_lock(alice_bob_channel.our_state, secrethash) assert lock assert_synched_channel_state( token_network_identifier, alice_app, deposit, [lock], bob_app, deposit, [], ) # get proof, that locked transfermessage was in merkle tree, with locked.root unlock_proof = channel.compute_proof_for_lock( alice_bob_channel.our_state, secret, lock, ) assert validate_proof( unlock_proof.merkle_proof, merkleroot(bob_alice_channel.partner_state.merkletree), sha3(lock.encoded), ) assert unlock_proof.lock_encoded == lock.encoded assert unlock_proof.secret == secret # A ChannelClose event will be generated, this will be polled by both apps # and each must start a task for calling settle RaidenAPI(bob_app.raiden).channel_close( registry_address, token_address, alice_app.raiden.address, ) # Unlock will not be called because the secret was not revealed assert lock.expiration > alice_app.raiden.chain.block_number() assert lock.secrethash == sha3(secret) nettingchannel_proxy = bob_app.raiden.chain.netting_channel( bob_alice_channel.identifier, ) nettingchannel_proxy.unlock(unlock_proof) waiting.wait_for_settle( alice_app.raiden, registry_address, token_address, [alice_bob_channel.identifier], alice_app.raiden.alarm.wait_time, ) alice_bob_channel = get_channelstate(alice_app, bob_app, token_network_identifier) bob_alice_channel = get_channelstate(bob_app, alice_app, token_network_identifier) alice_netted_balance = alice_initial_balance + deposit - alice_to_bob_amount bob_netted_balance = bob_initial_balance + deposit + alice_to_bob_amount assert token_proxy.balance_of(alice_app.raiden.address) == alice_netted_balance assert token_proxy.balance_of(bob_app.raiden.address) == bob_netted_balance # Now let's query the WAL to see if the state changes were logged as expected state_changes = alice_app.raiden.wal.storage.get_statechanges_by_identifier( from_identifier=0, to_identifier='latest', ) alice_bob_channel = get_channelstate(alice_app, bob_app, token_network_identifier) bob_alice_channel = get_channelstate(bob_app, alice_app, token_network_identifier) assert must_contain_entry(state_changes, ContractReceiveChannelUnlock, { 'payment_network_identifier': registry_address, 'token_address': token_address, 'channel_identifier': alice_bob_channel.identifier, 'secrethash': secrethash, 'secret': secret, 'receiver': bob_app.raiden.address, })
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, deposit): """ Any transfer following a secret revealed must update the locksroot, so that an attacker cannot reuse a secret to double claim a lock.""" app0, app1 = raiden_network token_address = token_addresses[0] amount = 30 address0 = app0.raiden.address address1 = app1.raiden.address deposit0 = deposit deposit1 = deposit token_proxy = app0.raiden.chain.token(token_address) initial_balance0 = token_proxy.balance_of(address0) initial_balance1 = token_proxy.balance_of(address1) # Using a pending mediated transfer because this allows us to compute the # merkle proof identifier = 1 secret = pending_mediated_transfer( raiden_network, token_address, amount, identifier, ) hashlock = sha3(secret) # Compute the merkle proof for the pending transfer, and then unlock channelstate_0_1 = get_channelstate(app0, app1, token_address) lock = channel.get_lock(channelstate_0_1.our_state, hashlock) unlock_proof = channel.compute_proof_for_lock( channelstate_0_1.our_state, secret, lock, ) claim_lock(raiden_network, identifier, token_address, secret) # Make a new transfer direct_transfer(app0, app1, token_address, amount, identifier=1) RaidenAPI(app1.raiden).channel_close(token_address, app0.raiden.address) # The direct transfer locksroot must not contain the unlocked lock, the # withdraw must fail. netting_channel = app1.raiden.chain.netting_channel( channelstate_0_1.identifier) with pytest.raises(Exception): netting_channel.withdraw( UnlockProofState(unlock_proof, lock.encoded, secret)) waiting.wait_for_settle( app1.raiden, app1.raiden.default_registry.address, token_address, [channelstate_0_1.identifier], app1.raiden.alarm.wait_time, ) expected_balance0 = initial_balance0 + deposit0 - amount * 2 expected_balance1 = initial_balance1 + deposit1 + amount * 2 assert token_proxy.balance_of(address0) == expected_balance0 assert token_proxy.balance_of(address1) == expected_balance1
def test_batch_unlock(raiden_network, token_addresses, secret_registry_address, deposit): """Batch unlock can be called after the channel is settled.""" alice_app, bob_app = raiden_network registry_address = alice_app.raiden.default_registry.address token_address = token_addresses[0] token_proxy = alice_app.raiden.chain.token(token_address) token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(alice_app), alice_app.raiden.default_registry.address, token_address, ) alice_initial_balance = token_proxy.balance_of(alice_app.raiden.address) bob_initial_balance = token_proxy.balance_of(bob_app.raiden.address) alice_to_bob_amount = 10 identifier = 1 secret = pending_mediated_transfer( raiden_network, token_network_identifier, alice_to_bob_amount, identifier, ) secrethash = sha3(secret) secret_registry_proxy = alice_app.raiden.chain.secret_registry( secret_registry_address, ) secret_registry_proxy.register_secret(secret) alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_identifier) lock = channel.get_lock(alice_bob_channel_state.our_state, secrethash) assert lock assert lock.expiration > alice_app.raiden.get_block_number() assert lock.secrethash == sha3(secret) # This is the current state of the protocol: # # A -> B LockedTransfer # B -> A SecretRequest # - protocol didn't continue assert_synched_channel_state( token_network_identifier, alice_app, deposit, [lock], bob_app, deposit, [], ) # A ChannelClose event will be generated, this will be polled by both apps # and each must start a task for calling settle RaidenAPI(bob_app.raiden).channel_close( registry_address, token_address, alice_app.raiden.address, ) waiting.wait_for_settle( alice_app.raiden, registry_address, token_address, [alice_bob_channel_state.identifier], alice_app.raiden.alarm.sleep_time, ) # wait for the node to call batch unlock with gevent.Timeout(10): wait_for_batch_unlock( alice_app, token_network_identifier, alice_bob_channel_state.partner_state.address, alice_bob_channel_state.our_state.address, ) alice_new_balance = alice_initial_balance + deposit - alice_to_bob_amount bob_new_balance = bob_initial_balance + deposit + alice_to_bob_amount assert token_proxy.balance_of(alice_app.raiden.address) == alice_new_balance assert token_proxy.balance_of(bob_app.raiden.address) == bob_new_balance
def test_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(token_addresses, raiden_chain, deposit): """ An attacker can try to steal tokens from a hub or the last node in a path. The attacker needs to use two addresses (A1 and A2) and connect both to the hub H. Once connected a mediated transfer is initialized from A1 to A2 through H. Once the node A2 receives the mediated transfer the attacker uses the known secret and reveal to close and settle the channel H-A2, without revealing the secret to H's raiden node. The intention is to make the hub transfer the token but for him to be unable to require the token A1.""" amount = 30 token = token_addresses[0] app0, app1, app2 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token, ) # the attacker owns app0 and app2 and creates a transfer through app1 identifier = 1 secret = pending_mediated_transfer( raiden_chain, token_network_identifier, amount, identifier, ) secrethash = sha3(secret) attack_channel = get_channelstate(app2, app1, token_network_identifier) attack_transfer = None # TODO attack_contract = attack_channel.external_state.netting_channel.address hub_contract = ( get_channelstate(app1, app0, token_network_identifier) .external_state .netting_channel.address ) # the attacker can create a merkle proof of the locked transfer lock = attack_channel.partner_state.get_lock_by_secrethash(secrethash) unlock_proof = attack_channel.partner_state.compute_proof_for_lock(secret, lock) # start the settle counter attack_balance_proof = attack_transfer.to_balanceproof() attack_channel.netting_channel.channel_close(attack_balance_proof) # wait until the last block to reveal the secret, hopefully we are not # missing a block during the test wait_until_block(app2.raiden.chain, attack_transfer.lock.expiration - 1) # since the attacker knows the secret he can net the lock attack_channel.netting_channel.unlock( UnlockProofState(unlock_proof, attack_transfer.lock, secret), ) # XXX: verify that the secret was publicized # at this point the hub might not know the secret yet, and won't be able to # claim the token from the channel A1 - H # the attacker settles the contract app2.raiden.chain.next_block() attack_channel.netting_channel.settle(token, attack_contract) # at this point the attacker has the "stolen" funds attack_contract = app2.raiden.chain.token_hashchannel[token][attack_contract] assert attack_contract.participants[app2.raiden.address]['netted'] == deposit + amount assert attack_contract.participants[app1.raiden.address]['netted'] == deposit - amount # and the hub's channel A1-H doesn't hub_contract = app1.raiden.chain.token_hashchannel[token][hub_contract] assert hub_contract.participants[app0.raiden.address]['netted'] == deposit assert hub_contract.participants[app1.raiden.address]['netted'] == deposit # to mitigate the attack the Hub _needs_ to use a lower expiration for the # locked transfer between H-A2 than A1-H. For A2 to acquire the token # it needs to make the secret public in the blockchain so it publishes the # secret through an event and the Hub is able to require its funds app1.raiden.chain.next_block() # XXX: verify that the Hub has found the secret, close and settle the channel # the hub has acquired its token hub_contract = app1.raiden.chain.token_hashchannel[token][hub_contract] assert hub_contract.participants[app0.raiden.address]['netted'] == deposit + amount assert hub_contract.participants[app1.raiden.address]['netted'] == deposit - amount
def test_settled_lock(token_addresses, raiden_network, 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_start_end_attack(token_addresses, raiden_chain, deposit): """ An attacker can try to steal tokens from a hub or the last node in a path. The attacker needs to use two addresses (A1 and A2) and connect both to the hub H. Once connected a mediated transfer is initialized from A1 to A2 through H. Once the node A2 receives the mediated transfer the attacker uses the known secret and reveal to close and settle the channel H-A2, without revealing the secret to H's raiden node. The intention is to make the hub transfer the token but for him to be unable to require the token A1.""" amount = 30 token = token_addresses[0] app0, app1, app2 = raiden_chain # pylint: disable=unbalanced-tuple-unpacking token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token, ) # the attacker owns app0 and app2 and creates a transfer through app1 identifier = 1 secret = pending_mediated_transfer( raiden_chain, token_network_identifier, amount, identifier, ) secrethash = sha3(secret) attack_channel = get_channelstate(app2, app1, token_network_identifier) attack_transfer = None # TODO attack_contract = attack_channel.external_state.netting_channel.address hub_contract = (get_channelstate( app1, app0, token_network_identifier).external_state.netting_channel.address) # the attacker can create a merkle proof of the locked transfer lock = attack_channel.partner_state.get_lock_by_secrethash(secrethash) unlock_proof = attack_channel.partner_state.compute_proof_for_lock( secret, lock) # start the settle counter attack_balance_proof = attack_transfer.to_balanceproof() attack_channel.netting_channel.channel_close(attack_balance_proof) # wait until the last block to reveal the secret, hopefully we are not # missing a block during the test wait_until_block(app2.raiden.chain, attack_transfer.lock.expiration - 1) # since the attacker knows the secret he can net the lock attack_channel.netting_channel.unlock( UnlockProofState(unlock_proof, attack_transfer.lock, secret), ) # XXX: verify that the secret was publicized # at this point the hub might not know the secret yet, and won't be able to # claim the token from the channel A1 - H # the attacker settles the contract app2.raiden.chain.next_block() attack_channel.netting_channel.settle(token, attack_contract) # at this point the attacker has the "stolen" funds attack_contract = app2.raiden.chain.token_hashchannel[token][ attack_contract] assert attack_contract.participants[ app2.raiden.address]['netted'] == deposit + amount assert attack_contract.participants[ app1.raiden.address]['netted'] == deposit - amount # and the hub's channel A1-H doesn't hub_contract = app1.raiden.chain.token_hashchannel[token][hub_contract] assert hub_contract.participants[app0.raiden.address]['netted'] == deposit assert hub_contract.participants[app1.raiden.address]['netted'] == deposit # to mitigate the attack the Hub _needs_ to use a lower expiration for the # locked transfer between H-A2 than A1-H. For A2 to acquire the token # it needs to make the secret public in the blockchain so it publishes the # secret through an event and the Hub is able to require its funds app1.raiden.chain.next_block() # XXX: verify that the Hub has found the secret, close and settle the channel # the hub has acquired its token hub_contract = app1.raiden.chain.token_hashchannel[token][hub_contract] assert hub_contract.participants[ app0.raiden.address]['netted'] == deposit + amount assert hub_contract.participants[ app1.raiden.address]['netted'] == deposit - amount
def test_settled_lock(token_addresses, raiden_network, 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_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
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