def set_total_channel_deposit( self, registry_address: typing.PaymentNetworkID, token_address: typing.TokenAddress, partner_address: typing.Address, total_deposit: typing.TokenAmount, retry_timeout: typing.NetworkTimeout = DEFAULT_RETRY_TIMEOUT, ): """ Set the `total_deposit` in the channel with the peer at `partner_address` and the given `token_address` in order to be able to do transfers. Raises: InvalidAddress: If either token_address or partner_address is not 20 bytes long. TransactionThrew: May happen for multiple reasons: - If the token approval fails, e.g. the token may validate if account has enough balance for the allowance. - The deposit failed, e.g. the allowance did not set the token aside for use and the user spent it before deposit was called. - The channel was closed/settled between the allowance call and the deposit call. AddressWithoutCode: The channel was settled during the deposit execution. DepositOverLimit: The total deposit amount is higher than the limit. """ chain_state = views.state_from_raiden(self.raiden) token_networks = views.get_token_network_addresses_for( chain_state, registry_address, ) channel_state = views.get_channelstate_for( chain_state=chain_state, payment_network_id=registry_address, token_address=token_address, partner_address=partner_address, ) if not is_binary_address(token_address): raise InvalidAddress( 'Expected binary address format for token in channel deposit') if not is_binary_address(partner_address): raise InvalidAddress( 'Expected binary address format for partner in channel deposit' ) if token_address not in token_networks: raise UnknownTokenAddress('Unknown token address') if channel_state is None: raise InvalidAddress( 'No channel with partner_address for the given token') token = self.raiden.chain.token(token_address) token_network_registry = self.raiden.chain.token_network_registry( registry_address) token_network_address = token_network_registry.get_token_network( token_address) token_network_proxy = self.raiden.chain.token_network( token_network_address) channel_proxy = self.raiden.chain.payment_channel( token_network_address=token_network_proxy.address, channel_id=channel_state.identifier, ) balance = token.balance_of(self.raiden.address) if self.raiden.config['environment_type'] == Environment.PRODUCTION: deposit_limit = (token_network_proxy.proxy.contract.functions. channel_participant_deposit_limit().call()) if total_deposit > deposit_limit: raise DepositOverLimit( 'The deposit of {} is bigger than the current limit of {}'. format( total_deposit, deposit_limit, ), ) addendum = total_deposit - channel_state.our_state.contract_balance # If this check succeeds it does not imply the the `deposit` will # succeed, since the `deposit` transaction may race with another # transaction. if not balance >= addendum: msg = 'Not enough balance to deposit. {} Available={} Needed={}'.format( pex(token_address), balance, addendum, ) raise InsufficientFunds(msg) # If concurrent operations are happening on the channel, fail the request with channel_proxy.lock_or_raise(): # set_total_deposit calls approve # token.approve(netcontract_address, addendum) channel_proxy.set_total_deposit(total_deposit) target_address = self.raiden.address waiting.wait_for_participant_newbalance( raiden=self.raiden, payment_network_id=registry_address, token_address=token_address, partner_address=partner_address, target_address=target_address, target_balance=total_deposit, retry_timeout=retry_timeout, )
def channel_batch_close( self, registry_address, token_address, partner_addresses, poll_timeout=DEFAULT_POLL_TIMEOUT, ): """Close a channel opened with `partner_address` for the given `token_address`. Race condition, this can fail if channel was closed externally. """ if not is_binary_address(token_address): raise InvalidAddress( 'Expected binary address format for token in channel close') if not all(map(is_binary_address, partner_addresses)): raise InvalidAddress( 'Expected binary address format for partner in channel close') valid_tokens = views.get_token_network_addresses_for( views.state_from_raiden(self.raiden), registry_address, ) if token_address not in valid_tokens: raise UnknownTokenAddress('Token address is not known.') node_state = views.state_from_raiden(self.raiden) channels_to_close = views.filter_channels_by_partneraddress( node_state, registry_address, token_address, partner_addresses, ) token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_raiden(self.raiden), registry_address, token_address, ) # If concurrent operations are happening on one of the channels, fail entire # request. with ExitStack() as stack: # Put all the locks in this outer context so that the netting channel functions # don't release the locks when their context goes out of scope for channel_state in channels_to_close: channel = self.raiden.chain.netting_channel( channel_state.identifier) # Check if we can acquire the lock. If we can't raise an exception, which # will cause the ExitStack to exit, releasing all locks acquired so far if not channel.channel_operations_lock.acquire(blocking=False): raise ChannelBusyError( f'Channel with id {channel_state.identifier} is ' f'busy with another ongoing operation.') stack.push(channel.channel_operations_lock) for channel_state in channels_to_close: channel_close = ActionChannelClose( token_network_identifier, channel_state.identifier, ) self.raiden.handle_state_change(channel_close) msg = 'After {} seconds the deposit was not properly processed.'.format( poll_timeout) channel_ids = [ channel_state.identifier for channel_state in channels_to_close ] with gevent.Timeout(poll_timeout, EthNodeCommunicationError(msg)): waiting.wait_for_close( self.raiden, registry_address, token_address, channel_ids, self.raiden.alarm.wait_time, )
def channel_batch_close( self, registry_address: typing.PaymentNetworkID, token_address: typing.TokenAddress, partner_addresses: typing.List[typing.Address], retry_timeout: typing.NetworkTimeout = DEFAULT_RETRY_TIMEOUT, ): """Close a channel opened with `partner_address` for the given `token_address`. Race condition, this can fail if channel was closed externally. """ if not is_binary_address(token_address): raise InvalidAddress('Expected binary address format for token in channel close') if not all(map(is_binary_address, partner_addresses)): raise InvalidAddress('Expected binary address format for partner in channel close') valid_tokens = views.get_token_network_addresses_for( views.state_from_raiden(self.raiden), registry_address, ) if token_address not in valid_tokens: raise UnknownTokenAddress('Token address is not known.') chain_state = views.state_from_raiden(self.raiden) channels_to_close = views.filter_channels_by_partneraddress( chain_state, registry_address, token_address, partner_addresses, ) token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_raiden(self.raiden), registry_address, token_address, ) # If concurrent operations are happening on one of the channels, fail entire # request. with ExitStack() as stack: # Put all the locks in this outer context so that the netting channel functions # don't release the locks when their context goes out of scope for channel_state in channels_to_close: channel = self.raiden.chain.payment_channel( token_network_identifier, channel_state.identifier, ) stack.enter_context(channel.lock_or_raise()) for channel_state in channels_to_close: channel_close = ActionChannelClose( token_network_identifier, channel_state.identifier, ) self.raiden.handle_state_change(channel_close) channel_ids = [channel_state.identifier for channel_state in channels_to_close] waiting.wait_for_close( self.raiden, registry_address, token_address, channel_ids, retry_timeout, )
def transfer_async( self, registry_address: typing.PaymentNetworkID, token_address: typing.TokenAddress, amount: typing.TokenAmount, target: typing.Address, identifier: typing.PaymentID = None, secret: typing.Secret = None, secret_hash: typing.SecretHash = None, ): if not isinstance(amount, int): raise InvalidAmount('Amount not a number') if amount <= 0: raise InvalidAmount('Amount negative') if not is_binary_address(token_address): raise InvalidAddress('token address is not valid.') if not is_binary_address(target): raise InvalidAddress('target address is not valid.') if secret is not None: if len(secret) != SECRET_HEXSTRING_LENGTH: raise InvalidSecretOrSecretHash( 'secret length should be ' + str(SECRET_HEXSTRING_LENGTH) + '.', ) if not is_hex(secret): raise InvalidSecretOrSecretHash( 'provided secret is not an hexadecimal string.') secret = to_bytes(hexstr=secret) if secret_hash is not None: if len(secret_hash) != SECRET_HASH_HEXSTRING_LENGTH: raise InvalidSecretOrSecretHash( 'secret_hash length should be ' + str(SECRET_HASH_HEXSTRING_LENGTH) + '.', ) if not is_hex(secret_hash): raise InvalidSecretOrSecretHash( 'secret_hash is not an hexadecimal string.') secret_hash = to_bytes(hexstr=secret_hash) if secret is None and secret_hash is not None: raise InvalidSecretOrSecretHash( 'secret_hash without a secret is not supported yet.') if secret is not None and secret_hash is not None and secret_hash != sha3( secret): raise InvalidSecretOrSecretHash( 'provided secret and secret_hash do not match.') valid_tokens = views.get_token_identifiers( views.state_from_raiden(self.raiden), registry_address, ) if token_address not in valid_tokens: raise UnknownTokenAddress('Token address is not known.') log.debug( 'Initiating transfer', initiator=pex(self.raiden.address), target=pex(target), token=pex(token_address), amount=amount, identifier=identifier, ) payment_network_identifier = self.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state=views.state_from_raiden(self.raiden), payment_network_id=payment_network_identifier, token_address=token_address, ) payment_status = self.raiden.mediated_transfer_async( token_network_identifier=token_network_identifier, amount=amount, target=target, identifier=identifier, secret=secret, secret_hash=secret_hash, ) return payment_status
def channel_deposit( self, registry_address, token_address, partner_address, amount, poll_timeout=DEFAULT_POLL_TIMEOUT, ): """ Deposit `amount` in the channel with the peer at `partner_address` and the given `token_address` in order to be able to do transfers. Raises: InvalidAddress: If either token_address or partner_address is not 20 bytes long. TransactionThrew: May happen for multiple reasons: - If the token approval fails, e.g. the token may validate if account has enough balance for the allowance. - The deposit failed, e.g. the allowance did not set the token aside for use and the user spent it before deposit was called. - The channel was closed/settled between the allowance call and the deposit call. AddressWithoutCode: The channel was settled during the deposit execution. """ node_state = views.state_from_raiden(self.raiden) token_networks = views.get_token_network_addresses_for( node_state, registry_address, ) channel_state = views.get_channelstate_for( node_state, registry_address, token_address, partner_address, ) if not is_binary_address(token_address): raise InvalidAddress( 'Expected binary address format for token in channel deposit') if not is_binary_address(partner_address): raise InvalidAddress( 'Expected binary address format for partner in channel deposit' ) if token_address not in token_networks: raise UnknownTokenAddress('Unknown token address') if channel_state is None: raise InvalidAddress( 'No channel with partner_address for the given token') token = self.raiden.chain.token(token_address) balance = token.balance_of(self.raiden.address) # If this check succeeds it does not imply the the `deposit` will # succeed, since the `deposit` transaction may race with another # transaction. if not balance >= amount: msg = 'Not enough balance to deposit. {} Available={} Tried={}'.format( pex(token_address), balance, amount, ) raise InsufficientFunds(msg) netcontract_address = channel_state.identifier channel_proxy = self.raiden.chain.netting_channel(netcontract_address) # If concurrent operations are happening on the channel, fail the request if not channel_proxy.channel_operations_lock.acquire(blocking=False): raise ChannelBusyError( f'Channel with id {channel_state.identifier} is ' f'busy with another ongoing operation') with releasing(channel_proxy.channel_operations_lock): token.approve(netcontract_address, amount) channel_proxy.deposit(amount) old_balance = channel_state.our_state.contract_balance target_balance = old_balance + amount msg = 'After {} seconds the deposit was not properly processed.'.format( poll_timeout) # Wait until the `ChannelNewBalance` event is processed. with gevent.Timeout(poll_timeout, EthNodeCommunicationError(msg)): target_address = self.raiden.address waiting.wait_for_participant_newbalance( self.raiden, registry_address, token_address, partner_address, target_address, target_balance, self.raiden.alarm.wait_time, )
def channel_batch_close( self, registry_address: typing.PaymentNetworkID, token_address: typing.TokenAddress, partner_addresses: typing.List[typing.Address], retry_timeout: typing.NetworkTimeout = DEFAULT_RETRY_TIMEOUT, ): """Close a channel opened with `partner_address` for the given `token_address`. Race condition, this can fail if channel was closed externally. """ if not is_binary_address(token_address): raise InvalidAddress( 'Expected binary address format for token in channel close') if not all(map(is_binary_address, partner_addresses)): raise InvalidAddress( 'Expected binary address format for partner in channel close') valid_tokens = views.get_token_identifiers( chain_state=views.state_from_raiden(self.raiden), payment_network_id=registry_address, ) if token_address not in valid_tokens: raise UnknownTokenAddress('Token address is not known.') chain_state = views.state_from_raiden(self.raiden) channels_to_close = views.filter_channels_by_partneraddress( chain_state=chain_state, payment_network_id=registry_address, token_address=token_address, partner_addresses=partner_addresses, ) token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state=views.state_from_raiden(self.raiden), payment_network_id=registry_address, token_address=token_address, ) greenlets: typing.List[Greenlet] = list() for channel_state in channels_to_close: channel_close = ActionChannelClose( token_network_identifier=token_network_identifier, channel_identifier=channel_state.identifier, ) greenlets.extend(self.raiden.handle_state_change(channel_close), ) gevent.joinall(greenlets, raise_error=True) channel_ids = [ channel_state.identifier for channel_state in channels_to_close ] waiting.wait_for_close( raiden=self.raiden, payment_network_id=registry_address, token_address=token_address, channel_ids=channel_ids, retry_timeout=retry_timeout, )
def get_channel_list( self, registry_address: typing.PaymentNetworkID, token_address: typing.TokenAddress = None, partner_address: typing.Address = None, ) -> typing.List[NettingChannelState]: """Returns a list of channels associated with the optionally given `token_address` and/or `partner_address`. Args: token_address: an optionally provided token address partner_address: an optionally provided partner address Return: A list containing all channels the node participates. Optionally filtered by a token address and/or partner address. Raises: KeyError: An error occurred when the token address is unknown to the node. """ if registry_address and not is_binary_address(registry_address): raise InvalidAddress( 'Expected binary address format for registry in get_channel_list' ) if token_address and not is_binary_address(token_address): raise InvalidAddress( 'Expected binary address format for token in get_channel_list') if partner_address: if not is_binary_address(partner_address): raise InvalidAddress( 'Expected binary address format for partner in get_channel_list', ) if not token_address: raise UnknownTokenAddress( 'Provided a partner address but no token address') if token_address and partner_address: channel_state = views.get_channelstate_for( chain_state=views.state_from_raiden(self.raiden), payment_network_id=registry_address, token_address=token_address, partner_address=partner_address, ) if channel_state: result = [channel_state] else: result = [] elif token_address: result = views.list_channelstate_for_tokennetwork( chain_state=views.state_from_raiden(self.raiden), payment_network_id=registry_address, token_address=token_address, ) else: result = views.list_all_channelstate( chain_state=views.state_from_raiden(self.raiden), ) return result
def set_total_channel_deposit( self, registry_address: PaymentNetworkID, token_address: TokenAddress, partner_address: Address, total_deposit: TokenAmount, retry_timeout: NetworkTimeout = DEFAULT_RETRY_TIMEOUT, ): """ Set the `total_deposit` in the channel with the peer at `partner_address` and the given `token_address` in order to be able to do transfers. Raises: InvalidAddress: If either token_address or partner_address is not 20 bytes long. TransactionThrew: May happen for multiple reasons: - If the token approval fails, e.g. the token may validate if account has enough balance for the allowance. - The deposit failed, e.g. the allowance did not set the token aside for use and the user spent it before deposit was called. - The channel was closed/settled between the allowance call and the deposit call. AddressWithoutCode: The channel was settled during the deposit execution. DepositOverLimit: The total deposit amount is higher than the limit. """ chain_state = views.state_from_raiden(self.raiden) token_addresses = views.get_token_identifiers(chain_state, registry_address) channel_state = views.get_channelstate_for( chain_state=chain_state, payment_network_id=registry_address, token_address=token_address, partner_address=partner_address, ) if not is_binary_address(token_address): raise InvalidAddress("Expected binary address format for token in channel deposit") if not is_binary_address(partner_address): raise InvalidAddress("Expected binary address format for partner in channel deposit") if token_address not in token_addresses: raise UnknownTokenAddress("Unknown token address") if channel_state is None: raise InvalidAddress("No channel with partner_address for the given token") if self.raiden.config["environment_type"] == Environment.PRODUCTION: per_token_network_deposit_limit = RED_EYES_PER_TOKEN_NETWORK_LIMIT else: per_token_network_deposit_limit = UINT256_MAX token = self.raiden.chain.token(token_address) token_network_registry = self.raiden.chain.token_network_registry(registry_address) token_network_address = token_network_registry.get_token_network(token_address) token_network_proxy = self.raiden.chain.token_network(token_network_address) channel_proxy = self.raiden.chain.payment_channel( canonical_identifier=channel_state.canonical_identifier ) if total_deposit == 0: raise DepositMismatch("Attempted to deposit with total deposit being 0") addendum = total_deposit - channel_state.our_state.contract_balance total_network_balance = token.balance_of(registry_address) if total_network_balance + addendum > per_token_network_deposit_limit: raise DepositOverLimit( f"The deposit of {addendum} will exceed the " f"token network limit of {per_token_network_deposit_limit}" ) balance = token.balance_of(self.raiden.address) functions = token_network_proxy.proxy.contract.functions deposit_limit = functions.channel_participant_deposit_limit().call() if total_deposit > deposit_limit: raise DepositOverLimit( f"The additional deposit of {addendum} will exceed the " f"channel participant limit of {deposit_limit}" ) # If this check succeeds it does not imply the the `deposit` will # succeed, since the `deposit` transaction may race with another # transaction. if not balance >= addendum: msg = "Not enough balance to deposit. {} Available={} Needed={}".format( pex(token_address), balance, addendum ) raise InsufficientFunds(msg) # set_total_deposit calls approve # token.approve(netcontract_address, addendum) channel_proxy.set_total_deposit( total_deposit=total_deposit, block_identifier=views.state_from_raiden(self.raiden).block_hash, ) target_address = self.raiden.address waiting.wait_for_participant_newbalance( raiden=self.raiden, payment_network_id=registry_address, token_address=token_address, partner_address=partner_address, target_address=target_address, target_balance=total_deposit, retry_timeout=retry_timeout, )
def transfer_async( self, registry_address: PaymentNetworkID, token_address: TokenAddress, amount: TokenAmount, target: Address, identifier: PaymentID = None, secret: Secret = None, secrethash: SecretHash = None, ): current_state = views.state_from_raiden(self.raiden) payment_network_identifier = self.raiden.default_registry.address if not isinstance(amount, int): raise InvalidAmount("Amount not a number") if amount <= 0: raise InvalidAmount("Amount negative") if amount > UINT256_MAX: raise InvalidAmount("Amount too large") if not is_binary_address(token_address): raise InvalidAddress("token address is not valid.") if token_address not in views.get_token_identifiers(current_state, registry_address): raise UnknownTokenAddress("Token address is not known.") if not is_binary_address(target): raise InvalidAddress("target address is not valid.") valid_tokens = views.get_token_identifiers( views.state_from_raiden(self.raiden), registry_address ) if token_address not in valid_tokens: raise UnknownTokenAddress("Token address is not known.") if secret is not None and not isinstance(secret, typing.T_Secret): raise InvalidSecret("secret is not valid.") if secrethash is not None and not isinstance(secrethash, typing.T_SecretHash): raise InvalidSecretHash("secrethash is not valid.") log.debug( "Initiating transfer", initiator=pex(self.raiden.address), target=pex(target), token=pex(token_address), amount=amount, identifier=identifier, ) token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state=current_state, payment_network_id=payment_network_identifier, token_address=token_address, ) payment_status = self.raiden.mediated_transfer_async( token_network_identifier=token_network_identifier, amount=amount, target=target, identifier=identifier, secret=secret, secrethash=secrethash, ) return payment_status
def set_total_channel_deposit( self, registry_address: TokenNetworkRegistryAddress, token_address: TokenAddress, partner_address: Address, total_deposit: TokenAmount, retry_timeout: NetworkTimeout = DEFAULT_RETRY_TIMEOUT, ) -> None: """ Set the `total_deposit` in the channel with the peer at `partner_address` and the given `token_address` in order to be able to do transfers. Raises: InvalidBinaryAddress: If either token_address or partner_address is not 20 bytes long. RaidenRecoverableError: May happen for multiple reasons: - If the token approval fails, e.g. the token may validate if account has enough balance for the allowance. - The deposit failed, e.g. the allowance did not set the token aside for use and the user spent it before deposit was called. - The channel was closed/settled between the allowance call and the deposit call. AddressWithoutCode: The channel was settled during the deposit execution. DepositOverLimit: The total deposit amount is higher than the limit. UnexpectedChannelState: The channel is no longer in an open state. """ chain_state = views.state_from_raiden(self.raiden) token_addresses = views.get_token_identifiers(chain_state, registry_address) channel_state = views.get_channelstate_for( chain_state=chain_state, token_network_registry_address=registry_address, token_address=token_address, partner_address=partner_address, ) if not is_binary_address(token_address): raise InvalidBinaryAddress( "Expected binary address format for token in channel deposit") if not is_binary_address(partner_address): raise InvalidBinaryAddress( "Expected binary address format for partner in channel deposit" ) if token_address not in token_addresses: raise UnknownTokenAddress("Unknown token address") if channel_state is None: raise NonexistingChannel( "No channel with partner_address for the given token") confirmed_block_identifier = chain_state.block_hash token = self.raiden.proxy_manager.token( token_address, block_identifier=confirmed_block_identifier) token_network_registry = self.raiden.proxy_manager.token_network_registry( registry_address, block_identifier=confirmed_block_identifier) token_network_address = token_network_registry.get_token_network( token_address=token_address, block_identifier=confirmed_block_identifier) if token_network_address is None: raise UnknownTokenAddress( f"Token {to_checksum_address(token_address)} is not registered " f"with the network {to_checksum_address(registry_address)}.") token_network_proxy = self.raiden.proxy_manager.token_network( address=token_network_address, block_identifier=confirmed_block_identifier) channel_proxy = self.raiden.proxy_manager.payment_channel( channel_state=channel_state, block_identifier=confirmed_block_identifier) blockhash = chain_state.block_hash token_network_proxy = channel_proxy.token_network safety_deprecation_switch = token_network_proxy.safety_deprecation_switch( block_identifier=blockhash) balance = token.balance_of(self.raiden.address, block_identifier=blockhash) network_balance = token.balance_of( address=Address(token_network_address), block_identifier=blockhash) token_network_deposit_limit = token_network_proxy.token_network_deposit_limit( block_identifier=blockhash) addendum = total_deposit - channel_state.our_state.contract_balance channel_participant_deposit_limit = token_network_proxy.channel_participant_deposit_limit( block_identifier=blockhash) total_channel_deposit = total_deposit + channel_state.partner_state.contract_balance is_channel_open = channel.get_status( channel_state) == ChannelState.STATE_OPENED if not is_channel_open: raise UnexpectedChannelState("Channel is not in an open state.") if safety_deprecation_switch: msg = ("This token_network has been deprecated. " "All channels in this network should be closed and " "the usage of the newly deployed token network contract " "is highly encouraged.") raise TokenNetworkDeprecated(msg) if total_deposit <= channel_state.our_state.contract_balance: raise DepositMismatch("Total deposit did not increase.") # If this check succeeds it does not imply the `deposit` will # succeed, since the `deposit` transaction may race with another # transaction. if not (balance >= addendum): msg = "Not enough balance to deposit. {} Available={} Needed={}".format( to_checksum_address(token_address), balance, addendum) raise InsufficientFunds(msg) if network_balance + addendum > token_network_deposit_limit: msg = f"Deposit of {addendum} would have exceeded the token network deposit limit." raise DepositOverLimit(msg) if total_deposit > channel_participant_deposit_limit: msg = (f"Deposit of {total_deposit} is larger than the " f"channel participant deposit limit") raise DepositOverLimit(msg) if total_channel_deposit >= UINT256_MAX: raise DepositOverLimit("Deposit overflow") try: channel_proxy.approve_and_set_total_deposit( total_deposit=total_deposit, block_identifier=blockhash) except RaidenRecoverableError as e: log.info(f"Deposit failed. {str(e)}") target_address = self.raiden.address waiting.wait_for_participant_deposit( raiden=self.raiden, token_network_registry_address=registry_address, token_address=token_address, partner_address=partner_address, target_address=target_address, target_balance=total_deposit, retry_timeout=retry_timeout, )
def set_total_channel_withdraw( self, registry_address: TokenNetworkRegistryAddress, token_address: TokenAddress, partner_address: Address, total_withdraw: WithdrawAmount, retry_timeout: NetworkTimeout = DEFAULT_RETRY_TIMEOUT, ) -> None: """ Set the `total_withdraw` in the channel with the peer at `partner_address` and the given `token_address`. Raises: InvalidBinaryAddress: If either token_address or partner_address is not 20 bytes long. RaidenUnrecoverableError: May happen for multiple reasons: - During preconditions checks, if the channel was not open at the time of the approve_and_set_total_deposit call. - If the transaction fails during gas estimation or if a previous withdraw transaction with the same value was already mined. DepositMismatch: The total withdraw amount did not increase. """ chain_state = views.state_from_raiden(self.raiden) token_addresses = views.get_token_identifiers(chain_state, registry_address) channel_state = views.get_channelstate_for( chain_state=chain_state, token_network_registry_address=registry_address, token_address=token_address, partner_address=partner_address, ) if not is_binary_address(token_address): raise InvalidBinaryAddress( "Expected binary address format for token in channel deposit") if not is_binary_address(partner_address): raise InvalidBinaryAddress( "Expected binary address format for partner in channel deposit" ) if token_address not in token_addresses: raise UnknownTokenAddress("Unknown token address") if channel_state is None: raise NonexistingChannel( "No channel with partner_address for the given token") if total_withdraw <= channel_state.our_total_withdraw: raise WithdrawMismatch( f"Total withdraw {total_withdraw} did not increase") current_balance = channel.get_balance( sender=channel_state.our_state, receiver=channel_state.partner_state) amount_to_withdraw = total_withdraw - channel_state.our_total_withdraw if amount_to_withdraw > current_balance: raise InsufficientFunds( "The withdraw of {} is bigger than the current balance of {}". format(amount_to_withdraw, current_balance)) self.raiden.withdraw( canonical_identifier=channel_state.canonical_identifier, total_withdraw=total_withdraw) waiting.wait_for_withdraw_complete( raiden=self.raiden, canonical_identifier=channel_state.canonical_identifier, total_withdraw=total_withdraw, retry_timeout=retry_timeout, )
def transfer_async( self, registry_address: TokenNetworkRegistryAddress, token_address: TokenAddress, amount: PaymentAmount, target: TargetAddress, identifier: PaymentID = None, secret: Secret = None, secrethash: SecretHash = None, lock_timeout: BlockTimeout = None, ) -> "PaymentStatus": current_state = views.state_from_raiden(self.raiden) token_network_registry_address = self.raiden.default_registry.address if not isinstance(amount, int): # pragma: no unittest raise InvalidAmount("Amount not a number") if Address(target) == self.address: raise SamePeerAddress("Address must be different than ours") if amount <= 0: raise InvalidAmount("Amount negative") if amount > UINT256_MAX: raise InvalidAmount("Amount too large") if not is_binary_address(token_address): raise InvalidBinaryAddress("token address is not valid.") if token_address not in views.get_token_identifiers( current_state, registry_address): raise UnknownTokenAddress("Token address is not known.") if not is_binary_address(target): raise InvalidBinaryAddress("target address is not valid.") valid_tokens = views.get_token_identifiers( views.state_from_raiden(self.raiden), registry_address) if token_address not in valid_tokens: raise UnknownTokenAddress("Token address is not known.") if secret is not None and not isinstance(secret, T_Secret): raise InvalidSecret("secret is not valid.") if secrethash is not None and not isinstance(secrethash, T_SecretHash): raise InvalidSecretHash("secrethash is not valid.") if identifier is None: identifier = create_default_identifier() if identifier <= 0: raise InvalidPaymentIdentifier( "Payment identifier cannot be 0 or negative") if identifier > UINT64_MAX: raise InvalidPaymentIdentifier("Payment identifier is too large") log.debug( "Initiating transfer", initiator=to_checksum_address(self.raiden.address), target=to_checksum_address(target), token=to_checksum_address(token_address), amount=amount, identifier=identifier, ) token_network_address = views.get_token_network_address_by_token_address( chain_state=current_state, token_network_registry_address=token_network_registry_address, token_address=token_address, ) if token_network_address is None: raise UnknownTokenAddress( f"Token {to_checksum_address(token_address)} is not registered " f"with the network {to_checksum_address(registry_address)}.") payment_status = self.raiden.mediated_transfer_async( token_network_address=token_network_address, amount=amount, target=target, identifier=identifier, secret=secret, secrethash=secrethash, lock_timeout=lock_timeout, ) return payment_status
def transfer_async( self, registry_address: PaymentNetworkID, token_address: TokenAddress, amount: TokenAmount, target: Address, identifier: PaymentID = None, secret: Secret = None, secrethash: SecretHash = None, ): if not isinstance(amount, int): raise InvalidAmount("Amount not a number") if amount <= 0: raise InvalidAmount("Amount negative") if not is_binary_address(token_address): raise InvalidAddress("token address is not valid.") if not is_binary_address(target): raise InvalidAddress("target address is not valid.") if secret is not None: if len(secret) != SECRET_HEXSTRING_LENGTH: raise InvalidSecretOrSecretHash( "secret length should be " + str(SECRET_HEXSTRING_LENGTH) + "." ) if not is_hex(secret): raise InvalidSecretOrSecretHash("provided secret is not an hexadecimal string.") secret = to_bytes(hexstr=secret) if secrethash is not None: if len(secrethash) != SECRETHASH_HEXSTRING_LENGTH: raise InvalidSecretOrSecretHash( "secret_hash length should be " + str(SECRETHASH_HEXSTRING_LENGTH) + "." ) if not is_hex(secrethash): raise InvalidSecretOrSecretHash("secret_hash is not an hexadecimal string.") secrethash = to_bytes(hexstr=secrethash) # if both secret and secrethash were provided we check that sha3(secret) # matches the secerthash. Note that it is valid to provide a secert_hash # without providing a secret if secret is not None and secrethash is not None and secrethash != sha3(secret): raise InvalidSecretOrSecretHash("provided secret and secret_hash do not match.") valid_tokens = views.get_token_identifiers( views.state_from_raiden(self.raiden), registry_address ) if token_address not in valid_tokens: raise UnknownTokenAddress("Token address is not known.") log.debug( "Initiating transfer", initiator=pex(self.raiden.address), target=pex(target), token=pex(token_address), amount=amount, identifier=identifier, ) payment_network_identifier = self.raiden.default_registry.address token_network_identifier = views.get_token_network_identifier_by_token_address( chain_state=views.state_from_raiden(self.raiden), payment_network_id=payment_network_identifier, token_address=token_address, ) payment_status = self.raiden.mediated_transfer_async( token_network_identifier=token_network_identifier, amount=amount, target=target, identifier=identifier, secret=secret, secrethash=secrethash, ) return payment_status
def set_total_channel_deposit( self, registry_address, token_address, partner_address, total_deposit, poll_timeout=DEFAULT_POLL_TIMEOUT, retry_timeout=DEFAULT_RETRY_TIMEOUT, ): """ Set the `total_deposit` in the channel with the peer at `partner_address` and the given `token_address` in order to be able to do transfers. Raises: InvalidAddress: If either token_address or partner_address is not 20 bytes long. TransactionThrew: May happen for multiple reasons: - If the token approval fails, e.g. the token may validate if account has enough balance for the allowance. - The deposit failed, e.g. the allowance did not set the token aside for use and the user spent it before deposit was called. - The channel was closed/settled between the allowance call and the deposit call. AddressWithoutCode: The channel was settled during the deposit execution. DepositOverLimit: The total deposit amount is higher than the limit. """ chain_state = views.state_from_raiden(self.raiden) token_networks = views.get_token_network_addresses_for( chain_state, registry_address, ) channel_state = views.get_channelstate_for( chain_state, registry_address, token_address, partner_address, ) if not is_binary_address(token_address): raise InvalidAddress('Expected binary address format for token in channel deposit') if not is_binary_address(partner_address): raise InvalidAddress('Expected binary address format for partner in channel deposit') if token_address not in token_networks: raise UnknownTokenAddress('Unknown token address') if channel_state is None: raise InvalidAddress('No channel with partner_address for the given token') token = self.raiden.chain.token(token_address) netcontract_address = channel_state.identifier token_network_registry = self.raiden.chain.token_network_registry(registry_address) token_network_proxy = token_network_registry.token_network_by_token(token_address) channel_proxy = self.raiden.chain.payment_channel( token_network_proxy.address, netcontract_address, ) balance = token.balance_of(self.raiden.address) deposit_limit = token_network_proxy.proxy.contract.functions.deposit_limit().call() if total_deposit > deposit_limit: raise DepositOverLimit( 'The deposit of {} is bigger than the current limit of {}'.format( total_deposit, deposit_limit, ), ) if total_deposit <= channel_state.our_state.contract_balance: # no action required return addendum = total_deposit - channel_state.our_state.contract_balance # If this check succeeds it does not imply the the `deposit` will # succeed, since the `deposit` transaction may race with another # transaction. if not balance >= addendum: msg = 'Not enough balance to deposit. {} Available={} Needed={}'.format( pex(token_address), balance, addendum, ) raise InsufficientFunds(msg) # If concurrent operations are happening on the channel, fail the request if not channel_proxy.channel_operations_lock.acquire(blocking=False): raise ChannelBusyError( f'Channel with id {channel_state.identifier} is ' f'busy with another ongoing operation', ) with releasing(channel_proxy.channel_operations_lock): # set_total_deposit calls approve # token.approve(netcontract_address, addendum) channel_proxy.set_total_deposit(total_deposit) msg = 'After {} seconds the deposit was not properly processed.'.format( poll_timeout, ) # Wait until the `ChannelNewBalance` event is processed. with gevent.Timeout(poll_timeout, EthNodeCommunicationError(msg)): target_address = self.raiden.address waiting.wait_for_participant_newbalance( self.raiden, registry_address, token_address, partner_address, target_address, total_deposit, retry_timeout, )