def set_expired_pairs(transfers_pair, block_number): """ Set the state of expired transfers, and return the failed events. """ pending_transfers_pairs = get_pending_transfer_pairs(transfers_pair) events = list() for pair in pending_transfers_pairs: if block_number > pair.payer_transfer.expiration: assert pair.payee_state == 'payee_expired' assert pair.payee_transfer.expiration < pair.payer_transfer.expiration if pair.payer_state != 'payer_expired': pair.payer_state = 'payer_expired' withdraw_failed = EventWithdrawFailed( pair.payer_transfer.identifier, pair.payer_transfer.hashlock, 'lock expired', ) events.append(withdraw_failed) elif block_number > pair.payee_transfer.expiration: assert pair.payee_state not in STATE_TRANSFER_PAID assert pair.payee_transfer.expiration < pair.payer_transfer.expiration if pair.payee_state != 'payee_expired': pair.payee_state = 'payee_expired' unlock_failed = EventUnlockFailed( pair.payee_transfer.identifier, pair.payee_transfer.hashlock, 'lock expired', ) events.append(unlock_failed) return events
def events_for_cancel_current_route(transfer_description): unlock_failed = EventUnlockFailed( identifier=transfer_description.identifier, secrethash=transfer_description.secrethash, reason='route was canceled', ) return [unlock_failed]
def events_for_cancel_current_route( transfer_description: TransferDescriptionWithSecretState, ) -> List[Event]: unlock_failed = EventUnlockFailed( identifier=transfer_description.payment_identifier, secrethash=transfer_description.secrethash, reason="route was canceled", ) return [unlock_failed]
def events_to_remove_expired_locks( mediator_state: MediatorTransferState, channelidentifiers_to_channels: typing.ChannelMap, block_number: typing.BlockNumber, pseudo_random_generator: random.Random, ): """ Clear the channels which have expired locks. This only considers the *sent* transfers, received transfers can only be updated by the partner. """ events = list() for transfer_pair in mediator_state.transfers_pair: balance_proof = transfer_pair.payee_transfer.balance_proof channel_identifier = balance_proof.channel_identifier channel_state = channelidentifiers_to_channels.get(channel_identifier) assert channel_state, "Couldn't find channel for channel_id: {}".format(channel_identifier) secrethash = mediator_state.secrethash lock = None if secrethash in channel_state.our_state.secrethashes_to_lockedlocks: assert secrethash not in channel_state.our_state.secrethashes_to_unlockedlocks lock = channel_state.our_state.secrethashes_to_lockedlocks.get(secrethash) elif secrethash in channel_state.our_state.secrethashes_to_unlockedlocks: lock = channel_state.our_state.secrethashes_to_unlockedlocks.get(secrethash) if lock: lock_expiration_threshold = ( lock.expiration + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS * 2 ) has_lock_expired, _ = channel.is_lock_expired( end_state=channel_state.our_state, lock=lock, block_number=block_number, lock_expiration_threshold=lock_expiration_threshold, ) if has_lock_expired: transfer_pair.payee_state = 'payee_expired' expired_lock_events = channel.events_for_expired_lock( channel_state=channel_state, locked_lock=lock, pseudo_random_generator=pseudo_random_generator, ) events.extend(expired_lock_events) unlock_failed = EventUnlockFailed( transfer_pair.payee_transfer.payment_identifier, transfer_pair.payee_transfer.lock.secrethash, 'lock expired', ) events.append(unlock_failed) return events
def set_expired_pairs(transfers_pair, block_number): """ Set the state transfers to the expired state and return the failed events.""" pending_transfers_pairs = get_pending_transfer_pairs(transfers_pair) events = list() for pair in pending_transfers_pairs: has_payee_transfer_expired = ( block_number > pair.payee_transfer.lock.expiration and pair.payee_state != 'payee_expired' ) has_payer_transfer_expired = ( block_number > pair.payer_transfer.lock.expiration 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_failed = EventUnlockClaimFailed( pair.payer_transfer.payment_identifier, pair.payer_transfer.lock.secrethash, 'lock expired', ) events.append(unlock_failed) elif has_payee_transfer_expired: pair.payee_state = 'payee_expired' unlock_failed = EventUnlockFailed( pair.payee_transfer.payment_identifier, pair.payee_transfer.lock.secrethash, 'lock expired', ) events.append(unlock_failed) return events
def events_for_cancel_current_route( route_state: RouteState, transfer_description: TransferDescriptionWithSecretState ) -> List[Event]: return [ EventUnlockFailed( identifier=transfer_description.payment_identifier, secrethash=transfer_description.secrethash, reason="route was canceled", ), EventRouteFailed( secrethash=transfer_description.secrethash, route=route_state.route, token_network_address=transfer_description.token_network_address, ), ]
def handle_block( initiator_state: InitiatorTransferState, state_change: Block, channel_state: NettingChannelState, pseudo_random_generator: random.Random, ) -> TransitionResult[Optional[InitiatorTransferState]]: """ Checks if the lock has expired, and if it has sends a remove expired lock and emits the failing events. """ secrethash = initiator_state.transfer.lock.secrethash locked_lock = channel_state.our_state.secrethashes_to_lockedlocks.get( secrethash) if not locked_lock: if channel_state.partner_state.secrethashes_to_lockedlocks.get( secrethash): return TransitionResult(initiator_state, list()) else: # if lock is not in our or our partner's locked locks then the # task can go return TransitionResult(None, list()) lock_expiration_threshold = BlockNumber( locked_lock.expiration + DEFAULT_WAIT_BEFORE_LOCK_REMOVAL, ) lock_has_expired, _ = channel.is_lock_expired( end_state=channel_state.our_state, lock=locked_lock, block_number=state_change.block_number, lock_expiration_threshold=lock_expiration_threshold, ) events: List[Event] = list() if lock_has_expired: is_channel_open = channel.get_status( channel_state) == CHANNEL_STATE_OPENED if is_channel_open: expired_lock_events = channel.events_for_expired_lock( channel_state=channel_state, locked_lock=locked_lock, pseudo_random_generator=pseudo_random_generator, ) events.extend(expired_lock_events) if initiator_state.received_secret_request: reason = 'bad secret request message from target' else: reason = 'lock expired' transfer_description = initiator_state.transfer_description payment_identifier = transfer_description.payment_identifier # TODO: When we introduce multiple transfers per payment this needs to be # reconsidered. As we would want to try other routes once a route # has failed, and a transfer failing does not mean the entire payment # would have to fail. # Related issue: https://github.com/raiden-network/raiden/issues/2329 payment_failed = EventPaymentSentFailed( payment_network_identifier=transfer_description. payment_network_identifier, token_network_identifier=transfer_description. token_network_identifier, identifier=payment_identifier, target=transfer_description.target, reason=reason, ) unlock_failed = EventUnlockFailed( identifier=payment_identifier, secrethash=initiator_state.transfer_description.secrethash, reason=reason, ) lock_exists = channel.lock_exists_in_either_channel_side( channel_state=channel_state, secrethash=secrethash, ) return TransitionResult( # If the lock is either in our state or partner state we keep the # task around to wait for the LockExpired messages to sync. # Check https://github.com/raiden-network/raiden/issues/3183 initiator_state if lock_exists else None, events + [payment_failed, unlock_failed], ) else: return TransitionResult(initiator_state, events)
def try_new_route(state): assert state.route is None, 'cannot try a new route while one is being used' # TODO: # - Route ranking. An upper layer should rate each route to optimize # the fee price/quality of each route and add a rate from in the range # [0.0,1.0]. # - Add in a policy per route: # - filtering, e.g. so the user may have a per route maximum transfer # value based on fixed value or reputation. # - reveal time computation # - These policy details are better hidden from this implementation and # changes should be applied through the use of Route state changes. # Find a single route that may fulfill the request, this uses a single # route intentionally try_route = None while state.routes.available_routes: route = state.routes.available_routes.pop(0) if route.available_balance < state.transfer.amount: state.routes.ignored_routes.append(route) else: try_route = route break unlock_failed = None if state.message: unlock_failed = EventUnlockFailed( identifier=state.transfer.identifier, hashlock=state.transfer.hashlock, reason='route was canceled', ) if try_route is None: # No available route has sufficient balance for the current transfer, # cancel it. # # At this point we can just discard all the state data, this is only # valid because we are the initiator and we know that the secret was # not released. transfer_failed = EventTransferSentFailed( identifier=state.transfer.identifier, reason='no route available', ) events = [transfer_failed] if unlock_failed: events.append(unlock_failed) iteration = TransitionResult(None, events) else: state.route = try_route secret = next(state.random_generator) hashlock = sha3(secret) # The initiator doesn't need to learn the secret, so there is no need # to decrement reveal_timeout from the lock timeout. # # The lock_expiration could be set to a value larger than # settle_timeout, this is not useful since the next hop will take this # channel settle_timeout as an upper limit for expiration. # # The two nodes will most likely disagree on latest block, as far as # the expiration goes this is no problem. lock_expiration = state.block_number + try_route.settle_timeout identifier = state.transfer.identifier transfer = LockedTransferState( identifier, state.transfer.amount, state.transfer.token, state.transfer.initiator, state.transfer.target, lock_expiration, hashlock, secret, ) message = SendMediatedTransfer( transfer.identifier, transfer.token, transfer.amount, transfer.hashlock, state.our_address, transfer.target, lock_expiration, try_route.node_address, ) state.transfer = transfer state.message = message events = [message] if unlock_failed: events.append(unlock_failed) iteration = TransitionResult(state, events) return iteration