Add country data to display of service nodes.

To enable:

```
$ sudo apt install python3-geoip2  # or: 'pip install geoip2' for the latest
$ sudo apt install geoipupdate
```

If necessary, sign up for an account with MaxMind:

  https://www.maxmind.com/en/geolite2/signup

Edit `/etc/GeoIP.conf` with the details of your MaxMind account:

```
AccountID 123456
LicenseKey xxxxxxxxxxxxxxxx
EditionIDs GeoLite2-City GeoLite2-ASN
```

Edit `/etc/cron.d/geoipupdate`:

```
 # MaxMind database updates take place on Tuesdays and Fridays.
 #
00 12    * * 2,5   root    test -x /usr/bin/geoipupdate && grep -q '^AccountID .*[^0]\+' /etc/GeoIP.conf && /usr/bin/geoipupdate
```

Edit `./local_config.py`:

```
config.geoip_dir = '/var/lib/GeoIP'
```
This commit is contained in:
Ian Macdonald 2022-06-28 15:50:17 +02:00
parent 07182822c8
commit 3c55bb8106
No known key found for this signature in database
GPG Key ID: AE4C20556BA626FA
6 changed files with 23 additions and 2 deletions

View File

@ -6,3 +6,4 @@ from observer import config
# Example config override:
#config.blocks_per_page = 10
config.geoip_dir = '/var/lib/GeoIP'

View File

@ -26,6 +26,7 @@ import sha3
import config
import local_config
from lmq import FutureJSON, omq_connection
import geoip2.database
# Make a dict of config.* to pass to templating
conf = {x: getattr(config, x) for x in dir(config) if not x.startswith('__')}
@ -47,7 +48,6 @@ class Hex64Converter(BaseConverter):
app.url_map.converters['hex64'] = Hex64Converter
@app.template_filter('format_datetime')
def format_datetime(value, format='long'):
return babel.dates.format_datetime(value, format, tzinfo=babel.dates.get_timezone('UTC'))
@ -186,10 +186,11 @@ def get_sns_future(omq, oxend):
'last_reward_transaction_index', 'active', 'funded', 'earned_downtime_blocks',
'service_node_version', 'contributors', 'total_contributed', 'total_reserved',
'staking_requirement', 'portions_for_operator', 'operator_address', 'pubkey_ed25519',
'last_uptime_proof', 'state_height', 'swarm_id') } })
'last_uptime_proof', 'state_height', 'swarm_id', 'public_ip') } })
def get_sns(sns_future, info_future):
info = info_future.get()
geoip = geoip2.database.Reader(config.geoip_dir + '/GeoLite2-City.mmdb')
awaiting_sns, active_sns, inactive_sns = [], [], []
sn_states = sns_future.get()
sn_states = sn_states['service_node_states'] if 'service_node_states' in sn_states else []
@ -197,6 +198,7 @@ def get_sns(sns_future, info_future):
sn['contribution_open'] = sn['staking_requirement'] - sn['total_reserved']
sn['contribution_required'] = sn['staking_requirement'] - sn['total_contributed']
sn['num_contributions'] = sum(len(x['locked_contributions']) for x in sn['contributors'] if 'locked_contributions' in x)
sn['country'] = geoip.city(sn['public_ip']).country.names['en']
if sn['active']:
active_sns.append(sn)
@ -570,6 +572,8 @@ def show_sn(pubkey, more_details=False):
hfinfo = FutureJSON(omq, oxend, 'rpc.hard_fork_info', 10)
sn = sn_req(omq, oxend, pubkey).get()
quos = get_quorums_future(omq, oxend, info.get()['height'])
geoip_c = geoip2.database.Reader(config.geoip_dir + '/GeoLite2-City.mmdb')
geoip_a = geoip2.database.Reader(config.geoip_dir + '/GeoLite2-ASN.mmdb')
if 'service_node_states' not in sn or not sn['service_node_states']:
@ -588,6 +592,11 @@ def show_sn(pubkey, more_details=False):
sn['num_reserved_spots'] = sum(x["amount"] < x["reserved"] for x in sn["contributors"])
# Available open contribution spots:
sn['num_open_spots'] = 0 if sn['total_reserved'] >= sn['staking_requirement'] else max(0, 4 - sn['num_contributions'] - sn['num_reserved_spots'])
sn['country'] = geoip_c.city(sn['public_ip']).country.names['en']
city = geoip_c.city(sn['public_ip']).city
if city.names:
sn['city'] = city.names['en']
sn['asn'] = geoip_a.asn(sn['public_ip']).autonomous_system_organization
if more_details:
formatter = HtmlFormatter(cssclass="syntax-highlight", style="paraiso-dark")

View File

@ -8,6 +8,7 @@
<td title="Can also be the height of the last activation, IP penalty, or recommission">Last Reward Height</td>
<td>Last Uptime Proof</td>
<td>Expiry Date UTC (Estimated)</td>
<td>Country</td>
</tr>
</thead>
@ -28,6 +29,7 @@
Staking Infinitely
{%endif%}
</td>
<td>{{sn.country}}</td>
</tr>
{%endfor%}
</tbody>

View File

@ -9,6 +9,7 @@
<td>Open For Contribution</td>
<td>Min. Contribution</td>
<td>Expiry Date UTC (Estimated)</td>
<td>Country</td>
</tr>
</thead>
@ -35,6 +36,7 @@
Staking Infinitely
{%endif%}
</td>
<td>{{sn.country}}</td>
</tr>
{%endfor%}
{%if limit_awaiting and (awaiting_sns | length) > limit_awaiting%}

View File

@ -7,6 +7,7 @@
<td>Decommission Height</td>
<td>Last Uptime Proof</td>
<td>Blocks until Dereg.</td>
<td>Country</td>
</tr>
</thead>
@ -19,6 +20,7 @@
<td>{%if sn.decomm_blocks_remaining > 0%}{{sn.decomm_blocks_remaining}} ({{(sn.decomm_blocks_remaining * 120) | reltime}})
{%-else%}None (deregistration imminent!)
{%-endif%}</td>
<td>{{sn.country}}</td>
</tr>
{%endfor%}

View File

@ -19,6 +19,11 @@
<h4 style="margin:5px"><label>Lokinet Address:</label> {{sn.pubkey_ed25519 | base32z}}.snode</h4>
{%endif%}
<h4 style="margin:5px"><label>Operator Address:</label> {{sn.operator_address}}</h4>
<h4 style="margin:5px"><label>Country:</label> {{sn.country}}</h4>
{%if sn.city %}
<h4 style="margin:5px"><label>City:</label> {{sn.city}}</h4>
{%endif%}
<h4 style="margin:5px"><label>Network:</label> {{sn.asn}}</h4>
</div>
<img class="qr" src="/qr/{{sn.service_node_pubkey}}" title="{{sn.service_node_pubkey}}">
</div>