mirror of https://github.com/nucypher/nucypher.git
Very simple initial implementation of Moe's new `dashboard` command
parent
83ca610c6a
commit
2e6029dfd7
|
@ -39,7 +39,7 @@ from nucypher.crypto.powers import SigningPower, TransactingPower
|
|||
from nucypher.keystore.keypairs import HostingKeypair
|
||||
from nucypher.keystore.threading import ThreadedSession
|
||||
from nucypher.network.server import TLSHostingPower
|
||||
from nucypher.network.status_app.moe import MoeStatusApp
|
||||
|
||||
|
||||
|
||||
class Moe(Character):
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import click
|
||||
from flask import Flask
|
||||
from umbral.keys import UmbralPrivateKey
|
||||
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
|
||||
from nucypher.blockchain.eth.registry import InMemoryContractRegistry, LocalContractRegistry
|
||||
|
@ -7,11 +9,15 @@ from nucypher.characters.chaotic import Moe
|
|||
from nucypher.cli import actions
|
||||
from nucypher.cli.config import nucypher_click_config
|
||||
from nucypher.cli.types import NETWORK_PORT, EXISTING_READABLE_FILE
|
||||
from nucypher.keystore.keypairs import HostingKeypair
|
||||
from nucypher.network.middleware import RestMiddleware
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
|
||||
from twisted.internet import reactor
|
||||
|
||||
from nucypher.network.server import TLSHostingPower
|
||||
from nucypher.network.status_app.crawler import NetworkCrawler
|
||||
from nucypher.network.status_app.moe import MoeDashboardApp
|
||||
|
||||
|
||||
@click.group()
|
||||
|
@ -26,7 +32,7 @@ def moe():
|
|||
@click.option('--teacher', 'teacher_uri', help="An Ursula URI to start learning from (seednode)", type=click.STRING)
|
||||
@click.option('--registry-filepath', help="Custom contract registry filepath", type=EXISTING_READABLE_FILE)
|
||||
@click.option('--min-stake', help="The minimum stake the teacher must have to be a teacher", type=click.INT, default=0)
|
||||
@click.option('--network', help="Network Domain Name", type=click.STRING)
|
||||
@click.option('--network', help="Network Domain Name", type=click.STRING, default='goerli')
|
||||
@click.option('--host', help="The host to run Moe services on", type=click.STRING, default='127.0.0.1')
|
||||
@click.option('--certificate-filepath', help="Pre-signed TLS certificate filepath")
|
||||
@click.option('--tls-key-filepath', help="TLS private key filepath")
|
||||
|
@ -51,12 +57,9 @@ def network_crawler(click_config,
|
|||
emitter = click_config.emitter
|
||||
emitter.clear()
|
||||
emitter.banner(MOE_BANNER)
|
||||
emitter.echo("> NETWORK CRAWLER")
|
||||
|
||||
BlockchainInterfaceFactory.initialize_interface(provider_uri=provider_uri)
|
||||
if registry_filepath:
|
||||
registry = LocalContractRegistry.from_latest_publication()
|
||||
else:
|
||||
registry = InMemoryContractRegistry.from_latest_publication()
|
||||
registry = __get_registry(provider_uri, registry_filepath)
|
||||
|
||||
# Teacher Ursula
|
||||
teacher_uris = [teacher_uri] if teacher_uri else None
|
||||
|
@ -90,26 +93,84 @@ def network_crawler(click_config,
|
|||
|
||||
|
||||
@moe.command()
|
||||
def dashboard():
|
||||
@click.option('--host', help="The host to run Moe services on", type=click.STRING, default='127.0.0.1')
|
||||
@click.option('--http-port', help="The network port to run Moe services on", type=NETWORK_PORT, default=12500)
|
||||
@click.option('--registry-filepath', help="Custom contract registry filepath", type=EXISTING_READABLE_FILE)
|
||||
@click.option('--certificate-filepath', help="Pre-signed TLS certificate filepath")
|
||||
@click.option('--tls-key-filepath', help="TLS private key filepath")
|
||||
@click.option('--provider', 'provider_uri', help="Blockchain provider's URI", type=click.STRING)
|
||||
@click.option('--network', help="Network Domain Name", type=click.STRING, default='goerli')
|
||||
@click.option('--dry-run', '-x', help="Execute normally without actually starting the node", is_flag=True)
|
||||
@nucypher_click_config
|
||||
def dashboard(click_config,
|
||||
host,
|
||||
http_port,
|
||||
registry_filepath,
|
||||
certificate_filepath,
|
||||
tls_key_filepath,
|
||||
provider_uri,
|
||||
network,
|
||||
dry_run,
|
||||
):
|
||||
"""
|
||||
Run UI dashboard of NuCypher network.
|
||||
"""
|
||||
# #
|
||||
# # WSGI Service
|
||||
# #
|
||||
# self.rest_app = Flask("fleet-monitor")
|
||||
# rest_app = self.rest_app
|
||||
# MoeStatusApp(moe=self,
|
||||
# title='Moe Monitoring Application',
|
||||
# flask_server=self.rest_app,
|
||||
# route_url='/')
|
||||
|
||||
# Banner
|
||||
emitter = click_config.emitter
|
||||
emitter.clear()
|
||||
emitter.banner(MOE_BANNER)
|
||||
emitter.echo("> UI DASHBOARD")
|
||||
|
||||
registry = __get_registry(provider_uri, registry_filepath)
|
||||
|
||||
#
|
||||
# #
|
||||
# # Server
|
||||
# #
|
||||
# deployer = self._crypto_power.power_ups(TLSHostingPower).get_deployer(rest_app=rest_app, port=self.http_port)
|
||||
# if not dry_run:
|
||||
# deployer.run()
|
||||
# WSGI Service
|
||||
#
|
||||
# pass
|
||||
pass
|
||||
rest_app = Flask("moe-dashboard")
|
||||
MoeDashboardApp(title='Moe Dashboard Application',
|
||||
flask_server=rest_app,
|
||||
route_url='/',
|
||||
registry=registry,
|
||||
network=network)
|
||||
|
||||
#
|
||||
# Server
|
||||
#
|
||||
tls_hosting_power = __get_tls_hosting_power(host=host,
|
||||
tls_certificate_filepath=certificate_filepath,
|
||||
tls_private_key_filepath=tls_key_filepath)
|
||||
emitter.message(f"Running Moe Dashboard - https://{host}:{http_port}")
|
||||
deployer = tls_hosting_power.get_deployer(rest_app=rest_app, port=http_port)
|
||||
if not dry_run:
|
||||
deployer.run()
|
||||
|
||||
|
||||
def __get_registry(provider_uri, registry_filepath):
|
||||
BlockchainInterfaceFactory.initialize_interface(provider_uri=provider_uri)
|
||||
if registry_filepath:
|
||||
registry = LocalContractRegistry.from_latest_publication()
|
||||
else:
|
||||
registry = InMemoryContractRegistry.from_latest_publication()
|
||||
|
||||
return registry
|
||||
|
||||
|
||||
def __get_tls_hosting_power(host: str = None,
|
||||
tls_certificate_filepath: str = None,
|
||||
tls_private_key_filepath: str = None):
|
||||
# Pre-Signed
|
||||
if tls_certificate_filepath and tls_private_key_filepath:
|
||||
with open(tls_private_key_filepath, 'rb') as file:
|
||||
tls_private_key = UmbralPrivateKey.from_bytes(file.read())
|
||||
tls_hosting_keypair = HostingKeypair(curve=ec.SECP384R1,
|
||||
host=host,
|
||||
certificate_filepath=tls_certificate_filepath,
|
||||
private_key=tls_private_key)
|
||||
|
||||
# Self-Sign
|
||||
else:
|
||||
tls_hosting_keypair = HostingKeypair(curve=ec.SECP384R1, host=host)
|
||||
|
||||
tls_hosting_power = TLSHostingPower(keypair=tls_hosting_keypair, host=host)
|
||||
return tls_hosting_power
|
||||
|
|
|
@ -10,7 +10,7 @@ from nucypher.characters.chaotic import Moe
|
|||
from influxdb import InfluxDBClient
|
||||
from maya import MayaDT
|
||||
|
||||
from nucypher.network.status_app.db import InfluxCrawlerClient
|
||||
from nucypher.network.status_app.db import BlockchainCrawlerClient
|
||||
import sqlite3
|
||||
import os
|
||||
|
||||
|
@ -235,8 +235,9 @@ class NetworkCrawler:
|
|||
and self._locked_tokens_learning_task.running
|
||||
and self._moe_known_nodes_learning_task.running)
|
||||
|
||||
def get_blockchain_crawler_client(self):
|
||||
return InfluxCrawlerClient(host='localhost', port=8086, database=self.BLOCKCHAIN_DB_NAME)
|
||||
@staticmethod
|
||||
def get_blockchain_crawler_client():
|
||||
return BlockchainCrawlerClient(host='localhost', port=8086, database=NetworkCrawler.BLOCKCHAIN_DB_NAME)
|
||||
|
||||
def get_node_db_row_information(self, node_info, teacher_address, current_period):
|
||||
# Staker address
|
||||
|
|
|
@ -5,7 +5,7 @@ from influxdb import InfluxDBClient
|
|||
from maya import MayaDT
|
||||
|
||||
|
||||
class InfluxCrawlerClient:
|
||||
class BlockchainCrawlerClient:
|
||||
"""
|
||||
Performs operations on data in the MoeBlockchainCrawler DB.
|
||||
|
||||
|
@ -70,4 +70,4 @@ class InfluxCrawlerClient:
|
|||
return num_stakers_dict
|
||||
|
||||
def close(self):
|
||||
self._client.close()
|
||||
self._client.close()
|
||||
|
|
|
@ -6,11 +6,13 @@ import plotly.graph_objs as go
|
|||
from dash.dependencies import Output, Input
|
||||
from maya import MayaDT
|
||||
|
||||
from nucypher.blockchain.eth.agents import StakingEscrowAgent, ContractAgency
|
||||
from nucypher.blockchain.eth.token import NU
|
||||
from nucypher.network.status_app.base import NetworkStatusPage
|
||||
from nucypher.network.status_app.crawler import NetworkCrawler
|
||||
|
||||
|
||||
class MoeStatusApp(NetworkStatusPage):
|
||||
class MoeDashboardApp(NetworkStatusPage):
|
||||
"""
|
||||
Status application for 'Moe' monitoring node.
|
||||
"""
|
||||
|
@ -24,15 +26,14 @@ class MoeStatusApp(NetworkStatusPage):
|
|||
'fillFrame': False,
|
||||
'displayModeBar': False}
|
||||
|
||||
def __init__(self, moe, *args, **kwargs):
|
||||
def __init__(self, registry, network, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.moe = moe
|
||||
|
||||
from nucypher.network.status_app.crawler import NetworkCrawler
|
||||
self.moe_crawler = NetworkCrawler(moe=moe)
|
||||
self.moe_crawler.start()
|
||||
self.blockchain_db_client = NetworkCrawler.get_blockchain_crawler_client()
|
||||
self.registry = registry
|
||||
self.staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=self.registry)
|
||||
|
||||
self.moe_db_client = self.moe_crawler.get_blockchain_crawler_client()
|
||||
self.network = network
|
||||
|
||||
self.dash_app.layout = html.Div([
|
||||
dcc.Location(id='url', refresh=False),
|
||||
|
@ -57,7 +58,7 @@ class MoeStatusApp(NetworkStatusPage):
|
|||
html.Div(id='time-remaining'),
|
||||
html.Div(id='domains'),
|
||||
html.Div(id='active-stakers'),
|
||||
html.Div(id='stakers-breakdown'),
|
||||
html.Div(id='staker-breakdown'),
|
||||
html.Div(id='staked-tokens'),
|
||||
], id='stats'),
|
||||
|
||||
|
@ -65,15 +66,15 @@ class MoeStatusApp(NetworkStatusPage):
|
|||
html.Div([
|
||||
html.Div(id='prev-num-stakers-graph'),
|
||||
html.Div(id='prev-locked-stake-graph'),
|
||||
html.Div(id='locked-stake-graph'),
|
||||
#html.Div(id='locked-stake-graph'),
|
||||
], id='widgets'),
|
||||
|
||||
# States and Known Nodes Table
|
||||
html.Div([
|
||||
html.Div(id='prev-states'),
|
||||
html.Br(),
|
||||
html.Div(id='known-nodes'),
|
||||
])
|
||||
# html.Div([
|
||||
# html.Div(id='prev-states'),
|
||||
# html.Br(),
|
||||
# html.Div(id='known-nodes'),
|
||||
# ])
|
||||
]),
|
||||
|
||||
], id='main'),
|
||||
|
@ -96,42 +97,41 @@ class MoeStatusApp(NetworkStatusPage):
|
|||
def header(pathname):
|
||||
return self.header()
|
||||
|
||||
@self.dash_app.callback(Output('prev-states', 'children'),
|
||||
[Input('state-update-button', 'n_clicks'),
|
||||
Input('minute-interval', 'n_intervals')])
|
||||
def state(n_clicks, n_intervals):
|
||||
return self.previous_states(moe)
|
||||
# @self.dash_app.callback(Output('prev-states', 'children'),
|
||||
# [Input('state-update-button', 'n_clicks'),
|
||||
# Input('minute-interval', 'n_intervals')])
|
||||
# def state(n_clicks, n_intervals):
|
||||
# return self.previous_states(moe)
|
||||
|
||||
@self.dash_app.callback(Output('known-nodes', 'children'),
|
||||
[Input('node-update-button', 'n_clicks'),
|
||||
Input('minute-interval', 'n_intervals')])
|
||||
def known_nodes(n_clicks, n_intervals):
|
||||
return self.known_nodes(moe)
|
||||
# @self.dash_app.callback(Output('known-nodes', 'children'),
|
||||
# [Input('node-update-button', 'n_clicks'),
|
||||
# Input('minute-interval', 'n_intervals')])
|
||||
# def known_nodes(n_clicks, n_intervals):
|
||||
# return self.known_nodes(moe)
|
||||
|
||||
@self.dash_app.callback(Output('active-stakers', 'children'),
|
||||
[Input('minute-interval', 'n_intervals')])
|
||||
def active_stakers(n):
|
||||
confirmed, pending, inactive = moe.staking_agent.partition_stakers_by_activity()
|
||||
confirmed, pending, inactive = self.staking_agent.partition_stakers_by_activity()
|
||||
total_stakers = len(confirmed) + len(pending) + len(inactive)
|
||||
return html.Div([html.H4("Active Ursulas"),
|
||||
html.H5(f"{len(confirmed)}/{total_stakers}")])
|
||||
|
||||
@self.dash_app.callback(Output('stakers-breakdown', 'children'),
|
||||
@self.dash_app.callback(Output('staker-breakdown', 'children'),
|
||||
[Input('minute-interval', 'n_intervals')])
|
||||
def stakers_breakdown(n):
|
||||
confirmed, pending, inactive = moe.staking_agent.partition_stakers_by_activity()
|
||||
confirmed, pending, inactive = self.staking_agent.partition_stakers_by_activity()
|
||||
stakers = dict()
|
||||
stakers['Active'] = len(confirmed)
|
||||
stakers['Pending'] = len(pending)
|
||||
stakers['Inactive'] = len(inactive)
|
||||
staker_type_numbers = list(stakers.values())
|
||||
staker_breakdown = list(stakers.values())
|
||||
fig = go.Figure(
|
||||
data=[
|
||||
go.Pie(
|
||||
labels=list(stakers.keys()),
|
||||
values=staker_type_numbers,
|
||||
name='Stakers',
|
||||
marker=go.bar.Marker(color=staker_type_numbers, colorscale='Viridis')
|
||||
values=staker_breakdown,
|
||||
name='Stakers'
|
||||
)
|
||||
],
|
||||
layout=go.Layout(
|
||||
|
@ -146,7 +146,7 @@ class MoeStatusApp(NetworkStatusPage):
|
|||
[Input('minute-interval', 'n_intervals')])
|
||||
def current_period(pathname):
|
||||
return html.Div([html.H4("Current Period"),
|
||||
html.H5(moe.staking_agent.get_current_period())])
|
||||
html.H5(self.staking_agent.get_current_period())])
|
||||
|
||||
@self.dash_app.callback(Output('time-remaining', 'children'),
|
||||
[Input('minute-interval', 'n_intervals')])
|
||||
|
@ -161,16 +161,15 @@ class MoeStatusApp(NetworkStatusPage):
|
|||
@self.dash_app.callback(Output('domains', 'children'),
|
||||
[Input('url', 'pathname')]) # on page-load
|
||||
def domains(pathname):
|
||||
domains = ' | '.join(moe.learning_domains)
|
||||
return html.Div([
|
||||
html.H4('Learning Domains'),
|
||||
html.H5(domains),
|
||||
html.H4('Domain'),
|
||||
html.H5(self.network),
|
||||
])
|
||||
|
||||
@self.dash_app.callback(Output('staked-tokens', 'children'),
|
||||
[Input('minute-interval', 'n_intervals')])
|
||||
def staked_tokens(n):
|
||||
nu = NU.from_nunits(moe.staking_agent.get_global_locked_tokens())
|
||||
nu = NU.from_nunits(self.staking_agent.get_global_locked_tokens())
|
||||
return html.Div([
|
||||
html.H4('Staked Tokens'),
|
||||
html.H5(f"{nu}"),
|
||||
|
@ -180,7 +179,7 @@ class MoeStatusApp(NetworkStatusPage):
|
|||
[Input('daily-interval', 'n_intervals')])
|
||||
def prev_locked_tokens(n):
|
||||
prior_periods = 30
|
||||
locked_tokens_dict = self.moe_db_client.get_historical_locked_tokens_over_range(prior_periods)
|
||||
locked_tokens_dict = self.blockchain_db_client.get_historical_locked_tokens_over_range(prior_periods)
|
||||
token_values = list(locked_tokens_dict.values())
|
||||
fig = go.Figure(data=[
|
||||
go.Bar(
|
||||
|
@ -207,7 +206,7 @@ class MoeStatusApp(NetworkStatusPage):
|
|||
[Input('daily-interval', 'n_intervals')])
|
||||
def historical_known_nodes(n):
|
||||
prior_periods = 30
|
||||
num_stakers_dict = self.moe_db_client.get_historical_num_stakers_over_range(prior_periods)
|
||||
num_stakers_dict = self.blockchain_db_client.get_historical_num_stakers_over_range(prior_periods)
|
||||
marker_color = 'rgb(0, 163, 239)'
|
||||
fig = go.Figure(data=[
|
||||
go.Scatter(
|
||||
|
@ -230,31 +229,31 @@ class MoeStatusApp(NetworkStatusPage):
|
|||
fig['layout'].update(autosize=True, width=None, height=None)
|
||||
return dcc.Graph(figure=fig, id='prev-stakers-graph', config=self.GRAPH_CONFIG)
|
||||
|
||||
@self.dash_app.callback(Output('locked-stake-graph', 'children'),
|
||||
[Input('daily-interval', 'n_intervals')])
|
||||
def future_locked_tokens(n):
|
||||
token_counter = self.moe_crawler.snapshot['future_locked_tokens']
|
||||
periods = len(token_counter)
|
||||
period_range = list(range(1, periods + 1))
|
||||
token_counter_values = list(token_counter.values())
|
||||
fig = go.Figure(data=[
|
||||
go.Bar(
|
||||
textposition='auto',
|
||||
x=period_range,
|
||||
y=token_counter_values,
|
||||
name='Stake',
|
||||
marker=go.bar.Marker(color=token_counter_values, colorscale='Viridis')
|
||||
)
|
||||
],
|
||||
layout=go.Layout(
|
||||
title=f'Staked NU over the next {periods} days.',
|
||||
xaxis={'title': 'Days'},
|
||||
yaxis={'title': 'NU Tokens', 'rangemode': 'tozero'},
|
||||
showlegend=False,
|
||||
legend=go.layout.Legend(x=0, y=1.0),
|
||||
paper_bgcolor='rgba(0,0,0,0)',
|
||||
plot_bgcolor='rgba(0,0,0,0)'
|
||||
))
|
||||
|
||||
fig['layout'].update(autosize=True, width=None, height=None)
|
||||
return dcc.Graph(figure=fig, id='locked-graph', config=self.GRAPH_CONFIG)
|
||||
# @self.dash_app.callback(Output('locked-stake-graph', 'children'),
|
||||
# [Input('daily-interval', 'n_intervals')])
|
||||
# def future_locked_tokens(n):
|
||||
# token_counter = self.moe_crawler.snapshot['future_locked_tokens']
|
||||
# periods = len(token_counter)
|
||||
# period_range = list(range(1, periods + 1))
|
||||
# token_counter_values = list(token_counter.values())
|
||||
# fig = go.Figure(data=[
|
||||
# go.Bar(
|
||||
# textposition='auto',
|
||||
# x=period_range,
|
||||
# y=token_counter_values,
|
||||
# name='Stake',
|
||||
# marker=go.bar.Marker(color=token_counter_values, colorscale='Viridis')
|
||||
# )
|
||||
# ],
|
||||
# layout=go.Layout(
|
||||
# title=f'Staked NU over the next {periods} days.',
|
||||
# xaxis={'title': 'Days'},
|
||||
# yaxis={'title': 'NU Tokens', 'rangemode': 'tozero'},
|
||||
# showlegend=False,
|
||||
# legend=go.layout.Legend(x=0, y=1.0),
|
||||
# paper_bgcolor='rgba(0,0,0,0)',
|
||||
# plot_bgcolor='rgba(0,0,0,0)'
|
||||
# ))
|
||||
#
|
||||
# fig['layout'].update(autosize=True, width=None, height=None)
|
||||
# return dcc.Graph(figure=fig, id='locked-graph', config=self.GRAPH_CONFIG)
|
||||
|
|
Loading…
Reference in New Issue