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 # lock.amount includes the fees, transfer_description.amount is the actual # payment amount, for the transfer to be valid and the unlock allowed the # target must receive an amount between these values. is_valid_secretrequest = ( state_change.amount <= lock.amount and state_change.amount >= initiator_state.transfer_description.amount and state_change.expiration == lock.expiration ## and initiator_state.transfer_description.secret != EMPTY_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 secret = transfer_description.secret revealsecret = SendSecretReveal( recipient=Address(recipient), channel_identifier=CHANNEL_IDENTIFIER_GLOBAL_QUEUE, message_identifier=message_identifier, secret=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: initiator_state.received_secret_request = True iteration = TransitionResult(initiator_state, list()) else: iteration = TransitionResult(initiator_state, list()) return iteration
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 == Address( 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) if not is_message_from_target: return TransitionResult(initiator_state, list()) 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" if initiator_state.received_secret_request: # A secret request was received earlier, all subsequent are ignored # as it might be an attack. return TransitionResult(initiator_state, list()) # 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 is_valid_secretrequest: # 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_UNORDERED_QUEUE, ) initiator_state.transfer_state = "transfer_secret_revealed" initiator_state.received_secret_request = True return TransitionResult(initiator_state, [revealsecret]) else: 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, ) return TransitionResult(initiator_state, [invalid_request])
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=typing.Address(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 handle_offchain_secretreveal_light( target_state: TargetTransferState, state_change: ReceiveSecretRevealLight, channel_state: NettingChannelState, pseudo_random_generator: random.Random, block_number: BlockNumber, ) -> TransitionResult[TargetTransferState]: """ Validates and handles a ReceiveSecretReveal state change. """ valid_secret = is_valid_secret_reveal( state_change=state_change, transfer_secrethash=target_state.transfer.lock.secrethash, secret=state_change.secret, ) has_transfer_expired = channel.is_transfer_expired( transfer=target_state.transfer, affected_channel=channel_state, block_number=block_number ) if valid_secret and not has_transfer_expired: # TODO mmarcosmartinez7 this cannot be done without LC interaction # channel.register_offchain_secret( # channel_state=channel_state, # secret=state_change.secret, # secrethash=state_change.secrethash, # ) route = target_state.route message_identifier = message_identifier_from_prng(pseudo_random_generator) target_state.state = TargetTransferState.OFFCHAIN_SECRET_REVEAL target_state.secret = state_change.secret recipient = route.node_address # Store reveal secret 7, create reveal secret 9 and store it for LC signing. received_reveal_secret = state_change.secret_reveal_message reveal_secret_to_send_event = SendSecretReveal( recipient=recipient, channel_identifier=CHANNEL_IDENTIFIER_GLOBAL_QUEUE, message_identifier=message_identifier, secret=target_state.secret, ) reveal_secret_to_send_msg = message_from_sendevent(reveal_secret_to_send_event) store_received_reveal = StoreMessageEvent( received_reveal_secret.message_identifier, target_state.transfer.payment_identifier, 7, received_reveal_secret, True ) store_reveal_to_send = StoreMessageEvent( message_identifier, target_state.transfer.payment_identifier, 9, reveal_secret_to_send_msg, False ) iteration = TransitionResult(target_state, [store_received_reveal, store_reveal_to_send]) else: # TODO: event for byzantine behavior iteration = TransitionResult(target_state, list()) return iteration