def sanity_check( old_state: Optional[TargetTransferState], new_state: Optional[TargetTransferState], channel_state: NettingChannelState, ) -> None: was_running = old_state is not None is_running = new_state is not None is_cleared = was_running and not is_running if is_cleared: lock = channel.get_lock( end_state=channel_state.partner_state, secrethash=old_state.transfer.lock.secrethash, ) assert lock is None, 'The lock must be cleared once the task exists' elif is_running: # old_state can be None if the task is starting lock = channel.get_lock( end_state=channel_state.partner_state, secrethash=new_state.transfer.lock.secrethash, ) assert lock is not None, 'The lock must not be cleared while the task is running'
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 setup_initiator_tests( amount=UNIT_TRANSFER_AMOUNT, partner_balance=EMPTY, our_address=EMPTY, partner_address=EMPTY, block_number=1, ) -> InitiatorSetup: """Commonly used setup code for initiator manager and channel""" prng = random.Random() channel1 = factories.make_channel( our_balance=amount, partner_balance=partner_balance, our_address=our_address, partner_address=partner_address, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channel_map = {channel1.identifier: channel1} available_routes = [factories.route_from_channel(channel1)] current_state = make_initiator_manager_state( available_routes, factories.UNIT_TRANSFER_DESCRIPTION, channel_map, prng, block_number, ) lock = channel.get_lock( channel1.our_state, current_state.initiator.transfer_description.secrethash, ) setup = InitiatorSetup( current_state=current_state, block_number=block_number, channel=channel1, channel_map=channel_map, available_routes=available_routes, prng=prng, lock=lock, ) return setup
def events_for_withdraw_if_closed(channelidentifiers_to_channels, transfers_pair, secret, secrethash): """ Withdraw 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 withdraw 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 withdraw is done by the channel if not payer_channel_open: pair.payer_state = 'payer_waiting_withdraw' partner_state = payer_channel.partner_state lock = channel.get_lock(partner_state, secrethash) unlock_proof = channel.compute_proof_for_lock( partner_state, secret, lock, ) withdraw = ContractSendChannelWithdraw( payer_channel.identifier, [unlock_proof], ) events.append(withdraw) return events
def handle_lock_expired( mediator_state: MediatorTransferState, state_change: ReceiveLockExpired, channelidentifiers_to_channels: typing.ChannelMap, ): events = list() transfer_pair: MediationPairState for transfer_pair in mediator_state.transfers_pair: balance_proof = transfer_pair.payer_transfer.balance_proof channel_state = channelidentifiers_to_channels.get(balance_proof.channel_identifier) if not channel_state: return TransitionResult(mediator_state, list()) result = channel.handle_receive_lock_expired(channel_state, state_change) if not channel.get_lock(result.new_state.partner_state, mediator_state.secrethash): transfer_pair.payer_state = 'payer_expired' events.extend(result.events) return TransitionResult(mediator_state, events)
def handle_lock_expired( payment_state: InitiatorPaymentState, state_change: ReceiveLockExpired, channelidentifiers_to_channels: ChannelMap, pseudo_random_generator: random.Random, block_number: BlockNumber, ) -> TransitionResult: """Initiator also needs to handle LockExpired messages when refund transfers are involved. A -> B -> C - A sends locked transfer to B - B attempted to forward to C but has not enough capacity - B sends a refund transfer with the same secrethash back to A - When the lock expires B will also send a LockExpired message to A - A needs to be able to properly process it Related issue: https://github.com/raiden-network/raiden/issues/3183 """ channel_identifier = payment_state.initiator.channel_identifier channel_state = channelidentifiers_to_channels[channel_identifier] secrethash = payment_state.initiator.transfer.lock.secrethash result = channel.handle_receive_lock_expired( channel_state=channel_state, state_change=state_change, block_number=block_number, ) if not channel.get_lock(result.new_state.partner_state, secrethash): transfer = payment_state.initiator.transfer unlock_failed = EventUnlockClaimFailed( identifier=transfer.payment_identifier, secrethash=transfer.lock.secrethash, reason='Lock expired', ) result.events.append(unlock_failed) return TransitionResult(payment_state, result.events)
def handle_lock_expired( payment_state: InitiatorPaymentState, state_change: ReceiveLockExpired, channelidentifiers_to_channels: typing.ChannelMap, pseudo_random_generator: random.Random, block_number: typing.BlockNumber, ) -> TransitionResult: """Initiator also needs to handle LockExpired messages when refund transfers are involved. A -> B -> C - A sends locked transfer to B - B attempted to forward to C but has not enough capacity - B sends a refund transfer with the same secrethash back to A - When the lock expires B will also send a LockExpired message to A - A needs to be able to properly process it Related issue: https://github.com/raiden-network/raiden/issues/3183 """ channel_identifier = payment_state.initiator.channel_identifier channel_state = channelidentifiers_to_channels[channel_identifier] secrethash = payment_state.initiator.transfer.lock.secrethash result = channel.handle_receive_lock_expired( channel_state=channel_state, state_change=state_change, block_number=block_number, ) if not channel.get_lock(result.new_state.partner_state, secrethash): transfer = payment_state.initiator.transfer unlock_failed = EventUnlockClaimFailed( identifier=transfer.payment_identifier, secrethash=transfer.lock.secrethash, reason='Lock expired', ) result.events.append(unlock_failed) return TransitionResult(payment_state, result.events)
def handle_lock_expired( target_state: TargetTransferState, state_change: ReceiveLockExpired, channel_state: NettingChannelState, block_number: BlockNumber, ) -> TransitionResult[Optional[TargetTransferState]]: """Remove expired locks from channel states.""" result = channel.handle_receive_lock_expired( channel_state=channel_state, state_change=state_change, block_number=block_number ) assert result.new_state, "handle_receive_lock_expired should not delete the task" if not channel.get_lock(result.new_state.partner_state, target_state.transfer.lock.secrethash): transfer = target_state.transfer unlock_failed = EventUnlockClaimFailed( identifier=transfer.payment_identifier, secrethash=transfer.lock.secrethash, reason="Lock expired", ) result.events.append(unlock_failed) return TransitionResult(None, result.events) return TransitionResult(target_state, result.events)
def handle_lock_expired( mediator_state: MediatorTransferState, state_change: ReceiveLockExpired, channelidentifiers_to_channels: typing.ChannelMap, block_number: typing.BlockNumber, ): events = list() for transfer_pair in mediator_state.transfers_pair: balance_proof = transfer_pair.payer_transfer.balance_proof channel_state = channelidentifiers_to_channels.get(balance_proof.channel_identifier) if not channel_state: return TransitionResult(mediator_state, list()) result = channel.handle_receive_lock_expired( channel_state=channel_state, state_change=state_change, block_number=block_number, ) events.extend(result.events) if not channel.get_lock(result.new_state.partner_state, mediator_state.secrethash): transfer_pair.payer_state = 'payer_expired' if mediator_state.waiting_transfer: waiting_channel = channelidentifiers_to_channels[ mediator_state.waiting_transfer.transfer.balance_proof.channel_identifier ] result = channel.handle_receive_lock_expired( channel_state=waiting_channel, state_change=state_change, block_number=block_number, ) events.extend(result.events) return TransitionResult(mediator_state, events)
def handle_lock_expired( target_state: TargetTransferState, state_change: ReceiveLockExpired, channel_state: NettingChannelState, block_number: BlockNumber, ): """Remove expired locks from channel states.""" result = channel.handle_receive_lock_expired( channel_state=channel_state, state_change=state_change, block_number=block_number, ) if not channel.get_lock(result.new_state.partner_state, target_state.transfer.lock.secrethash): transfer = target_state.transfer unlock_failed = EventUnlockClaimFailed( identifier=transfer.payment_identifier, secrethash=transfer.lock.secrethash, reason='Lock expired', ) result.events.append(unlock_failed) return TransitionResult(None, result.events) return TransitionResult(target_state, result.events)
def test_lock_expiry(raiden_network, token_addresses, 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, ) hold_event_handler = HoldOffChainSecretRequest() wait_message_handler = WaitForMessage() bob_app.raiden.message_handler = wait_message_handler bob_app.raiden.raiden_event_handler = hold_event_handler 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_channelidentifiers[ bob_app.raiden.address ] alice_to_bob_amount = 10 identifier = 1 target = bob_app.raiden.address transfer_1_secret = factories.make_secret(0) transfer_1_secrethash = sha3(transfer_1_secret) transfer_2_secret = factories.make_secret(1) transfer_2_secrethash = sha3(transfer_2_secret) hold_event_handler.hold_secretrequest_for(secrethash=transfer_1_secrethash) transfer1_received = wait_message_handler.wait_for_message( LockedTransfer, {'lock': {'secrethash': transfer_1_secrethash}}, ) transfer2_received = wait_message_handler.wait_for_message( LockedTransfer, {'lock': {'secrethash': transfer_2_secrethash}}, ) remove_expired_lock_received = wait_message_handler.wait_for_message( LockExpired, {'secrethash': transfer_1_secrethash}, ) alice_app.raiden.start_mediated_transfer_with_secret( token_network_identifier, alice_to_bob_amount, target, identifier, transfer_1_secret, ) transfer1_received.wait() 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 remove_expired_lock_received.wait() 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 hold_event_handler.hold_secretrequest_for(secrethash=transfer_2_secrethash) alice_app.raiden.start_mediated_transfer_with_secret( token_network_identifier, alice_to_bob_amount, target, identifier, transfer_2_secret, ) transfer2_received.wait() # 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 handle_secretrequest( initiator_state: InitiatorTransferState, state_change: ReceiveSecretRequest, channel_state: NettingChannelState, pseudo_random_generator: random.Random, ) -> TransitionResult: is_message_from_target = ( state_change.sender == initiator_state.transfer_description.target and state_change.secrethash == initiator_state.transfer_description.secrethash and state_change.payment_identifier == initiator_state.transfer_description.payment_identifier) lock = channel.get_lock( channel_state.our_state, initiator_state.transfer_description.secrethash, ) already_received_secret_request = initiator_state.received_secret_request is_valid_secretrequest = (state_change.amount == initiator_state.transfer_description.amount and state_change.expiration == lock.expiration) if already_received_secret_request and is_message_from_target: # A secret request was received earlier, all subsequent are ignored # as it might be an attack iteration = TransitionResult(initiator_state, list()) elif is_valid_secretrequest and is_message_from_target: # Reveal the secret to the target node and wait for its confirmation. # At this point the transfer is not cancellable anymore as either the lock # timeouts or a secret reveal is received. # # Note: The target might be the first hop # message_identifier = message_identifier_from_prng( pseudo_random_generator) transfer_description = initiator_state.transfer_description recipient = transfer_description.target revealsecret = SendSecretReveal( recipient=recipient, channel_identifier=CHANNEL_IDENTIFIER_GLOBAL_QUEUE, message_identifier=message_identifier, secret=transfer_description.secret, ) initiator_state.revealsecret = revealsecret initiator_state.received_secret_request = True iteration = TransitionResult(initiator_state, [revealsecret]) elif not is_valid_secretrequest and is_message_from_target: cancel = EventPaymentSentFailed( payment_network_identifier=channel_state. payment_network_identifier, token_network_identifier=channel_state.token_network_identifier, identifier=initiator_state.transfer_description.payment_identifier, target=initiator_state.transfer_description.target, reason='bad secret request message from target', ) initiator_state.received_secret_request = True iteration = TransitionResult(initiator_state, [cancel]) else: iteration = TransitionResult(initiator_state, list()) return iteration
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 test_lock_expiry(raiden_network, token_addresses, 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, ) hold_event_handler = HoldOffChainSecretRequest() wait_message_handler = WaitForMessage() bob_app.raiden.message_handler = wait_message_handler bob_app.raiden.raiden_event_handler = hold_event_handler 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_channelidentifiers[ bob_app.raiden.address] alice_to_bob_amount = 10 identifier = 1 target = bob_app.raiden.address transfer_1_secret = factories.make_secret(0) transfer_1_secrethash = sha3(transfer_1_secret) transfer_2_secret = factories.make_secret(1) transfer_2_secrethash = sha3(transfer_2_secret) hold_event_handler.hold_secretrequest_for(secrethash=transfer_1_secrethash) transfer1_received = wait_message_handler.wait_for_message( LockedTransfer, {'lock': { 'secrethash': transfer_1_secrethash }}, ) transfer2_received = wait_message_handler.wait_for_message( LockedTransfer, {'lock': { 'secrethash': transfer_2_secrethash }}, ) remove_expired_lock_received = wait_message_handler.wait_for_message( LockExpired, {'secrethash': transfer_1_secrethash}, ) alice_app.raiden.start_mediated_transfer_with_secret( token_network_identifier, alice_to_bob_amount, target, identifier, transfer_1_secret, ) transfer1_received.wait() 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 remove_expired_lock_received.wait() 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 hold_event_handler.hold_secretrequest_for(secrethash=transfer_2_secrethash) alice_app.raiden.start_mediated_transfer_with_secret( token_network_identifier, alice_to_bob_amount, target, identifier, transfer_2_secret, ) transfer2_received.wait() # 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_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_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_lock_expiry( raiden_network: List[App], token_addresses: List[TokenAddress], deposit: TokenAmount ) -> None: """Test lock expiry and removal.""" alice_app, bob_app = raiden_network token_address = token_addresses[0] token_network_address = views.get_token_network_address_by_token_address( views.state_from_app(alice_app), alice_app.raiden.default_registry.address, token_address ) assert token_network_address hold_event_handler = bob_app.raiden.raiden_event_handler wait_message_handler = bob_app.raiden.message_handler msg = "hold event handler necessary to control messages" assert isinstance(hold_event_handler, HoldRaidenEventHandler), msg assert isinstance(wait_message_handler, WaitForMessage), msg token_network = views.get_token_network_by_address( views.state_from_app(alice_app), token_network_address ) assert token_network channel_state = get_channelstate(alice_app, bob_app, token_network_address) channel_identifier = channel_state.identifier assert ( channel_identifier in token_network.partneraddresses_to_channelidentifiers[bob_app.raiden.address] ) alice_to_bob_amount = PaymentAmount(10) identifier = factories.make_payment_id() target = TargetAddress(bob_app.raiden.address) transfer_1_secret = factories.make_secret(0) transfer_1_secrethash = sha256_secrethash(transfer_1_secret) transfer_2_secret = factories.make_secret(1) transfer_2_secrethash = sha256_secrethash(transfer_2_secret) hold_event_handler.hold_secretrequest_for(secrethash=transfer_1_secrethash) transfer1_received = wait_message_handler.wait_for_message( LockedTransfer, {"lock": {"secrethash": transfer_1_secrethash}} ) transfer2_received = wait_message_handler.wait_for_message( LockedTransfer, {"lock": {"secrethash": transfer_2_secrethash}} ) remove_expired_lock_received = wait_message_handler.wait_for_message( LockExpired, {"secrethash": transfer_1_secrethash} ) alice_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=alice_to_bob_amount, target=target, identifier=identifier, secret=transfer_1_secret, ) transfer1_received.wait() alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_address) lock = channel.get_lock(alice_bob_channel_state.our_state, transfer_1_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, Balance(deposit), [lock], bob_app, Balance(deposit), [] ) # Verify lock is registered in both channel states alice_channel_state = get_channelstate(alice_app, bob_app, token_network_address) assert transfer_1_secrethash in alice_channel_state.our_state.secrethashes_to_lockedlocks bob_channel_state = get_channelstate(bob_app, alice_app, token_network_address) 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 remove_expired_lock_received.wait() alice_channel_state = get_channelstate(alice_app, bob_app, token_network_address) 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_address) 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 = PaymentAmount(10) identifier = factories.make_payment_id() hold_event_handler.hold_secretrequest_for(secrethash=transfer_2_secrethash) alice_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=alice_to_bob_amount, target=target, identifier=identifier, secret=transfer_2_secret, ) transfer2_received.wait() # 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_address) assert transfer_2_secrethash in bob_channel_state.partner_state.secrethashes_to_lockedlocks
def test_regression_send_refund(): """Regression test for discarded refund transfer. The handle_refundtransfer used to discard events from the channel state machine, which led to the state being updated but the message to the partner was never sent. Also, for issue: https://github.com/raiden-network/raiden/issues/3170 It was noticed that when receiving the same refund transfer twice, the mediator would detect an invalid refund and clear the mediator state. So the test also checks that mediator rejects the duplicate transfer and keeps the mediator state unchanged. """ pseudo_random_generator = random.Random() setup = factories.make_transfers_pair(3) mediator_state = MediatorTransferState( secrethash=UNIT_SECRETHASH, routes=setup.channels.get_routes(), ) mediator_state.transfers_pair = setup.transfers_pair last_pair = setup.transfers_pair[-1] channel_identifier = last_pair.payee_transfer.balance_proof.channel_identifier lock_expiration = last_pair.payee_transfer.lock.expiration received_transfer = factories.make_signed_transfer_state( amount=UNIT_TRANSFER_AMOUNT, initiator=UNIT_TRANSFER_INITIATOR, target=UNIT_TRANSFER_TARGET, expiration=lock_expiration, secret=UNIT_SECRET, payment_identifier=UNIT_TRANSFER_IDENTIFIER, channel_identifier=channel_identifier, pkey=setup.channels.partner_privatekeys[2], sender=setup.channels.partner_address(2), ) # All three channels have been used routes = [] refund_state_change = ReceiveTransferRefund( transfer=received_transfer, routes=routes, ) iteration = mediator.handle_refundtransfer( mediator_state=mediator_state, mediator_state_change=refund_state_change, channelidentifiers_to_channels=setup.channel_map, nodeaddresses_to_networkstates=setup.channels. nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) first_pair = setup.transfers_pair[0] first_payer_transfer = first_pair.payer_transfer payer_channel = mediator.get_payer_channel(setup.channel_map, first_pair) lock = channel.get_lock( end_state=payer_channel.partner_state, secrethash=UNIT_SECRETHASH, ) token_network_identifier = first_payer_transfer.balance_proof.token_network_identifier assert search_for_item( iteration.events, SendRefundTransfer, { 'recipient': setup.channels.partner_address(0), 'queue_identifier': { 'recipient': setup.channels.partner_address(0), 'channel_identifier': first_payer_transfer.balance_proof.channel_identifier, }, 'transfer': { 'payment_identifier': UNIT_TRANSFER_IDENTIFIER, 'token': UNIT_TOKEN_ADDRESS, 'balance_proof': { 'transferred_amount': 0, 'locked_amount': 10, 'locksroot': lock.lockhash, 'token_network_identifier': token_network_identifier, 'channel_identifier': first_payer_transfer.balance_proof.channel_identifier, 'chain_id': first_payer_transfer.balance_proof.chain_id, }, 'lock': { 'amount': lock.amount, 'expiration': lock.expiration, 'secrethash': lock.secrethash, }, 'initiator': UNIT_TRANSFER_INITIATOR, 'target': UNIT_TRANSFER_TARGET, }, }) duplicate_iteration = mediator.handle_refundtransfer( mediator_state=iteration.new_state, mediator_state_change=refund_state_change, channelidentifiers_to_channels=setup.channel_map, nodeaddresses_to_networkstates=setup.channels. nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) assert search_for_item(duplicate_iteration.events, SendRefundTransfer, {}) is None assert duplicate_iteration.new_state is not None assert duplicate_iteration.new_state == iteration.new_state
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 run_test_lock_expiry(raiden_network, token_addresses, deposit): 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 ) hold_event_handler = bob_app.raiden.raiden_event_handler wait_message_handler = bob_app.raiden.message_handler 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_channelidentifiers[bob_app.raiden.address] ) alice_to_bob_amount = 10 identifier = 1 target = bob_app.raiden.address transfer_1_secret = factories.make_secret(0) transfer_1_secrethash = sha3(transfer_1_secret) transfer_2_secret = factories.make_secret(1) transfer_2_secrethash = sha3(transfer_2_secret) hold_event_handler.hold_secretrequest_for(secrethash=transfer_1_secrethash) transfer1_received = wait_message_handler.wait_for_message( LockedTransfer, {"lock": {"secrethash": transfer_1_secrethash}} ) transfer2_received = wait_message_handler.wait_for_message( LockedTransfer, {"lock": {"secrethash": transfer_2_secrethash}} ) remove_expired_lock_received = wait_message_handler.wait_for_message( LockExpired, {"secrethash": transfer_1_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, payment_hash_invoice=EMPTY_PAYMENT_HASH_INVOICE, secret=transfer_1_secret, ) transfer1_received.wait() 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 remove_expired_lock_received.wait() 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 hold_event_handler.hold_secretrequest_for(secrethash=transfer_2_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, payment_hash_invoice=EMPTY_PAYMENT_HASH_INVOICE, secret=transfer_2_secret, ) transfer2_received.wait() # 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_regression_send_refund(): """Regression test for discarded refund transfer. The handle_refundtransfer used to discard events from the channel state machine, which led to the state being updated but the message to the partner was never sent. Also, for issue: https://github.com/raiden-network/raiden/issues/3170 It was noticed that when receiving the same refund transfer twice, the mediator would detect an invalid refund and clear the mediator state. So the test also checks that mediator rejects the duplicate transfer and keeps the mediator state unchanged. """ pseudo_random_generator = random.Random() setup = factories.make_transfers_pair(3) mediator_state = MediatorTransferState(UNIT_SECRETHASH) mediator_state.transfers_pair = setup.transfers_pair last_pair = setup.transfers_pair[-1] channel_identifier = last_pair.payee_transfer.balance_proof.channel_identifier lock_expiration = last_pair.payee_transfer.lock.expiration received_transfer = factories.make_signed_transfer( amount=UNIT_TRANSFER_AMOUNT, initiator=UNIT_TRANSFER_INITIATOR, target=UNIT_TRANSFER_TARGET, expiration=lock_expiration, secret=UNIT_SECRET, payment_identifier=UNIT_TRANSFER_IDENTIFIER, channel_identifier=channel_identifier, pkey=setup.channels.partner_privatekeys[2], sender=setup.channels.partner_address(2), ) # All three channels have been used routes = [] refund_state_change = ReceiveTransferRefund( transfer=received_transfer, routes=routes, ) iteration = mediator.handle_refundtransfer( mediator_state=mediator_state, mediator_state_change=refund_state_change, channelidentifiers_to_channels=setup.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) first_pair = setup.transfers_pair[0] first_payer_transfer = first_pair.payer_transfer payer_channel = mediator.get_payer_channel(setup.channel_map, first_pair) lock = channel.get_lock( end_state=payer_channel.partner_state, secrethash=UNIT_SECRETHASH, ) token_network_identifier = first_payer_transfer.balance_proof.token_network_identifier assert must_contain_entry(iteration.events, SendRefundTransfer, { 'recipient': setup.channels.partner_address(0), 'queue_identifier': { 'recipient': setup.channels.partner_address(0), 'channel_identifier': first_payer_transfer.balance_proof.channel_identifier, }, 'transfer': { 'payment_identifier': UNIT_TRANSFER_IDENTIFIER, 'token': UNIT_TOKEN_ADDRESS, 'balance_proof': { 'transferred_amount': 0, 'locked_amount': 10, 'locksroot': lock.lockhash, 'token_network_identifier': token_network_identifier, 'channel_identifier': first_payer_transfer.balance_proof.channel_identifier, 'chain_id': first_payer_transfer.balance_proof.chain_id, }, 'lock': { 'amount': lock.amount, 'expiration': lock.expiration, 'secrethash': lock.secrethash, }, 'initiator': UNIT_TRANSFER_INITIATOR, 'target': UNIT_TRANSFER_TARGET, }, }) duplicate_iteration = mediator.handle_refundtransfer( mediator_state=iteration.new_state, mediator_state_change=refund_state_change, channelidentifiers_to_channels=setup.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) assert must_contain_entry(duplicate_iteration.events, SendRefundTransfer, {}) is None assert duplicate_iteration.new_state is not None assert duplicate_iteration.new_state == iteration.new_state
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 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_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_batch_unlock_after_restart(raiden_network, token_addresses, deposit): """Simulate the case where: - A sends B a transfer - B sends A a transfer - Secrets were never revealed - B closes channel - A crashes - Wait for settle - Wait for unlock from B - Restart A At this point, the current unlock logic will try to unlock iff the node gains from unlocking. Which means that the node will try to unlock either side. In the above scenario, each node will unlock its side. This test makes sure that we do NOT invalidate A's unlock transaction based on the ContractReceiveChannelBatchUnlock caused by B's unlock. """ alice_app, bob_app = raiden_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( chain_state=views.state_from_app(alice_app), token_network_registry_address=alice_app.raiden.default_registry.address, token_address=token_address, ) assert token_network_address timeout = 10 token_network = views.get_token_network_by_address( chain_state=views.state_from_app(alice_app), token_network_address=token_network_address ) assert token_network channel_identifier = get_channelstate(alice_app, bob_app, token_network_address).identifier assert ( channel_identifier in token_network.partneraddresses_to_channelidentifiers[bob_app.raiden.address] ) alice_to_bob_amount = 10 identifier = 1 alice_transfer_secret = Secret(sha3(alice_app.raiden.address)) alice_transfer_secrethash = sha256_secrethash(alice_transfer_secret) bob_transfer_secret = Secret(sha3(bob_app.raiden.address)) bob_transfer_secrethash = sha256_secrethash(bob_transfer_secret) alice_transfer_hold = bob_app.raiden.raiden_event_handler.hold_secretrequest_for( secrethash=alice_transfer_secrethash ) bob_transfer_hold = alice_app.raiden.raiden_event_handler.hold_secretrequest_for( secrethash=bob_transfer_secrethash ) alice_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=alice_to_bob_amount, target=bob_app.raiden.address, identifier=identifier, secret=alice_transfer_secret, ) bob_app.raiden.start_mediated_transfer_with_secret( token_network_address=token_network_address, amount=alice_to_bob_amount, target=alice_app.raiden.address, identifier=identifier + 1, secret=bob_transfer_secret, ) alice_transfer_hold.wait(timeout=timeout) bob_transfer_hold.wait(timeout=timeout) alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_address) alice_lock = channel.get_lock(alice_bob_channel_state.our_state, alice_transfer_secrethash) bob_lock = channel.get_lock(alice_bob_channel_state.partner_state, bob_transfer_secrethash) assert alice_lock assert bob_lock # This is the current state of protocol: # # A -> B LockedTransfer # - protocol didn't continue assert_synced_channel_state( token_network_address=token_network_address, app0=alice_app, balance0=deposit, pending_locks0=[alice_lock], app1=bob_app, balance1=deposit, pending_locks1=[bob_lock], ) # 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=registry_address, token_address=token_address, partner_address=alice_app.raiden.address, ) # wait for the close transaction to be mined, this is necessary to compute # the timeout for the settle with gevent.Timeout(timeout): waiting.wait_for_close( raiden=alice_app.raiden, token_network_registry_address=registry_address, token_address=token_address, channel_ids=[alice_bob_channel_state.identifier], retry_timeout=alice_app.raiden.alarm.sleep_time, ) channel_closed = raiden_state_changes_search_for_item( bob_app.raiden, ContractReceiveChannelClosed, { "canonical_identifier": { "token_network_address": token_network_address, "channel_identifier": alice_bob_channel_state.identifier, } }, ) assert isinstance(channel_closed, ContractReceiveChannelClosed) settle_max_wait_block = BlockNumber( channel_closed.block_number + alice_bob_channel_state.settle_timeout * 2 ) settle_timeout = BlockTimeout( RuntimeError("settle did not happen"), bob_app.raiden, settle_max_wait_block, alice_app.raiden.alarm.sleep_time, ) with settle_timeout: waiting.wait_for_settle( raiden=alice_app.raiden, token_network_registry_address=registry_address, token_address=token_address, channel_ids=[alice_bob_channel_state.identifier], retry_timeout=alice_app.raiden.alarm.sleep_time, ) with gevent.Timeout(timeout): wait_for_batch_unlock( app=bob_app, token_network_address=token_network_address, receiver=alice_bob_channel_state.partner_state.address, sender=alice_bob_channel_state.our_state.address, ) alice_app.start() with gevent.Timeout(timeout): wait_for_batch_unlock( app=alice_app, token_network_address=token_network_address, receiver=alice_bob_channel_state.partner_state.address, sender=alice_bob_channel_state.our_state.address, )
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_regression_send_refund(): """Regression test for discarded refund transfer. The handle_refundtransfer used to discard events from the channel state machine, which led to the state being updated but the message to the partner was never sent. """ pseudo_random_generator = random.Random() setup = factories.make_transfers_pair(3) mediator_state = MediatorTransferState(UNIT_SECRETHASH) mediator_state.transfers_pair = setup.transfers_pair last_pair = setup.transfers_pair[-1] channel_identifier = last_pair.payee_transfer.balance_proof.channel_identifier lock_expiration = last_pair.payee_transfer.lock.expiration received_transfer = factories.make_signed_transfer( amount=UNIT_TRANSFER_AMOUNT, initiator=UNIT_TRANSFER_INITIATOR, target=UNIT_TRANSFER_TARGET, expiration=lock_expiration, secret=UNIT_SECRET, payment_identifier=UNIT_TRANSFER_IDENTIFIER, channel_identifier=channel_identifier, pkey=setup.channels.partner_privatekeys[2], sender=setup.channels.partner_address(2), ) # All three channels have been used routes = [] refund_state_change = ReceiveTransferRefund( transfer=received_transfer, routes=routes, ) iteration = mediator.handle_refundtransfer( mediator_state=mediator_state, mediator_state_change=refund_state_change, channelidentifiers_to_channels=setup.channel_map, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) first_pair = setup.transfers_pair[0] first_payer_transfer = first_pair.payer_transfer payer_channel = mediator.get_payer_channel(setup.channel_map, first_pair) lock = channel.get_lock( end_state=payer_channel.partner_state, secrethash=UNIT_SECRETHASH, ) token_network_identifier = first_payer_transfer.balance_proof.token_network_identifier assert must_contain_entry( iteration.events, SendRefundTransfer, { 'recipient': setup.channels.partner_address(0), 'queue_identifier': { 'recipient': setup.channels.partner_address(0), 'channel_identifier': first_payer_transfer.balance_proof.channel_identifier, }, 'transfer': { 'payment_identifier': UNIT_TRANSFER_IDENTIFIER, 'token': UNIT_TOKEN_ADDRESS, 'balance_proof': { 'transferred_amount': 0, 'locked_amount': 10, 'locksroot': lock.lockhash, 'token_network_identifier': token_network_identifier, 'channel_identifier': first_payer_transfer.balance_proof.channel_identifier, 'chain_id': first_payer_transfer.balance_proof.chain_id, }, 'lock': { 'amount': lock.amount, 'expiration': lock.expiration, 'secrethash': lock.secrethash, }, 'initiator': UNIT_TRANSFER_INITIATOR, 'target': UNIT_TRANSFER_TARGET, }, })
def handle_secretrequest( initiator_state: InitiatorTransferState, state_change: ReceiveSecretRequest, channel_state: NettingChannelState, pseudo_random_generator: random.Random, ) -> TransitionResult[InitiatorTransferState]: is_message_from_target = ( state_change.sender == initiator_state.transfer_description.target and state_change.secrethash == initiator_state.transfer_description.secrethash and state_change.payment_identifier == initiator_state.transfer_description.payment_identifier) lock = channel.get_lock(channel_state.our_state, initiator_state.transfer_description.secrethash) # This should not ever happen. This task clears itself when the lock is # removed. assert lock is not None, "channel is does not have the transfer's lock" already_received_secret_request = initiator_state.received_secret_request # transfer_description.amount is the actual payment amount without fees. # For the transfer to be valid and the unlock allowed the target must # receive at least that amount. is_valid_secretrequest = ( state_change.amount >= initiator_state.transfer_description.amount and state_change.expiration == lock.expiration and initiator_state.transfer_description.secret != ABSENT_SECRET) if already_received_secret_request and is_message_from_target: # A secret request was received earlier, all subsequent are ignored # as it might be an attack iteration = TransitionResult(initiator_state, list()) elif is_valid_secretrequest and is_message_from_target: # Reveal the secret to the target node and wait for its confirmation. # At this point the transfer is not cancellable anymore as either the lock # timeouts or a secret reveal is received. # # Note: The target might be the first hop # message_identifier = message_identifier_from_prng( pseudo_random_generator) transfer_description = initiator_state.transfer_description recipient = transfer_description.target revealsecret = SendSecretReveal( recipient=Address(recipient), message_identifier=message_identifier, secret=transfer_description.secret, canonical_identifier=CANONICAL_IDENTIFIER_GLOBAL_QUEUE, ) initiator_state.transfer_state = "transfer_secret_revealed" initiator_state.received_secret_request = True iteration = TransitionResult(initiator_state, [revealsecret]) elif not is_valid_secretrequest and is_message_from_target: initiator_state.received_secret_request = True invalid_request = EventInvalidSecretRequest( payment_identifier=state_change.payment_identifier, intended_amount=initiator_state.transfer_description.amount, actual_amount=state_change.amount, ) iteration = TransitionResult(initiator_state, [invalid_request]) else: iteration = TransitionResult(initiator_state, list()) return iteration
def test_regression_send_refund(): """Regression test for discarded refund transfer. The handle_refundtransfer used to discard events from the channel state machine, which led to the state being updated but the message to the partner was never sent. Also, for issue: https://github.com/raiden-network/raiden/issues/3170 It was noticed that when receiving the same refund transfer twice, the mediator would detect an invalid refund and clear the mediator state. So the test also checks that mediator rejects the duplicate transfer and keeps the mediator state unchanged. """ pseudo_random_generator = random.Random() setup = factories.make_transfers_pair(3) mediator_state = MediatorTransferState(secrethash=UNIT_SECRETHASH, routes=[]) mediator_state.transfers_pair = setup.transfers_pair last_pair = setup.transfers_pair[-1] canonical_identifier = last_pair.payee_transfer.balance_proof.canonical_identifier lock_expiration = last_pair.payee_transfer.lock.expiration received_transfer = factories.create( factories.LockedTransferSignedStateProperties( expiration=lock_expiration, payment_identifier=UNIT_TRANSFER_IDENTIFIER, canonical_identifier=canonical_identifier, sender=setup.channels.partner_address(2), pkey=setup.channels.partner_privatekeys[2], message_identifier=factories.make_message_identifier(), )) # All three channels have been used refund_state_change = ReceiveTransferRefund( transfer=received_transfer, balance_proof=received_transfer.balance_proof, sender=received_transfer.balance_proof.sender, # pylint: disable=no-member ) iteration = mediator.handle_refundtransfer( mediator_state=mediator_state, mediator_state_change=refund_state_change, channelidentifiers_to_channels=setup.channel_map, nodeaddresses_to_networkstates=setup.channels. nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) first_pair = setup.transfers_pair[0] first_payer_transfer = first_pair.payer_transfer payer_channel = mediator.get_payer_channel(setup.channel_map, first_pair) lock = channel.get_lock(end_state=payer_channel.partner_state, secrethash=UNIT_SECRETHASH) token_network_address = first_payer_transfer.balance_proof.token_network_address assert search_for_item( iteration.events, SendRefundTransfer, { "recipient": setup.channels.partner_address(0), "queue_identifier": { "recipient": setup.channels.partner_address(0), "canonical_identifier": { "chain_identifier": first_payer_transfer.balance_proof.chain_id, "token_network_address": token_network_address, "channel_identifier": first_payer_transfer.balance_proof.channel_identifier, }, }, "transfer": { "payment_identifier": UNIT_TRANSFER_IDENTIFIER, "token": UNIT_TOKEN_ADDRESS, "balance_proof": { "transferred_amount": 0, "locked_amount": UNIT_TRANSFER_AMOUNT, "locksroot": keccak(lock.encoded), "token_network_address": token_network_address, "channel_identifier": first_payer_transfer.balance_proof.channel_identifier, "chain_id": first_payer_transfer.balance_proof.chain_id, }, "lock": { "amount": lock.amount, "expiration": lock.expiration, "secrethash": lock.secrethash, }, "initiator": UNIT_TRANSFER_INITIATOR, "target": UNIT_TRANSFER_TARGET, }, }, ) duplicate_iteration = mediator.handle_refundtransfer( mediator_state=iteration.new_state, mediator_state_change=refund_state_change, channelidentifiers_to_channels=setup.channel_map, nodeaddresses_to_networkstates=setup.channels. nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=setup.block_number, ) assert search_for_item(duplicate_iteration.events, SendRefundTransfer, {}) is None assert duplicate_iteration.new_state is not None assert duplicate_iteration.new_state == iteration.new_state
def run_test_batch_unlock_after_restart(raiden_network, token_addresses, deposit): """Simulate the case where: - A sends B a transfer - B sends A a transfer - Secrets were never revealed - B closes channel - A crashes - Wait for settle - Wait for unlock from B - Restart A At this point, the current unlock logic will try to unlock iff the node gains from unlocking. Which means that the node will try to unlock either side. In the above scenario, each node will unlock its side. This test makes sure that we do NOT invalidate A's unlock transaction based on the ContractReceiveChannelBatchUnlock caused by B's unlock. """ alice_app, bob_app = raiden_network registry_address = alice_app.raiden.default_registry.address token_address = token_addresses[0] token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state=views.state_from_app(alice_app), payment_network_id=alice_app.raiden.default_registry.address, token_address=token_address, ) hold_event_handler = HoldOffChainSecretRequest() bob_app.raiden.raiden_event_handler = hold_event_handler alice_app.raiden.raiden_event_handler = hold_event_handler token_network = views.get_token_network_by_identifier( chain_state=views.state_from_app(alice_app), token_network_id=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_to_bob_amount = 10 identifier = 1 alice_transfer_secret = sha3(alice_app.raiden.address) alice_transfer_secrethash = sha3(alice_transfer_secret) bob_transfer_secret = sha3(bob_app.raiden.address) bob_transfer_secrethash = sha3(bob_transfer_secret) alice_transfer_hold = hold_event_handler.hold_secretrequest_for( secrethash=alice_transfer_secrethash) bob_transfer_hold = hold_event_handler.hold_secretrequest_for( secrethash=bob_transfer_secrethash) alice_app.raiden.start_mediated_transfer_with_secret( token_network_identifier=token_network_identifier, amount=alice_to_bob_amount, fee=0, target=bob_app.raiden.address, identifier=identifier, secret=alice_transfer_secret, ) bob_app.raiden.start_mediated_transfer_with_secret( token_network_identifier=token_network_identifier, amount=alice_to_bob_amount, fee=0, target=alice_app.raiden.address, identifier=identifier + 1, secret=bob_transfer_secret, ) alice_transfer_hold.wait() bob_transfer_hold.wait() alice_bob_channel_state = get_channelstate(alice_app, bob_app, token_network_identifier) alice_lock = channel.get_lock(alice_bob_channel_state.our_state, alice_transfer_secrethash) bob_lock = channel.get_lock(alice_bob_channel_state.partner_state, bob_transfer_secrethash) # This is the current state of protocol: # # A -> B LockedTransfer # - protocol didn't continue assert_synced_channel_state( token_network_identifier=token_network_identifier, app0=alice_app, balance0=deposit, pending_locks0=[alice_lock], app1=bob_app, balance1=deposit, pending_locks1=[bob_lock], ) # 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=registry_address, token_address=token_address, partner_address=alice_app.raiden.address, ) alice_app.stop() waiting.wait_for_settle( raiden=alice_app.raiden, payment_network_id=registry_address, token_address=token_address, channel_ids=[alice_bob_channel_state.identifier], retry_timeout=alice_app.raiden.alarm.sleep_time, ) # wait for the node to call batch unlock timeout = 10 with gevent.Timeout(timeout): wait_for_batch_unlock( app=bob_app, token_network_id=token_network_identifier, participant=alice_bob_channel_state.partner_state.address, partner=alice_bob_channel_state.our_state.address, ) alice_app.start() with gevent.Timeout(timeout): wait_for_batch_unlock( app=alice_app, token_network_id=token_network_identifier, participant=alice_bob_channel_state.partner_state.address, partner=alice_bob_channel_state.our_state.address, )
def test_state_wait_secretrequest_invalid_amount_and_sender(): amount = UNIT_TRANSFER_AMOUNT block_number = 1 pseudo_random_generator = random.Random() channel1 = factories.make_channel( our_balance=amount, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channel_map = {channel1.identifier: channel1} available_routes = [factories.route_from_channel(channel1)] current_state = make_initiator_manager_state( available_routes, factories.UNIT_TRANSFER_DESCRIPTION, channel_map, pseudo_random_generator, block_number, ) lock = channel.get_lock( channel1.our_state, current_state.initiator.transfer_description.secrethash, ) state_change = ReceiveSecretRequest( UNIT_TRANSFER_IDENTIFIER, lock.amount + 1, lock.expiration, lock.secrethash, UNIT_TRANSFER_INITIATOR, ) iteration = initiator_manager.state_transition( current_state, state_change, channel_map, pseudo_random_generator, block_number, ) assert len(iteration.events) == 0 assert iteration.new_state.initiator.received_secret_request is False # Now the proper target sends the message, this should be applied state_change_2 = ReceiveSecretRequest( UNIT_TRANSFER_IDENTIFIER, lock.amount, lock.expiration, lock.secrethash, UNIT_TRANSFER_TARGET, ) iteration2 = initiator_manager.state_transition( iteration.new_state, state_change_2, channel_map, pseudo_random_generator, block_number, ) assert iteration2.new_state.initiator.received_secret_request is True assert isinstance(iteration2.events[0], SendSecretReveal)