def new_netting_channel( self, partner: typing.Address, settle_timeout: int, ) -> typing.ChannelID: """ Creates a new channel in the TokenNetwork contract. Args: partner: The peer to open the channel with. settle_timeout: The settle timeout to use for this channel. Returns: The ChannelID of the new netting channel. """ if not is_binary_address(partner): raise InvalidAddress( 'Expected binary address format for channel partner') invalid_timeout = (settle_timeout < self.settlement_timeout_min() or settle_timeout > self.settlement_timeout_max()) if invalid_timeout: raise InvalidSettleTimeout( 'settle_timeout must be in range [{}, {}], is {}'.format( self.settlement_timeout_min(), self.settlement_timeout_max(), settle_timeout, )) if self.node_address == partner: raise SamePeerAddress( 'The other peer must not have the same address as the client.') log_details = { 'peer1': pex(self.node_address), 'peer2': pex(partner), } log.debug('new_netting_channel called', **log_details) # Prevent concurrent attempts to open a channel with the same token and # partner address. if partner not in self.open_channel_transactions: new_open_channel_transaction = AsyncResult() self.open_channel_transactions[ partner] = new_open_channel_transaction try: transaction_hash = self._new_netting_channel( partner, settle_timeout) except Exception as e: log.critical('new_netting_channel failed', **log_details) new_open_channel_transaction.set_exception(e) raise else: new_open_channel_transaction.set(transaction_hash) finally: self.open_channel_transactions.pop(partner, None) else: # All other concurrent threads should block on the result of opening this channel self.open_channel_transactions[partner].get() channel_created = self.channel_exists_and_not_settled( self.node_address, partner) if channel_created is False: log.critical('new_netting_channel failed', **log_details) raise RaidenUnrecoverableError('creating new channel failed') channel_identifier = self.detail_channel(self.node_address, partner).channel_identifier log_details['channel_identifier'] = channel_identifier log.info('new_netting_channel successful', **log_details) return channel_identifier
def expect_token_swap( self, identifier, maker_token, maker_amount, maker_address, taker_token, taker_amount, taker_address): """ Register an expected transfer for this node. If a MediatedMessage is received for the `maker_asset` with `maker_amount` then proceed to send a MediatedTransfer to `maker_address` for `taker_asset` with `taker_amout`. """ if not isaddress(maker_token): raise InvalidAddress( 'Address for maker token is not in expected binary format in expect_token_swap' ) if not isaddress(maker_address): raise InvalidAddress( 'Address for maker is not in expected binary format in expect_token_swap' ) if not isaddress(taker_token): raise InvalidAddress( 'Address for taker token is not in expected binary format in expect_token_swap' ) if not isaddress(taker_address): raise InvalidAddress( 'Address for taker is not in expected binary format in expect_token_swap' ) channelgraphs = self.raiden.channelgraphs if taker_token not in channelgraphs: log.error('Unknown token {}'.format(pex(taker_token))) return if maker_token not in channelgraphs: log.error('Unknown token {}'.format(pex(maker_token))) return # the taker is expecting the maker transfer key = SwapKey( identifier, maker_token, maker_amount, ) token_swap = TokenSwap( identifier, maker_token, maker_amount, maker_address, taker_token, taker_amount, taker_address, ) self.raiden.swapkeys_tokenswaps[key] = token_swap
def token_swap_async(self, identifier, maker_token, maker_amount, maker_address, taker_token, taker_amount, taker_address): """ Start a token swap operation by sending a MediatedTransfer with `maker_amount` of `maker_token` to `taker_address`. Only proceed when a new valid MediatedTransfer is received with `taker_amount` of `taker_token`. """ if not isaddress(maker_token): raise InvalidAddress( 'Address for maker token is not in expected binary format in token swap' ) if not isaddress(maker_address): raise InvalidAddress( 'Address for maker is not in expected binary format in token swap' ) if not isaddress(taker_token): raise InvalidAddress( 'Address for taker token is not in expected binary format in token swap' ) if not isaddress(taker_address): raise InvalidAddress( 'Address for taker is not in expected binary format in token swap' ) channelgraphs = self.raiden.channelgraphs if taker_token not in channelgraphs: log.error('Unknown token {}'.format(pex(taker_token))) return if maker_token not in channelgraphs: log.error('Unknown token {}'.format(pex(maker_token))) return token_swap = TokenSwap( identifier, maker_token, maker_amount, maker_address, taker_token, taker_amount, taker_address, ) async_result = AsyncResult() task = MakerTokenSwapTask( self.raiden, token_swap, async_result, ) task.start() # the maker is expecting the taker transfer key = SwapKey( identifier, taker_token, taker_amount, ) self.raiden.swapkeys_greenlettasks[key] = task self.raiden.swapkeys_tokenswaps[key] = token_swap return async_result
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 closing transactions were 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 get_channel_list(self, registry_address, token_address=None, partner_address=None): """Returns a list of channels associated with the optionally given `token_address` and/or `partner_address`. Args: token_address (bin): an optionally provided token address partner_address (bin): 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 token_address and not is_binary_address(token_address): raise InvalidAddress( 'Expected binary address format for token in get_channel_list') if partner_address and not is_binary_address(partner_address): raise InvalidAddress( 'Expected binary address format for partner in get_channel_list' ) result = list() if token_address and partner_address: channel_state = views.get_channelstate_for( views.state_from_raiden(self.raiden), registry_address, token_address, partner_address, ) if channel_state: result = [channel_state] else: result = [] elif token_address: result = views.list_channelstate_for_tokennetwork( views.state_from_raiden(self.raiden), registry_address, token_address, ) elif partner_address: result = views.list_channelstate_for_tokennetwork( views.state_from_raiden(self.raiden), registry_address, partner_address, ) else: result = views.list_all_channelstate( views.state_from_raiden(self.raiden), ) return result
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 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_open( self, registry_address: typing.PaymentNetworkID, token_address: typing.TokenAddress, partner_address: typing.Address, settle_timeout: typing.BlockTimeout = None, retry_timeout: typing.NetworkTimeout = DEFAULT_RETRY_TIMEOUT, ) -> typing.ChannelID: """ Open a channel with the peer at `partner_address` with the given `token_address`. """ if settle_timeout is None: settle_timeout = self.raiden.config['settle_timeout'] if settle_timeout < self.raiden.config['reveal_timeout'] * 2: raise InvalidSettleTimeout( 'settle_timeout can not be smaller than double the reveal_timeout', ) if not is_binary_address(registry_address): raise InvalidAddress('Expected binary address format for registry in channel open') if not is_binary_address(token_address): raise InvalidAddress('Expected binary address format for token in channel open') if not is_binary_address(partner_address): raise InvalidAddress('Expected binary address format for partner in channel open') chain_state = views.state_from_raiden(self.raiden) channel_state = views.get_channelstate_for( chain_state, registry_address, token_address, partner_address, ) if channel_state: raise DuplicatedChannelError('Channel with given partner address already exists') registry = self.raiden.chain.token_network_registry(registry_address) token_network_address = registry.get_token_network(token_address) if token_network_address is None: raise TokenNotRegistered( 'Token network for token %s does not exist' % to_checksum_address(token_address), ) token_network = self.raiden.chain.token_network( registry.get_token_network(token_address), ) has_enough_reserve, estimated_required_reserve = has_enough_gas_reserve( self.raiden, channels_to_open=1, ) if not has_enough_reserve: raise InsufficientGasReserve(( 'The account balance is below the estimated amount necessary to ' 'finish the lifecycles of all active channels. A balance of at ' f'least {estimated_required_reserve} wei is required.' )) try: token_network.new_netting_channel( partner_address, settle_timeout, ) except DuplicatedChannelError: log.info('partner opened channel first') waiting.wait_for_newchannel( self.raiden, registry_address, token_address, partner_address, retry_timeout, ) chain_state = views.state_from_raiden(self.raiden) channel_state = views.get_channelstate_for( chain_state, registry_address, token_address, partner_address, ) return channel_state.identifier
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, ), ) 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 get(self, node_address): try: return self.nodeid_to_hostport[node_address] except KeyError: raise InvalidAddress('Unknown address {}'.format( pex(node_address)))
def channel_open( self, registry_address, token_address, partner_address, settle_timeout=None, reveal_timeout=None, poll_timeout=DEFAULT_POLL_TIMEOUT, retry_timeout=DEFAULT_RETRY_TIMEOUT, ): """ Open a channel with the peer at `partner_address` with the given `token_address`. """ if reveal_timeout is None: reveal_timeout = self.raiden.config['reveal_timeout'] if settle_timeout is None: settle_timeout = self.raiden.config['settle_timeout'] if settle_timeout <= reveal_timeout: raise InvalidSettleTimeout( 'reveal_timeout can not be larger-or-equal to settle_timeout', ) if not is_binary_address(registry_address): raise InvalidAddress( 'Expected binary address format for registry in channel open') if not is_binary_address(token_address): raise InvalidAddress( 'Expected binary address format for token in channel open') if not is_binary_address(partner_address): raise InvalidAddress( 'Expected binary address format for partner in channel open') chain_state = views.state_from_raiden(self.raiden) channel_state = views.get_channelstate_for( chain_state, registry_address, token_address, partner_address, ) if channel_state: raise DuplicatedChannelError( 'Channel with given partner address already exists') registry = self.raiden.chain.token_network_registry(registry_address) token_network_address = registry.get_token_network(token_address) if token_network_address is None: raise TokenNotRegistered( 'Token network for token %s does not exist' % to_checksum_address(token_address), ) token_network = self.raiden.chain.token_network( registry.get_token_network(token_address), ) try: token_network.new_netting_channel( partner_address, settle_timeout, ) except DuplicatedChannelError: log.info('partner opened channel first') msg = 'After {} seconds the channel was not properly created.'.format( poll_timeout, ) with gevent.Timeout(poll_timeout, EthNodeCommunicationError(msg)): waiting.wait_for_newchannel( self.raiden, registry_address, token_address, partner_address, retry_timeout, ) chain_state = views.state_from_raiden(self.raiden) channel_state = views.get_channelstate_for( chain_state, registry_address, token_address, partner_address, ) return channel_state.identifier
def new_netting_channel( self, partner: typing.Address, settle_timeout: int, ) -> typing.ChannelID: """ Creates a new channel in the TokenNetwork contract. Args: partner: The peer to open the channel with. settle_timeout: The settle timout to use for this channel. Returns: The address of the new netting channel. """ if not is_binary_address(partner): raise InvalidAddress( 'Expected binary address format for channel partner') invalid_timeout = (settle_timeout < NETTINGCHANNEL_SETTLE_TIMEOUT_MIN or settle_timeout > NETTINGCHANNEL_SETTLE_TIMEOUT_MAX) if invalid_timeout: raise InvalidSettleTimeout( 'settle_timeout must be in range [{}, {}]'.format( NETTINGCHANNEL_SETTLE_TIMEOUT_MIN, NETTINGCHANNEL_SETTLE_TIMEOUT_MAX, )) if self.node_address == partner: raise SamePeerAddress( 'The other peer must not have the same address as the client.') # Prevent concurrent attempts to open a channel with the same token and # partner address. if partner not in self.open_channel_transactions: new_open_channel_transaction = AsyncResult() self.open_channel_transactions[ partner] = new_open_channel_transaction try: transaction_hash = self._new_netting_channel( partner, settle_timeout) except Exception as e: new_open_channel_transaction.set_exception(e) raise else: new_open_channel_transaction.set(transaction_hash) finally: self.open_channel_transactions.pop(partner, None) else: # All other concurrent threads should block on the result of opening this channel transaction_hash = self.open_channel_transactions[partner].get() channel_created = self.channel_exists(partner) if channel_created is False: log.error( 'creating new channel failed', peer1=pex(self.node_address), peer2=pex(partner), ) raise RuntimeError('creating new channel failed') channel_identifier = self.detail_channel(partner)['channel_identifier'] log.info( 'new_netting_channel called', peer1=pex(self.node_address), peer2=pex(partner), channel_identifier=channel_identifier, ) return channel_identifier
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, )
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_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.') 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 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 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. 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(self.raiden.address.encode('hex')) 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. wait_timeout_secs = 10 # FIXME: hardcoded timeout sucess = wait_until( lambda: channel.contract_balance != old_balance, wait_timeout_secs, self.raiden.alarm.wait_time, ) if not sucess: raise EthNodeCommunicationError( 'After {} seconds the deposit was not properly processed.'.format( wait_timeout_secs ) ) return channel