def handle_block(target_state, channel_state, block_number): """ After Raiden learns about a new block this function must be called to handle expiration of the hash time lock. """ transfer = target_state.transfer secret_known = channel.is_secret_known( channel_state.partner_state, transfer.lock.hashlock, ) if not secret_known and block_number > transfer.lock.expiration: # XXX: emit the event only once failed = EventWithdrawFailed( identifier=transfer.identifier, hashlock=transfer.lock.hashlock, reason='lock expired', ) target_state.state = 'expired' events = [failed] elif target_state.state != 'waiting_close': # only emit the close event once events = events_for_close(target_state, channel_state, block_number) else: events = list() iteration = TransitionResult(target_state, events) return iteration
def clear_if_finalized(iteration): """ Clears the state if the transfer was either completed or failed. """ state = iteration.new_state if state is None: return iteration if state.from_transfer.secret is None and state.block_number > state.from_transfer.expiration: failed = EventWithdrawFailed( identifier=state.from_transfer.identifier, hashlock=state.from_transfer.hashlock, reason='lock expired', ) iteration = TransitionResult(None, [failed]) elif state.state == 'balance_proof': transfer_success = EventTransferReceivedSuccess( state.from_transfer.identifier, state.from_transfer.amount, state.from_transfer.initiator, ) unlock_success = EventWithdrawSuccess( state.from_transfer.identifier, state.from_transfer.hashlock, ) iteration = TransitionResult(None, [transfer_success, unlock_success]) return iteration
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 handle_inittarget( state_change, channel_state, pseudo_random_generator, block_number, ): """ Handles an ActionInitTarget state change. """ transfer = state_change.transfer route = state_change.route target_state = TargetTransferState( route, transfer, ) assert channel_state.identifier == transfer.balance_proof.channel_address is_valid, _, errormsg = channel.handle_receive_lockedtransfer( channel_state, transfer, ) safe_to_wait = is_safe_to_wait( transfer.lock.expiration, channel_state.reveal_timeout, block_number, ) # if there is not enough time to safely withdraw the token on-chain # silently let the transfer expire. if is_valid and safe_to_wait: message_identifier = message_identifier_from_prng( pseudo_random_generator) recipient = transfer.initiator queue_name = b'global' secret_request = SendSecretRequest( recipient, queue_name, message_identifier, transfer.payment_identifier, transfer.lock.amount, transfer.lock.secrethash, ) iteration = TransitionResult(target_state, [secret_request]) else: if not is_valid: failure_reason = errormsg elif not safe_to_wait: failure_reason = 'lock expiration is not safe' withdraw_failed = EventWithdrawFailed( identifier=transfer.payment_identifier, secrethash=transfer.lock.secrethash, reason=failure_reason, ) iteration = TransitionResult(target_state, [withdraw_failed]) return iteration
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' withdraw_failed = EventWithdrawFailed( pair.payer_transfer.payment_identifier, pair.payer_transfer.lock.secrethash, 'lock expired', ) events.append(withdraw_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 handle_inittarget(state_change, channel_state, block_number): """ Handles an ActionInitTarget state change. """ transfer = state_change.transfer route = state_change.route target_state = TargetTransferState( route, transfer, ) assert channel_state.identifier == transfer.balance_proof.channel_address is_valid, errormsg = channel.handle_receive_mediatedtransfer( channel_state, transfer, ) safe_to_wait = is_safe_to_wait( transfer.lock.expiration, channel_state.reveal_timeout, block_number, ) # if there is not enough time to safely withdraw the token on-chain # silently let the transfer expire. if is_valid and safe_to_wait: secret_request = SendSecretRequest( transfer.identifier, transfer.lock.amount, transfer.lock.hashlock, transfer.initiator, ) iteration = TransitionResult(target_state, [secret_request]) else: if not is_valid: failure_reason = errormsg elif not safe_to_wait: failure_reason = 'lock expiration is not safe' withdraw_failed = EventWithdrawFailed( identifier=transfer.identifier, hashlock=transfer.lock.hashlock, reason=failure_reason, ) iteration = TransitionResult(target_state, [withdraw_failed]) return iteration