def events_for_unlock_if_closed( channelidentifiers_to_channels, transfers_pair, secret, secrethash, ): """ Unlock on chain if the payer channel is closed and the secret is known. If a channel is closed because of another task a balance proof will not be received, so there is no reason to wait for the unsafe region before calling close. This may break the reverse reveal order: Path: A -- B -- C -- B -- D B learned the secret from D and has revealed to C. C has not confirmed yet. channel(A, B).closed is True. B will unlock on channel(A, B) before C's confirmation. A may learn the secret faster than other nodes. """ events = list() pending_transfers_pairs = get_pending_transfer_pairs(transfers_pair) for pair in pending_transfers_pairs: payer_channel = get_payer_channel(channelidentifiers_to_channels, pair) payer_channel_open = channel.get_status( payer_channel) == CHANNEL_STATE_OPENED # The unlock is done by the channel if not payer_channel_open: pair.payer_state = 'payer_waiting_unlock' partner_state = payer_channel.partner_state lock = channel.get_lock(partner_state, secrethash) unlock_proof = channel.compute_proof_for_lock( partner_state, secret, lock, ) unlock = ContractSendChannelBatchUnlock( payer_channel.token_network_identifier, payer_channel.identifier, [unlock_proof], ) events.append(unlock) return events
def events_for_unlock_if_closed( channelidentifiers_to_channels, transfers_pair, secret, secrethash): """ Unlock on chain if the payer channel is closed and the secret is known. If a channel is closed because of another task a balance proof will not be received, so there is no reason to wait for the unsafe region before calling close. This may break the reverse reveal order: Path: A -- B -- C -- B -- D B learned the secret from D and has revealed to C. C has not confirmed yet. channel(A, B).closed is True. B will unlock on channel(A, B) before C's confirmation. A may learn the secret faster than other nodes. """ events = list() pending_transfers_pairs = get_pending_transfer_pairs(transfers_pair) for pair in pending_transfers_pairs: payer_channel = get_payer_channel(channelidentifiers_to_channels, pair) payer_channel_open = channel.get_status(payer_channel) == CHANNEL_STATE_OPENED # The unlock is done by the channel if not payer_channel_open: pair.payer_state = 'payer_waiting_unlock' partner_state = payer_channel.partner_state lock = channel.get_lock(partner_state, secrethash) unlock_proof = channel.compute_proof_for_lock( partner_state, secret, lock, ) unlock = ContractSendChannelBatchUnlock( payer_channel.token_network_identifier, payer_channel.identifier, [unlock_proof], ) events.append(unlock) return events
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_settled_lock(token_addresses, raiden_network, deposit): """ Any transfer following a secret revealed must update the locksroot, so that an attacker cannot reuse a secret to double claim a lock.""" app0, app1 = raiden_network registry_address = app0.raiden.default_registry.address token_address = token_addresses[0] amount = 30 token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(app0), app0.raiden.default_registry.address, token_address, ) address0 = app0.raiden.address address1 = app1.raiden.address deposit0 = deposit deposit1 = deposit token_proxy = app0.raiden.chain.token(token_address) initial_balance0 = token_proxy.balance_of(address0) initial_balance1 = token_proxy.balance_of(address1) # Using a pending mediated transfer because this allows us to compute the # merkle proof identifier = 1 secret = pending_mediated_transfer( raiden_network, token_network_identifier, amount, identifier, ) secrethash = sha3(secret) # Compute the merkle proof for the pending transfer, and then unlock channelstate_0_1 = get_channelstate(app0, app1, token_network_identifier) lock = channel.get_lock(channelstate_0_1.our_state, secrethash) unlock_proof = channel.compute_proof_for_lock( channelstate_0_1.our_state, secret, lock, ) claim_lock(raiden_network, identifier, token_network_identifier, secret) # Make a new transfer direct_transfer(app0, app1, token_network_identifier, amount, identifier=1) RaidenAPI(app1.raiden).channel_close( registry_address, token_address, app0.raiden.address, ) # The direct transfer locksroot must not contain the unlocked lock, the # unlock must fail. netting_channel = app1.raiden.chain.netting_channel(channelstate_0_1.identifier) with pytest.raises(Exception): netting_channel.unlock(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_withdraw( tester_registry_address, deposit, settle_timeout, reveal_timeout, tester_channels, tester_chain, tester_token, ): pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] pseudo_random_generator = random.Random() address0 = privatekey_to_address(pkey0) address1 = privatekey_to_address(pkey1) initial_balance0 = tester_token.balanceOf(address0, sender=pkey0) initial_balance1 = tester_token.balanceOf(address1, sender=pkey0) lock_amount = 31 lock_expiration = tester_chain.block.number + reveal_timeout + 5 secret = b'secretsecretsecretsecretsecretse' secrethash = sha3(secret) new_block = Block(tester_chain.block.number) channel.state_transition( channel0, new_block, pseudo_random_generator, new_block.block_number, ) channel.state_transition( channel1, new_block, pseudo_random_generator, new_block.block_number, ) lock0 = Lock(lock_amount, lock_expiration, secrethash) mediated0 = make_mediated_transfer( tester_registry_address, channel0, channel1, address0, address1, lock0, pkey0, secret, ) # withdraw the pending transfer sent to us by our partner lock_state = lockstate_from_lock(mediated0.lock) proof = channel.compute_proof_for_lock( channel1.partner_state, secret, lock_state, ) mediated0_hash = sha3(mediated0.packed().data[:-65]) nettingchannel.close( mediated0.nonce, mediated0.transferred_amount, mediated0.locksroot, mediated0_hash, mediated0.signature, sender=pkey1, ) tester_chain.mine(number_of_blocks=1) nettingchannel.withdraw( proof.lock_encoded, b''.join(proof.merkle_proof), proof.secret, sender=pkey1, ) tester_chain.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey0) balance0 = initial_balance0 + deposit - lock0.amount balance1 = initial_balance1 + deposit + lock0.amount assert tester_token.balanceOf(address0, sender=pkey0) == balance0 assert tester_token.balanceOf(address1, sender=pkey0) == balance1 assert tester_token.balanceOf(nettingchannel.address, sender=pkey0) == 0
def test_withdraw_both_participants( tester_registry_address, deposit, settle_timeout, reveal_timeout, tester_channels, tester_chain, tester_token, ): pkey0, pkey1, nettingchannel, channel0, channel1 = tester_channels[0] pseudo_random_generator = random.Random() address0 = privatekey_to_address(pkey0) address1 = privatekey_to_address(pkey1) initial_balance0 = tester_token.balanceOf(address0, sender=pkey0) initial_balance1 = tester_token.balanceOf(address1, sender=pkey0) secret = b'secretsecretsecretsecretsecretse' secrethash = sha3(secret) lock_amount = 31 lock01_expiration = tester_chain.block.number + settle_timeout - 1 * reveal_timeout lock10_expiration = tester_chain.block.number + settle_timeout - 2 * reveal_timeout new_block = Block(tester_chain.block.number) channel.state_transition( channel0, new_block, pseudo_random_generator, new_block.block_number, ) channel.state_transition( channel1, new_block, pseudo_random_generator, new_block.block_number, ) # using the same secrethash and amount is intentional lock01 = Lock(lock_amount, lock01_expiration, secrethash) lock10 = Lock(lock_amount, lock10_expiration, secrethash) mediated01 = make_mediated_transfer( tester_registry_address, channel0, channel1, address0, address1, lock01, pkey0, secret, ) mediated10 = make_mediated_transfer( tester_registry_address, channel1, channel0, address1, address0, lock10, pkey1, secret, ) mediated01_hash = sha3(mediated01.packed().data[:-65]) nettingchannel.close( mediated01.nonce, mediated01.transferred_amount, mediated01.locksroot, mediated01_hash, mediated01.signature, sender=pkey1, ) tester_chain.mine(number_of_blocks=1) mediated10_hash = sha3(mediated10.packed().data[:-65]) nettingchannel.updateTransfer( mediated10.nonce, mediated10.transferred_amount, mediated10.locksroot, mediated10_hash, mediated10.signature, sender=pkey0, ) tester_chain.mine(number_of_blocks=1) lock_state01 = lockstate_from_lock(mediated01.lock) proof01 = channel.compute_proof_for_lock( channel1.partner_state, secret, lock_state01, ) nettingchannel.withdraw( proof01.lock_encoded, b''.join(proof01.merkle_proof), proof01.secret, sender=pkey1, ) lock_state10 = lockstate_from_lock(mediated10.lock) proof10 = channel.compute_proof_for_lock( channel0.partner_state, secret, lock_state10, ) nettingchannel.withdraw( proof10.lock_encoded, b''.join(proof10.merkle_proof), proof10.secret, sender=pkey0, ) tester_chain.mine(number_of_blocks=settle_timeout + 1) nettingchannel.settle(sender=pkey0) balance0 = initial_balance0 + deposit - lock01.amount + lock10.amount balance1 = initial_balance1 + deposit + lock01.amount - lock10.amount assert tester_token.balanceOf(address0, sender=pkey0) == balance0 assert tester_token.balanceOf(address1, sender=pkey0) == balance1 assert tester_token.balanceOf(nettingchannel.address, sender=pkey0) == 0