def deposit(self, token_address, partner_address, amount): """ Deposit `amount` in the channel with the peer at `partner_address` and the given `token_address` in order to be able to do transfers. """ if not isaddress(token_address): raise InvalidAddress( 'Expected binary address format for token in channel deposit') if not isaddress(partner_address): raise InvalidAddress( 'Expected binary address format for partner in channel deposit' ) graph = self.raiden.channelgraphs[token_address] channel = graph.partneraddress_channel[partner_address] netcontract_address = channel.external_state.netting_channel.address assert len(netcontract_address) # Obtain a reference to the token and approve the amount for funding token = self.raiden.chain.token(token_address) balance = token.balance_of(self.raiden.address.encode('hex')) if not balance >= amount: msg = "Not enough balance for token'{}' [{}]: have={}, need={}".format( token.proxy.name(), pex(token_address), balance, amount) raise InsufficientFunds(msg) token.approve(netcontract_address, amount) # Obtain the netting channel and fund it by depositing the amount netting_channel = self.raiden.chain.netting_channel( netcontract_address) netting_channel.deposit(amount) return channel
def check_for_insufficient_eth( self, address: Address, transaction_name: str, transaction_executed: bool, required_gas: int, block_identifier: BlockSpecification, ): """ After estimate gas failure checks if our address has enough balance. If the account did not have enough ETH balance to execute the, transaction then it raises an `InsufficientFunds` error. Note: This check contains a race condition, it could be the case that a new block is mined changing the account's balance. https://github.com/raiden-network/raiden/issues/3890#issuecomment-485857726 """ if transaction_executed: return our_address = to_checksum_address(self.address) balance = self.web3.eth.getBalance(our_address, block_identifier) required_balance = required_gas * self.gas_price() if balance < required_balance: msg = f"Failed to execute {transaction_name} due to insufficient ETH" log.critical(msg, required_wei=required_balance, actual_wei=balance) raise InsufficientFunds(msg)
def transact(self, function_name: str, startgas: int, *args, **kargs): data = ContractProxy.get_transaction_data(self.contract.abi, function_name, args) try: txhash = self.jsonrpc_client.send_transaction( to=self.contract.address, startgas=startgas, value=kargs.pop('value', 0), data=decode_hex(data), **kargs, ) except ValueError as e: action = inspect_client_error(e, self.jsonrpc_client.eth_node) if action == ClientErrorInspectResult.INSUFFICIENT_FUNDS: raise InsufficientFunds('Insufficient ETH for transaction') elif action == ClientErrorInspectResult.TRANSACTION_UNDERPRICED: raise ReplacementTransactionUnderpriced( 'Transaction was rejected. This is potentially ' 'caused by the reuse of the previous transaction ' 'nonce as well as paying an amount of gas less than or ' 'equal to the previous transaction\'s gas amount', ) elif action == ClientErrorInspectResult.TRANSACTION_PENDING: raise TransactionAlreadyPending( 'The transaction has already been submitted. Please ' 'wait until is has been mined or increase the gas price.', ) raise e return txhash
def transact( self, function_name: str, startgas: int, *args, **kargs, ) -> typing.TransactionHash: data = ContractProxy.get_transaction_data( self.contract.abi, function_name, args=args, kwargs=kargs, ) try: txhash = self.jsonrpc_client.send_transaction( to=self.contract.address, startgas=startgas, value=kargs.pop('value', 0), data=decode_hex(data), ) except ValueError as e: action = inspect_client_error(e, self.jsonrpc_client.eth_node) if action == ClientErrorInspectResult.INSUFFICIENT_FUNDS: raise InsufficientFunds('Insufficient ETH for transaction') elif action == ClientErrorInspectResult.TRANSACTION_UNDERPRICED: raise ReplacementTransactionUnderpriced( 'Transaction was rejected. This is potentially ' 'caused by the reuse of the previous transaction ' 'nonce as well as paying an amount of gas less than or ' 'equal to the previous transaction\'s gas amount', ) elif action == ClientErrorInspectResult.TRANSACTION_PENDING: raise TransactionAlreadyPending( 'The transaction has already been submitted. Please ' 'wait until is has been mined or increase the gas price.', ) elif action == ClientErrorInspectResult.TRANSACTION_ALREADY_IMPORTED: # This is like TRANSACTION_PENDING is for geth but happens in parity # Unlike with geth this can also happen without multiple transactions # being sent via RPC -- due to probably some parity bug: # https://github.com/raiden-network/raiden/issues/3211 # We will try to not crash by looking into the local parity # transaction pool to retrieve the transaction hash hex_address = to_checksum_address(self.jsonrpc_client.address) txhash = self.jsonrpc_client.parity_get_pending_transaction_hash_by_nonce( address=hex_address, nonce=self.jsonrpc_client._available_nonce, ) if txhash: raise TransactionAlreadyPending( 'Transaction was submitted via parity but parity saw it as' ' already pending. Could not find the transaction in the ' 'local transaction pool. Bailing ...', ) return txhash raise e return txhash
def deposit(self, token_address, partner_address, amount): """ Deposit `amount` in the channel with the peer at `partner_address` and the given `token_address` in order to be able to do transfers. """ if not isaddress(token_address): raise InvalidAddress( 'Expected binary address format for token in channel deposit') if not isaddress(partner_address): raise InvalidAddress( 'Expected binary address format for partner in channel deposit' ) graph = self.raiden.token_to_channelgraph[token_address] channel = graph.partneraddress_to_channel[partner_address] netcontract_address = channel.external_state.netting_channel.address assert len(netcontract_address) # Obtain a reference to the token and approve the amount for funding token = self.raiden.chain.token(token_address) balance = token.balance_of(self.raiden.address.encode('hex')) if not balance >= amount: msg = "Not enough balance for token'{}' [{}]: have={}, need={}".format( token.proxy.name(), pex(token_address), balance, amount) raise InsufficientFunds(msg) token.approve(netcontract_address, amount) # Obtain the netting channel and fund it by depositing the amount old_balance = channel.contract_balance netting_channel = self.raiden.chain.netting_channel( netcontract_address) # actually make the deposit, and wait for it to be mined netting_channel.deposit(amount) # Wait until the balance has been updated via a state transition triggered # by processing the `ChannelNewBalance` event. # Usually, it'll take a single gevent.sleep, as we already have waited # for it to be mined, and only need to give the event handling greenlet # the chance to process the event, but let's wait 10s to be safe wait_timeout_secs = 10 # FIXME: hardcoded timeout if not wait_until( lambda: channel.contract_balance != old_balance, wait_timeout_secs, self.raiden.alarm.wait_time, ): raise EthNodeCommunicationError( 'After {} seconds the deposit was not properly processed.'. format(wait_timeout_secs)) return channel
def deposit(self, token_address, partner_address, amount): """ Deposit `amount` in the channel with the peer at `partner_address` and the given `token_address` in order to be able to do transfers. """ if not isaddress(token_address): raise InvalidAddress('Expected binary address format for token in channel deposit') if not isaddress(partner_address): raise InvalidAddress('Expected binary address format for partner in channel deposit') graph = self.raiden.token_to_channelgraph[token_address] channel = graph.partneraddress_to_channel[partner_address] netcontract_address = channel.external_state.netting_channel.address assert len(netcontract_address) # Obtain a reference to the token and approve the amount for funding token = self.raiden.chain.token(token_address) balance = token.balance_of(self.raiden.address.encode('hex')) if not balance >= amount: msg = "Not enough balance for token'{}' [{}]: have={}, need={}".format( token.proxy.name(), pex(token_address), balance, amount ) raise InsufficientFunds(msg) token.approve(netcontract_address, amount) # Obtain the netting channel and fund it by depositing the amount old_balance = channel.contract_balance netting_channel = self.raiden.chain.netting_channel(netcontract_address) netting_channel.deposit(amount) # Wait until the balance has been updated via a state transition triggered # by processing the `ChannelNewBalance` event wait_for = gevent.spawn( channel.wait_for_balance_update, old_balance, self.raiden.alarm.wait_time ) wait_timeout_secs = 60 gevent.wait([wait_for], timeout=wait_timeout_secs) # If balance is still the same then that means we did not get the event # after `timeout` seconds. if old_balance == channel.contract_balance: raise EthNodeCommunicationError( 'After {} seconds the deposit event was not seen by the ethereum node.'.format( wait_timeout_secs ) ) return channel
def transact(self, function_name: str, *args, **kargs): data = ContractProxy.get_transaction_data(self.contract.abi, function_name, args) try: txhash = self.jsonrpc_client.send_transaction( to=self.contract.address, value=kargs.pop('value', 0), data=decode_hex(data), **kargs, ) except ValueError as e: action = inspect_client_error(e, self.jsonrpc_client.eth_node) if action == ClientErrorInspectResult.INSUFFICIENT_FUNDS: raise InsufficientFunds('Insufficient ETH for transaction') raise e return txhash
def check_for_insufficient_eth( self, transaction_name: str, transaction_executed: bool, required_gas: int, block_identifier: BlockSpecification, ): """ After estimate gas failure checks if our address has enough balance. If the account did not have enough ETH balance to execute the, transaction then it raises an `InsufficientFunds` error """ if transaction_executed: return our_address = to_checksum_address(self.address) balance = self.web3.eth.getBalance(our_address, block_identifier) required_balance = required_gas * self.gas_price() if balance < required_balance: msg = f'Failed to execute {transaction_name} due to insufficient ETH' log.critical(msg, required_wei=required_balance, actual_wei=balance) raise InsufficientFunds(msg)
def deposit(self, 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. """ if not isaddress(token_address): raise InvalidAddress( 'Expected binary address format for token in channel deposit') if not isaddress(partner_address): raise InvalidAddress( 'Expected binary address format for partner in channel deposit' ) graph = self.raiden.token_to_channelgraph.get(token_address) if graph is None: raise InvalidAddress('Unknown token address') channel = graph.partneraddress_to_channel.get(partner_address) if channel is None: raise InvalidAddress( 'No channel with partner_address for the given token') if channel.token_address != token_address: raise InvalidAddress( 'token_address does not match the netting channel attribute') token = self.raiden.chain.token(token_address) netcontract_address = channel.external_state.netting_channel.address old_balance = channel.contract_balance # Checking the balance is not helpful since this requires multiple # transactions that can race, e.g. the deposit check succeed but the # user spent his balance before deposit. balance = token.balance_of(hexlify(self.raiden.address)) if not balance >= amount: msg = 'Not enough balance to deposit. {} Available={} Tried={}'.format( pex(token_address), balance, amount, ) raise InsufficientFunds(msg) token.approve(netcontract_address, amount) channel_proxy = self.raiden.chain.netting_channel(netcontract_address) channel_proxy.deposit(amount) # Wait until the `ChannelNewBalance` event is processed. # # Usually a single sleep is sufficient, since the `deposit` waits for # the transaction to be polled. sucess = wait_until( lambda: channel.contract_balance != old_balance, poll_timeout, self.raiden.alarm.wait_time, ) if not sucess: raise EthNodeCommunicationError( 'After {} seconds the deposit was not properly processed.'. format(poll_timeout)) return channel
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, 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_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_proxy.address, netcontract_address, ) balance = token.balance_of(self.raiden.address) if self.raiden.config['network_type'] == NetworkType.MAIN: 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, ), ) 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 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( self.raiden, registry_address, token_address, partner_address, target_address, total_deposit, retry_timeout, )
def channel_deposit(self, 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) registry_address = self.raiden.default_registry.address 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 isaddress(token_address): raise InvalidAddress( 'Expected binary address format for token in channel deposit') if not isaddress(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(hexlify(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)): waiting.wait_for_newbalance( self.raiden, registry_address, token_address, partner_address, target_balance, self.raiden.alarm.wait_time, )
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_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 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. """ 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) 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, )
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, )