class RaidenService(object): # pylint: disable=too-many-instance-attributes """ A Raiden node. """ def __init__(self, chain, privkey, transport, discovery, config): # pylint: disable=too-many-arguments self.registries = list() self.managers_by_asset_address = dict() self.managers_by_address = dict() self.event_listeners = list() self.chain = chain self.config = config self.privkey = privkey self.address = privtoaddr(privkey) self.protocol = RaidenProtocol(transport, discovery, self) transport.protocol = self.protocol message_handler = RaidenMessageHandler(self) event_handler = RaidenEventHandler(self) self.api = RaidenAPI(self) self.event_handler = event_handler self.message_handler = message_handler self.on_message = message_handler.on_message self.on_event = event_handler.on_event def __repr__(self): return '<{} {}>'.format(self.__class__.__name__, pex(self.address)) def get_manager_by_asset_address(self, asset_address_bin): """ Return the manager for the given `asset_address_bin`. """ return self.managers_by_asset_address[asset_address_bin] def get_manager_by_address(self, manager_address_bin): return self.managers_by_address[manager_address_bin] def find_channel_by_address(self, netting_channel_address_bin): for manager in self.managers_by_address.itervalues(): channel = manager.address_channel.get(netting_channel_address_bin) if channel is not None: return channel raise ValueError('unknow channel {}'.format(encode_hex(netting_channel_address_bin))) def sign(self, message): """ Sign message inplace. """ if not isinstance(message, SignedMessage): raise ValueError('{} is not signable.'.format(repr(message))) message.sign(self.privkey) 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 send_and_wait(self, recipient, message, timeout, event): """ 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`. event (gevent.event.AsyncResult): Event that will receive the result. Returns: None: If the wait timed out object: The result from the event """ self.send(recipient, message) return event.wait(timeout) def message_for_task(self, message, hashlock): """ Sends the message to the corresponding task. The corresponding task is found by matching the hashlock. Return: bool: True if a correspoding task is found, False otherwise. """ for asset_manager in self.managers_by_asset_address.values(): task = asset_manager.transfermanager.transfertasks.get(hashlock) if task is not None: task.on_event(message) return True return False def register_registry(self, registry): """ Register the registry and intialize all the related assets and channels. """ translator = ContractTranslator(REGISTRY_ABI) assetadded = registry.assetadded_filter() all_manager_addresses = registry.manager_addresses() asset_listener = LogListenerTask( assetadded, self.on_event, translator, ) asset_listener.start() self.event_listeners.append(asset_listener) self.registries.append(registry) for manager_address in all_manager_addresses: channel_manager = self.chain.manager(manager_address) self.register_channel_manager(channel_manager) def register_channel_manager(self, channel_manager): """ Discover and register the channels for the given asset. """ translator = ContractTranslator(CHANNEL_MANAGER_ABI) # To avoid missing changes, first create the filter, call the # contract and then start polling. channelnew = channel_manager.channelnew_filter(self.address) all_netting_contracts = channel_manager.channels_by_participant(self.address) channel_listener = LogListenerTask( channelnew, self.on_event, translator, ) channel_listener.start() self.event_listeners.append(channel_listener) asset_address_bin = channel_manager.asset_address() channel_manager_address_bin = channel_manager.address edges = channel_manager.channels_addresses() channel_graph = ChannelGraph(edges) asset_manager = AssetManager( self, asset_address_bin, channel_manager_address_bin, channel_graph, ) self.managers_by_asset_address[asset_address_bin] = asset_manager self.managers_by_address[channel_manager_address_bin] = asset_manager for netting_contract_address in all_netting_contracts: asset_manager.register_channel_by_address( netting_contract_address, self.config['reveal_timeout'], ) def stop(self): for listener in self.event_listeners: listener.stop_event.set(True) self.chain.uninstall_filter(listener.filter_.filter_id_raw)
class RaidenService(object): """ Runs a service on a node """ def __init__(self, chain, privkey, transport, discovery, config): # pylint: disable=too-many-arguments self.chain = chain self.config = config self.privkey = privkey self.address = privtoaddr(privkey) self.protocol = RaidenProtocol(transport, discovery, self) transport.protocol = self.protocol self.assetmanagers = dict() self.api = RaidenAPI(self) def __repr__(self): return '<{} {}>'.format(self.__class__.__name__, pex(self.address)) def setup_asset(self, asset_address, reveal_timeout): """ Initialize a `AssetManager`, and for each open channel that this node has create a corresponding `Channel`. Args: asset_address (address): A list of asset addresses that need to be considered. reveal_timeout (int): Minimum number of blocks required for the settling of a netting contract. """ netting_address = self.chain.nettingaddresses_by_asset_participant( asset_address, self.address, ) asset_manager = self.get_or_create_asset_manager(asset_address) for nettingcontract_address in netting_address: self.setup_channel( asset_manager, asset_address, nettingcontract_address, reveal_timeout, ) def get_or_create_asset_manager(self, asset_address): """ Return the AssetManager for the given `asset_address`. """ if asset_address not in self.assetmanagers: edges = self.chain.addresses_by_asset(asset_address) channel_graph = ChannelGraph(edges) asset_manager = AssetManager(self, asset_address, channel_graph) self.assetmanagers[asset_address] = asset_manager return self.assetmanagers[asset_address] def setup_channel(self, asset_manager, asset_address, nettingcontract_address, reveal_timeout): """ Initialize the Channel for the given netting contract. """ channel_details = self.chain.netting_contract_detail( asset_address, nettingcontract_address, self.address, ) our_state = ChannelEndState( self.address, channel_details['our_balance'], ) partner_state = ChannelEndState( channel_details['partner_address'], channel_details['partner_balance'], ) channel = Channel( self.chain, asset_address, nettingcontract_address, our_state, partner_state, reveal_timeout, ) asset_manager.add_channel(channel_details['partner_address'], channel) def has_path(self, asset, target): if asset not in self.assetmanagers: return False graph = self.assetmanagers[asset].channelgraph return graph.has_path(self.address, target) def sign(self, msg): assert isinstance(msg, messages.SignedMessage) return msg.sign(self.privkey) def on_message(self, msg, msghash): log.debug('ON MESSAGE {} {}'.format(pex(self.address), msg)) method = 'on_%s' % msg.__class__.__name__.lower() # update activity monitor (which also does pings to all addresses in channels) getattr(self, method)(msg) self.protocol.send_ack(msg.sender, messages.Ack(self.address, msghash)) def on_message_failsafe(self, msg, msghash): method = 'on_%s' % msg.__class__.__name__.lower() # update activity monitor (which also does pings to all addresses in channels) try: getattr(self, method)(msg) except messages.BaseError as error: self.protocol.send_ack(msg.sender, error) else: self.protocol.send_ack(msg.sender, messages.Ack(self.address, msghash)) def send(self, recipient, msg): # assert msg.sender assert isaddress(recipient) self.protocol.send(recipient, msg) def on_baseerror(self, msg): pass def on_ping(self, msg): pass # ack already sent, activity monitor should have been notified in on_message def on_transfer(self, msg): asset_manager = self.assetmanagers[msg.asset] asset_manager.transfermanager.on_transfer(msg) on_lockedtransfer = on_directtransfer = on_transfer def on_mediatedtransfer(self, msg): asset_manager = self.assetmanagers[msg.asset] asset_manager.transfermanager.on_mediatedtransfer(msg) # events, that need to find a TransferTask def on_event_for_transfertask(self, msg): if isinstance(msg, messages.LockedTransfer): hashlock = msg.lock.hashlock else: # TransferTimeout, Secret, SecretRequest, ConfirmTransfer hashlock = msg.hashlock for asset_manager in self.assetmanagers.values(): if hashlock in asset_manager.transfermanager.transfertasks: asset_manager.transfermanager.transfertasks[hashlock].on_event(msg) return True on_secretrequest = on_transfertimeout = on_canceltransfer = on_event_for_transfertask def on_secret(self, msg): self.on_event_for_transfertask(msg) for asset_manager in self.assetmanagers.values(): asset_manager.on_secret(msg) def on_transferrequest(self, msg): asset_manager = self.assetmanagers[msg.asset] asset_manager.transfermanager.on_tranferrequest(msg) # other def on_rejected(self, msg): pass def on_hashlockrequest(self, msg): pass def on_exchangerequest(self, msg): pass
class RaidenService(object): """ Runs a service on a node """ def __init__(self, chain, privkey, transport, discovery): self.chain = chain self.privkey = privkey self.address = privtoaddr(privkey) self.protocol = RaidenProtocol(transport, discovery, self) transport.protocol = self.protocol self.assetmanagers = dict() self.api = RaidenAPI(self) def __repr__(self): return '<{} {}>'.format(self.__class__.__name__, pex(self.address)) def setup_asset(self, asset_address, min_locktime): """ Initialize a `AssetManager`, and for each open channel that this node has create a corresponding `Channel`. Args: asset_address (address): A list of asset addresses that need to be considered. min_locktime (int): Minimum number of blocks required for the settling of a netting contract. """ netting_address = self.chain.nettingaddresses_by_asset_participant( asset_address, self.address, ) asset_manager = self.get_or_create_asset_manager(asset_address) for nettingcontract_address in netting_address: self.setup_channel( asset_manager, asset_address, nettingcontract_address, min_locktime, ) def get_or_create_asset_manager(self, asset_address): """ Return the AssetManager for the given `asset_address`. """ if asset_address not in self.assetmanagers: edges = self.chain.addresses_by_asset(asset_address) channel_graph = ChannelGraph(edges) asset_manager = AssetManager(self, asset_address, channel_graph) self.assetmanagers[asset_address] = asset_manager return self.assetmanagers[asset_address] def setup_channel(self, asset_manager, asset_address, nettingcontract_address, min_locktime): """ Initialize the Channel for the given netting contract. """ channel_details = self.chain.netting_contract_detail( asset_address, nettingcontract_address, self.address, ) our_state = ChannelEndState( self.address, channel_details['our_balance'], ) partner_state = ChannelEndState( channel_details['partner_address'], channel_details['partner_balance'], ) channel = Channel( self.chain, asset_address, nettingcontract_address, our_state, partner_state, min_locktime=min_locktime, ) asset_manager.add_channel(channel_details['partner_address'], channel) def has_path(self, asset, target): assetmanager = self.assetmanagers.get(asset) if assetmanager is not None: channel = assetmanager.channelgraph.graph if target in channel.nodes(): return nx.has_path(channel, self.address, target) return False def sign(self, msg): assert isinstance(msg, messages.SignedMessage) return msg.sign(self.privkey) def on_message(self, msg, msghash): log.debug('ON MESSAGE {} {}'.format(pex(self.address), msg)) method = 'on_%s' % msg.__class__.__name__.lower() # update activity monitor (which also does pings to all addresses in channels) getattr(self, method)(msg) self.protocol.send_ack(msg.sender, messages.Ack(self.address, msghash)) def on_message_failsafe(self, msg, msghash): method = 'on_%s' % msg.__class__.__name__.lower() # update activity monitor (which also does pings to all addresses in channels) try: getattr(self, method)(msg) except messages.BaseError as error: self.protocol.send_ack(msg.sender, error) else: self.protocol.send_ack(msg.sender, messages.Ack(self.address, msghash)) def send(self, recipient, msg): # assert msg.sender assert isaddress(recipient) self.protocol.send(recipient, msg) def on_baseerror(self, msg): pass def on_ping(self, msg): pass # ack already sent, activity monitor should have been notified in on_message def on_transfer(self, msg): asset_manager = self.assetmanagers[msg.asset] asset_manager.transfermanager.on_transfer(msg) on_lockedtransfer = on_directtransfer = on_transfer def on_mediatedtransfer(self, msg): asset_manager = self.assetmanagers[msg.asset] asset_manager.transfermanager.on_mediatedtransfer(msg) # events, that need to find a TransferTask def on_event_for_transfertask(self, msg): if isinstance(msg, messages.LockedTransfer): hashlock = msg.lock.hashlock else: # TransferTimeout, Secret, SecretRequest, ConfirmTransfer hashlock = msg.hashlock for asset_manager in self.assetmanagers.values(): if hashlock in asset_manager.transfermanager.transfertasks: asset_manager.transfermanager.transfertasks[hashlock].on_event( msg) return True on_secretrequest = on_transfertimeout = on_canceltransfer = on_event_for_transfertask def on_secret(self, msg): self.on_event_for_transfertask(msg) for asset_manager in self.assetmanagers.values(): asset_manager.on_secret(msg) def on_transferrequest(self, msg): asset_manager = self.assetmanagers[msg.asset] asset_manager.transfermanager.on_tranferrequest(msg) # other def on_rejected(self, msg): pass def on_hashlockrequest(self, msg): pass def on_exchangerequest(self, msg): pass
class RaidenService(object): # pylint: disable=too-many-instance-attributes """ A Raiden node. """ def __init__(self, chain, privkey, transport, discovery, config): # pylint: disable=too-many-arguments self.registries = list() self.managers_by_asset_address = dict() self.managers_by_address = dict() self.event_listeners = list() self.chain = chain self.config = config self.privkey = privkey self.address = privtoaddr(privkey) self.protocol = RaidenProtocol(transport, discovery, self) transport.protocol = self.protocol message_handler = RaidenMessageHandler(self) event_handler = RaidenEventHandler(self) self.api = RaidenAPI(self) self.event_handler = event_handler self.message_handler = message_handler self.on_message = message_handler.on_message self.on_event = event_handler.on_event def __repr__(self): return '<{} {}>'.format(self.__class__.__name__, pex(self.address)) def get_manager_by_asset_address(self, asset_address_bin): """ Return the manager for the given `asset_address_bin`. """ return self.managers_by_asset_address[asset_address_bin] def get_manager_by_address(self, manager_address_bin): return self.managers_by_address[manager_address_bin] def find_channel_by_address(self, netting_channel_address_bin): for manager in self.managers_by_address.itervalues(): channel = manager.address_channel.get(netting_channel_address_bin) if channel is not None: return channel raise ValueError('unknow channel {}'.format( encode_hex(netting_channel_address_bin))) def sign(self, message): """ Sign message inplace. """ if not isinstance(message, SignedMessage): raise ValueError('{} is not signable.'.format(repr(message))) message.sign(self.privkey) 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 send_and_wait(self, recipient, message, timeout, event): """ 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`. event (gevent.event.AsyncResult): Event that will receive the result. Returns: None: If the wait timed out object: The result from the event """ self.send(recipient, message) return event.wait(timeout) def message_for_task(self, message, hashlock): """ Sends the message to the corresponding task. The corresponding task is found by matching the hashlock. Return: bool: True if a correspoding task is found, False otherwise. """ for asset_manager in self.managers_by_asset_address.values(): task = asset_manager.transfermanager.transfertasks.get(hashlock) if task is not None: task.on_event(message) return True return False def register_registry(self, registry): """ Register the registry and intialize all the related assets and channels. """ translator = ContractTranslator(REGISTRY_ABI) assetadded = registry.assetadded_filter() all_manager_addresses = registry.manager_addresses() asset_listener = LogListenerTask( assetadded, self.on_event, translator, ) asset_listener.start() self.event_listeners.append(asset_listener) self.registries.append(registry) for manager_address in all_manager_addresses: channel_manager = self.chain.manager(manager_address) self.register_channel_manager(channel_manager) def register_channel_manager(self, channel_manager): """ Discover and register the channels for the given asset. """ translator = ContractTranslator(CHANNEL_MANAGER_ABI) # To avoid missing changes, first create the filter, call the # contract and then start polling. channelnew = channel_manager.channelnew_filter(self.address) all_netting_contracts = channel_manager.channels_by_participant( self.address) channel_listener = LogListenerTask( channelnew, self.on_event, translator, ) channel_listener.start() self.event_listeners.append(channel_listener) asset_address_bin = channel_manager.asset_address() channel_manager_address_bin = channel_manager.address edges = channel_manager.channels_addresses() channel_graph = ChannelGraph(edges) asset_manager = AssetManager( self, asset_address_bin, channel_manager_address_bin, channel_graph, ) self.managers_by_asset_address[asset_address_bin] = asset_manager self.managers_by_address[channel_manager_address_bin] = asset_manager for netting_contract_address in all_netting_contracts: asset_manager.register_channel_by_address( netting_contract_address, self.config['reveal_timeout'], ) def stop(self): for listener in self.event_listeners: listener.stop_event.set(True) self.chain.uninstall_filter(listener.filter_.filter_id_raw)