11import hashlib
22import logging
3- from typing import Any , Dict
4- from urllib . parse import urlparse
3+ import math
4+ from typing import Dict , List
55
66import gevent
7- from raiden .constants import DISCOVERY_DEFAULT_ROOM , Environment
8- from raiden .network .transport .matrix .utils import (
9- USER_PRESENCE_TO_ADDRESS_REACHABILITY ,
10- AddressReachability ,
11- UserPresence ,
12- address_from_userid ,
13- join_broadcast_room ,
14- login ,
15- make_client ,
16- make_room_alias
17- )
18- from raiden .settings import DEFAULT_MATRIX_KNOWN_SERVERS
19- from raiden .utils .cli import get_matrix_servers
20- from raiden .utils .signer import LocalSigner , Signer
21- from raiden_contracts .utils .type_aliases import ChainID
7+ import requests
8+ from eth_utils .address import to_canonical_address , to_checksum_address
9+ from raiden .constants import BLOCK_ID_LATEST
10+ from raiden .network .pathfinding import get_random_pfs
11+ from raiden .network .proxies .service_registry import ServiceRegistry
12+ from raiden .network .rpc .client import JSONRPCClient
13+ from requests import ConnectionError , HTTPError , Timeout
14+ from web3 import Web3
15+
16+ from metrics_backend .utils import Address
2217
2318log = logging .getLogger (__name__ )
2419
@@ -27,66 +22,72 @@ class PresenceService(gevent.Greenlet):
2722 def __init__ (
2823 self ,
2924 privkey_seed : str ,
30- chain_id : ChainID ,
31- production_environment : bool ,
32- server : str = None
25+ contract_manager ,
26+ web3 : Web3 ,
27+ block_confirmations : int ,
28+ service_registry_address : Address ,
29+ poll_interval : int = 30 ,
30+ error_poll_interval : int = 600 ,
3331 ) -> None :
34- """ Creates a new presence service listening on matrix presence updates
35- in the discovery room
36-
37- Args:
38- privkey_seed: Seed for generating a private key for matrix login
39- chain_id: Chain id to listen on presence
40- production_environment: Determines which matrix server to use if none is explicitly set
41- server: Matrix server
42- """
32+ """Creates a new presence service getting the online status of nodes from a PFS"""
4333 super ().__init__ ()
44- self .signer = LocalSigner (hashlib .sha256 (privkey_seed .encode ()).digest ())
45- self .server = server
46- self .chain_id = chain_id
47- self .production_environment = production_environment
48-
49- self .is_running = gevent .event .Event ()
50-
34+ self .running = False
35+ self .poll_interval = poll_interval
36+ self .error_poll_interval = error_poll_interval
37+ jsonrpc_client = JSONRPCClient (
38+ web3 = web3 ,
39+ privkey = hashlib .sha256 (privkey_seed .encode ()).digest (),
40+ block_num_confirmations = block_confirmations ,
41+ )
42+ self .service_registry = ServiceRegistry (
43+ jsonrpc_client = jsonrpc_client ,
44+ service_registry_address = service_registry_address ,
45+ contract_manager = contract_manager ,
46+ block_identifier = BLOCK_ID_LATEST ,
47+ )
5148 self .nodes_presence_status : Dict [bytes , bool ] = {}
52- log .info ('Using address %s for matrix login' , self .signer .address_hex )
5349
5450 def _run (self ):
55- available_servers = [self .server ]
56- if not self .server :
57- environment_type = Environment .PRODUCTION if self .production_environment else Environment .DEVELOPMENT
58- available_servers_url = DEFAULT_MATRIX_KNOWN_SERVERS [environment_type ]
59- available_servers = get_matrix_servers (available_servers_url )
60-
61- client = make_client (lambda x : False , lambda x : None , available_servers )
62- self .server = client .api .base_url
63- server_name = urlparse (self .server ).netloc
64- login (client = client , signer = self .signer )
65- client .add_presence_listener (self .handle_presence_update )
66- client .start_listener_thread (30_000 , 1_000 )
51+ self .running = True
6752
68- discovery_room_alias = make_room_alias (
69- self .chain_id , DISCOVERY_DEFAULT_ROOM
53+ pfs_url = get_random_pfs (
54+ service_registry = self .service_registry ,
55+ block_identifier = BLOCK_ID_LATEST ,
56+ pathfinding_max_fee = math .inf ,
7057 )
71- join_broadcast_room (client , f'#{ discovery_room_alias } :{ server_name } ' )
58+ if pfs_url is None :
59+ self .running = False
60+ log .warning (
61+ "Could not get a PFS from ServiceRegistry %s. Disabling presence monitoring." ,
62+ to_checksum_address (self .service_registry .address ),
63+ )
64+ return
7265
73- log .info ('Presence monitoring started, server: %s' , self .server )
74- self .is_running .wait ()
75- client .stop ()
66+ log .info ("Presence service started, PFS: %s" , pfs_url )
67+ log .info ("Presence polling interval: %ss" , self .poll_interval )
68+ while self .running :
69+ try :
70+ response = requests .get (f"{ pfs_url } /api/v1/online_addresses" )
71+ response .raise_for_status ()
72+ self .update_presence (response .json ())
73+ gevent .sleep (self .poll_interval )
74+ except (ConnectionError , HTTPError , Timeout ):
75+ log .warning (
76+ "Error while trying to request from the PFS. Retrying in %d seconds." ,
77+ self .error_poll_interval ,
78+ )
79+ gevent .sleep (self .error_poll_interval )
7680
77- def stop (self ):
78- self .is_running .set ()
81+ log .info ("Stopped presence service" )
7982
80- def handle_presence_update (self , event : Dict [str , Any ], update_id : int ) -> None :
81- presence = UserPresence (event ["content" ]["presence" ])
82- reachable = USER_PRESENCE_TO_ADDRESS_REACHABILITY [presence ] is AddressReachability .REACHABLE
83- node_address = address_from_userid (event ["sender" ])
84- self .nodes_presence_status [node_address ] = reachable
83+ def stop (self ):
84+ self .running = False
8585
86+ def update_presence (self , online_addresses : List [str ]):
87+ self .nodes_presence_status = {
88+ to_canonical_address (address ): True for address in online_addresses
89+ }
8690 log .info (
87- 'Presence update, server: %s, user_id: %s, presence: %s, update_id: %d' ,
88- self .server ,
89- event ["sender" ],
90- presence ,
91- update_id
91+ "Presence update, number of online nodes: %d" ,
92+ len (online_addresses ),
9293 )
0 commit comments