mirror of
https://github.com/oxen-io/oxen-observer.git
synced 2023-12-14 09:22:54 +01:00
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.
This commit is contained in:
parent
cecaddc18a
commit
fc90738a0e
3 changed files with 232 additions and 2 deletions
40
observer.py
40
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()
|
||||
|
|
|
@ -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;
|
||||
|
|
101
templates/quorums.html
Normal file
101
templates/quorums.html
Normal file
|
@ -0,0 +1,101 @@
|
|||
|
||||
{% extends "_basic.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="Wrapper">
|
||||
|
||||
<h2>Service Node testing quorums:</h2>
|
||||
<div class="TitleUnderliner"></div>
|
||||
|
||||
<div class="obligations quorums">
|
||||
{%for q in quorums.obligation[-18:] | reverse%}
|
||||
<div>
|
||||
<h2 title="This testing quorum is determined by block {{q.height}}">
|
||||
{# 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%}
|
||||
</h2>
|
||||
|
||||
<div class="validators">
|
||||
<label>Validators:</label>
|
||||
{%for v in q.quorum.validators%}
|
||||
<a class="sn" href="/sn/{{v}}" title="{{v}}">{{v}}</a>
|
||||
{%endfor%}
|
||||
</div>
|
||||
<div class="workers">
|
||||
<label>SNs to test:</label>
|
||||
{%for w in q.quorum.workers%}
|
||||
<a class="sn" href="/sn/{{w}}" title="{{w}}">{{w}}</a>
|
||||
{%endfor%}
|
||||
</div>
|
||||
</div>
|
||||
{%endfor%}
|
||||
</div>
|
||||
|
||||
<h2>Recent pulse quorums:</h2>
|
||||
<h4 class="curr-height">Current block height: <span class="omg">{{info.height}}</span></h4>
|
||||
<div class="TitleUnderliner"></div>
|
||||
|
||||
<div class="pulse quorums">
|
||||
{%for q in quorums.pulse[-24:] | reverse%}
|
||||
{%if q.height == info.height%}
|
||||
<div title="The shows primary pulse block producer and signers. Note that a backup quorum may produce and sign the block instead if the primary quorum takes too long to produce a block.">
|
||||
<h2>Next block</h2>
|
||||
{%else%}
|
||||
<div>
|
||||
<h2>Block {{q.height}}</h2>
|
||||
{%endif%}
|
||||
|
||||
<div class="workers">
|
||||
<label>Block producer:</label>
|
||||
{%for w in q.quorum.workers%}{#should only be one#}
|
||||
<a class="sn" href="/sn/{{w}}" title="{{w}}">{{w}}</a>
|
||||
{%endfor%}
|
||||
</div>
|
||||
<div class="validators">
|
||||
<label>Validators:</label>
|
||||
{%for v in q.quorum.validators%}
|
||||
<a class="sn" href="/sn/{{v}}" title="Validator {{loop.index}}/{{q.quorum.validators | length}}">{{v}}</a>
|
||||
{%endfor%}
|
||||
</div>
|
||||
</div>
|
||||
{%endfor%}
|
||||
</div>
|
||||
|
||||
|
||||
<h2>Recent checkpoint quorums:</h2>
|
||||
<h4 class="curr-height">Current block height: <span class="omg">{{info.height}}</span></h4>
|
||||
<div class="TitleUnderliner"></div>
|
||||
|
||||
<div class="checkpoint quorums">
|
||||
{%for q in quorums.checkpoint[-12:] | reverse%}
|
||||
<div>
|
||||
<h2 title="This checkpoint quorum is determined by block {{q.height - 11}}">
|
||||
Block {{q.height}}
|
||||
</h2>
|
||||
|
||||
<div class="validators">
|
||||
<!--<label>Service Nodes:</label>-->
|
||||
{%for v in q.quorum.validators%}
|
||||
<a class="sn" href="/sn/{{v}}" title="{{v}}
|
||||
Validator {{loop.index}}/{{q.quorum.validators | length}}">{{v}}</a>
|
||||
{%endfor%}
|
||||
</div>
|
||||
</div>
|
||||
{%endfor%}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
Loading…
Reference in a new issue