def test_lock_expiry_updates_balance_proof(): setup = setup_initiator_tests(amount=UNIT_TRANSFER_AMOUNT * 2, block_number=10) transfer = setup.current_state.initiator.transfer assert transfer.lock.secrethash in setup.channel.our_state.secrethashes_to_lockedlocks nonce_before_lock_expiry = setup.channel.our_state.balance_proof.nonce # Trigger lock expiry state_change = Block( block_number=channel.get_sender_expiration_threshold(transfer.lock), gas_limit=1, block_hash=factories.make_transaction_hash(), ) initiator_manager.state_transition( setup.current_state, state_change, setup.channel_map, setup.prng, setup.block_number, ) balance_proof = setup.channel.our_state.balance_proof assert balance_proof.nonce == nonce_before_lock_expiry + 1 assert balance_proof.transferred_amount == 0 assert balance_proof.locked_amount == 0
def test_init_with_maximum_pending_transfers_exceeded(): channel1 = factories.make_channel( our_balance=2 * MAXIMUM_PENDING_TRANSFERS * UNIT_TRANSFER_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)] pseudo_random_generator = random.Random() transitions = list() block_number = 1 for _ in range(MAXIMUM_PENDING_TRANSFERS + 1): init_state_change = ActionInitInitiator(make_transfer_description(), available_routes) transitions.append(initiator_manager.state_transition( None, init_state_change, channel_map, pseudo_random_generator, block_number, )) failed_transition = transitions.pop() assert all( isinstance(transition.new_state, InitiatorPaymentState) for transition in transitions ) assert failed_transition.new_state is None assert len(failed_transition.events) == 1 assert isinstance(failed_transition.events[0], EventPaymentSentFailed)
def test_state_wait_unlock_invalid(): setup = setup_initiator_tests() identifier = setup.channel.identifier target_address = factories.HOP2 # setup the state for the wait unlock setup.current_state.initiator.revealsecret = SendSecretReveal( recipient=target_address, channel_identifier=CHANNEL_IDENTIFIER_GLOBAL_QUEUE, message_identifier=identifier, secret=UNIT_SECRET, ) before_state = deepcopy(setup.current_state) state_change = ReceiveSecretReveal( secret=UNIT_SECRET, sender=factories.ADDR, ) iteration = initiator_manager.state_transition( setup.current_state, state_change, setup.channel_map, setup.prng, setup.block_number, ) assert not iteration.events assert iteration.new_state == before_state
def test_state_wait_unlock_valid(): setup = setup_initiator_tests() # setup the state for the wait unlock setup.current_state.initiator.revealsecret = SendSecretReveal( recipient=UNIT_TRANSFER_TARGET, channel_identifier=CHANNEL_IDENTIFIER_GLOBAL_QUEUE, message_identifier=UNIT_TRANSFER_IDENTIFIER, secret=UNIT_SECRET, ) state_change = ReceiveSecretReveal( secret=UNIT_SECRET, sender=setup.channel.partner_state.address, ) iteration = initiator_manager.state_transition( setup.current_state, state_change, setup.channel_map, setup.prng, setup.block_number, ) assert len(iteration.events) == 3 assert any(isinstance(e, SendBalanceProof) for e in iteration.events) assert any(isinstance(e, EventPaymentSentSuccess) for e in iteration.events) assert any(isinstance(e, EventUnlockSuccess) for e in iteration.events) balance_proof = next(e for e in iteration.events if isinstance(e, SendBalanceProof)) complete = next(e for e in iteration.events if isinstance(e, EventPaymentSentSuccess)) assert balance_proof.recipient == setup.channel.partner_state.address assert complete.identifier == UNIT_TRANSFER_IDENTIFIER assert iteration.new_state is None, 'state must be cleaned'
def test_invalid_cancelpayment(): """ A payment can *NOT* be cancelled if a secret for any transfer has been revealed. """ setup = setup_initiator_tests( amount=2 * MAXIMUM_PENDING_TRANSFERS * UNIT_TRANSFER_AMOUNT, ) receive_secret_request = ReceiveSecretRequest( UNIT_TRANSFER_IDENTIFIER, setup.lock.amount, setup.lock.expiration, setup.lock.secrethash, UNIT_TRANSFER_TARGET, ) secret_transition = initiator_manager.state_transition( payment_state=setup.current_state, state_change=receive_secret_request, channelidentifiers_to_channels=setup.channel_map, pseudo_random_generator=setup.prng, block_number=1, ) iteration = initiator_manager.handle_cancelpayment( payment_state=secret_transition.new_state, channel_state=setup.channel, ) msg = 'The secret *has* been revealed, the payment must not be cancelled' assert iteration.new_state is not None, msg assert not iteration.events, msg
def test_next_route(): amount = UNIT_TRANSFER_AMOUNT channel1 = factories.make_channel( our_balance=amount, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channel2 = factories.make_channel( our_balance=0, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channel3 = factories.make_channel( our_balance=amount, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) pseudo_random_generator = random.Random() channel_map = { channel1.identifier: channel1, channel2.identifier: channel2, channel3.identifier: channel3, } available_routes = [ factories.route_from_channel(channel1), factories.route_from_channel(channel2), factories.route_from_channel(channel3), ] block_number = 10 state = make_initiator_manager_state( available_routes, factories.UNIT_TRANSFER_DESCRIPTION, channel_map, pseudo_random_generator, block_number, ) msg = 'an initialized state must use the first valid route' assert state.initiator.channel_identifier == channel1.identifier, msg assert not state.cancelled_channels state_change = ActionCancelRoute( UNIT_REGISTRY_IDENTIFIER, channel1.identifier, available_routes, ) iteration = initiator_manager.state_transition( state, state_change, channel_map, pseudo_random_generator, block_number, ) # HOP3 should be ignored because it doesnt have enough balance assert iteration.new_state.cancelled_channels == [channel1.identifier]
def test_state_wait_secretrequest_invalid_amount_and_sender(): setup = setup_initiator_tests() state_change = ReceiveSecretRequest( UNIT_TRANSFER_IDENTIFIER, setup.lock.amount + 1, setup.lock.expiration, setup.lock.secrethash, UNIT_TRANSFER_INITIATOR, ) iteration = initiator_manager.state_transition( setup.current_state, state_change, setup.channel_map, setup.prng, setup.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, setup.lock.amount, setup.lock.expiration, setup.lock.secrethash, UNIT_TRANSFER_TARGET, ) iteration2 = initiator_manager.state_transition( iteration.new_state, state_change_2, setup.channel_map, setup.prng, setup.block_number, ) assert iteration2.new_state.initiator.received_secret_request is True assert isinstance(iteration2.events[0], SendSecretReveal)
def test_state_wait_secretrequest_invalid_amount(): setup = setup_initiator_tests() state_change = ReceiveSecretRequest( UNIT_TRANSFER_IDENTIFIER, setup.lock.amount + 1, setup.lock.expiration, setup.lock.secrethash, UNIT_TRANSFER_TARGET, ) iteration = initiator_manager.state_transition( setup.current_state, state_change, setup.channel_map, setup.prng, setup.block_number, ) assert len(iteration.events) == 1 assert isinstance(iteration.events[0], EventPaymentSentFailed) assert iteration.new_state.initiator.received_secret_request is True state_change_2 = ReceiveSecretRequest( UNIT_TRANSFER_IDENTIFIER, setup.lock.amount, setup.lock.expiration, setup.lock.secrethash, UNIT_TRANSFER_TARGET, ) iteration2 = initiator_manager.state_transition( iteration.new_state, state_change_2, setup.channel_map, setup.prng, setup.block_number, ) assert len(iteration2.events) == 0
def subdispatch_initiatortask( chain_state: ChainState, state_change: StateChange, token_network_identifier: TokenNetworkID, secrethash: SecretHash, ) -> TransitionResult: block_number = chain_state.block_number sub_task = chain_state.payment_mapping.secrethashes_to_task.get(secrethash) if not sub_task: is_valid_subtask = True manager_state = None elif sub_task and isinstance(sub_task, InitiatorTask): is_valid_subtask = ( token_network_identifier == sub_task.token_network_identifier ) manager_state = sub_task.manager_state else: is_valid_subtask = False events = list() if is_valid_subtask: pseudo_random_generator = chain_state.pseudo_random_generator token_network_state = views.get_token_network_by_identifier( chain_state, token_network_identifier, ) iteration = initiator_manager.state_transition( manager_state, state_change, token_network_state.channelidentifiers_to_channels, pseudo_random_generator, block_number, ) events = iteration.events if iteration.new_state: sub_task = InitiatorTask( token_network_identifier, iteration.new_state, ) chain_state.payment_mapping.secrethashes_to_task[secrethash] = sub_task elif secrethash in chain_state.payment_mapping.secrethashes_to_task: del chain_state.payment_mapping.secrethashes_to_task[secrethash] return TransitionResult(chain_state, events)
def test_refund_transfer_no_more_routes(): amount = UNIT_TRANSFER_AMOUNT refund_pkey, refund_address = factories.make_privkey_address() setup = setup_initiator_tests( amount=amount, partner_balance=amount, our_address=UNIT_TRANSFER_INITIATOR, partner_address=refund_address, ) original_transfer = setup.current_state.initiator.transfer refund_transfer = factories.make_signed_transfer( amount, original_transfer.initiator, original_transfer.target, original_transfer.lock.expiration, UNIT_SECRET, payment_identifier=original_transfer.payment_identifier, channel_identifier=setup.channel.identifier, pkey=refund_pkey, sender=refund_address, ) state_change = ReceiveTransferRefundCancelRoute( routes=setup.available_routes, transfer=refund_transfer, secret=random_secret(), ) iteration = initiator_manager.state_transition( setup.current_state, state_change, setup.channel_map, setup.prng, setup.block_number, ) assert iteration.new_state is None unlocked_failed = next(e for e in iteration.events if isinstance(e, EventUnlockFailed)) sent_failed = next(e for e in iteration.events if isinstance(e, EventPaymentSentFailed)) assert unlocked_failed assert sent_failed
def subdispatch_initiatortask(node_state, state_change, payment_network_identifier, token_network_identifier, hashlock): block_number = node_state.block_number sub_task = node_state.payment_mapping.hashlocks_to_task.get(hashlock) if not sub_task: is_valid_subtask = True manager_state = None elif sub_task and isinstance(sub_task, PaymentMappingState.InitiatorTask): is_valid_subtask = ( payment_network_identifier == sub_task.payment_network_identifier and token_network_identifier == sub_task.token_network_identifier) manager_state = sub_task.manager_state else: is_valid_subtask = False events = list() if is_valid_subtask: token_network_state = get_token_network( node_state, payment_network_identifier, token_network_identifier, ) iteration = initiator_manager.state_transition( manager_state, state_change, token_network_state.channelidentifiers_to_channels, block_number, ) events = iteration.events if iteration.new_state: sub_task = PaymentMappingState.InitiatorTask( payment_network_identifier, token_network_identifier, iteration.new_state, ) node_state.payment_mapping.hashlocks_to_task[hashlock] = sub_task return TransitionResult(node_state, events)
def test_state_wait_secretrequest_valid(): 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, lock.expiration, lock.secrethash, UNIT_TRANSFER_TARGET, ) iteration = initiator_manager.state_transition( current_state, state_change, channel_map, pseudo_random_generator, block_number, ) assert len(iteration.events) == 1 assert isinstance(iteration.events[0], SendSecretReveal)
def test_next_route(): amount = UNIT_TRANSFER_AMOUNT channel1 = factories.make_channel(our_balance=amount, token_address=UNIT_TOKEN_ADDRESS) channel2 = factories.make_channel(our_balance=0, token_address=UNIT_TOKEN_ADDRESS) channel3 = factories.make_channel(our_balance=amount, token_address=UNIT_TOKEN_ADDRESS) channelmap = { channel1.identifier: channel1, channel2.identifier: channel2, channel3.identifier: channel3, } available_routes = [ factories.route_from_channel(channel1), factories.route_from_channel(channel2), factories.route_from_channel(channel3), ] block_number = 10 state = make_initiator_state( available_routes, factories.UNIT_TRANSFER_DESCRIPTION, channelmap, block_number, ) msg = 'an initialized state must use the first valid route' assert state.initiator.channel_identifier == channel1.identifier, msg assert not state.cancelled_channels state_change = ActionCancelRoute( channel1.identifier, available_routes, ) iteration = initiator_manager.state_transition( state, state_change, channelmap, block_number, ) # HOP3 should be ignored because it doesnt have enough balance assert iteration.new_state.cancelled_channels == [channel1.identifier]
def test_init_with_usable_routes(): channel1 = factories.make_channel( our_balance=UNIT_TRANSFER_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)] pseudo_random_generator = random.Random() init_state_change = ActionInitInitiator( factories.UNIT_TRANSFER_DESCRIPTION, available_routes, ) block_number = 1 transition = initiator_manager.state_transition( None, init_state_change, channel_map, pseudo_random_generator, block_number, ) assert isinstance(transition.new_state, InitiatorPaymentState) assert transition.events, 'we have a valid route, the mediated transfer event must be emitted' payment_state = transition.new_state assert payment_state.initiator.transfer_description == factories.UNIT_TRANSFER_DESCRIPTION mediated_transfers = [e for e in transition.events if isinstance(e, SendLockedTransfer)] assert len(mediated_transfers) == 1, 'mediated_transfer should /not/ split the transfer' send_mediated_transfer = mediated_transfers[0] transfer = send_mediated_transfer.transfer expiration = initiator.get_initial_lock_expiration(block_number, channel1.reveal_timeout) assert transfer.balance_proof.token_network_identifier == channel1.token_network_identifier assert transfer.lock.amount == factories.UNIT_TRANSFER_DESCRIPTION.amount assert transfer.lock.expiration == expiration assert transfer.lock.secrethash == factories.UNIT_TRANSFER_DESCRIPTION.secrethash assert send_mediated_transfer.recipient == channel1.partner_state.address
def test_cancel_transfer(): amount = UNIT_TRANSFER_AMOUNT block_number = 1 pseudo_random_generator = random.Random() channel1 = factories.make_channel( our_balance=amount, our_address=UNIT_TRANSFER_INITIATOR, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channelmap = {channel1.identifier: channel1} available_routes = [factories.route_from_channel(channel1)] current_state = make_initiator_state( available_routes, factories.UNIT_TRANSFER_DESCRIPTION, channelmap, pseudo_random_generator, block_number, ) state_change = ActionCancelPayment( payment_identifier=UNIT_TRANSFER_IDENTIFIER, ) iteration = initiator_manager.state_transition( current_state, state_change, channelmap, pseudo_random_generator, block_number, ) assert iteration.new_state is None assert len(iteration.events) == 2 unlocked_failed = next(e for e in iteration.events if isinstance(e, EventUnlockFailed)) sent_failed = next(e for e in iteration.events if isinstance(e, EventTransferSentFailed)) assert unlocked_failed assert sent_failed
def test_state_wait_unlock_invalid(): identifier = identifier = 1 block_number = 1 target_address = factories.HOP2 token = factories.UNIT_TOKEN_ADDRESS channel1 = factories.make_channel( our_balance=UNIT_TRANSFER_AMOUNT, token_address=UNIT_TOKEN_ADDRESS, ) channelmap = {channel1.identifier: channel1} available_routes = [factories.route_from_channel(channel1)] current_state = make_initiator_state( available_routes, factories.UNIT_TRANSFER_DESCRIPTION, channelmap, block_number, ) # setup the state for the wait unlock current_state.initiator.revealsecret = SendRevealSecret( identifier, UNIT_SECRET, token, target_address, ) before_state = deepcopy(current_state) state_change = ReceiveSecretReveal( secret=UNIT_SECRET, sender=factories.ADDR, ) iteration = initiator_manager.state_transition( current_state, state_change, channelmap, block_number, ) assert not iteration.events assert iteration.new_state == before_state
def test_init_with_usable_routes(): channel1 = factories.make_channel( our_balance=UNIT_TRANSFER_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)] pseudo_random_generator = random.Random() init_state_change = ActionInitInitiator( factories.UNIT_TRANSFER_DESCRIPTION, available_routes, ) block_number = 1 transition = initiator_manager.state_transition( None, init_state_change, channel_map, pseudo_random_generator, block_number, ) assert isinstance(transition.new_state, InitiatorPaymentState) assert transition.events, 'we have a valid route, the mediated transfer event must be emitted' payment_state = transition.new_state assert payment_state.initiator.transfer_description == factories.UNIT_TRANSFER_DESCRIPTION mediated_transfers = [e for e in transition.events if isinstance(e, SendLockedTransfer)] assert len(mediated_transfers) == 1, 'mediated_transfer should /not/ split the transfer' send_mediated_transfer = mediated_transfers[0] transfer = send_mediated_transfer.transfer expiration = initiator.get_initial_lock_expiration(block_number, channel1.reveal_timeout) assert transfer.balance_proof.token_network_identifier == channel1.token_network_identifier assert transfer.lock.amount == factories.UNIT_TRANSFER_DESCRIPTION.amount assert transfer.lock.expiration == expiration assert transfer.lock.secrethash == factories.UNIT_TRANSFER_DESCRIPTION.secrethash assert send_mediated_transfer.recipient == channel1.partner_state.address
def test_cancel_transfer(): setup = setup_initiator_tests() state_change = ActionCancelPayment( payment_identifier=UNIT_TRANSFER_IDENTIFIER, ) iteration = initiator_manager.state_transition( setup.current_state, state_change, setup.channel_map, setup.prng, setup.block_number, ) assert iteration.new_state is None assert len(iteration.events) == 2 unlocked_failed = next(e for e in iteration.events if isinstance(e, EventUnlockFailed)) sent_failed = next(e for e in iteration.events if isinstance(e, EventPaymentSentFailed)) assert unlocked_failed assert sent_failed
def test_cancel_transfer(): setup = setup_initiator_tests() state_change = ActionCancelPayment( payment_identifier=UNIT_TRANSFER_IDENTIFIER, ) iteration = initiator_manager.state_transition( setup.current_state, state_change, setup.channel_map, setup.prng, setup.block_number, ) assert iteration.new_state is None assert len(iteration.events) == 2 unlocked_failed = next(e for e in iteration.events if isinstance(e, EventUnlockFailed)) sent_failed = next(e for e in iteration.events if isinstance(e, EventPaymentSentFailed)) assert unlocked_failed assert sent_failed
def test_initiator_lock_expired_must_not_be_sent_if_channel_is_closed(): """ If the channel is closed there is no rason to send balance proofs off-chain, so a remove expired lock must not be sent when the channel is closed. """ block_number = 10 setup = setup_initiator_tests(amount=UNIT_TRANSFER_AMOUNT * 2, block_number=block_number) channel_closed = ContractReceiveChannelClosed( transaction_hash=factories.make_transaction_hash(), transaction_from=factories.make_address(), token_network_identifier=setup.channel.token_network_identifier, channel_identifier=setup.channel.identifier, block_number=block_number, ) channel_close_transition = channel.state_transition( channel_state=setup.channel, state_change=channel_closed, pseudo_random_generator=setup.prng, block_number=block_number, ) channel_state = channel_close_transition.new_state expiration_block_number = setup.lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2 block = Block( block_number=expiration_block_number, gas_limit=1, block_hash=factories.make_transaction_hash(), ) channel_map = {channel_state.identifier: channel_state} iteration = initiator_manager.state_transition( setup.current_state, block, channel_map, setup.prng, expiration_block_number, ) assert events.must_contain_entry(iteration.events, SendLockExpired, {}) is None
def subdispatch_initiatortask( chain_state: ChainState, state_change: StateChange, token_network_address: TokenNetworkAddress, secrethash: SecretHash, ) -> TransitionResult[ChainState]: token_network_state = get_token_network_by_address(chain_state, token_network_address) if not token_network_state: return TransitionResult(chain_state, []) sub_task = chain_state.payment_mapping.secrethashes_to_task.get(secrethash) if not sub_task: manager_state = None else: if ( not isinstance(sub_task, InitiatorTask) or token_network_address != sub_task.token_network_address ): return TransitionResult(chain_state, []) manager_state = sub_task.manager_state iteration = initiator_manager.state_transition( payment_state=manager_state, state_change=state_change, channelidentifiers_to_channels=token_network_state.channelidentifiers_to_channels, addresses_to_channel=chain_state.addresses_to_channel, nodeaddresses_to_networkstates=chain_state.nodeaddresses_to_networkstates, pseudo_random_generator=chain_state.pseudo_random_generator, block_number=chain_state.block_number, ) events: List[Event] = iteration.events if iteration.new_state: chain_state.payment_mapping.secrethashes_to_task[secrethash] = InitiatorTask( token_network_address, iteration.new_state ) elif secrethash in chain_state.payment_mapping.secrethashes_to_task: del chain_state.payment_mapping.secrethashes_to_task[secrethash] return TransitionResult(chain_state, events)
def test_initiator_lock_expired_must_not_be_sent_if_channel_is_closed(): """ If the channel is closed there is no rason to send balance proofs off-chain, so a remove expired lock must not be sent when the channel is closed. """ block_number = 10 setup = setup_initiator_tests(amount=UNIT_TRANSFER_AMOUNT * 2, block_number=block_number) channel_closed = ContractReceiveChannelClosed( transaction_hash=factories.make_transaction_hash(), transaction_from=factories.make_address(), token_network_identifier=setup.channel.token_network_identifier, channel_identifier=setup.channel.identifier, block_number=block_number, ) channel_close_transition = channel.state_transition( channel_state=setup.channel, state_change=channel_closed, pseudo_random_generator=setup.prng, block_number=block_number, ) channel_state = channel_close_transition.new_state expiration_block_number = channel.get_sender_expiration_threshold(setup.lock) block = Block( block_number=expiration_block_number, gas_limit=1, block_hash=factories.make_transaction_hash(), ) channel_map = {channel_state.identifier: channel_state} iteration = initiator_manager.state_transition( setup.current_state, block, channel_map, setup.prng, expiration_block_number, ) assert events.must_contain_entry(iteration.events, SendLockExpired, {}) is None
def make_initiator_manager_state( routes, transfer_description, channel_map, pseudo_random_generator, block_number, ): init_state_change = ActionInitInitiator( transfer_description, routes, ) inital_state = None iteration = initiator_manager.state_transition( inital_state, init_state_change, channel_map, pseudo_random_generator, block_number, ) return iteration.new_state
def test_init_without_routes(): block_number = 1 routes = [] payment_network_identifier = factories.make_address() init_state_change = ActionInitInitiator( payment_network_identifier, factories.UNIT_TRANSFER_DESCRIPTION, routes, ) channelmap = dict() iteration = initiator_manager.state_transition( None, init_state_change, channelmap, block_number, ) assert iteration.new_state is None assert len(iteration.events) == 1 assert isinstance(iteration.events[0], EventTransferSentFailed) assert iteration.new_state is None
def make_initiator_manager_state( routes, transfer_description, channel_map, pseudo_random_generator, block_number, ): init_state_change = ActionInitInitiator( transfer_description, routes, ) inital_state = None iteration = initiator_manager.state_transition( inital_state, init_state_change, channel_map, pseudo_random_generator, block_number, ) return iteration.new_state
def test_state_wait_unlock_valid(): setup = setup_initiator_tests() # setup the state for the wait unlock setup.current_state.initiator.revealsecret = SendSecretReveal( recipient=UNIT_TRANSFER_TARGET, channel_identifier=CHANNEL_IDENTIFIER_GLOBAL_QUEUE, message_identifier=UNIT_TRANSFER_IDENTIFIER, secret=UNIT_SECRET, ) state_change = ReceiveSecretReveal( secret=UNIT_SECRET, sender=setup.channel.partner_state.address, ) iteration = initiator_manager.state_transition( setup.current_state, state_change, setup.channel_map, setup.prng, setup.block_number, ) assert len(iteration.events) == 3 assert any(isinstance(e, SendBalanceProof) for e in iteration.events) assert any( isinstance(e, EventPaymentSentSuccess) for e in iteration.events) assert any(isinstance(e, EventUnlockSuccess) for e in iteration.events) balance_proof = next(e for e in iteration.events if isinstance(e, SendBalanceProof)) complete = next(e for e in iteration.events if isinstance(e, EventPaymentSentSuccess)) assert balance_proof.recipient == setup.channel.partner_state.address assert complete.identifier == UNIT_TRANSFER_IDENTIFIER assert iteration.new_state is None, 'state must be cleaned'
def test_init_without_routes(): block_number = 1 routes = [] pseudo_random_generator = random.Random() init_state_change = ActionInitInitiator( factories.UNIT_TRANSFER_DESCRIPTION, routes, ) channel_map = dict() iteration = initiator_manager.state_transition( None, init_state_change, channel_map, pseudo_random_generator, block_number, ) assert iteration.new_state is None assert len(iteration.events) == 1 assert isinstance(iteration.events[0], EventPaymentSentFailed) assert iteration.new_state is None
def make_initiator_state(routes, transfer_description, channelmap, block_number, payment_network_identifier=None): if payment_network_identifier is None: payment_network_identifier = factories.make_address() init_state_change = ActionInitInitiator( payment_network_identifier, transfer_description, routes, ) inital_state = None iteration = initiator_manager.state_transition( inital_state, init_state_change, channelmap, block_number, ) return iteration.new_state
def test_init_without_routes(): block_number = 1 routes = [] pseudo_random_generator = random.Random() init_state_change = ActionInitInitiator( factories.UNIT_TRANSFER_DESCRIPTION, routes, ) channelmap = dict() iteration = initiator_manager.state_transition( None, init_state_change, channelmap, pseudo_random_generator, block_number, ) assert iteration.new_state is None assert len(iteration.events) == 1 assert isinstance(iteration.events[0], EventTransferSentFailed) assert iteration.new_state is None
def subdispatch_to_paymenttask(node_state, state_change, secrethash): block_number = node_state.block_number sub_task = node_state.payment_mapping.secrethashes_to_task.get(secrethash) events = list() sub_iteration = None if sub_task: pseudo_random_generator = node_state.pseudo_random_generator if isinstance(sub_task, PaymentMappingState.InitiatorTask): token_network_identifier = sub_task.token_network_identifier token_network_state = views.get_token_network_by_identifier( node_state, token_network_identifier, ) if token_network_state: sub_iteration = initiator_manager.state_transition( sub_task.manager_state, state_change, token_network_state.channelidentifiers_to_channels, pseudo_random_generator, block_number, ) events = sub_iteration.events elif isinstance(sub_task, PaymentMappingState.MediatorTask): token_network_identifier = sub_task.token_network_identifier token_network_state = views.get_token_network_by_identifier( node_state, token_network_identifier, ) if token_network_state: sub_iteration = mediator.state_transition( sub_task.mediator_state, state_change, token_network_state.channelidentifiers_to_channels, pseudo_random_generator, block_number, ) events = sub_iteration.events elif isinstance(sub_task, PaymentMappingState.TargetTask): token_network_identifier = sub_task.token_network_identifier channel_identifier = sub_task.channel_identifier token_network_state = views.get_token_network_by_identifier( node_state, token_network_identifier, ) channel_state = views.get_channelstate_by_token_network_identifier( node_state, token_network_identifier, channel_identifier, ) if channel_state: sub_iteration = target.state_transition( sub_task.target_state, state_change, channel_state, pseudo_random_generator, block_number, ) events = sub_iteration.events if sub_iteration and sub_iteration.new_state is None: del node_state.payment_mapping.secrethashes_to_task[secrethash] return TransitionResult(node_state, events)
def test_initiator_lock_expired(): amount = UNIT_TRANSFER_AMOUNT * 2 block_number = 1 refund_pkey, refund_address = factories.make_privkey_address() pseudo_random_generator = random.Random() channel1 = factories.make_channel( our_balance=amount, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channel2 = factories.make_channel( our_balance=0, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) pseudo_random_generator = random.Random() channel_map = { channel1.identifier: channel1, channel2.identifier: channel2, } available_routes = [ factories.route_from_channel(channel1), factories.route_from_channel(channel2), ] block_number = 10 current_state = make_initiator_manager_state( available_routes, factories.UNIT_TRANSFER_DESCRIPTION, channel_map, pseudo_random_generator, block_number, ) transfer = current_state.initiator.transfer assert transfer.lock.secrethash in channel1.our_state.secrethashes_to_lockedlocks # Trigger lock expiry state_change = Block( block_number=transfer.lock.expiration + DEFAULT_NUMBER_OF_CONFIRMATIONS_BLOCK, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, channel_map, pseudo_random_generator, block_number, ) assert events.must_contain_entry(iteration.events, SendLockExpired, { 'balance_proof': { 'nonce': 2, 'transferred_amount': 0, 'locked_amount': 0, }, 'secrethash': transfer.lock.secrethash, 'recipient': channel1.partner_state.address, }) assert transfer.lock.secrethash not in channel1.our_state.secrethashes_to_lockedlocks # Create 2 other transfers transfer2_state = make_initiator_manager_state( available_routes, make_transfer_description('transfer2'), channel_map, pseudo_random_generator, 30, ) transfer2_lock = transfer2_state.initiator.transfer.lock transfer3_state = make_initiator_manager_state( available_routes, make_transfer_description('transfer3'), channel_map, pseudo_random_generator, 32, ) transfer3_lock = transfer3_state.initiator.transfer.lock assert len(channel1.our_state.secrethashes_to_lockedlocks) == 2 assert transfer2_lock.secrethash in channel1.our_state.secrethashes_to_lockedlocks expiration_block_number = transfer2_lock.expiration + DEFAULT_NUMBER_OF_CONFIRMATIONS_BLOCK block = Block( block_number=expiration_block_number, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( transfer2_state, block, channel_map, pseudo_random_generator, expiration_block_number, ) # Transfer 2 expired assert transfer2_lock.secrethash not in channel1.our_state.secrethashes_to_lockedlocks # Transfer 3 is still there assert transfer3_lock.secrethash in channel1.our_state.secrethashes_to_lockedlocks
def subdispatch_to_paymenttask( chain_state: ChainState, state_change: StateChange, secrethash: SecretHash, ) -> TransitionResult[ChainState]: block_number = chain_state.block_number sub_task = chain_state.payment_mapping.secrethashes_to_task.get(secrethash) events: List[Event] = list() sub_iteration = None if sub_task: pseudo_random_generator = chain_state.pseudo_random_generator if isinstance(sub_task, InitiatorTask): token_network_identifier = sub_task.token_network_identifier token_network_state = views.get_token_network_by_identifier( chain_state, token_network_identifier, ) if token_network_state: sub_iteration = initiator_manager.state_transition( sub_task.manager_state, state_change, token_network_state.channelidentifiers_to_channels, pseudo_random_generator, block_number, ) events = sub_iteration.events elif isinstance(sub_task, MediatorTask): token_network_identifier = sub_task.token_network_identifier token_network_state = views.get_token_network_by_identifier( chain_state, token_network_identifier, ) if token_network_state: sub_iteration = mediator.state_transition( sub_task.mediator_state, state_change, token_network_state.channelidentifiers_to_channels, chain_state.nodeaddresses_to_networkstates, pseudo_random_generator, block_number, ) events = sub_iteration.events elif isinstance(sub_task, TargetTask): token_network_identifier = sub_task.token_network_identifier channel_identifier = sub_task.channel_identifier channel_state = views.get_channelstate_by_token_network_identifier( chain_state, token_network_identifier, channel_identifier, ) if channel_state: sub_iteration = target.state_transition( sub_task.target_state, state_change, channel_state, pseudo_random_generator, block_number, ) events = sub_iteration.events if sub_iteration and sub_iteration.new_state is None: del chain_state.payment_mapping.secrethashes_to_task[secrethash] return TransitionResult(chain_state, events)
def subdispatch_to_paymenttask(node_state, state_change, secrethash): block_number = node_state.block_number sub_task = node_state.payment_mapping.secrethashes_to_task.get(secrethash) events = list() if sub_task: if isinstance(sub_task, PaymentMappingState.InitiatorTask): payment_network_identifier = sub_task.payment_network_identifier token_address = sub_task.token_address token_network_state = get_token_network( node_state, payment_network_identifier, token_address, ) if token_network_state: sub_iteration = initiator_manager.state_transition( sub_task.manager_state, state_change, token_network_state.channelidentifiers_to_channels, block_number, ) events = sub_iteration.events elif isinstance(sub_task, PaymentMappingState.MediatorTask): payment_network_identifier = sub_task.payment_network_identifier token_address = sub_task.token_address token_network_state = get_token_network( node_state, payment_network_identifier, token_address, ) if token_network_state: sub_iteration = mediator.state_transition( sub_task.mediator_state, state_change, token_network_state.channelidentifiers_to_channels, block_number, ) events = sub_iteration.events elif isinstance(sub_task, PaymentMappingState.TargetTask): payment_network_identifier = sub_task.payment_network_identifier token_address = sub_task.token_address channel_identifier = sub_task.channel_identifier channel_state = views.get_channelstate_by_tokenaddress( node_state, payment_network_identifier, token_address, channel_identifier, ) if channel_state: sub_iteration = target.state_transition( sub_task.target_state, state_change, channel_state, block_number, ) events = sub_iteration.events return TransitionResult(node_state, events)
def subdispatch_to_paymenttask( chain_state: ChainState, state_change: StateChange, secrethash: SecretHash, ) -> TransitionResult: block_number = chain_state.block_number sub_task = chain_state.payment_mapping.secrethashes_to_task.get(secrethash) events = list() sub_iteration = None if sub_task: pseudo_random_generator = chain_state.pseudo_random_generator if isinstance(sub_task, InitiatorTask): token_network_identifier = sub_task.token_network_identifier token_network_state = views.get_token_network_by_identifier( chain_state, token_network_identifier, ) if token_network_state: sub_iteration = initiator_manager.state_transition( sub_task.manager_state, state_change, token_network_state.channelidentifiers_to_channels, pseudo_random_generator, block_number, ) events = sub_iteration.events elif isinstance(sub_task, MediatorTask): token_network_identifier = sub_task.token_network_identifier token_network_state = views.get_token_network_by_identifier( chain_state, token_network_identifier, ) if token_network_state: sub_iteration = mediator.state_transition( sub_task.mediator_state, state_change, token_network_state.channelidentifiers_to_channels, pseudo_random_generator, block_number, ) events = sub_iteration.events elif isinstance(sub_task, TargetTask): token_network_identifier = sub_task.token_network_identifier channel_identifier = sub_task.channel_identifier channel_state = views.get_channelstate_by_token_network_identifier( chain_state, token_network_identifier, channel_identifier, ) if channel_state: sub_iteration = target.state_transition( sub_task.target_state, state_change, channel_state, pseudo_random_generator, block_number, ) events = sub_iteration.events if sub_iteration and sub_iteration.new_state is None: del chain_state.payment_mapping.secrethashes_to_task[secrethash] return TransitionResult(chain_state, events)
def test_refund_transfer_no_more_routes(): amount = UNIT_TRANSFER_AMOUNT refund_pkey, refund_address = factories.make_privkey_address() setup = setup_initiator_tests( amount=amount, partner_balance=amount, our_address=UNIT_TRANSFER_INITIATOR, partner_address=refund_address, ) original_transfer = setup.current_state.initiator.transfer refund_transfer = factories.make_signed_transfer( amount, original_transfer.initiator, original_transfer.target, original_transfer.lock.expiration, UNIT_SECRET, payment_identifier=original_transfer.payment_identifier, channel_identifier=setup.channel.identifier, pkey=refund_pkey, sender=refund_address, ) state_change = ReceiveTransferRefundCancelRoute( routes=setup.available_routes, transfer=refund_transfer, secret=random_secret(), ) iteration = initiator_manager.state_transition( setup.current_state, state_change, setup.channel_map, setup.prng, setup.block_number, ) current_state = iteration.new_state # As per the description of the issue here: # https://github.com/raiden-network/raiden/issues/3146#issuecomment-447378046 # We can fail the payment but can't delete the payment task if there are no # more routes, but we have to wait for the lock expiration assert iteration.new_state is not None unlocked_failed = next(e for e in iteration.events if isinstance(e, EventUnlockFailed)) sent_failed = next(e for e in iteration.events if isinstance(e, EventPaymentSentFailed)) assert unlocked_failed assert sent_failed invalid_balance_proof = factories.make_signed_balance_proof( nonce=2, transferred_amount=original_transfer.balance_proof.transferred_amount, locked_amount=0, token_network_address=original_transfer.balance_proof.token_network_identifier, channel_identifier=setup.channel.identifier, locksroot=EMPTY_MERKLE_ROOT, extra_hash=original_transfer.lock.secrethash, sender_address=refund_address, ) balance_proof = factories.make_signed_balance_proof( nonce=2, transferred_amount=original_transfer.balance_proof.transferred_amount, locked_amount=0, token_network_address=original_transfer.balance_proof.token_network_identifier, channel_identifier=setup.channel.identifier, locksroot=EMPTY_MERKLE_ROOT, extra_hash=original_transfer.lock.secrethash, sender_address=refund_address, private_key=refund_pkey, ) invalid_lock_expired_state_change = ReceiveLockExpired( invalid_balance_proof, secrethash=original_transfer.lock.secrethash, message_identifier=5, ) lock_expired_state_change = ReceiveLockExpired( balance_proof, secrethash=original_transfer.lock.secrethash, message_identifier=5, ) before_expiry_block = original_transfer.lock.expiration - 1 expiry_block = channel.get_sender_expiration_threshold(original_transfer.lock) # a block before lock expiration, no events should be emitted current_state = iteration.new_state state_change = Block( block_number=before_expiry_block, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, setup.channel_map, setup.prng, expiry_block, ) assert not iteration.events assert iteration.new_state, 'payment task should not be deleted at this block' # process an invalid lock expired message before lock expiration current_state = iteration.new_state iteration = initiator_manager.state_transition( current_state, invalid_lock_expired_state_change, setup.channel_map, setup.prng, before_expiry_block, ) assert iteration.new_state, 'payment task should not be deleted at this lock expired' # should not be accepted assert not events.must_contain_entry(iteration.events, SendProcessed, {}) assert events.must_contain_entry(iteration.events, EventInvalidReceivedLockExpired, {}) # process a valid lock expired message before lock expiration current_state = iteration.new_state iteration = initiator_manager.state_transition( current_state, lock_expired_state_change, setup.channel_map, setup.prng, before_expiry_block, ) assert iteration.new_state, 'payment task should not be deleted at this lock expired' # should not be accepted assert not events.must_contain_entry(iteration.events, SendProcessed, {}) # now we get to the lock expiration block current_state = iteration.new_state state_change = Block( block_number=expiry_block, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, setup.channel_map, setup.prng, expiry_block, ) assert events.must_contain_entry(iteration.events, SendLockExpired, {}) # Since there was a refund transfer the payment task must not have been deleted assert iteration.new_state is not None # process the lock expired message after lock expiration current_state = iteration.new_state iteration = initiator_manager.state_transition( current_state, lock_expired_state_change, setup.channel_map, setup.prng, expiry_block, ) # should be accepted assert events.must_contain_entry(iteration.events, SendProcessed, {}) assert iteration.new_state, 'payment task should be there waiting for next block' # process the the block after lock expiration current_state = iteration.new_state state_change = Block( block_number=expiry_block + 1, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, setup.channel_map, setup.prng, expiry_block + 1, ) assert iteration.new_state is None, 'from this point on the payment task should go'
def test_initiator_lock_expired(): amount = UNIT_TRANSFER_AMOUNT * 2 channel1 = factories.make_channel( our_balance=amount, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channel2 = factories.make_channel( our_balance=0, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) pseudo_random_generator = random.Random() channel_map = { channel1.identifier: channel1, channel2.identifier: channel2, } available_routes = [ factories.route_from_channel(channel1), factories.route_from_channel(channel2), ] block_number = 10 transfer_description = factories.make_transfer_description( secret=UNIT_SECRET, payment_network_identifier=channel1.payment_network_identifier, ) current_state = make_initiator_manager_state( available_routes, transfer_description, channel_map, pseudo_random_generator, block_number, ) transfer = current_state.initiator.transfer assert transfer.lock.secrethash in channel1.our_state.secrethashes_to_lockedlocks # Trigger lock expiry state_change = Block( block_number=channel.get_sender_expiration_threshold(transfer.lock), gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, channel_map, pseudo_random_generator, block_number, ) assert events.must_contain_entry(iteration.events, SendLockExpired, { 'balance_proof': { 'nonce': 2, 'transferred_amount': 0, 'locked_amount': 0, }, 'secrethash': transfer.lock.secrethash, 'recipient': channel1.partner_state.address, }) # Since the lock expired make sure we also get the payment sent failed event assert events.must_contain_entry(iteration.events, EventPaymentSentFailed, { 'payment_network_identifier': channel1.payment_network_identifier, 'token_network_identifier': channel1.token_network_identifier, 'identifier': UNIT_TRANSFER_IDENTIFIER, 'target': transfer.target, 'reason': "transfer's lock has expired", }) assert transfer.lock.secrethash not in channel1.our_state.secrethashes_to_lockedlocks msg = 'the initiator payment task must be deleted at block of the lock expiration' assert not iteration.new_state, msg # Create 2 other transfers transfer2_state = make_initiator_manager_state( available_routes, make_transfer_description('transfer2'), channel_map, pseudo_random_generator, 30, ) transfer2_lock = transfer2_state.initiator.transfer.lock transfer3_state = make_initiator_manager_state( available_routes, make_transfer_description('transfer3'), channel_map, pseudo_random_generator, 32, ) transfer3_lock = transfer3_state.initiator.transfer.lock assert len(channel1.our_state.secrethashes_to_lockedlocks) == 2 assert transfer2_lock.secrethash in channel1.our_state.secrethashes_to_lockedlocks expiration_block_number = channel.get_sender_expiration_threshold(transfer2_lock) block = Block( block_number=expiration_block_number, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( transfer2_state, block, channel_map, pseudo_random_generator, expiration_block_number, ) # Transfer 2 expired assert transfer2_lock.secrethash not in channel1.our_state.secrethashes_to_lockedlocks # Transfer 3 is still there assert transfer3_lock.secrethash in channel1.our_state.secrethashes_to_lockedlocks
def test_refund_transfer_no_more_routes(): amount = UNIT_TRANSFER_AMOUNT block_number = 1 refund_pkey, refund_address = factories.make_privkey_address() pseudo_random_generator = random.Random() channel1 = factories.make_channel( our_balance=amount, partner_balance=amount, our_address=UNIT_TRANSFER_INITIATOR, partner_address=refund_address, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channelmap = {channel1.identifier: channel1} available_routes = [factories.route_from_channel(channel1)] current_state = make_initiator_state( available_routes, factories.UNIT_TRANSFER_DESCRIPTION, channelmap, pseudo_random_generator, block_number, ) original_transfer = current_state.initiator.transfer channel_identifier = current_state.initiator.channel_identifier channel_state = channelmap[channel_identifier] expiration = original_transfer.lock.expiration - channel_state.reveal_timeout - TRANSIT_BLOCKS refund_transfer = factories.make_signed_transfer( amount, original_transfer.initiator, original_transfer.target, expiration, UNIT_SECRET, payment_identifier=original_transfer.payment_identifier, channel_identifier=channel1.identifier, pkey=refund_pkey, sender=refund_address, ) state_change = ReceiveTransferRefundCancelRoute( sender=channel_state.partner_state.address, routes=available_routes, transfer=refund_transfer, secret=random_secret(), ) iteration = initiator_manager.state_transition( current_state, state_change, channelmap, pseudo_random_generator, block_number, ) assert iteration.new_state is None unlocked_failed = next(e for e in iteration.events if isinstance(e, EventUnlockFailed)) sent_failed = next(e for e in iteration.events if isinstance(e, EventTransferSentFailed)) assert unlocked_failed assert sent_failed
def test_initiator_lock_expired(): amount = UNIT_TRANSFER_AMOUNT * 2 channel1 = factories.make_channel( our_balance=amount, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channel2 = factories.make_channel( our_balance=0, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) pseudo_random_generator = random.Random() channel_map = { channel1.identifier: channel1, channel2.identifier: channel2, } available_routes = [ factories.route_from_channel(channel1), factories.route_from_channel(channel2), ] block_number = 10 transfer_description = factories.make_transfer_description( secret=UNIT_SECRET, payment_network_identifier=channel1.payment_network_identifier, ) current_state = make_initiator_manager_state( available_routes, transfer_description, channel_map, pseudo_random_generator, block_number, ) transfer = current_state.initiator.transfer assert transfer.lock.secrethash in channel1.our_state.secrethashes_to_lockedlocks # Trigger lock expiry state_change = Block( block_number=transfer.lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, channel_map, pseudo_random_generator, block_number, ) assert events.must_contain_entry( iteration.events, SendLockExpired, { 'balance_proof': { 'nonce': 2, 'transferred_amount': 0, 'locked_amount': 0, }, 'secrethash': transfer.lock.secrethash, 'recipient': channel1.partner_state.address, }) # Since the lock expired make sure we also get the payment sent failed event assert events.must_contain_entry( iteration.events, EventPaymentSentFailed, { 'payment_network_identifier': channel1.payment_network_identifier, 'token_network_identifier': channel1.token_network_identifier, 'identifier': UNIT_TRANSFER_IDENTIFIER, 'target': transfer.target, 'reason': "transfer's lock has expired", }) assert transfer.lock.secrethash not in channel1.our_state.secrethashes_to_lockedlocks msg = 'the initiator payment task must be deleted at block of the lock expiration' assert not iteration.new_state, msg # Create 2 other transfers transfer2_state = make_initiator_manager_state( available_routes, make_transfer_description('transfer2'), channel_map, pseudo_random_generator, 30, ) transfer2_lock = transfer2_state.initiator.transfer.lock transfer3_state = make_initiator_manager_state( available_routes, make_transfer_description('transfer3'), channel_map, pseudo_random_generator, 32, ) transfer3_lock = transfer3_state.initiator.transfer.lock assert len(channel1.our_state.secrethashes_to_lockedlocks) == 2 assert transfer2_lock.secrethash in channel1.our_state.secrethashes_to_lockedlocks expiration_block_number = transfer2_lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2 block = Block( block_number=expiration_block_number, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( transfer2_state, block, channel_map, pseudo_random_generator, expiration_block_number, ) # Transfer 2 expired assert transfer2_lock.secrethash not in channel1.our_state.secrethashes_to_lockedlocks # Transfer 3 is still there assert transfer3_lock.secrethash in channel1.our_state.secrethashes_to_lockedlocks
def test_refund_transfer_no_more_routes(): amount = UNIT_TRANSFER_AMOUNT refund_pkey, refund_address = factories.make_privkey_address() setup = setup_initiator_tests( amount=amount, partner_balance=amount, our_address=UNIT_TRANSFER_INITIATOR, partner_address=refund_address, ) original_transfer = setup.current_state.initiator.transfer refund_transfer = factories.make_signed_transfer( amount, original_transfer.initiator, original_transfer.target, original_transfer.lock.expiration, UNIT_SECRET, payment_identifier=original_transfer.payment_identifier, channel_identifier=setup.channel.identifier, pkey=refund_pkey, sender=refund_address, ) state_change = ReceiveTransferRefundCancelRoute( routes=setup.available_routes, transfer=refund_transfer, secret=random_secret(), ) iteration = initiator_manager.state_transition( setup.current_state, state_change, setup.channel_map, setup.prng, setup.block_number, ) current_state = iteration.new_state # As per the description of the issue here: # https://github.com/raiden-network/raiden/issues/3146#issuecomment-447378046 # We can fail the payment but can't delete the payment task if there are no # more routes, but we have to wait for the lock expiration assert iteration.new_state is not None unlocked_failed = next(e for e in iteration.events if isinstance(e, EventUnlockFailed)) sent_failed = next(e for e in iteration.events if isinstance(e, EventPaymentSentFailed)) assert unlocked_failed assert sent_failed invalid_balance_proof = factories.make_signed_balance_proof( nonce=2, transferred_amount=original_transfer.balance_proof.transferred_amount, locked_amount=0, token_network_address=original_transfer.balance_proof. token_network_identifier, channel_identifier=setup.channel.identifier, locksroot=EMPTY_MERKLE_ROOT, extra_hash=original_transfer.lock.secrethash, sender_address=refund_address, ) balance_proof = factories.make_signed_balance_proof( nonce=2, transferred_amount=original_transfer.balance_proof.transferred_amount, locked_amount=0, token_network_address=original_transfer.balance_proof. token_network_identifier, channel_identifier=setup.channel.identifier, locksroot=EMPTY_MERKLE_ROOT, extra_hash=original_transfer.lock.secrethash, sender_address=refund_address, private_key=refund_pkey, ) invalid_lock_expired_state_change = ReceiveLockExpired( invalid_balance_proof, secrethash=original_transfer.lock.secrethash, message_identifier=5, ) lock_expired_state_change = ReceiveLockExpired( balance_proof, secrethash=original_transfer.lock.secrethash, message_identifier=5, ) before_expiry_block = original_transfer.lock.expiration - 1 expiry_block = original_transfer.lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2 # a block before lock expiration, no events should be emitted current_state = iteration.new_state state_change = Block( block_number=before_expiry_block, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, setup.channel_map, setup.prng, expiry_block, ) assert not iteration.events assert iteration.new_state, 'payment task should not be deleted at this block' # process an invalid lock expired message before lock expiration current_state = iteration.new_state iteration = initiator_manager.state_transition( current_state, invalid_lock_expired_state_change, setup.channel_map, setup.prng, before_expiry_block, ) assert iteration.new_state, 'payment task should not be deleted at this lock expired' # should not be accepted assert not events.must_contain_entry(iteration.events, SendProcessed, {}) assert events.must_contain_entry(iteration.events, EventInvalidReceivedLockExpired, {}) # process a valid lock expired message before lock expiration current_state = iteration.new_state iteration = initiator_manager.state_transition( current_state, lock_expired_state_change, setup.channel_map, setup.prng, before_expiry_block, ) assert iteration.new_state, 'payment task should not be deleted at this lock expired' # should not be accepted assert not events.must_contain_entry(iteration.events, SendProcessed, {}) # now we get to the lock expiration block current_state = iteration.new_state state_change = Block( block_number=expiry_block, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, setup.channel_map, setup.prng, expiry_block, ) assert events.must_contain_entry(iteration.events, SendLockExpired, {}) # Since there was a refund transfer the payment task must not have been deleted assert iteration.new_state is not None # process the lock expired message after lock expiration current_state = iteration.new_state iteration = initiator_manager.state_transition( current_state, lock_expired_state_change, setup.channel_map, setup.prng, expiry_block, ) # should be accepted assert events.must_contain_entry(iteration.events, SendProcessed, {}) assert iteration.new_state, 'payment task should be there waiting for next block' # process the the block after lock expiration current_state = iteration.new_state state_change = Block( block_number=expiry_block + 1, gas_limit=1, block_hash=factories.make_transaction_hash(), ) iteration = initiator_manager.state_transition( current_state, state_change, setup.channel_map, setup.prng, expiry_block + 1, ) assert iteration.new_state is None, 'from this point on the payment task should go'
def test_refund_transfer_next_route(): amount = UNIT_TRANSFER_AMOUNT our_address = factories.ADDR refund_pkey, refund_address = factories.make_privkey_address() pseudo_random_generator = random.Random() channel1 = factories.make_channel( our_balance=amount, partner_balance=amount, our_address=our_address, partner_address=refund_address, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channel2 = factories.make_channel( our_balance=0, our_address=our_address, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channel3 = factories.make_channel( our_balance=amount, our_address=our_address, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channelmap = { channel1.identifier: channel1, channel2.identifier: channel2, channel3.identifier: channel3, } available_routes = [ factories.route_from_channel(channel1), factories.route_from_channel(channel2), factories.route_from_channel(channel3), ] block_number = 10 current_state = make_initiator_state( available_routes, factories.UNIT_TRANSFER_DESCRIPTION, channelmap, pseudo_random_generator, block_number, ) original_transfer = current_state.initiator.transfer channel_identifier = current_state.initiator.channel_identifier channel_state = channelmap[channel_identifier] expiration = original_transfer.lock.expiration - channel_state.reveal_timeout - TRANSIT_BLOCKS refund_transfer = factories.make_signed_transfer( amount, our_address, original_transfer.target, expiration, UNIT_SECRET, payment_identifier=original_transfer.payment_identifier, channel_identifier=channel1.identifier, pkey=refund_pkey, sender=refund_address, ) assert channel_state.partner_state.address == refund_address state_change = ReceiveTransferRefundCancelRoute( sender=refund_address, routes=available_routes, transfer=refund_transfer, secret=random_secret(), ) iteration = initiator_manager.state_transition( current_state, state_change, channelmap, pseudo_random_generator, block_number, ) assert iteration.new_state is not None route_cancelled = next(e for e in iteration.events if isinstance(e, EventUnlockFailed)) new_transfer = next(e for e in iteration.events if isinstance(e, SendLockedTransfer)) assert route_cancelled, 'The previous transfer must be cancelled' assert new_transfer, 'No mediated transfer event emitted, should have tried a new route' msg = 'the new transfer must use a new secret / secrethash' assert new_transfer.transfer.lock.secrethash != refund_transfer.lock.secrethash, msg assert iteration.new_state.initiator is not None
def subdispatch_to_paymenttask( chain_state: ChainState, state_change: StateChange, secrethash: SecretHash) -> TransitionResult[ChainState]: block_number = chain_state.block_number block_hash = chain_state.block_hash sub_task = chain_state.payment_mapping.secrethashes_to_task.get(secrethash) events: List[Event] = list() if sub_task: pseudo_random_generator = chain_state.pseudo_random_generator sub_iteration: Union[TransitionResult[InitiatorPaymentState], TransitionResult[MediatorTransferState], TransitionResult[TargetTransferState], ] if isinstance(sub_task, InitiatorTask): token_network_identifier = sub_task.token_network_identifier token_network_state = get_token_network_by_address( chain_state, token_network_identifier) if token_network_state: sub_iteration = initiator_manager.state_transition( sub_task.manager_state, state_change, token_network_state.channelidentifiers_to_channels, pseudo_random_generator, block_number, ) events = sub_iteration.events if sub_iteration.new_state is None: del chain_state.payment_mapping.secrethashes_to_task[ secrethash] elif isinstance(sub_task, MediatorTask): token_network_identifier = sub_task.token_network_identifier token_network_state = get_token_network_by_address( chain_state, token_network_identifier) if token_network_state: channelids_to_channels = token_network_state.channelidentifiers_to_channels sub_iteration = mediator.state_transition( mediator_state=sub_task.mediator_state, state_change=state_change, channelidentifiers_to_channels=channelids_to_channels, nodeaddresses_to_networkstates=chain_state. nodeaddresses_to_networkstates, pseudo_random_generator=pseudo_random_generator, block_number=block_number, block_hash=block_hash, ) events = sub_iteration.events if sub_iteration.new_state is None: del chain_state.payment_mapping.secrethashes_to_task[ secrethash] elif isinstance(sub_task, TargetTask): token_network_identifier = sub_task.token_network_identifier channel_identifier = sub_task.channel_identifier channel_state = views.get_channelstate_by_canonical_identifier( chain_state=chain_state, canonical_identifier=CanonicalIdentifier( chain_identifier=chain_state.chain_id, token_network_address=token_network_identifier, channel_identifier=channel_identifier, ), ) if channel_state: sub_iteration = target.state_transition( target_state=sub_task.target_state, state_change=state_change, channel_state=channel_state, pseudo_random_generator=pseudo_random_generator, block_number=block_number, ) events = sub_iteration.events if sub_iteration.new_state is None: del chain_state.payment_mapping.secrethashes_to_task[ secrethash] return TransitionResult(chain_state, events)
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)
def test_refund_transfer_next_route(): amount = UNIT_TRANSFER_AMOUNT our_address = factories.ADDR refund_pkey, refund_address = factories.make_privkey_address() pseudo_random_generator = random.Random() channel1 = factories.make_channel( our_balance=amount, partner_balance=amount, our_address=our_address, partner_address=refund_address, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channel2 = factories.make_channel( our_balance=0, our_address=our_address, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channel3 = factories.make_channel( our_balance=amount, our_address=our_address, token_address=UNIT_TOKEN_ADDRESS, token_network_identifier=UNIT_TOKEN_NETWORK_ADDRESS, ) channel_map = { channel1.identifier: channel1, channel2.identifier: channel2, channel3.identifier: channel3, } available_routes = [ factories.route_from_channel(channel1), factories.route_from_channel(channel2), factories.route_from_channel(channel3), ] block_number = 10 current_state = make_initiator_manager_state( available_routes, factories.UNIT_TRANSFER_DESCRIPTION, channel_map, pseudo_random_generator, block_number, ) original_transfer = current_state.initiator.transfer refund_transfer = factories.make_signed_transfer( amount, our_address, original_transfer.target, original_transfer.lock.expiration, UNIT_SECRET, payment_identifier=original_transfer.payment_identifier, channel_identifier=channel1.identifier, pkey=refund_pkey, sender=refund_address, ) assert channel1.partner_state.address == refund_address state_change = ReceiveTransferRefundCancelRoute( routes=available_routes, transfer=refund_transfer, secret=random_secret(), ) iteration = initiator_manager.state_transition( current_state, state_change, channel_map, pseudo_random_generator, block_number, ) assert iteration.new_state is not None route_cancelled = next(e for e in iteration.events if isinstance(e, EventUnlockFailed)) new_transfer = next(e for e in iteration.events if isinstance(e, SendLockedTransfer)) assert route_cancelled, 'The previous transfer must be cancelled' assert new_transfer, 'No mediated transfer event emitted, should have tried a new route' msg = 'the new transfer must use a new secret / secrethash' assert new_transfer.transfer.lock.secrethash != refund_transfer.lock.secrethash, msg assert iteration.new_state.initiator is not None