def new_netting_channel(self, peer1, peer2, settle_timeout): """ Creates a new netting contract between peer1 and peer2. Raises: ValueError: If peer1 or peer2 is not a valid address. """ if not isaddress(peer1): raise ValueError('The pee1 must be a valid address') if not isaddress(peer2): raise ValueError('The peer2 must be a valid address') if privatekey_to_address(self.private_key) == peer1: other = peer2 else: other = peer1 netting_channel_address_hex = self.proxy.newChannel(other, settle_timeout) self.tester_state.mine(number_of_blocks=1) channel = NettingChannelTesterMock( self.tester_state, self.private_key, netting_channel_address_hex, ) return decode_hex(channel.address)
def open_channel( self, token_address, partner_address, settle_timeout=None, reveal_timeout=None): invalid_timeout = ( settle_timeout < NETTINGCHANNEL_SETTLE_TIMEOUT_MIN or settle_timeout > NETTINGCHANNEL_SETTLE_TIMEOUT_MAX ) if invalid_timeout: raise InvalidSettleTimeout('`settle_timeout` should be in range [{}, {}].'.format( NETTINGCHANNEL_SETTLE_TIMEOUT_MIN, NETTINGCHANNEL_SETTLE_TIMEOUT_MAX )) if not isaddress(token_address): raise InvalidAddress('Expected binary address format for token in channel open') if not isaddress(partner_address): raise InvalidAddress('Expected binary address format for partner in channel open') reveal_value = reveal_timeout if reveal_timeout is not None else self.reveal_timeout channel = self.make_channel( token_address=token_address, partner_address=partner_address, settle_timeout=settle_timeout, reveal_timeout=reveal_value ) self.channels.append(channel) return channel
def transfer_async(self, asset_address, amount, target, identifier=None, callback=None): # pylint: disable=too-many-arguments if not isinstance(amount, (int, long)): raise InvalidAmount('Amount not a number') if amount <= 0: raise InvalidAmount('Amount negative') asset_address_bin = safe_address_decode(asset_address) target_bin = safe_address_decode(target) if not isaddress(asset_address_bin) or asset_address_bin not in self.assets: raise InvalidAddress('asset address is not valid.') if not isaddress(target_bin): raise InvalidAddress('target address is not valid.') asset_manager = self.raiden.get_manager_by_asset_address(asset_address_bin) if not asset_manager.has_path(self.raiden.address, target_bin): raise NoPathError('No path to address found') transfer_manager = asset_manager.transfermanager async_result = transfer_manager.transfer_async( amount, target_bin, identifier=identifier, callback=callback, ) return async_result
def settle(self, asset_address, partner_address): """ Settle a closed channel with `partner_address` for the given `asset_address`. """ asset_address_bin = safe_address_decode(asset_address) partner_address_bin = safe_address_decode(partner_address) if not isaddress(asset_address_bin) or asset_address_bin not in self.assets: raise InvalidAddress('asset address is not valid.') if not isaddress(partner_address_bin): raise InvalidAddress('partner_address is not valid.') manager = self.raiden.get_manager_by_asset_address(asset_address_bin) channel = manager.get_channel_by_partner_address(partner_address_bin) if channel.isopen: raise InvalidState('channel is still open.') netting_channel = channel.external_state.netting_channel if not (self.raiden.chain.client.blocknumber() >= (channel.external_state.closed_block + netting_channel.detail(self.raiden.address)['settle_timeout'])): raise InvalidState('settlement period not over.') netting_channel.settle() return netting_channel
def transfer(self, asset_address, amount, target, callback=None): """ Do a transfer with `target` with the given `amount` of `asset_address`. """ if not isinstance(amount, (int, long)): raise InvalidAmount('Amount not a number') if amount <= 0: raise InvalidAmount('Amount negative') asset_address_bin = safe_address_decode(asset_address) target_bin = safe_address_decode(target) asset_manager = self.raiden.get_manager_by_asset_address(asset_address_bin) if not isaddress(asset_address_bin) or asset_address_bin not in self.assets: raise InvalidAddress('asset address is not valid.') if not isaddress(target_bin): raise InvalidAddress('target address is not valid.') if not asset_manager.has_path(self.raiden.address, target_bin): raise NoPathError('No path to address found') transfer_manager = self.raiden.managers_by_asset_address[asset_address_bin].transfermanager task = transfer_manager.transfer(amount, target_bin, callback=callback) task.join()
def close(self, asset_address, partner_address): """ Close a channel opened with `partner_address` for the given `asset_address`. """ asset_address_bin = safe_address_decode(asset_address) partner_address_bin = safe_address_decode(partner_address) if not isaddress(asset_address_bin) or asset_address_bin not in self.assets: raise InvalidAddress('asset address is not valid.') if not isaddress(partner_address_bin): raise InvalidAddress('partner_address is not valid.') manager = self.raiden.get_manager_by_asset_address(asset_address_bin) channel = manager.get_channel_by_partner_address(partner_address_bin) first_transfer = None if channel.received_transfers: first_transfer = channel.received_transfers[-1] second_transfer = None if channel.sent_transfers: second_transfer = channel.sent_transfers[-1] netting_channel = channel.external_state.netting_channel netting_channel.close( self.raiden.address, first_transfer, second_transfer, )
def make_graph(edge_list): """ Return a graph that represents the connections among the netting contracts. Args: edge_list (List[(address1, address2)]): All the channels that compose the graph. Returns: Graph A networkx.Graph instance were the graph nodes are nodes in the network and the edges are nodes that have a channel between them. """ for edge in edge_list: if len(edge) != 2: raise ValueError('All values in edge_list must be of length two (origin, destination)') origin, destination = edge if not isaddress(origin) or not isaddress(destination): raise ValueError('All values in edge_list must be valid addresses') graph = networkx.Graph() # undirected graph, for bidirectional channels for first, second in edge_list: graph.add_edge(first, second) return graph
def settle(self, token_address, partner_address): """ Settle a closed channel with `partner_address` for the given `token_address`. """ if not isaddress(token_address): raise InvalidAddress('Expected binary address format for token in channel settle') if not isaddress(partner_address): raise InvalidAddress('Expected binary address format for partner in channel settle') if not isaddress(token_address) or token_address not in self.tokens: raise InvalidAddress('token address is not valid.') if not isaddress(partner_address): raise InvalidAddress('partner_address is not valid.') graph = self.raiden.token_to_channelgraph[token_address] channel = graph.partneraddress_to_channel[partner_address] if channel.can_transfer: raise InvalidState('channel is still open.') netting_channel = channel.external_state.netting_channel current_block = self.raiden.chain.block_number() settle_timeout = netting_channel.detail()['settle_timeout'] settle_expiration = channel.external_state.closed_block + settle_timeout if current_block <= settle_expiration: raise InvalidState('settlement period is not yet over.') netting_channel.settle() return channel
def request_transfer(self, asset_address, amount, target): if not isaddress(asset_address) or asset_address not in self.assets: raise InvalidAddress('asset address is not valid.') if not isaddress(target): raise InvalidAddress('target address is not valid.') transfer_manager = self.raiden.assetmanagers[asset_address].transfermanager transfer_manager.request_transfer(amount, target)
def get_channel_list(self, 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 isaddress(token_address): raise InvalidAddress('Expected binary address format for token in get_channel_list') if partner_address and not isaddress(partner_address): raise InvalidAddress('Expected binary address format for partner in get_channel_list') result = list() if token_address and partner_address: graph = self.raiden.token_to_channelgraph[token_address] channel = graph.partneraddress_to_channel.get(partner_address) if channel: result = [channel] elif token_address: graph = self.raiden.token_to_channelgraph.get(token_address) if graph: result = list(graph.address_to_channel.values()) elif partner_address: partner_channels = [ graph.partneraddress_to_channel[partner_address] for graph in self.raiden.token_to_channelgraph.values() if partner_address in graph.partneraddress_to_channel ] result = partner_channels else: all_channels = list() for graph in self.raiden.token_to_channelgraph.values(): all_channels.extend(graph.address_to_channel.values()) result = all_channels return result
def get_shortest_paths(self, source, target): """Compute all shortest paths in the graph. Returns: generator of lists: A generator of all paths between source and target. """ if not isaddress(source) or not isaddress(target): raise ValueError('both source and target must be valid addresses') return networkx.all_shortest_paths(self.graph, source, target)
def __init__( self, jsonrpc_client, discovery_address, startgas, gasprice, poll_timeout=DEFAULT_POLL_TIMEOUT): if not isaddress(discovery_address): raise ValueError('discovery_address must be a valid address') check_address_has_code(jsonrpc_client, discovery_address, 'Discovery') proxy = jsonrpc_client.new_contract_proxy( CONTRACT_MANAGER.get_abi(CONTRACT_ENDPOINT_REGISTRY), address_encoder(discovery_address), ) self.address = discovery_address self.proxy = proxy self.client = jsonrpc_client self.startgas = startgas self.gasprice = gasprice self.poll_timeout = poll_timeout self.not_found_address = '0x' + '0' * 40
def send_async(self, receiver_address, message): if not isaddress(receiver_address): raise ValueError('Invalid address {}'.format(pex(receiver_address))) if isinstance(message, Ack): raise ValueError('Do not use send for Ack messages or Errors') if len(message.encode()) > self.max_message_size: raise ValueError('message size exceeds the maximum {}'.format(self.max_message_size)) messagedata = message.encode() # Adding the receiver address into the echohash to avoid collisions # among different receivers. # (Messages that are not unique per receiver # can result in hash colision, eg. Secret message sent to more than one # node, this hash collision has the undesired effect of aborting # message resubmission once a single node replied with an Ack) echohash = sha3(messagedata + receiver_address) # Don't add the same message twice into the queue if echohash not in self.echohash_asyncresult: ack_result = AsyncResult() self.echohash_asyncresult[echohash] = WaitAck(ack_result, receiver_address) # state changes are local to each channel/asset queue_name = getattr(message, 'asset', '') self._send(receiver_address, queue_name, message, messagedata, echohash) else: waitack = self.echohash_asyncresult[echohash] ack_result = waitack.ack_result return ack_result
def get_channel_events(self, channel_address, from_block, to_block=None): if not isaddress(channel_address): raise InvalidAddress( 'Expected binary address format for channel in get_channel_events' ) returned_events = get_all_netting_channel_events( self.raiden.chain, channel_address, events=ALL_EVENTS, from_block=from_block, to_block=to_block, ) raiden_events = self.raiden.transaction_log.get_events_in_block_range( from_block=from_block, to_block=to_block ) # Here choose which raiden internal events we want to expose to the end user for event in raiden_events: is_user_transfer_event = isinstance(event.event_object, ( EventTransferSentSuccess, EventTransferSentFailed, EventTransferReceivedSuccess )) if is_user_transfer_event: new_event = { 'block_number': event.block_number, '_event_type': type(event.event_object).__name__, } new_event.update(event.event_object.__dict__) returned_events.append(new_event) return returned_events
def add_token(self, token_address): if not isaddress(token_address): raise ValueError('token_address must be a valid address') transaction_hash = estimate_and_transact( self.proxy, 'addToken', self.startgas, self.gasprice, token_address, ) self.client.poll(unhexlify(transaction_hash), timeout=self.poll_timeout) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: raise TransactionThrew('AddToken', receipt_or_none) manager_address = self.manager_address_by_token(token_address) if manager_address is None: log.error('Transaction failed and check_transaction_threw didnt detect it') raise RuntimeError('channelManagerByToken failed') if log.isEnabledFor(logging.INFO): log.info( 'add_token called', token_address=pex(token_address), registry_address=pex(self.address), manager_address=pex(manager_address), ) return manager_address
def __init__( self, our_address, channelmanager_address, token_address, edge_list, channels_details): if not isaddress(token_address): raise ValueError('token_address must be a valid address') graph = make_graph(edge_list) self.address_to_channel = dict() self.graph = graph self.our_address = our_address self.partneraddress_to_channel = dict() self.token_address = token_address self.channelmanager_address = channelmanager_address for details in channels_details: try: self.add_channel(details) except ValueError as e: log.warn( 'Error at registering opened channel contract. Perhaps contract is invalid?', error=str(e), channel_address=pex(details.channel_address) )
def send_ping(self, receiver_address): if not isaddress(receiver_address): raise ValueError('Invalid address {}'.format(pex(receiver_address))) nonce = self._ping_nonces[receiver_address] self._ping_nonces[receiver_address] += 1 message = Ping(nonce) self.raiden.sign(message) if log.isEnabledFor(logging.INFO): log.info( 'SENDING PING %s > %s', pex(self.raiden.address), pex(receiver_address) ) message_data = message.encode() echohash = sha3(message_data + receiver_address) async_result = AsyncResult() if echohash not in self.echohash_asyncresult: self.echohash_asyncresult[echohash] = WaitAck(async_result, receiver_address) # Just like ACK, a PING message is sent directly. No need for queuing self.transport.send( self.raiden, self.discovery.get(receiver_address), message_data ) return async_result
def __init__(self, raiden, asset_address, channel_manager_address, channel_graph): """ Args: raiden (RaidenService): a node's service asset_address (bin): the asset address managed by this instance channel_manager_address (bin): The channel manager address. channelgraph (networkx.Graph): a graph representing the raiden network """ if not isaddress(asset_address): raise ValueError("asset_address must be a valid address") self.partneraddress_channel = dict() #: maps the partner address to the channel instance self.address_channel = dict() #: maps the channel address to the channel instance # This is a map from a hashlock to a list of channels, the same # hashlock can be used in more than one AssetManager (for exchanges), a # channel should be removed from this list only when the lock is # released/withdrawed but not when the secret is registered. self.hashlock_channel = defaultdict(list) #: channels waiting on the conditional lock self.asset_address = asset_address self.channel_manager_address = channel_manager_address self.channelgraph = channel_graph self.raiden = raiden transfermanager = TransferManager(self) self.transfermanager = transfermanager
def __init__( self, jsonrpc_client, registry_address, startgas, gasprice, poll_timeout=DEFAULT_POLL_TIMEOUT): # pylint: disable=too-many-arguments if not isaddress(registry_address): raise ValueError('registry_address must be a valid address') check_address_has_code(jsonrpc_client, registry_address, 'Registry') proxy = jsonrpc_client.new_contract_proxy( CONTRACT_MANAGER.get_abi(CONTRACT_REGISTRY), address_encoder(registry_address), ) self.address = registry_address self.proxy = proxy self.client = jsonrpc_client self.startgas = startgas self.gasprice = gasprice self.poll_timeout = poll_timeout self.address_to_channelmanager = dict() self.token_to_channelmanager = dict()
def manager_by_token(self, token_address): """ Find the channel manager for `token_address` and return a proxy to interact with it. If the token is not already registered it raises `EthNodeCommunicationError`, since we try to instantiate a Channel manager with an empty address. """ if not isaddress(token_address): raise ValueError('token_address must be a valid address') if token_address not in self.token_to_channelmanager: check_address_has_code(self.client, token_address) # check that the token exists manager_address = self.manager_address_by_token(token_address) if manager_address is None: raise NoTokenManager( 'Manager for token 0x{} does not exist'.format(hexlify(token_address)) ) manager = ChannelManager( self.client, manager_address, self.startgas, self.gasprice, self.poll_timeout, ) self.token_to_channelmanager[token_address] = manager self.address_to_channelmanager[manager_address] = manager return self.token_to_channelmanager[token_address]
def new_netting_channel(self, peer1, peer2, settle_timeout): """ Creates a new netting contract between peer1 and peer2. Raises: ValueError: If peer1 or peer2 is not a valid address. """ if not isaddress(peer1): raise ValueError('The pee1 must be a valid address') if not isaddress(peer2): raise ValueError('The peer2 must be a valid address') if peer1 == peer2: raise ValueError('Cannot open a channel with itself') pair = tuple(sorted((peer1, peer2))) if pair in self.pair_channel: raise ValueError('({}, {}) already have a channel'.format( encode_hex(peer1), encode_hex(peer2) )) channel = NettingChannelMock( self.asset_address(), peer1, peer2, settle_timeout, ) self.pair_channel[pair] = channel self.participant_channels[peer1].append(channel) self.participant_channels[peer2].append(channel) BlockChainServiceMock.address_contract[channel.address] = channel data = { '_event_type': 'ChannelNew', 'netting_channel': channel.address, 'participant1': peer1, 'participant2': peer2, 'settle_timeout': settle_timeout, } event = ethereum_event(CHANNELNEW_EVENTID, CHANNELNEW_EVENT, data, self.address) for filter_ in BlockChainServiceMock.filters[self.address]: filter_.event(event) return channel.address
def connection_manager_for_token(self, token_address): if not isaddress(token_address): raise InvalidAddress('token address is not valid.') if token_address in self.tokens_to_connectionmanagers.keys(): manager = self.tokens_to_connectionmanagers[token_address] else: raise InvalidAddress('token is not registered.') return manager
def get_channel(self, channel_address): if not isaddress(channel_address): raise InvalidAddress('Expected binary address format for channel in get_channel') channel_list = self.get_channel_list() for channel in channel_list: if channel.channel_address == channel_address: return channel raise ChannelNotFound()
def new_netting_channel(self, peer1, peer2, settle_timeout): if not isaddress(peer1): raise ValueError('The pee1 must be a valid address') if not isaddress(peer2): raise ValueError('The peer2 must be a valid address') if privatekey_to_address(self.client.privkey) == peer1: other = peer2 else: other = peer1 transaction_hash = self.proxy.newChannel.transact( other, settle_timeout, startgas=self.startgas, gasprice=self.gasprice, ) self.client.poll(transaction_hash.decode('hex'), timeout=self.poll_timeout) # TODO: raise if the transaction failed because there is an existing # channel in place netting_channel_address_encoded = self.proxy.getChannelWith.call( other, startgas=self.startgas, ) if not netting_channel_address_encoded: log.error('netting_channel_address failed', peer1=pex(peer1), peer2=pex(peer2)) raise RuntimeError('netting_channel_address failed') netting_channel_address_bin = address_decoder(netting_channel_address_encoded) log.info( 'new_netting_channel called', peer1=pex(peer1), peer2=pex(peer2), netting_channel=pex(netting_channel_address_bin), ) return netting_channel_address_bin
def maybe_send_ack(self, receiver_address, ack_message): """ Send ack_message to receiver_address if the transport is running. """ if not isaddress(receiver_address): raise InvalidAddress('Invalid address {}'.format(pex(receiver_address))) if not isinstance(ack_message, Ack): raise ValueError('Use maybe_send_ack only for Ack messages') messagedata = ack_message.encode() self.receivedhashes_to_acks[ack_message.echo] = (receiver_address, messagedata) self._maybe_send_ack(*self.receivedhashes_to_acks[ack_message.echo])
def send(self, receiver_address, msg): if not isaddress(receiver_address): raise ValueError('Invalid address {}'.format(pex(receiver_address))) if isinstance(msg, (Ack, BaseError)): raise ValueError('Do not use send for Ack messages or Erorrs') if len(msg.encode()) > self.max_message_size: raise ValueError('message size excedes the maximum {}'.format(self.max_message_size)) return gevent.spawn(self._repeat_until_ack, receiver_address, msg)
def open( self, token_address, partner_address, settle_timeout=None, reveal_timeout=None): """ 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 isaddress(token_address): raise InvalidAddress('Expected binary address format for token in channel open') if not isaddress(partner_address): raise InvalidAddress('Expected binary address format for partner in channel open') channel_manager = self.raiden.default_registry.manager_by_token(token_address) assert token_address in self.raiden.token_to_channelgraph netcontract_address = channel_manager.new_netting_channel( partner_address, settle_timeout, ) while netcontract_address not in self.raiden.chain.address_to_nettingchannel: gevent.sleep(self.raiden.alarm.wait_time) graph = self.raiden.token_to_channelgraph[token_address] while partner_address not in graph.partneraddress_to_channel: gevent.sleep(self.raiden.alarm.wait_time) channel = graph.partneraddress_to_channel[partner_address] return channel
def send(self, recipient, message): """ Send `message` to `recipient` using the raiden protocol. The protocol will take care of resending the message on a given interval until an Acknowledgment is received or a given number of tries. """ if not isaddress(recipient): raise ValueError('recipient is not a valid address.') self.protocol.send(recipient, message)
def transfer(self, asset_address, amount, target, callback=None): if not isinstance(amount, (int, long)): raise InvalidAmount('Amount not a number') if amount <= 0: raise InvalidAmount('Amount negative') asset_address = safe_address_decode(asset_address) target = safe_address_decode(target) if not isaddress(asset_address) or asset_address not in self.assets: raise InvalidAddress('asset address is not valid.') if not isaddress(target): raise InvalidAddress('target address is not valid.') if not self.raiden.has_path(asset_address, target): raise NoPathError('No path to address found') transfer_manager = self.raiden.assetmanagers[asset_address].transfermanager transfer_manager.transfer(amount, target, callback=callback)
def new_netting_contract(self, asset_address, peer1, peer2): """ Creates a new netting contract between peer1 and peer2. Raises: ValueError: If peer1 or peer2 is not a valid address. """ if not isaddress(peer1): raise ValueError('The pee1 must be a valid address') if not isaddress(peer2): raise ValueError('The peer2 must be a valid address') netcontract_address = bytes(sha3(peer1 + peer2)[:20]) hash_channel = self.asset_hashchannel[asset_address] if netcontract_address in hash_channel: raise ValueError('netting contract already exists') channel = NettingChannelContract(asset_address, netcontract_address, peer1, peer2) hash_channel[netcontract_address] = channel return netcontract_address
def register_token(self, token_address): """ Will register the token at `token_address` with raiden. If it's already registered, will throw an exception. """ if not isaddress(token_address): raise InvalidAddress('token_address must be a valid address in binary') try: self.raiden.default_registry.manager_by_token(token_address) except NoTokenManager: channel_manager_address = self.raiden.default_registry.add_token(token_address) self.raiden.register_channel_manager(channel_manager_address) return channel_manager_address raise ValueError('Token already registered')
def __init__(self, raiden, asset_address, channel_graph): """ Args: raiden (RaidenService): a node's service asset_address (address): the asset address managed by this instance channelgraph (networkx.Graph): a graph representing the raiden network """ if not isaddress(asset_address): raise ValueError('asset_address must be a valid address') self.asset_address = asset_address self.channelgraph = channel_graph transfermanager = TransferManager(self, raiden) self.channels = dict() #: mapping form partner_address -> channel object self.transfermanager = transfermanager #: handle's raiden transfers
def __init__(self, our_address, channelmanager_address, token_address, edge_list, channels_details, block_number): if not isaddress(token_address): raise ValueError('token_address must be a valid address') graph = make_graph(edge_list) self.address_channel = dict() self.graph = graph self.our_address = our_address self.partneraddress_channel = dict() self.token_address = token_address self.channelmanager_address = channelmanager_address for details in channels_details: self.add_channel(details, block_number)
def netting_channel(self, netting_channel_address): """ Return a proxy to interact with a NettingChannelContract. """ if not isaddress(netting_channel_address): raise ValueError('netting_channel_address must be a valid address') if netting_channel_address not in self.address_to_nettingchannel: channel = NettingChannel( self.client, netting_channel_address, self.startgas, self.gasprice, self.poll_timeout, ) self.address_to_nettingchannel[netting_channel_address] = channel return self.address_to_nettingchannel[netting_channel_address]
def send_async(self, receiver_address, message): if not isaddress(receiver_address): raise ValueError('Invalid address {}'.format( pex(receiver_address))) if isinstance(message, (Processed, Ping)): raise ValueError( 'Do not use send for `Processed` or `Ping` messages') messagedata = message.encode() if len(messagedata) > UDP_MAX_MESSAGE_SIZE: raise ValueError('message size exceeds the maximum {}'.format( UDP_MAX_MESSAGE_SIZE)) message_id = messageid_from_data(messagedata, receiver_address) # All messages must be ordered, but only on a per channel basis. token_address = getattr(message, 'token', b'') # Ignore duplicated messages if message_id not in self.messageids_to_states: async_result = AsyncResult() self.messageids_to_states[message_id] = SentMessageState( async_result, receiver_address, ) queue = self.get_channel_queue( receiver_address, token_address, ) if log.isEnabledFor(logging.DEBUG): log.debug( 'SENDING MESSAGE', to=pex(receiver_address), node=pex(self.raiden.address), message=message, message_id=message_id, ) queue.put(messagedata) else: wait_processed = self.messageids_to_states[message_id] async_result = wait_processed.async_result return async_result
def register(self, node_address, host, port): if node_address != self.node_address: raise ValueError('You can only register your own endpoint.') if not isaddress(node_address): raise ValueError('node_address must be a valid address') try: socket.inet_pton(socket.AF_INET, host) except OSError: raise ValueError('invalid ip address provided: {}'.format(host)) if not isinstance(port, (int, long)): raise ValueError('port must be a valid number') endpoint = host_port_to_endpoint(host, port) self.discovery_proxy.register_endpoint(node_address, endpoint)
def new_channel_manager_contract(self, asset_address): """ The equivalent of instatiating a new `ChannelManagerContract` contract that will manage channels for a given asset in the blockchain. Raises: ValueError: If asset_address is not a valid address or is already registered. """ if not isaddress(asset_address): raise ValueError('The asset must be a valid address') if asset_address in self.asset_hashchannel: raise ValueError('This asset already has a registered contract') manager_address = make_address() self.asset_hashchannel[asset_address] = dict() self.asset_address[asset_address] = manager_address return manager_address
def connect( self, token_address, funds, initial_channel_target, joinable_funds_target): if not isaddress(token_address): raise InvalidAddress('not an address %s' % pex(token_address)) funding = int((funds * joinable_funds_target) / initial_channel_target) for i in range(0, initial_channel_target): channel = self.make_channel(token_address=token_address, balance=funding) self.channels.append(channel) connection_manager = ConnectionManagerMock(token_address, funds) self.connection_managers.append(connection_manager)
def send_and_wait(self, recipient, message, timeout): """ Send `message` to `recipient` and wait for the response or `timeout`. Args: recipient (address): The address of the node that will receive the message. message: The transfer message. timeout (float): How long should we wait for a response from `recipient`. Returns: None: If the wait timed out object: The result from the event """ if not isaddress(recipient): raise ValueError('recipient is not a valid address.') self.protocol.send_and_wait(recipient, message, timeout)
def manager_address_if_token_registered(self, token_address): """ If the token is registered then, return the channel manager address. Also make sure that the channel manager is registered with the node. Returns None otherwise. """ if not isaddress(token_address): raise InvalidAddress('token_address must be a valid address in binary') try: manager = self.raiden.default_registry.manager_by_token(token_address) if not self.raiden.channel_manager_is_registered(manager.address): self.raiden.register_channel_manager(manager.address) return manager.address except (EthNodeCommunicationError, TransactionFailed, NoTokenManager): return None
def maybe_send_processed(self, receiver_address, processed_message): """ Send processed_message to receiver_address if the transport is running. """ if not isaddress(receiver_address): raise InvalidAddress('Invalid address {}'.format( pex(receiver_address))) if not isinstance(processed_message, Processed): raise ValueError( 'Use _maybe_send_processed only for `Processed` messages') messagedata = processed_message.encode() self.receivedhashes_to_processedmessages[processed_message.echo] = ( receiver_address, messagedata) self._maybe_send_processed( *self.receivedhashes_to_processedmessages[processed_message.echo])
def token_network_leave(self, registry_address, token_address, only_receiving=True): """Close all channels and wait for settlement.""" if not isaddress(token_address): raise InvalidAddress( 'token_address must be a valid address in binary') if token_address not in self.get_tokens_list(registry_address): raise UnknownTokenAddress('token_address unknown') connection_manager = self.raiden.connection_manager_for_token( registry_address, token_address, ) return connection_manager.leave(only_receiving)
def manager(self, manager_address): """ Return a proxy to interact with a ChannelManagerContract. """ if not isaddress(manager_address): raise ValueError('manager_address must be a valid address') if manager_address not in self.address_to_channelmanager: manager = ChannelManager( self.client, manager_address, poll_timeout=self.poll_timeout, ) token_address = manager.token_address() self.token_to_channelmanager[token_address] = manager self.address_to_channelmanager[manager_address] = manager return self.address_to_channelmanager[manager_address]
def send_ack(self, receiver_address, msg): assert isinstance(msg, (Ack, BaseError)) assert isaddress(receiver_address) host_port = self.discovery.get(receiver_address) data = msg.encode() msghash = sha3(data) log.info('SENDING ACK {} > {} : [{}] [echo={}] {}'.format( pex(self.raiden.address), pex(receiver_address), pex(msghash), pex(msg.echo), msg, )) self.transport.send(self.raiden, host_port, msg.encode()) self.sent_acks[msg.echo] = (receiver_address, msg)
def get_token_network_events(self, token_address, from_block, to_block): if not isaddress(token_address): raise InvalidAddress( 'Expected binary address format for token in get_token_network_events' ) try: graph = self.raiden.token_to_channelgraph[token_address] return get_all_channel_manager_events( self.raiden.chain, graph.channelmanager_address, events=ALL_EVENTS, from_block=from_block, to_block=to_block, ) except KeyError: raise UnknownTokenAddress('The token address is not registered.')
def send_async(self, receiver_address, message): if not isaddress(receiver_address): raise ValueError('Invalid address {}'.format(pex(receiver_address))) if isinstance(message, Ack): raise ValueError('Do not use send for Ack messages or Errors') if len(message.encode()) > self.max_message_size: raise ValueError('message size exceeds the maximum {}'.format(self.max_message_size)) messagedata = message.encode() messagehash = sha3(messagedata + receiver_address) ack_result = AsyncResult() self.msghash_asyncresult[messagehash] = ack_result self._send(receiver_address, message, messagedata, messagehash) return ack_result
def __init__(self, jsonrpc_client, token_address, poll_timeout=DEFAULT_POLL_TIMEOUT): if not isaddress(token_address): raise ValueError('token_address must be a valid address') check_address_has_code(jsonrpc_client, token_address, 'Token') proxy = jsonrpc_client.new_contract_proxy( CONTRACT_MANAGER.get_contract_abi(CONTRACT_HUMAN_STANDARD_TOKEN), address_encoder(token_address), ) self.address = token_address self.proxy = proxy self.client = jsonrpc_client self.poll_timeout = poll_timeout
def connection_manager_for_token(self, registry_address, token_address): if not isaddress(token_address): raise InvalidAddress('token address is not valid.') known_token_networks = views.get_token_network_addresses_for( self.wal.state_manager.current_state, registry_address, ) if token_address not in known_token_networks: raise InvalidAddress('token is not registered.') manager = self.tokens_to_connectionmanagers.get(token_address) if manager is None: manager = ConnectionManager(self, registry_address, token_address) self.tokens_to_connectionmanagers[token_address] = manager return manager
def token_network(self, token_network_address: typing.TokenNetworkAddress): """ Return a proxy to interact with a TokenNetwork. """ if not isaddress(token_network_address): raise InvalidAddress( 'Expected binary address format for token network') if token_network_address not in self.address_to_tokennetwork: token_network = TokenNetwork( self.client, token_network_address, self.poll_timeout, ) token_address = token_network.token_address() self.token_to_tokennetwork[token_address] = token_network self.address_to_tokennetwork[token_network_address] = token_network return self.address_to_tokennetwork[token_network_address]
def send_async(self, receiver_address, message): if not isaddress(receiver_address): raise ValueError('Invalid address {}'.format( pex(receiver_address))) if isinstance(message, (Ack, Ping)): raise ValueError('Do not use send for Ack or Ping messages') # Messages that are not unique per receiver can result in hash # collision, e.g. Secret messages. The hash collision has the undesired # effect of aborting message resubmission once /one/ of the nodes # replied with an Ack, adding the receiver address into the echohash to # avoid these collisions. messagedata = message.encode() echohash = sha3(messagedata + receiver_address) if len(messagedata) > UDP_MAX_MESSAGE_SIZE: raise ValueError('message size exceeds the maximum {}'.format( UDP_MAX_MESSAGE_SIZE)) # All messages must be ordered, but only on a per channel basis. token_address = getattr(message, 'token', '') # Ignore duplicated messages if echohash not in self.senthashes_to_states: async_result = AsyncResult() self.senthashes_to_states[echohash] = SentMessageState( async_result, receiver_address, ) queue = self.get_channel_queue( receiver_address, token_address, ) queue.put(messagedata) else: waitack = self.senthashes_to_states[echohash] async_result = waitack.async_result return async_result
def send_ack(self, receiver_address, message): if not isaddress(receiver_address): raise ValueError('Invalid address {}'.format(pex(receiver_address))) if not isinstance(message, Ack): raise ValueError('Use send_Ack only for Ack messages or Erorrs') if log.isEnabledFor(logging.INFO): log.info( 'SENDING ACK %s > %s %s', pex(self.raiden.address), pex(receiver_address), message, ) messagedata = message.encode() host_port = self.get_host_port(receiver_address) self.receivedhashes_to_acks[message.echo] = (host_port, messagedata) self._send_ack(*self.receivedhashes_to_acks[message.echo])
def __init__(self, jsonrpc_client, discovery_address, poll_timeout=DEFAULT_POLL_TIMEOUT): if not isaddress(discovery_address): raise ValueError('discovery_address must be a valid address') check_address_has_code(jsonrpc_client, discovery_address, 'Discovery') proxy = jsonrpc_client.new_contract_proxy( CONTRACT_MANAGER.get_abi(CONTRACT_ENDPOINT_REGISTRY), address_encoder(discovery_address), ) self.address = discovery_address self.proxy = proxy self.client = jsonrpc_client self.poll_timeout = poll_timeout self.not_found_address = NULL_ADDRESS
def send_async( self, recipient: typing.Address, queue_name: bytes, message: 'Message', ): """ Send a new ordered message to recipient. Messages that use the same `queue_name` are ordered. """ if not isaddress(recipient): raise ValueError('Invalid address {}'.format(pex(recipient))) # These are not protocol messages, but transport specific messages if isinstance(message, (Delivered, Ping, Pong)): raise ValueError('Do not use send for {} messages'.format( message.__class__.__name__)) messagedata = message.encode() if len(messagedata) > UDP_MAX_MESSAGE_SIZE: raise ValueError('message size exceeds the maximum {}'.format( UDP_MAX_MESSAGE_SIZE)) # message identifiers must be unique message_id = message.message_identifier # ignore duplicates if message_id not in self.messageids_to_asyncresults: self.messageids_to_asyncresults[message_id] = AsyncResult() queue = self.get_queue_for(recipient, queue_name) queue.put((messagedata, message_id)) log.debug( 'MESSAGE QUEUED', node=pex(self.raiden.address), queue_name=queue_name, to=pex(recipient), message=message, )
def __init__(self, jsonrpc_client, manager_address, poll_timeout=DEFAULT_POLL_TIMEOUT): # pylint: disable=too-many-arguments if not isaddress(manager_address): raise ValueError('manager_address must be a valid address') check_address_has_code(jsonrpc_client, manager_address, 'Channel Manager') proxy = jsonrpc_client.new_contract_proxy( CONTRACT_MANAGER.get_abi(CONTRACT_CHANNEL_MANAGER), address_encoder(manager_address), ) self.address = manager_address self.proxy = proxy self.client = jsonrpc_client self.poll_timeout = poll_timeout
def send_ack(self, receiver_address, message): if not isaddress(receiver_address): raise ValueError('Invalid address {}'.format( pex(receiver_address))) if not isinstance(message, (Ack, BaseError)): raise ValueError('Use send_Ack only for Ack messages or Erorrs') host_port = self.discovery.get(receiver_address) data = message.encode() msghash = sha3(data) log.info('SENDING ACK {} > {} : [{}] [echo={}] {}'.format( pex(self.raiden.address), pex(receiver_address), pex(msghash), pex(message.echo), message, )) self.transport.send(self.raiden, host_port, data) self.sent_acks[message.echo] = (receiver_address, message)
def __init__(self, raiden, asset_address, channel_manager_address, channel_graph): """ Args: raiden (RaidenService): a node's service asset_address (address): the asset address managed by this instance channelgraph (networkx.Graph): a graph representing the raiden network """ if not isaddress(asset_address): raise ValueError('asset_address must be a valid address') self.partneraddress_channel = dict() #: maps the partner address to the channel instance self.address_channel = dict() #: maps the channel address to the channel instance self.hashlock_channel = defaultdict(list) ''' A list of channels that are waiting on the conditional lock. ''' self.asset_address = asset_address self.channel_manager_address = channel_manager_address self.channelgraph = channel_graph self.raiden = raiden transfermanager = TransferManager(self) self.transfermanager = transfermanager
def connect_token_network(self, token_address, funds, initial_channel_target=3, joinable_funds_target=.4): """Instruct the ConnectionManager to establish and maintain a connection to the token network. If the `token_address` is not already part of the raiden network, this will also register the token. Args: token_address (bin): the ERC20 token network to connect to. funds (int): the amount of funds that can be used by the ConnectionMananger. initial_channel_target (int): number of channels to open proactively. joinable_funds_target (float): fraction of the funds that will be used to join channels opened by other participants. """ if not isaddress(token_address): raise InvalidAddress( 'token_address must be a valid address in binary') try: connection_manager = self.raiden.connection_manager_for_token( token_address) except InvalidAddress: # token is not yet registered self.raiden.default_registry.add_token(token_address) # wait for registration while token_address not in self.raiden.tokens_to_connectionmanagers: gevent.sleep(self.raiden.alarm.wait_time) connection_manager = self.raiden.connection_manager_for_token( token_address) connection_manager.connect( funds, initial_channel_target=initial_channel_target, joinable_funds_target=joinable_funds_target)
def __init__(self, our_address, channelmanager_address, token_address, edge_list, channels_details): if not isaddress(token_address): raise ValueError('token_address must be a valid address') graph = make_graph(edge_list) self.address_to_channel = dict() self.graph = graph self.our_address = our_address self.partneraddress_to_channel = dict() self.token_address = token_address self.channelmanager_address = channelmanager_address for details in channels_details: try: self.add_channel(details) except ValueError as e: log.warn( 'Error at registering opened channel contract. Perhaps contract is invalid?', error=str(e), channel_address=pex(details.channel_address))
def send_ack(self, receiver_address, message): if not isaddress(receiver_address): raise ValueError('Invalid address {}'.format( pex(receiver_address))) if not isinstance(message, Ack): raise ValueError('Use send_Ack only for Ack messages or Erorrs') host_port = self.discovery.get(receiver_address) messagedata = message.encode() messagehash = sha3(messagedata) log.info('SENDING ACK {} > {} : [{}] [echo={}] {}'.format( pex(self.raiden.address), pex(receiver_address), pex(messagehash), pex(message.echo), message, )) self.msghash_acks[message.echo] = (host_port, messagedata, messagehash) self._send_ack(*self.msghash_acks[message.echo])
def send_async(self, receiver_address, message): if not isaddress(receiver_address): raise ValueError('Invalid address {}'.format( pex(receiver_address))) if isinstance(message, Ack): raise ValueError('Do not use send for Ack messages or Errors') if len(message.encode()) > self.max_message_size: raise ValueError('message size exceeds the maximum {}'.format( self.max_message_size)) messagedata = message.encode() # Adding the receiver address into the echohash to avoid collisions # among different receivers. # (Messages that are not unique per receiver # can result in hash colision, eg. Secret message sent to more than one # node, this hash collision has the undesired effect of aborting # message resubmission once a single node replied with an Ack) echohash = sha3(messagedata + receiver_address) # Don't add the same message twice into the queue if echohash not in self.echohash_asyncresult: ack_result = AsyncResult() self.echohash_asyncresult[echohash] = WaitAck( ack_result, receiver_address) # state changes are local to each channel/token queue_name = getattr(message, 'token', '') self._send(receiver_address, queue_name, message, messagedata, echohash) else: waitack = self.echohash_asyncresult[echohash] ack_result = waitack.ack_result return ack_result