From fc90738a0e7ed799a8cbea59beb4667a173378f5 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Sun, 11 Oct 2020 00:07:20 -0300 Subject: [PATCH] Add quorum details page This is completely different from the old one, made much more compact and usable using the full list of truncated SN pubkeys in flex boxes. --- observer.py | 40 +++++++++++++++- static/style.css | 93 +++++++++++++++++++++++++++++++++++++ templates/quorums.html | 101 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 templates/quorums.html diff --git a/observer.py b/observer.py index d47ed5a..2a41dfd 100644 --- a/observer.py +++ b/observer.py @@ -145,10 +145,10 @@ def base32z(hex): @app.template_filter('ellipsize') -def ellipsize(string, leading=10, trailing=5): +def ellipsize(string, leading=10, trailing=5, ellipsis='...'): if len(string) <= leading + trailing + 3: return string - return string[0:leading] + "..." + ('' if not trailing else string[-trailing:]) + return string[0:leading] + ellipsis + ('' if not trailing else string[-trailing:]) @app.after_request @@ -192,6 +192,29 @@ def get_sns(sns_future, info_future): awaiting_sns.append(sn) return awaiting_sns, active_sns, inactive_sns + +def get_quorums_future(lmq, lokid, height): + return FutureJSON(lmq, lokid, 'rpc.get_quorum_state', 30, + args={ 'start_height': height-50, 'end_height': height }) + + +def get_quorums(quorums_future): + oblig_quorums, checkpoint_quorums, pulse_quorums = [], [], [] + + quorums = quorums_future.get() + quorums = quorums['quorums'] if 'quorums' in quorums else [] + for q in quorums: + if q['quorum_type'] == 0: + oblig_quorums.append(q) + elif q['quorum_type'] == 1: + checkpoint_quorums.append(q) + elif q['quorum_type'] == 3: + pulse_quorums.append(q) + else: + print("Something getting wrong in quorums: found unknown quorum_type={}".format(q['quorum_type']), file=sys.stderr) + return oblig_quorums, checkpoint_quorums, pulse_quorums + + @app.context_processor def template_globals(): return { @@ -568,6 +591,19 @@ def show_tx(txid, more_details=False): ) +@app.route('/quorums') +def show_quorums(): + lmq, lokid = lmq_connection() + info = FutureJSON(lmq, lokid, 'rpc.get_info', 1) + quos = get_quorums_future(lmq, lokid, info.get()['height']) + obl_q, cp_q, pulse_q = get_quorums(quos) + + return flask.render_template('quorums.html', + info=info.get(), + quorums=dict(obligation=obl_q, checkpoint=cp_q, pulse=pulse_q), + ) + + @app.route('/search') def search(): lmq, lokid = lmq_connection() diff --git a/static/style.css b/static/style.css index 5340fdf..23b1bc5 100644 --- a/static/style.css +++ b/static/style.css @@ -317,6 +317,99 @@ td.voter-signature { text-overflow: ellipsis; } +div.quorums { + display: flex; + flex-wrap: wrap; + justify-content: space-evenly; + padding: 0; +} + +div.quorums>div { + border: 2px solid #008522; + border-radius: 5px; + padding: 0; + flex-grow: 1; +} +div.quorums.obligations>div { + flex-basis: 300px; + margin: 5px; +} +div.quorums.obligations { + margin: 0 -5px; +} +div.quorums.checkpoint>div { + flex-basis: 200px; + margin: 5px; +} +div.quorums.checkpoint { + margin: 0 -5px; +} +div.quorums.pulse>div { + flex-basis: 200px; + margin: 5px; +} +div.quorums.checkpoint { + margin: 0 -5px; +} +h4.curr-height { + margin-top: 0px; +} + +div.quorums>div>.validators, +div.quorums>div>.workers { + display: flex; + flex-flow: row wrap; + align-items: baseline; + /*justify-content: center;*/ + /*space-between;*/ +} +.quorums.obligations .workers, .quorums.pulse .validators { + border-top: 2px solid #008522; +} +.quorums .workers label, .quorums .validators label { + font-weight: bold; +} +.quorums .sn { + display: block; + flex-basis: 40px; + max-width: 60px; + flex-grow: 1; + background-color: #bbb; + border: 1px solid #555; + border-radius: 5px; + overflow: hidden; + text-overflow: ""; + margin: 3px 3px; + padding: 1px 1px 1px 1px; + font-size: 10px; + color: black; + text-decoration: none; +} +.quorums .validators .sn { + background-color: #777; + border: 1px solid #aaa; + color: white; +} +.quorums .sn:hover { + background-color: #006a1b; + border-color: #78be20; + color: #78be20; +} +.quorums.pulse .workers .sn { + flex-grow: 1; + max-width: 150px; +} +div.quorums h2 { + background-color: #008522; + color: white; + font-size: 14px; + font-weight: bold; + margin: 0; +} +div.quorums h5 { + margin: 0; +} + td.quorum-pubkey span { display: inline-block; width: 80ex; diff --git a/templates/quorums.html b/templates/quorums.html new file mode 100644 index 0000000..538eab3 --- /dev/null +++ b/templates/quorums.html @@ -0,0 +1,101 @@ + +{% extends "_basic.html" %} + +{% block content %} + +
+ +

Service Node testing quorums:

+
+ +
+ {%for q in quorums.obligation[-18:] | reverse%} +
+

+ {# quorum height + 13 here because a node only does an obligations test once the + node has at least 11 blocks on top of it, which means it is 13 blocks below the + blockchain height, which means it has to be (at least) the 12th oldest block, +1 + because info.height is the top height + 1 #} + Block {{q.height + 13}} + {%if q.height + 13 > info.height%} + (~{{(q.height + 13 - info.height) * 2}} minutes) + {%elif q.height + 13 == info.height%} + (testing now!) + {%else%} + (~{{(info.height - (q.height + 13)) * 2}} minutes ago) + {%endif%} +

+ +
+ + {%for v in q.quorum.validators%} + {{v}} + {%endfor%} +
+
+ + {%for w in q.quorum.workers%} + {{w}} + {%endfor%} +
+
+ {%endfor%} +
+ +

Recent pulse quorums:

+

Current block height: {{info.height}}

+
+ +
+ {%for q in quorums.pulse[-24:] | reverse%} + {%if q.height == info.height%} +
+

Next block

+ {%else%} +
+

Block {{q.height}}

+ {%endif%} + +
+ + {%for w in q.quorum.workers%}{#should only be one#} + {{w}} + {%endfor%} +
+
+ + {%for v in q.quorum.validators%} + {{v}} + {%endfor%} +
+
+ {%endfor%} +
+ + +

Recent checkpoint quorums:

+

Current block height: {{info.height}}

+
+ +
+ {%for q in quorums.checkpoint[-12:] | reverse%} +
+

+ Block {{q.height}} +

+ +
+ + {%for v in q.quorum.validators%} + {{v}} + {%endfor%} +
+
+ {%endfor%} +
+ + +
+ +{% endblock %}