def start_mediated_transfer_with_secret( self, token_network_identifier: TokenNetworkID, amount: TokenAmount, target: TargetAddress, identifier: PaymentID, secret: Secret, ) -> AsyncResult: secret_hash = sha3(secret) if self.default_secret_registry.check_registered(secret_hash): raise RaidenUnrecoverableError( f'Attempted to initiate a locked transfer with secrethash {pex(secret_hash)}.' f' That secret is already registered onchain.', ) self.start_health_check_for(Address(target)) if identifier is None: identifier = create_default_identifier() with self.payment_identifier_lock: payment_status = self.targets_to_identifiers_to_statuses[ target].get(identifier) if payment_status: payment_status_matches = payment_status.matches( token_network_identifier, amount, ) if not payment_status_matches: raise PaymentConflict( 'Another payment with the same id is in flight', ) return payment_status.payment_done payment_status = PaymentStatus( payment_identifier=identifier, amount=amount, token_network_identifier=token_network_identifier, payment_done=AsyncResult(), secret=secret, secret_hash=secret_hash, ) self.targets_to_identifiers_to_statuses[target][ identifier] = payment_status init_initiator_statechange = initiator_init( raiden=self, transfer_identifier=identifier, transfer_amount=amount, transfer_secret=secret, token_network_identifier=token_network_identifier, target_address=target, ) # Dispatch the state change even if there are no routes to create the # wal entry. self.handle_state_change(init_initiator_statechange) return payment_status.payment_done
def direct_transfer_async(self, token_network_identifier, amount, target, identifier): """ Do a direct transfer with target. Direct transfers are non cancellable and non expirable, since these transfers are a signed balance proof with the transferred amount incremented. Because the transfer is non cancellable, there is a level of trust with the target. After the message is sent the target is effectively paid and then it is not possible to revert. The async result will be set to False iff there is no direct channel with the target or the payer does not have balance to complete the transfer, otherwise because the transfer is non expirable the async result *will never be set to False* and if the message is sent it will hang until the target node acknowledge the message. This transfer should be used as an optimization, since only two packets are required to complete the transfer (from the payers perspective), whereas the mediated transfer requires 6 messages. """ self.start_health_check_for(target) if identifier is None: identifier = create_default_identifier() payment_status = self.targets_to_identifiers_to_statuses[target].get( identifier) if payment_status: if not payment_status.matches(PaymentType.DIRECT, token_network_identifier, amount): raise PaymentConflict( 'Another payment with the same id is in flight', ) return payment_status.payment_done direct_transfer = ActionTransferDirect( token_network_identifier, target, identifier, amount, ) payment_status = PaymentStatus( payment_type=PaymentType.DIRECT, payment_identifier=identifier, amount=amount, token_network_identifier=token_network_identifier, payment_done=AsyncResult(), ) self.targets_to_identifiers_to_statuses[target][ identifier] = payment_status self.handle_state_change(direct_transfer) return payment_status.payment_done
def start_mediated_transfer_with_secret( self, token_network_identifier: TokenNetworkID, amount: PaymentAmount, fee: FeeAmount, target: TargetAddress, identifier: PaymentID, secret: Secret, secrethash: SecretHash = None, ) -> PaymentStatus: if secrethash is None: secrethash = sha3(secret) elif secrethash != sha3(secret): raise InvalidSecretHash( "provided secret and secret_hash do not match.") if len(secret) != SECRET_LENGTH: raise InvalidSecret("secret of invalid length.") # We must check if the secret was registered against the latest block, # even if the block is forked away and the transaction that registers # the secret is removed from the blockchain. The rationale here is that # someone else does know the secret, regardless of the chain state, so # the node must not use it to start a payment. # # For this particular case, it's preferable to use `latest` instead of # having a specific block_hash, because it's preferable to know if the secret # was ever known, rather than having a consistent view of the blockchain. secret_registered = self.default_secret_registry.is_secret_registered( secrethash=secrethash, block_identifier="latest") if secret_registered: raise RaidenUnrecoverableError( f"Attempted to initiate a locked transfer with secrethash {pex(secrethash)}." f" That secret is already registered onchain.") self.start_health_check_for(Address(target)) if identifier is None: identifier = create_default_identifier() with self.payment_identifier_lock: payment_status = self.targets_to_identifiers_to_statuses[ target].get(identifier) if payment_status: payment_status_matches = payment_status.matches( token_network_identifier, amount) if not payment_status_matches: raise PaymentConflict( "Another payment with the same id is in flight") return payment_status payment_status = PaymentStatus( payment_identifier=identifier, amount=amount, token_network_identifier=token_network_identifier, payment_done=AsyncResult(), ) self.targets_to_identifiers_to_statuses[target][ identifier] = payment_status init_initiator_statechange = initiator_init( raiden=self, transfer_identifier=identifier, transfer_amount=amount, transfer_secret=secret, transfer_secrethash=secrethash, transfer_fee=fee, token_network_identifier=token_network_identifier, target_address=target, ) # Dispatch the state change even if there are no routes to create the # wal entry. self.handle_and_track_state_change(init_initiator_statechange) return payment_status