def handle_contract_send_channelunlock( self, raiden: RaidenService, channel_unlock_event: ContractSendChannelBatchUnlock, ): token_network_identifier = channel_unlock_event.token_network_identifier channel_identifier = channel_unlock_event.channel_identifier participant = channel_unlock_event.participant token_address = channel_unlock_event.token_address payment_channel: PaymentChannel = raiden.chain.payment_channel( token_network_address=token_network_identifier, channel_id=channel_identifier, ) token_network: TokenNetwork = payment_channel.token_network participants_details = token_network.detail_participants( participant1=raiden.address, participant2=participant, channel_identifier=channel_identifier, ) our_details = participants_details.our_details our_locksroot = our_details.locksroot partner_details = participants_details.partner_details partner_locksroot = partner_details.locksroot is_partner_unlock = ( partner_details.address == participant and partner_locksroot != EMPTY_HASH ) is_our_unlock = ( our_details.address == participant and our_locksroot != EMPTY_HASH ) if is_partner_unlock: state_change_record = get_state_change_with_balance_proof( storage=raiden.wal.storage, chain_id=raiden.chain.network_id, token_network_identifier=token_network_identifier, channel_identifier=channel_identifier, balance_hash=partner_details.balance_hash, sender=participants_details.partner_details.address, ) state_change_identifier = state_change_record.state_change_identifier elif is_our_unlock: event_record = get_event_with_balance_proof( storage=raiden.wal.storage, chain_id=raiden.chain.network_id, token_network_identifier=token_network_identifier, channel_identifier=channel_identifier, balance_hash=our_details.balance_hash, ) state_change_identifier = event_record.state_change_identifier else: state_change_identifier = 0 if not state_change_identifier: raise RaidenUnrecoverableError( f'Failed to find state/event that match current channel locksroots. ' f'token:{to_checksum_address(token_address)} ' f'token_network:{to_checksum_address(token_network_identifier)} ' f'channel:{channel_identifier} ' f'participant:{to_checksum_address(participant)} ' f'our_locksroot:{to_hex(our_locksroot)} ' f'partner_locksroot:{to_hex(partner_locksroot)} ', ) # Replay state changes until a channel state is reached where # this channel state has the participants balance hash. restored_channel_state = channel_state_until_state_change( raiden=raiden, payment_network_identifier=raiden.default_registry.address, token_address=token_address, channel_identifier=channel_identifier, state_change_identifier=state_change_identifier, ) our_state = restored_channel_state.our_state partner_state = restored_channel_state.partner_state if partner_state.address == participant: merkle_tree_leaves = get_batch_unlock(partner_state) elif our_state.address == participant: merkle_tree_leaves = get_batch_unlock(our_state) try: payment_channel.unlock(merkle_tree_leaves) except ChannelOutdatedError as e: log.error( str(e), node=pex(raiden.address), )
def handle_contract_send_channelunlock( self, raiden: RaidenService, channel_unlock_event: ContractSendChannelBatchUnlock, ): payment_channel: PaymentChannel = raiden.chain.payment_channel( channel_unlock_event.token_network_identifier, channel_unlock_event.channel_identifier, ) token_network: TokenNetwork = payment_channel.token_network # Fetch on-chain balance hashes for both participants participants_details = token_network.detail_participants( raiden.address, channel_unlock_event.participant, channel_unlock_event.channel_identifier, ) our_details = participants_details.our_details our_locksroot = our_details.locksroot partner_details = participants_details.partner_details partner_locksroot = partner_details.locksroot is_partner_unlock = (partner_details.address == channel_unlock_event.participant and partner_locksroot != EMPTY_HASH) is_our_unlock = (our_details.address == channel_unlock_event.participant and our_locksroot != EMPTY_HASH) if is_partner_unlock: record = raiden.wal.storage.get_latest_state_change_by_data_field({ 'balance_proof.chain_id': raiden.chain.network_id, 'balance_proof.token_network_identifier': to_checksum_address( channel_unlock_event.token_network_identifier, ), 'balance_proof.channel_identifier': channel_unlock_event.channel_identifier, 'balance_proof.sender': to_checksum_address( participants_details.partner_details.address, ), 'balance_proof.locksroot': serialize_bytes(partner_locksroot), }) elif is_our_unlock: record = raiden.wal.storage.get_latest_event_by_data_field({ 'balance_proof.chain_id': raiden.chain.network_id, 'balance_proof.token_network_identifier': to_checksum_address( channel_unlock_event.token_network_identifier, ), 'balance_proof.locksroot': serialize_bytes(our_locksroot), 'channel_identifier': channel_unlock_event.channel_identifier, }) else: raise RaidenUnrecoverableError( 'Failed to find state/event that match current channel locksroots', ) # Replay state changes until a channel state is reached where # this channel state has the participants balance hash. restored_channel_state = channel_state_until_state_change( raiden=raiden, payment_network_identifier=raiden.default_registry.address, token_address=channel_unlock_event.token_address, channel_identifier=channel_unlock_event.channel_identifier, state_change_identifier=record.state_change_identifier, ) # Compute merkle tree leaves from partner state our_state = restored_channel_state.our_state partner_state = restored_channel_state.partner_state if partner_state.address == channel_unlock_event.participant: # Partner account merkle_tree_leaves = get_batch_unlock(partner_state) elif our_state.address == channel_unlock_event.participant: # Our account merkle_tree_leaves = get_batch_unlock(our_state) try: payment_channel.unlock(merkle_tree_leaves) except ChannelOutdatedError as e: log.error( str(e), node=pex(raiden.address), )
def handle_contract_send_channelunlock( raiden: "RaidenService", chain_state: ChainState, channel_unlock_event: ContractSendChannelBatchUnlock, ): assert raiden.wal, "The Raiden Service must be initialize to handle events" canonical_identifier = channel_unlock_event.canonical_identifier token_network_identifier = canonical_identifier.token_network_address channel_identifier = canonical_identifier.channel_identifier participant = channel_unlock_event.participant payment_channel: PaymentChannel = raiden.chain.payment_channel( canonical_identifier=canonical_identifier ) channel_state = get_channelstate_by_token_network_and_partner( chain_state=chain_state, token_network_id=TokenNetworkID(token_network_identifier), partner_address=participant, ) if not channel_state: # channel was cleaned up already due to an unlock raise RaidenUnrecoverableError( f"Failed to find channel state with partner:" f"{to_checksum_address(participant)}, token_network:pex(token_network_identifier)" ) our_address = channel_state.our_state.address our_locksroot = channel_state.our_state.onchain_locksroot partner_address = channel_state.partner_state.address partner_locksroot = channel_state.partner_state.onchain_locksroot # we want to unlock because there are on-chain unlocked locks search_events = our_locksroot != EMPTY_HASH # we want to unlock, because there are unlocked/unclaimed locks search_state_changes = partner_locksroot != EMPTY_HASH if not search_events and not search_state_changes: # In the case that someone else sent the unlock we do nothing # Check https://github.com/raiden-network/raiden/issues/3152 # for more details log.warning( "Onchain unlock already mined", canonical_identifier=canonical_identifier, channel_identifier=canonical_identifier.channel_identifier, participant=to_checksum_address(participant), ) return if search_state_changes: state_change_record = get_state_change_with_balance_proof_by_locksroot( storage=raiden.wal.storage, canonical_identifier=canonical_identifier, locksroot=partner_locksroot, sender=partner_address, ) state_change_identifier = state_change_record.state_change_identifier if not state_change_identifier: raise RaidenUnrecoverableError( f"Failed to find state that matches the current channel locksroots. " f"chain_id:{raiden.chain.network_id} " f"token_network:{to_checksum_address(token_network_identifier)} " f"channel:{channel_identifier} " f"participant:{to_checksum_address(participant)} " f"our_locksroot:{to_hex(our_locksroot)} " f"partner_locksroot:{to_hex(partner_locksroot)} " ) restored_channel_state = channel_state_until_state_change( raiden=raiden, canonical_identifier=canonical_identifier, state_change_identifier=state_change_identifier, ) assert restored_channel_state is not None gain = get_batch_unlock_gain(restored_channel_state) skip_unlock = ( restored_channel_state.partner_state.address == participant and gain.from_partner_locks == 0 ) if not skip_unlock: unlock( raiden=raiden, payment_channel=payment_channel, end_state=restored_channel_state.partner_state, participant=our_address, partner=partner_address, ) if search_events: event_record = get_event_with_balance_proof_by_locksroot( storage=raiden.wal.storage, canonical_identifier=canonical_identifier, locksroot=our_locksroot, recipient=partner_address, ) state_change_identifier = event_record.state_change_identifier if not state_change_identifier: raise RaidenUnrecoverableError( f"Failed to find event that match current channel locksroots. " f"chain_id:{raiden.chain.network_id} " f"token_network:{to_checksum_address(token_network_identifier)} " f"channel:{channel_identifier} " f"participant:{to_checksum_address(participant)} " f"our_locksroot:{to_hex(our_locksroot)} " f"partner_locksroot:{to_hex(partner_locksroot)} " ) restored_channel_state = channel_state_until_state_change( raiden=raiden, canonical_identifier=canonical_identifier, state_change_identifier=state_change_identifier, ) assert restored_channel_state is not None gain = get_batch_unlock_gain(restored_channel_state) skip_unlock = ( restored_channel_state.our_state.address == participant and gain.from_our_locks == 0 ) if not skip_unlock: unlock( raiden=raiden, payment_channel=payment_channel, end_state=restored_channel_state.our_state, participant=partner_address, partner=our_address, )
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, ) # Take a snapshot early on alice_app.raiden.wal.snapshot() token_network = views.get_token_network_by_identifier( views.state_from_app(alice_app), token_network_identifier, ) channel_identifier = get_channelstate(alice_app, bob_app, token_network_identifier).identifier assert channel_identifier in token_network.partneraddresses_to_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) # Take snapshot before transfer alice_app.raiden.wal.snapshot() 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, [], ) # Take a snapshot early on alice_app.raiden.wal.snapshot() our_balance_proof = alice_bob_channel_state.our_state.balance_proof # Test WAL restore to return the latest channel state restored_channel_state = channel_state_until_state_change( raiden=alice_app.raiden, payment_network_identifier=alice_app.raiden.default_registry.address, token_address=token_address, channel_identifier=alice_bob_channel_state.identifier, state_change_identifier='latest', ) our_restored_balance_proof = restored_channel_state.our_state.balance_proof assert our_balance_proof == our_restored_balance_proof # A ChannelClose event will be generated, this will be polled by both apps # and each must start a task for calling settle RaidenAPI(bob_app.raiden).channel_close( registry_address, token_address, alice_app.raiden.address, ) secret_registry_proxy = alice_app.raiden.chain.secret_registry( secret_registry_address, ) secret_registry_proxy.register_secret(secret) 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_batch_unlock(raiden_network, token_addresses, secret_registry_address, deposit): """Tests that batch unlock is properly called. This test will start a single incomplete transfer, the secret will be revealed *on-chain*. The node that receives the tokens has to call unlock, the node that doesn't gain anything does nothing. """ alice_app, bob_app = raiden_network alice_address = alice_app.raiden.address bob_address = bob_app.raiden.address token_network_registry_address = alice_app.raiden.default_registry.address token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(alice_app), token_network_registry_address, token_address ) assert token_network_address hold_event_handler = bob_app.raiden.raiden_event_handler # Take a snapshot early on alice_app.raiden.wal.snapshot() canonical_identifier = get_channelstate( alice_app, bob_app, token_network_address ).canonical_identifier assert is_channel_registered(alice_app, bob_app, canonical_identifier) assert is_channel_registered(bob_app, alice_app, canonical_identifier) token_proxy = alice_app.raiden.proxy_manager.token(token_address) alice_initial_balance = token_proxy.balance_of(alice_app.raiden.address) bob_initial_balance = token_proxy.balance_of(bob_app.raiden.address) # Take snapshot before transfer alice_app.raiden.wal.snapshot() alice_to_bob_amount = 10 identifier = 1 secret = Secret(sha3(bob_address)) secrethash = sha256_secrethash(secret) secret_request_event = hold_event_handler.hold_secretrequest_for(secrethash=secrethash) alice_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=alice_to_bob_amount, target=bob_address, identifier=identifier, secret=secret, ) secret_request_event.get() # wait for the messages to be exchanged alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_address) lock = channel.get_lock(alice_bob_channel_state.our_state, secrethash) assert lock # This is the current state of the protocol: # # A -> B LockedTransfer # B -> A SecretRequest # - protocol didn't continue assert_synced_channel_state( token_network_address, alice_app, deposit, [lock], bob_app, deposit, [] ) # Test WAL restore to return the latest channel state alice_app.raiden.wal.snapshot() our_balance_proof = alice_bob_channel_state.our_state.balance_proof restored_channel_state = channel_state_until_state_change( raiden=alice_app.raiden, canonical_identifier=alice_bob_channel_state.canonical_identifier, state_change_identifier=HIGH_STATECHANGE_ULID, ) assert restored_channel_state our_restored_balance_proof = restored_channel_state.our_state.balance_proof assert our_balance_proof == our_restored_balance_proof # Close the channel before revealing the secret off-chain. This will leave # a pending lock in the channel which has to be unlocked on-chain. # # The token network will emit a ChannelClose event, this will be polled by # both apps and each must start a task for calling settle. RaidenAPI(bob_app.raiden).channel_close( token_network_registry_address, token_address, alice_app.raiden.address ) # The secret has to be registered manually because Bob never learned the # secret. The test is holding the SecretRequest to ensure the off-chain # unlock will not happen and the channel is closed with a pending lock. # # Alternatives would be to hold the unlock messages, or to stop and restart # the apps after the channel is closed. secret_registry_proxy = alice_app.raiden.proxy_manager.secret_registry(secret_registry_address) secret_registry_proxy.register_secret(secret=secret) msg = ( "The lock must still be part of the node state for the test to proceed, " "otherwise there is not unlock to be done." ) assert lock, msg msg = ( "The secret must be registered before the lock expires, in order for " "the unlock to happen on-chain. Otherwise the test will fail on the " "expected balances." ) assert lock.expiration > alice_app.raiden.get_block_number(), msg assert lock.secrethash == sha256_secrethash(secret) waiting.wait_for_settle( alice_app.raiden, token_network_registry_address, token_address, [alice_bob_channel_state.identifier], alice_app.raiden.alarm.sleep_time, ) msg = "The channel_state must not have been cleared, one of the ends has pending locks to do." assert is_channel_registered(alice_app, bob_app, canonical_identifier), msg assert is_channel_registered(bob_app, alice_app, canonical_identifier), msg msg = ( "Timeout while waiting for the unlock to be mined. This may happen if " "transaction is rejected, not mined, or the node's alarm task is " "not running." ) with gevent.Timeout(seconds=30, exception=AssertionError(msg)): # Wait for both nodes (Bob and Alice) to see the on-chain unlock wait_for_batch_unlock( app=alice_app, token_network_address=token_network_address, receiver=bob_address, sender=alice_address, ) wait_for_batch_unlock( app=bob_app, token_network_address=token_network_address, receiver=bob_address, sender=alice_address, ) msg = ( "The nodes have done the unlock, and both ends have seen it, now the " "channel must be cleared" ) assert not is_channel_registered(alice_app, bob_app, canonical_identifier), msg assert not is_channel_registered(bob_app, alice_app, canonical_identifier), msg alice_new_balance = alice_initial_balance + deposit - alice_to_bob_amount bob_new_balance = bob_initial_balance + deposit + alice_to_bob_amount msg = "Unexpected end balance after channel settlement with batch unlock." assert token_proxy.balance_of(alice_app.raiden.address) == alice_new_balance, msg assert token_proxy.balance_of(bob_app.raiden.address) == bob_new_balance, msg
def test_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, ) hold_event_handler = HoldOffChainSecretRequest() bob_app.raiden.raiden_event_handler = hold_event_handler # Take a snapshot early on alice_app.raiden.wal.snapshot() token_network = views.get_token_network_by_identifier( views.state_from_app(alice_app), token_network_identifier, ) channel_identifier = get_channelstate(alice_app, bob_app, token_network_identifier).identifier assert channel_identifier in token_network.partneraddresses_to_channelidentifiers[ bob_app.raiden.address ] alice_initial_balance = token_proxy.balance_of(alice_app.raiden.address) bob_initial_balance = token_proxy.balance_of(bob_app.raiden.address) # Take snapshot before transfer alice_app.raiden.wal.snapshot() alice_to_bob_amount = 10 identifier = 1 target = bob_app.raiden.address secret = sha3(target) secrethash = sha3(secret) hold_event_handler.hold_secretrequest_for(secrethash=secrethash) alice_app.raiden.start_mediated_transfer_with_secret( token_network_identifier, alice_to_bob_amount, target, identifier, secret, ) gevent.sleep(1) # wait for the messages to be exchanged alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_identifier) lock = channel.get_lock(alice_bob_channel_state.our_state, secrethash) # This is the current state of the protocol: # # A -> B LockedTransfer # B -> A SecretRequest # - protocol didn't continue assert_synced_channel_state( token_network_identifier, alice_app, deposit, [lock], bob_app, deposit, [], ) # Take a snapshot early on alice_app.raiden.wal.snapshot() our_balance_proof = alice_bob_channel_state.our_state.balance_proof # Test WAL restore to return the latest channel state restored_channel_state = channel_state_until_state_change( raiden=alice_app.raiden, payment_network_identifier=alice_app.raiden.default_registry.address, token_address=token_address, channel_identifier=alice_bob_channel_state.identifier, state_change_identifier='latest', ) our_restored_balance_proof = restored_channel_state.our_state.balance_proof assert our_balance_proof == our_restored_balance_proof # A ChannelClose event will be generated, this will be polled by both apps # and each must start a task for calling settle RaidenAPI(bob_app.raiden).channel_close( registry_address, token_address, alice_app.raiden.address, ) secret_registry_proxy = alice_app.raiden.chain.secret_registry( secret_registry_address, ) secret_registry_proxy.register_secret(secret) assert lock, 'the lock must still be part of the node state' msg = 'the secret must be registered before the lock expires' assert lock.expiration > alice_app.raiden.get_block_number(), msg assert lock.secrethash == sha3(secret) waiting.wait_for_settle( alice_app.raiden, registry_address, token_address, [alice_bob_channel_state.identifier], alice_app.raiden.alarm.sleep_time, ) token_network = views.get_token_network_by_identifier( views.state_from_app(bob_app), token_network_identifier, ) assert channel_identifier in token_network.partneraddresses_to_channelidentifiers[ alice_app.raiden.address ] # wait for 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_channelidentifiers[ alice_app.raiden.address ] alice_new_balance = alice_initial_balance + deposit - alice_to_bob_amount bob_new_balance = bob_initial_balance + deposit + alice_to_bob_amount assert token_proxy.balance_of(alice_app.raiden.address) == alice_new_balance assert token_proxy.balance_of(bob_app.raiden.address) == bob_new_balance
def run_test_batch_unlock(raiden_network, token_addresses, secret_registry_address, deposit, blockchain_type): """Batch unlock can be called after the channel is settled.""" alice_app, bob_app = raiden_network registry_address = alice_app.raiden.default_registry.address token_address = token_addresses[0] token_proxy = alice_app.raiden.chain.token(token_address) token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_app(alice_app), alice_app.raiden.default_registry.address, token_address) hold_event_handler = HoldOffChainSecretRequest() bob_app.raiden.raiden_event_handler = hold_event_handler # Take a snapshot early on alice_app.raiden.wal.snapshot() token_network = views.get_token_network_by_identifier( views.state_from_app(alice_app), token_network_identifier) channel_identifier = get_channelstate(alice_app, bob_app, token_network_identifier).identifier assert (channel_identifier in token_network.partneraddresses_to_channelidentifiers[ bob_app.raiden.address]) alice_initial_balance = token_proxy.balance_of(alice_app.raiden.address) bob_initial_balance = token_proxy.balance_of(bob_app.raiden.address) # Take snapshot before transfer alice_app.raiden.wal.snapshot() alice_to_bob_amount = 10 identifier = 1 target = bob_app.raiden.address secret = sha3(target) secrethash = sha3(secret) hold_event_handler.hold_secretrequest_for(secrethash=secrethash) alice_app.raiden.start_mediated_transfer_with_secret( token_network_identifier=token_network_identifier, amount=alice_to_bob_amount, fee=0, target=target, identifier=identifier, secret=secret, ) gevent.sleep(1) # wait for the messages to be exchanged alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_identifier) lock = channel.get_lock(alice_bob_channel_state.our_state, secrethash) # This is the current state of the protocol: # # A -> B LockedTransfer # B -> A SecretRequest # - protocol didn't continue assert_synced_channel_state(token_network_identifier, alice_app, deposit, [lock], bob_app, deposit, []) # Take a snapshot early on alice_app.raiden.wal.snapshot() our_balance_proof = alice_bob_channel_state.our_state.balance_proof # Test WAL restore to return the latest channel state restored_channel_state = channel_state_until_state_change( raiden=alice_app.raiden, canonical_identifier=alice_bob_channel_state.canonical_identifier, state_change_identifier="latest", ) our_restored_balance_proof = restored_channel_state.our_state.balance_proof assert our_balance_proof == our_restored_balance_proof # A ChannelClose event will be generated, this will be polled by both apps # and each must start a task for calling settle RaidenAPI(bob_app.raiden).channel_close(registry_address, token_address, alice_app.raiden.address) secret_registry_proxy = alice_app.raiden.chain.secret_registry( secret_registry_address) secret_registry_proxy.register_secret(secret=secret) assert lock, "the lock must still be part of the node state" msg = "the secret must be registered before the lock expires" assert lock.expiration > alice_app.raiden.get_block_number(), msg assert lock.secrethash == sha3(secret) waiting.wait_for_settle( alice_app.raiden, registry_address, token_address, [alice_bob_channel_state.identifier], alice_app.raiden.alarm.sleep_time, ) token_network = views.get_token_network_by_identifier( views.state_from_app(bob_app), token_network_identifier) assert (channel_identifier in token_network.partneraddresses_to_channelidentifiers[ alice_app.raiden.address]) # wait for the node to call batch unlock timeout = 30 if blockchain_type == "parity" else 10 with gevent.Timeout(timeout): 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_channelidentifiers[ alice_app.raiden.address]) alice_new_balance = alice_initial_balance + deposit - alice_to_bob_amount bob_new_balance = bob_initial_balance + deposit + alice_to_bob_amount assert token_proxy.balance_of( alice_app.raiden.address) == alice_new_balance assert token_proxy.balance_of(bob_app.raiden.address) == bob_new_balance
def handle_contract_send_channelunlock( self, raiden: RaidenService, channel_unlock_event: ContractSendChannelBatchUnlock, ): token_network_identifier = channel_unlock_event.token_network_identifier channel_identifier = channel_unlock_event.channel_identifier participant = channel_unlock_event.participant token_address = channel_unlock_event.token_address payment_channel: PaymentChannel = raiden.chain.payment_channel( token_network_address=token_network_identifier, channel_id=channel_identifier, ) token_network: TokenNetwork = payment_channel.token_network participants_details = token_network.detail_participants( participant1=raiden.address, participant2=participant, channel_identifier=channel_identifier, ) our_details = participants_details.our_details our_locksroot = our_details.locksroot partner_details = participants_details.partner_details partner_locksroot = partner_details.locksroot is_partner_unlock = (partner_details.address == participant and partner_locksroot != EMPTY_HASH) is_our_unlock = (our_details.address == participant and our_locksroot != EMPTY_HASH) if not is_partner_unlock and not is_our_unlock: # In the case that someone else sent the unlock we do nothing # Check https://github.com/raiden-network/raiden/issues/3152 # for more details log.warning( 'Onchain unlock already mined', token_address=token_address, channel_identifier=channel_identifier, participant=participant, ) return record = get_state_change_or_event_with_balance_proof( storage=raiden.wal.storage, chain_id=raiden.chain.network_id, token_network_identifier=token_network_identifier, channel_identifier=channel_identifier, is_our_unlock=is_our_unlock, is_partner_unlock=is_partner_unlock, our_balance_hash=our_details.balance_hash, partner_balance_hash=partner_details.balance_hash, sender=participants_details.partner_details.address, ) if not record.state_change_identifier: log.warning( f'Unlock not done. ' f'Failed to find state/event that match current channel locksroots. ' f'token:{to_checksum_address(token_address)} ' f'token_network:{to_checksum_address(token_network_identifier)} ' f'channel:{channel_identifier} ' f'participant:{to_checksum_address(participant)} ' f'our_locksroot:{to_hex(our_locksroot)} ' f'partner_locksroot:{to_hex(partner_locksroot)} ', ) return # Replay state changes until a channel state is reached where # this channel state has the participants balance hash. restored_channel_state = channel_state_until_state_change( raiden=raiden, payment_network_identifier=raiden.default_registry.address, token_address=token_address, channel_identifier=channel_identifier, state_change_identifier=record.state_change_identifier, ) our_state = restored_channel_state.our_state partner_state = restored_channel_state.partner_state if partner_state.address == participant: merkle_tree_leaves = get_batch_unlock(partner_state) elif our_state.address == participant: merkle_tree_leaves = get_batch_unlock(our_state) try: payment_channel.unlock(merkle_tree_leaves) except ChannelOutdatedError as e: log.error( str(e), node=pex(raiden.address), )
def handle_contract_send_channelunlock( raiden: "RaidenService", chain_state: ChainState, channel_unlock_event: ContractSendChannelBatchUnlock, ) -> None: assert raiden.wal, "The Raiden Service must be initialize to handle events" canonical_identifier = channel_unlock_event.canonical_identifier token_network_address = canonical_identifier.token_network_address channel_identifier = canonical_identifier.channel_identifier participant = channel_unlock_event.sender channel_state = get_channelstate_by_canonical_identifier( chain_state=state_from_raiden(raiden), canonical_identifier=canonical_identifier) if channel_state is None: raise RaidenUnrecoverableError( "ContractSendChannelBatchUnlock for inexesting channel.") confirmed_block_identifier = state_from_raiden(raiden).block_hash payment_channel: PaymentChannel = raiden.proxy_manager.payment_channel( channel_state=channel_state, block_identifier=confirmed_block_identifier) channel_state = get_channelstate_by_token_network_and_partner( chain_state=chain_state, token_network_address=token_network_address, partner_address=participant, ) if not channel_state: # channel was cleaned up already due to an unlock raise RaidenUnrecoverableError( f"Failed to find channel state with partner:" f"{to_checksum_address(participant)}, " f"token_network:{to_checksum_address(token_network_address)}") our_address = channel_state.our_state.address our_locksroot = channel_state.our_state.onchain_locksroot partner_address = channel_state.partner_state.address partner_locksroot = channel_state.partner_state.onchain_locksroot # we want to unlock because there are on-chain unlocked locks search_events = our_locksroot != LOCKSROOT_OF_NO_LOCKS # we want to unlock, because there are unlocked/unclaimed locks search_state_changes = partner_locksroot != LOCKSROOT_OF_NO_LOCKS if not search_events and not search_state_changes: # In the case that someone else sent the unlock we do nothing # Check https://github.com/raiden-network/raiden/issues/3152 # for more details log.warning( "Onchain unlock already mined", canonical_identifier=canonical_identifier, channel_identifier=canonical_identifier.channel_identifier, participant=to_checksum_address(participant), ) return if search_state_changes: state_change_record = get_state_change_with_balance_proof_by_locksroot( storage=raiden.wal.storage, canonical_identifier=canonical_identifier, locksroot=partner_locksroot, sender=partner_address, ) if state_change_record is None: raise RaidenUnrecoverableError( f"Failed to find state that matches the current channel locksroots. " f"chain_id:{raiden.rpc_client.chain_id} " f"token_network:{to_checksum_address(token_network_address)} " f"channel:{channel_identifier} " f"participant:{to_checksum_address(participant)} " f"our_locksroot:{to_hex(our_locksroot)} " f"partner_locksroot:{to_hex(partner_locksroot)} ") state_change_identifier = state_change_record.state_change_identifier restored_channel_state = channel_state_until_state_change( raiden=raiden, canonical_identifier=canonical_identifier, state_change_identifier=state_change_identifier, ) gain = get_batch_unlock_gain(restored_channel_state) skip_unlock = (restored_channel_state.partner_state.address == participant and gain.from_partner_locks == 0) if not skip_unlock: unlock( payment_channel=payment_channel, end_state=restored_channel_state.partner_state, sender=partner_address, receiver=our_address, given_block_identifier=channel_unlock_event. triggered_by_block_hash, ) if search_events: event_record = get_event_with_balance_proof_by_locksroot( storage=raiden.wal.storage, canonical_identifier=canonical_identifier, locksroot=our_locksroot, recipient=partner_address, ) if event_record is None: raise RaidenUnrecoverableError( f"Failed to find event that match current channel locksroots. " f"chain_id:{raiden.rpc_client.chain_id} " f"token_network:{to_checksum_address(token_network_address)} " f"channel:{channel_identifier} " f"participant:{to_checksum_address(participant)} " f"our_locksroot:{to_hex(our_locksroot)} " f"partner_locksroot:{to_hex(partner_locksroot)} ") state_change_identifier = event_record.state_change_identifier restored_channel_state = channel_state_until_state_change( raiden=raiden, canonical_identifier=canonical_identifier, state_change_identifier=state_change_identifier, ) gain = get_batch_unlock_gain(restored_channel_state) skip_unlock = (restored_channel_state.our_state.address == participant and gain.from_our_locks == 0) if not skip_unlock: try: unlock( payment_channel=payment_channel, end_state=restored_channel_state.our_state, sender=our_address, receiver=partner_address, given_block_identifier=channel_unlock_event. triggered_by_block_hash, ) except InsufficientEth as e: raise RaidenUnrecoverableError(str(e)) from e
def handle_contract_send_channelunlock( self, raiden: RaidenService, channel_unlock_event: ContractSendChannelBatchUnlock, ): token_network_identifier = channel_unlock_event.token_network_identifier channel_identifier = channel_unlock_event.channel_identifier participant = channel_unlock_event.participant token_address = channel_unlock_event.token_address payment_channel: PaymentChannel = raiden.chain.payment_channel( token_network_address=token_network_identifier, channel_id=channel_identifier, ) token_network: TokenNetwork = payment_channel.token_network participants_details = token_network.detail_participants( participant1=raiden.address, participant2=participant, channel_identifier=channel_identifier, ) our_details = participants_details.our_details our_locksroot = our_details.locksroot partner_details = participants_details.partner_details partner_locksroot = partner_details.locksroot is_partner_unlock = ( partner_details.address == participant and partner_locksroot != EMPTY_HASH ) is_our_unlock = ( our_details.address == participant and our_locksroot != EMPTY_HASH ) if is_partner_unlock: state_change_record = get_state_change_with_balance_proof_by_locksroot( storage=raiden.wal.storage, chain_id=raiden.chain.network_id, token_network_identifier=token_network_identifier, channel_identifier=channel_identifier, locksroot=partner_locksroot, sender=participants_details.partner_details.address, ) state_change_identifier = state_change_record.state_change_identifier elif is_our_unlock: event_record = get_event_with_balance_proof_by_locksroot( storage=raiden.wal.storage, chain_id=raiden.chain.network_id, token_network_identifier=token_network_identifier, channel_identifier=channel_identifier, locksroot=our_locksroot.balance_hash, ) state_change_identifier = event_record.state_change_identifier else: # In the case that someone else sent the unlock we do nothing # Check https://github.com/raiden-network/raiden/issues/3152 # for more details log.warning( 'Onchain unlock already mined', token_address=token_address, channel_identifier=channel_identifier, participant=participant, ) return if not state_change_identifier: raise RaidenUnrecoverableError( f'Failed to find state/event that match current channel locksroots. ' f'chain_id:{raiden.chain.network_id} ' f'token:{to_checksum_address(token_address)} ' f'token_network:{to_checksum_address(token_network_identifier)} ' f'channel:{channel_identifier} ' f'participant:{to_checksum_address(participant)} ' f'our_locksroot:{to_hex(our_locksroot)} ' f'our_balance_hash:{to_hex(our_details.balance_hash)} ' f'partner_locksroot:{to_hex(partner_locksroot)} ' f'partner_balancehash:{to_hex(partner_details.balance_hash)} ', ) # Replay state changes until a channel state is reached where # this channel state has the participants balance hash. restored_channel_state = channel_state_until_state_change( raiden=raiden, payment_network_identifier=raiden.default_registry.address, token_address=token_address, channel_identifier=channel_identifier, state_change_identifier=state_change_identifier, ) our_state = restored_channel_state.our_state partner_state = restored_channel_state.partner_state if partner_state.address == participant: merkle_tree_leaves = get_batch_unlock(partner_state) elif our_state.address == participant: merkle_tree_leaves = get_batch_unlock(our_state) try: payment_channel.unlock(merkle_tree_leaves) except ChannelOutdatedError as e: log.error( str(e), node=pex(raiden.address), )
def handle_contract_send_channelunlock( raiden: 'RaidenService', channel_unlock_event: ContractSendChannelBatchUnlock, ): token_network_identifier = channel_unlock_event.token_network_identifier channel_identifier = channel_unlock_event.channel_identifier canonical_identifier = CanonicalIdentifier( chain_identifier=raiden.chain.network_id, token_network_address=token_network_identifier, channel_identifier=channel_identifier, ) participant = channel_unlock_event.participant token_address = channel_unlock_event.token_address payment_channel: PaymentChannel = raiden.chain.payment_channel( canonical_identifier=canonical_identifier, ) channel_state = get_channelstate_by_token_network_and_partner( chain_state=state_from_raiden(raiden), token_network_id=token_network_identifier, partner_address=participant, ) if not channel_state: # channel was cleaned up already due to an unlock raise RaidenUnrecoverableError( f'Failed to find channel state with partner ' f'{participant}, token_network:pex(token_network_identifier)', ) our_address = channel_state.our_state.address our_locksroot = channel_state.our_state.onchain_locksroot partner_address = channel_state.partner_state.address partner_locksroot = channel_state.partner_state.onchain_locksroot # we want to unlock because there are on-chain unlocked locks search_events = our_locksroot != EMPTY_HASH # we want to unlock, because there are unlocked/unclaimed locks search_state_changes = partner_locksroot != EMPTY_HASH if not search_events and not search_state_changes: # In the case that someone else sent the unlock we do nothing # Check https://github.com/raiden-network/raiden/issues/3152 # for more details log.warning( 'Onchain unlock already mined', token_address=token_address, channel_identifier=canonical_identifier.channel_identifier, participant=participant, ) return if search_state_changes: state_change_record = get_state_change_with_balance_proof_by_locksroot( storage=raiden.wal.storage, canonical_identifier=canonical_identifier, locksroot=partner_locksroot, sender=partner_address, ) state_change_identifier = state_change_record.state_change_identifier if not state_change_identifier: raise RaidenUnrecoverableError( f'Failed to find state that matches the current channel locksroots. ' f'chain_id:{raiden.chain.network_id} ' f'token:{to_checksum_address(token_address)} ' f'token_network:{to_checksum_address(token_network_identifier)} ' f'channel:{channel_identifier} ' f'participant:{to_checksum_address(participant)} ' f'our_locksroot:{to_hex(our_locksroot)} ' f'partner_locksroot:{to_hex(partner_locksroot)} ', ) restored_channel_state = channel_state_until_state_change( raiden=raiden, payment_network_identifier=raiden.default_registry.address, token_address=token_address, channel_identifier=channel_identifier, state_change_identifier=state_change_identifier, ) gain = get_batch_unlock_gain(restored_channel_state, ) skip_unlock = (restored_channel_state.partner_state.address == participant and gain.from_partner_locks == 0) if not skip_unlock: unlock( raiden=raiden, payment_channel=payment_channel, end_state=restored_channel_state.partner_state, participant=our_address, partner=partner_address, ) if search_events: event_record = get_event_with_balance_proof_by_locksroot( storage=raiden.wal.storage, canonical_identifier=canonical_identifier, locksroot=our_locksroot, recipient=partner_address, ) state_change_identifier = event_record.state_change_identifier if not state_change_identifier: raise RaidenUnrecoverableError( f'Failed to find event that match current channel locksroots. ' f'chain_id:{raiden.chain.network_id} ' f'token:{to_checksum_address(token_address)} ' f'token_network:{to_checksum_address(token_network_identifier)} ' f'channel:{channel_identifier} ' f'participant:{to_checksum_address(participant)} ' f'our_locksroot:{to_hex(our_locksroot)} ' f'partner_locksroot:{to_hex(partner_locksroot)} ', ) restored_channel_state = channel_state_until_state_change( raiden=raiden, payment_network_identifier=raiden.default_registry.address, token_address=token_address, channel_identifier=canonical_identifier.channel_identifier, state_change_identifier=state_change_identifier, ) gain = get_batch_unlock_gain(restored_channel_state, ) skip_unlock = (restored_channel_state.our_state.address == participant and gain.from_our_locks == 0) if not skip_unlock: unlock( raiden=raiden, payment_channel=payment_channel, end_state=restored_channel_state.our_state, participant=partner_address, partner=our_address, )