def test_mediator_task_view(): """Same as above for mediator tasks.""" secret1 = factories.make_secret(1) locked_amount1 = 11 payee_transfer = factories.create( factories.LockedTransferUnsignedStateProperties(secret=secret1)) payer_transfer = factories.create( factories.LockedTransferSignedStateProperties( secret=secret1, payment_identifier=1, locked_amount=locked_amount1)) secrethash1 = payee_transfer.lock.secrethash initiator = payee_transfer.initiator initiator_channel = factories.create( factories.NettingChannelStateProperties( partner_state=factories.NettingChannelEndStateProperties( address=initiator, balance=100))) routes = [factories.make_route_from_channel(initiator_channel)] transfer_state1 = MediatorTransferState(secrethash=secrethash1, routes=routes) transfer_state1.transfers_pair.append( MediationPairState( payer_transfer=payer_transfer, payee_transfer=payee_transfer, payee_address=payee_transfer.target, )) task1 = MediatorTask( token_network_identifier=factories.UNIT_TOKEN_NETWORK_ADDRESS, mediator_state=transfer_state1, ) secret2 = factories.make_secret(2) locked_amount2 = 13 transfer2 = factories.create( factories.LockedTransferSignedStateProperties( secret=secret2, payment_identifier=2, locked_amount=locked_amount2)) secrethash2 = transfer2.lock.secrethash transfer_state2 = MediatorTransferState(secrethash=secrethash2, routes=routes) transfer_state2.waiting_transfer = WaitingTransferState(transfer=transfer2) task2 = MediatorTask( token_network_identifier=factories.UNIT_TOKEN_NETWORK_ADDRESS, mediator_state=transfer_state2, ) payment_mapping = {secrethash1: task1, secrethash2: task2} view = transfer_tasks_view(payment_mapping) assert len(view) == 2 if view[0].get("payment_identifier") == "1": pending_transfer, waiting_transfer = view else: waiting_transfer, pending_transfer = view assert pending_transfer.get("role") == waiting_transfer.get( "role") == "mediator" assert pending_transfer.get("payment_identifier") == "1" assert waiting_transfer.get("payment_identifier") == "2" assert pending_transfer.get("locked_amount") == str(locked_amount1) assert waiting_transfer.get("locked_amount") == str(locked_amount2)
def test_mediator_task_view(): """Same as above for mediator tasks.""" secret1 = factories.make_secret(1) locked_amount1 = TokenAmount(11) payee_transfer = factories.create( factories.LockedTransferUnsignedStateProperties(secret=secret1)) payer_transfer = factories.create( factories.LockedTransferSignedStateProperties( secret=secret1, payment_identifier=PaymentID(1), locked_amount=locked_amount1)) secrethash1 = payee_transfer.lock.secrethash route_state = RouteState(route=[payee_transfer.target]) transfer_state1 = MediatorTransferState(secrethash=secrethash1, routes=[route_state]) # pylint: disable=E1101 transfer_state1.transfers_pair.append( MediationPairState( payer_transfer=payer_transfer, payee_transfer=payee_transfer, payee_address=payee_transfer.target, )) task1 = MediatorTask( token_network_address=factories.UNIT_TOKEN_NETWORK_ADDRESS, mediator_state=transfer_state1) secret2 = factories.make_secret(2) locked_amount2 = TokenAmount(13) transfer2 = factories.create( factories.LockedTransferSignedStateProperties( secret=secret2, payment_identifier=PaymentID(2), locked_amount=locked_amount2)) secrethash2 = transfer2.lock.secrethash transfer_state2 = MediatorTransferState(secrethash=secrethash2, routes=[route_state]) transfer_state2.waiting_transfer = WaitingTransferState(transfer=transfer2) task2 = MediatorTask( token_network_address=factories.UNIT_TOKEN_NETWORK_ADDRESS, mediator_state=transfer_state2) payment_mapping = { secrethash1: cast(TransferTask, task1), secrethash2: cast(TransferTask, task2), } view = transfer_tasks_view(payment_mapping) assert len(view) == 2 if view[0].get("payment_identifier") == "1": pending_transfer, waiting_transfer = view else: waiting_transfer, pending_transfer = view assert pending_transfer.get("role") == waiting_transfer.get( "role") == "mediator" assert pending_transfer.get("payment_identifier") == "1" assert waiting_transfer.get("payment_identifier") == "2" assert pending_transfer.get("locked_amount") == str(locked_amount1) assert waiting_transfer.get("locked_amount") == str(locked_amount2)
def mediate_transfer( state, possible_routes, payer_channel, channelidentifiers_to_channels, pseudo_random_generator, payer_transfer, block_number, ): """ Try a new route or fail back to a refund. The mediator can safely try a new route knowing that the tokens from payer_transfer will cover the expenses of the mediation. If there is no route available that may be used at the moment of the call the mediator may send a refund back to the payer, allowing the payer to try a different route. """ available_routes = filter_used_routes( state.transfers_pair, possible_routes, ) assert payer_channel.partner_state.address == payer_transfer.balance_proof.sender transfer_pair, mediated_events = forward_transfer_pair( payer_transfer, available_routes, channelidentifiers_to_channels, pseudo_random_generator, block_number, ) if transfer_pair is None: assert not mediated_events if state.transfers_pair: original_pair = state.transfers_pair[0] original_channel = get_payer_channel( channelidentifiers_to_channels, original_pair, ) else: original_channel = payer_channel transfer_pair, mediated_events = backward_transfer_pair( original_channel, payer_transfer, pseudo_random_generator, block_number, ) if transfer_pair is None: assert not mediated_events mediated_events = list() state.waiting_transfer = WaitingTransferState(payer_transfer) else: # the list must be ordered from high to low expiration, expiration # handling depends on it state.transfers_pair.append(transfer_pair) return TransitionResult(state, mediated_events)
def events_for_expired_pairs( channelidentifiers_to_channels: typing.ChannelMap, transfers_pair: typing.List[MediationPairState], waiting_transfer: WaitingTransferState, block_number: typing.BlockNumber, ) -> typing.List[Event]: """ Informational events for expired locks. """ pending_transfers_pairs = get_pending_transfer_pairs(transfers_pair) events = list() for pair in pending_transfers_pairs: payer_balance_proof = pair.payer_transfer.balance_proof payer_channel = channelidentifiers_to_channels.get(payer_balance_proof.channel_identifier) payer_lock_expiration_threshold = ( pair.payer_transfer.lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2 ) has_payer_lock_expired, _ = channel.is_lock_expired( end_state=payer_channel.our_state, lock=pair.payer_transfer.lock, block_number=block_number, lock_expiration_threshold=payer_lock_expiration_threshold, ) has_payer_transfer_expired = ( has_payer_lock_expired and pair.payer_state != 'payer_expired' ) if has_payer_transfer_expired: # For safety, the correct behavior is: # # - If the payee has been paid, then the payer must pay too. # # And the corollary: # # - If the payer transfer has expired, then the payee transfer must # have expired too. # # The problem is that this corollary cannot be asserted. If a user # is running Raiden without a monitoring service, then it may go # offline after having paid a transfer to a payee, but without # getting a balance proof of the payer, and once it comes back # online the transfer may have expired. # # assert pair.payee_state == 'payee_expired' pair.payer_state = 'payer_expired' unlock_claim_failed = EventUnlockClaimFailed( pair.payer_transfer.payment_identifier, pair.payer_transfer.lock.secrethash, 'lock expired', ) events.append(unlock_claim_failed) if waiting_transfer and waiting_transfer.state != 'expired': waiting_transfer.state = 'expired' unlock_claim_failed = EventUnlockClaimFailed( waiting_transfer.transfer.payment_identifier, waiting_transfer.transfer.lock.secrethash, 'lock expired', ) events.append(unlock_claim_failed) return events