Skip to content

Commit bea462f

Browse files
committed
backend: Fetch online status of nodes from PFS
1 parent ed1c7ef commit bea462f

File tree

3 files changed

+97
-98
lines changed

3 files changed

+97
-98
lines changed

backend/metrics_backend/metrics_cli.py

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
from gevent import monkey, config # isort:skip # noqa
2-
config.resolver = ['dnspython', 'ares', 'block']
1+
from gevent import monkey # isort:skip # noqa
32
monkey.patch_all() # isort:skip # noqa
43

54
import contextlib
@@ -13,8 +12,9 @@
1312
import click
1413
import gevent
1514
import requests
16-
from eth_utils import is_checksum_address
15+
from eth_utils import is_checksum_address, to_canonical_address
1716
from raiden_contracts.constants import (
17+
CONTRACT_SERVICE_REGISTRY,
1818
CONTRACT_TOKEN_NETWORK_REGISTRY,
1919
CONTRACTS_VERSION
2020
)
@@ -25,7 +25,6 @@
2525
)
2626
from requests.exceptions import ConnectionError
2727
from web3 import HTTPProvider, Web3
28-
from web3.middleware import geth_poa_middleware
2928

3029
from metrics_backend.api.rest import NetworkInfoAPI
3130
from metrics_backend.metrics_service import MetricsService
@@ -41,7 +40,6 @@
4140
REQUIRED_CONFIRMATIONS = 5
4241
PRODUCTION_CONTRACTS_VERSION = '0.37.0'
4342
DEMOENV_CONTRACTS_VERSION = '0.37.0'
44-
DEMOENV_MATRIX_SERVER = 'https://transport.demo001.env.raiden.network'
4543

4644

4745
@contextlib.contextmanager
@@ -117,7 +115,6 @@ def main(
117115
try:
118116
log.info(f'Starting Web3 client for node at {eth_rpc}')
119117
web3 = Web3(HTTPProvider(eth_rpc))
120-
web3.middleware_onion.inject(geth_poa_middleware, layer=0)
121118
except ConnectionError:
122119
log.error(
123120
'Can not connect to the Ethereum client. Please check that it is running and that '
@@ -135,36 +132,37 @@ def main(
135132

136133
with no_ssl_verification():
137134
valid_params_given = is_checksum_address(registry_address) and start_block >= 0
138-
if not valid_params_given:
139-
try:
140-
contract_data = get_contracts_deployment_info(web3.eth.chainId, contracts_version)
135+
try:
136+
contract_data = get_contracts_deployment_info(web3.eth.chainId, contracts_version)
137+
service_registry_address = contract_data['contracts'][CONTRACT_SERVICE_REGISTRY]['address']
138+
if not valid_params_given:
141139
token_network_registry_info = contract_data['contracts'][CONTRACT_TOKEN_NETWORK_REGISTRY] # noqa
142140
registry_address = token_network_registry_info['address']
143141
start_block = max(0, token_network_registry_info['block_number'])
144-
except ValueError:
145-
log.error(
146-
'Provided registry address or start block are not valid and '
147-
'no deployed contracts were found'
148-
)
149-
sys.exit(1)
142+
except ValueError:
143+
log.error(
144+
'Provided registry address or start block are not valid and '
145+
'no deployed contracts were found'
146+
)
147+
sys.exit(1)
150148

151149
try:
150+
contract_manager = ContractManager(contracts_precompiled_path(contracts_version))
151+
152152
metrics_service = MetricsService(
153153
web3=web3,
154-
contract_manager=ContractManager(contracts_precompiled_path(contracts_version)),
154+
contract_manager=contract_manager,
155155
registry_address=registry_address,
156156
sync_start_block=start_block,
157157
required_confirmations=confirmations,
158158
)
159159

160-
matrix_server = None
161-
if environment == 'demo':
162-
matrix_server = DEMOENV_MATRIX_SERVER
163160
presence_service = PresenceService(
164-
f'EXPLORER_{web3.eth.chainId}',
165-
web3.eth.chainId,
166-
environment == 'production',
167-
matrix_server
161+
privkey_seed=f'EXPLORER_{web3.eth.chainId}',
162+
contract_manager=contract_manager,
163+
web3=web3,
164+
block_confirmations=REQUIRED_CONFIRMATIONS,
165+
service_registry_address=to_canonical_address(service_registry_address),
168166
)
169167

170168
# re-enable once deployment works
Lines changed: 68 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
11
import hashlib
22
import logging
3-
from typing import Any, Dict
4-
from urllib.parse import urlparse
3+
import math
4+
from typing import Dict, List
55

66
import 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

2318
log = 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
)

backend/requirements.txt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
Click==7.0
1+
Click==8.0.1
22

3-
web3
3+
web3==5.19.0
44
eth-utils
55

66
flask==1.1.2
77
flask_restful==0.3.8
8-
flask-cors==3.0.9
9-
gevent==20.6.2
10-
requests==2.24.0
11-
cachetools==4.1.1
8+
flask-cors==3.0.10
9+
gevent==21.1.2
10+
requests==2.25.1
11+
cachetools==4.2.2
1212

13-
raiden==1.2.0
14-
raiden-contracts==0.37.1
13+
raiden==2.0.0rc0
14+
raiden-contracts==0.37.5
1515
mypy-extensions

0 commit comments

Comments
 (0)