This commit is contained in:
Jason Rhinelander 2023-01-12 11:38:39 +11:00 committed by GitHub
commit 2f98b3a0f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 75 additions and 83 deletions

View File

@ -194,7 +194,7 @@ def get_sns(sns_future, info_future):
sn_states = sns_future.get()
sn_states = sn_states['service_node_states'] if 'service_node_states' in sn_states else []
for sn in sn_states:
sn['contribution_open'] = sn['staking_requirement'] - sn['total_reserved']
sn['contribution_open'] = sn['staking_requirement'] - sn.get('total_reserved', sn['total_contributed'])
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)
@ -242,7 +242,13 @@ def parse_mempool(mempool_future):
mp['_sorted'] = True
for tx in mp['transactions']:
tx['info'] = json.loads(tx["tx_json"])
if 'type' not in tx and 'tx_json' in tx:
# Legacy code for Oxen 10 and earlier
info = json.loads(tx["tx_json"])
# FIXME -- do we have parsed extra stuff already?
info['tx_extra_raw'] = bytes_to_hex(info['extra'])
del info['extra']
tx.update(info)
else:
mp['transactions'] = []
return mp
@ -327,17 +333,16 @@ def main(refresh=None, page=0, per_page=None, first=None, last=None, style=None)
if 'tx_hashes' in b:
txids += b['tx_hashes']
if txids:
txs = parse_txs(tx_req(omq, oxend, txids, cache_key='mempool').get())
txs = parse_txs(tx_req(omq, oxend, txids, cache_key='recent').get())
i = 0
for tx in txs:
if 'vin' in tx['info'] and len(tx['info']['vin']) == 1 and 'gen' in tx['info']['vin'][0]:
if 'vin' in tx and len(tx['vin']) == 1 and 'gen' in tx['vin'][0]:
tx['coinbase'] = True
# TXs should come back in the same order so we can just skip ahead one when the block
# height changes rather than needing to search for the block
if blocks[i]['height'] != tx['block_height']:
i += 1
while i < len(blocks) and blocks[i]['height'] != tx['block_height']:
print("Something getting wrong: missing txes?", file=sys.stderr)
i += 1
if i >= len(blocks):
print("Something getting wrong: have leftover txes")
@ -347,12 +352,17 @@ def main(refresh=None, page=0, per_page=None, first=None, last=None, style=None)
# Clean up the SN data a bit to make things easier for the templates
awaiting_sns, active_sns, inactive_sns = get_sns(sns, inforeq)
accrued = accrued.get()
accrued_total = (
sum(amt for wallet, amt in accrued['balances'].items()) if 'balances' in accrued else
sum(accrued['amounts']))
return flask.render_template('index.html',
info=info,
stake=stake.get(),
fees=base_fee.get(),
emission=coinbase.get(),
accrued_total=sum(accrued.get()['amounts']),
accrued_total=accrued_total,
hf=hfinfo.get(),
active_sns=active_sns,
active_swarms=len(set(x['swarm_id'] for x in active_sns)),
@ -398,8 +408,9 @@ def tx_req(omq, oxend, txids, cache_key='single', **kwargs):
return FutureJSON(omq, oxend, 'rpc.get_transactions', cache_seconds=10, cache_key=cache_key,
args={
"txs_hashes": txids,
"decode_as_json": True,
"decode_as_json": True, # Can drop once we no longer need Oxen 10 support
"tx_extra": True,
"tx_extra_raw": True,
"prune": True,
"stake_info": True,
},
@ -586,9 +597,9 @@ def show_sn(pubkey, more_details=False):
# Number of staked contributions
sn['num_contributions'] = sum(len(x["locked_contributions"]) for x in sn["contributors"] if "locked_contributions" in x)
# Number of unfilled, reserved contribution spots:
sn['num_reserved_spots'] = sum(x["amount"] < x["reserved"] for x in sn["contributors"])
sn['num_reserved_spots'] = sum('reserved' in x and 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['num_open_spots'] = 0 if sn.get('total_reserved', sn['total_contributed']) >= sn['staking_requirement'] else max(0, 4 - sn['num_contributions'] - sn['num_reserved_spots'])
if more_details:
formatter = HtmlFormatter(cssclass="syntax-highlight", style="paraiso-dark")
@ -635,14 +646,18 @@ def parse_txs(txs_rpc):
return []
for tx in txs_rpc['txs']:
if 'info' not in tx:
if 'type' not in tx and 'as_json' in tx:
# Pre Oxen 11 crammed the details into "as_json" that we have to parse again
# We have serialized JSON data inside a field in the JSON, because of oxend's
# multiple incompatible JSON generators 🤮:
tx['info'] = json.loads(tx["as_json"])
info = json.loads(tx["as_json"])
del tx['as_json']
# The "extra" field inside as_json is retardedly in per-byte integer values,
# convert it to a hex string 🤮:
tx['info']['extra'] = bytes_to_hex(tx['info']['extra'])
info['tx_extra_raw'] = bytes_to_hex(info['extra'])
del info['extra']
tx.update(info)
return txs_rpc['txs']
@ -742,19 +757,19 @@ def show_tx(txid, more_details=False):
# If this is a state change, see if we have the quorum stored to provide context
testing_quorum = None
if tx['info']['version'] >= 4 and 'sn_state_change' in tx['extra']:
if tx['version'] >= 4 and 'sn_state_change' in tx['extra']:
testing_quorum = FutureJSON(omq, oxend, 'rpc.get_quorum_state', 60, cache_key='tx_state_change',
args={ 'quorum_type': 0, 'start_height': tx['extra']['sn_state_change']['height'] })
kindex_info = {} # { amount => { keyindex => {output-info} } }
block_info_req = None
if 'vin' in tx['info']:
if len(tx['info']['vin']) == 1 and 'gen' in tx['info']['vin'][0]:
if 'vin' in tx:
if len(tx['vin']) == 1 and 'gen' in tx['vin'][0]:
tx['coinbase'] = True
elif tx['info']['vin'] and config.enable_mixins_details:
elif tx['vin'] and config.enable_mixins_details:
# Load output details for all outputs contained in the inputs
outs_req = []
for inp in tx['info']['vin']:
for inp in tx['vin']:
# Key positions are stored as offsets from the previous index rather than indices,
# so de-delta them back into indices:
if 'key_offsets' in inp['key'] and 'key_indices' not in inp['key']:
@ -766,7 +781,7 @@ def show_tx(txid, more_details=False):
kis.append(kbase)
del inp['key']['key_offsets']
outs_req = [{"amount":inp['key']['amount'], "index":ki} for inp in tx['info']['vin'] for ki in inp['key']['key_indices']]
outs_req = [{"amount":inp['key']['amount'], "index":ki} for inp in tx['vin'] for ki in inp['key']['key_indices']]
outputs = FutureJSON(omq, oxend, 'rpc.get_outs', args={
'get_txid': True,
'outputs': outs_req,
@ -778,7 +793,7 @@ def show_tx(txid, more_details=False):
'heights': [o["height"] for o in outputs]
})
i = 0
for inp in tx['info']['vin']:
for inp in tx['vin']:
amount = inp['key']['amount']
if amount not in kindex_info:
kindex_info[amount] = {}

View File

@ -50,7 +50,7 @@ Validator bits: {{"{:011b}".format(block.info.pulse.validator_bitset)}}"><label>
{%endif%}
{%set sum_burned = transactions | selectattr('extra.burn_amount') | sum(attribute='extra.burn_amount') %}
{%set sum_fees = transactions | selectattr('info.rct_signatures') | selectattr('info.rct_signatures.txnFee') | sum(attribute='info.rct_signatures.txnFee') - sum_burned%}
{%set sum_fees = transactions | selectattr('rct_signatures') | selectattr('rct_signatures.txnFee') | sum(attribute='rct_signatures.txnFee') - sum_burned%}
<span title="{{(block_header.reward - sum_fees) | oxen(fixed=True)}} created in this block.{%if sum_fees > 0%}
@ -91,9 +91,9 @@ Note that this value does not include earned transaction fees ({{sum_fees | oxen
</tr>
<tr>
<td><a href="/tx/{{miner_tx.tx_hash}}">{{miner_tx.tx_hash}}</a></td>
<td>{{miner_tx.info.vout | sum(attribute='amount') | oxen}}</td>
<td>{{miner_tx.vout | sum(attribute='amount') | oxen}}</td>
<td>{{miner_tx.size}}</td>
<td>{{miner_tx.info.version}}</td>
<td>{{miner_tx.version}}</td>
</tr>
</table>
{%endif%}
@ -122,7 +122,7 @@ Note that this value does not include earned transaction fees ({{sum_fees | oxen
<td><a href="/tx/{{tx.tx_hash}}">{{tx.tx_hash}}</a></td>
<td>{{fee.display(tx)}}</td>
<td></td>
<td>{{tx.info.vin | length}}/{{tx.info.vout | length}}</td>
<td>{{tx.vin | length}}/{{tx.vout | length}}</td>
<td>{{tx.size | si}}B</td>
</tr>
{% endfor %}

View File

@ -29,13 +29,13 @@
<td>{{symbol.display(tx)}}</td>
<td><a href="/tx/{{tx.id_hash}}">{{tx.id_hash}}</a></td>
<td>
{%if 'rct_signatures' in tx.info%}
{{fee.display(tx)}} / {{(tx.info.rct_signatures.txnFee * 1000 / tx.blob_size) | oxen(tag=false, decimals=4)}}
{%if 'rct_signatures' in tx%}
{{fee.display(tx)}} / {{(tx.rct_signatures.txnFee * 1000 / tx.blob_size) | oxen(tag=false, decimals=4)}}
{%else%}
N/A
{%endif%}
</td>
<td>{{tx.info.vin | length}}/{{tx.info.vout | length}}</td>
<td>{{tx.vin | length}}/{{tx.vout | length}}</td>
<td>{{tx.blob_size | si}}B</td>
</tr>
{% endfor %}

View File

@ -18,7 +18,8 @@
<tr>
{%include 'include/sn_kcf.html'%}
<td>{{sn.total_contributed | oxen(tag=false, fixed=true)}}</td>
{%if sn.total_reserved >= sn.staking_requirement%}
{% set total_stakes = sn.total_reserved if 'total_reserved' in sn else sn.total_contributed %}
{%if total_stakes >= sn.staking_requirement%}
<td title="All remaining contribution room is reserved for specific contributors">
⛔ {{sn.contribution_open | oxen(tag=false, fixed=true)}}
</td>

View File

@ -1,6 +1,6 @@
{% macro display(tx, show_zero=false) -%}
{% set fee =
(tx.info.rct_signatures.txnFee if 'rct_signatures' in tx.info and 'txnFee' in tx.info.rct_signatures else 0)
(tx.rct_signatures.txnFee if 'rct_signatures' in tx and 'txnFee' in tx.rct_signatures else 0)
-%}
{% if fee > 0 or show_zero -%}
{{ fee | oxen(tag=False, fixed=True, decimals=4) }}

View File

@ -1,6 +1,6 @@
{%- macro display(tx, text=false) -%}
{% if tx.info.version >= 4 -%}
{% if tx.info.type == 1 and 'sn_state_change' in tx.extra -%}
{% if tx.version >= 4 -%}
{% if tx.type == 1 and 'sn_state_change' in tx.extra -%}
{% if tx.extra.sn_state_change.type == 'decom' -%}
<span class="icon" title="Service Node decommission">👎{%if text%} decommission{%endif%}</span>
{% elif tx.extra.sn_state_change.type == 'recom' -%}
@ -12,9 +12,9 @@
{% else -%}
<span class="icon" title="Unknown state change transaction">❓{%if text%} unknown state change{%endif%}</span><!-- Either a bug or a malformed transaction -->
{% endif -%}
{% elif tx.info.type == 2 -%}
{% elif tx.type == 2 -%}
<span class="icon" title="Service Node stake unlock — {{tx.extra.sn_pubkey}}">🔓{%if text%} unlock{%endif%}</span>
{% elif tx.info.type == 4 and 'ons' in tx.extra -%}
{% elif tx.type == 4 and 'ons' in tx.extra -%}
{% if 'buy' in tx.extra.ons -%}
<span class="icon" title="Oxen Name Service Buying">🎫{%if text%} LNS purchase{%endif%}</span>
{% elif 'update' in tx.extra.ons -%}

View File

@ -177,7 +177,7 @@
<td><a href="/tx/{{b.txs[0].tx_hash}}">{{b.txs[0].tx_hash}}</a></td>
<td>{{fee.display(b.txs[0])}}</td>
<td>{{b.coinbase_payouts | oxen(tag=False, fixed=True, decimals=2)}}</td>
<td>0/{{b.txs[0].info.vout | length}}</td>
<td>0/{{b.txs[0].vout | length}}</td>
<td>{{b.txs[0].size | si}}</td>
</tr>
{% else %}
@ -203,7 +203,7 @@
<td><a href="/tx/{{tx.tx_hash}}">{{tx.tx_hash}}</a></td>
<td>{{fee.display(tx)}}</td>
<td></td>
<td>{{tx.info.vin | length}}/{{tx.info.vout | length}}</td>
<td>{{tx.vin | length}}/{{tx.vout | length}}</td>
<td>{{tx.size | si}}</td>
</tr>
{% endfor %}

View File

@ -59,8 +59,8 @@
{%endif%}
</span>
{%if sn.total_reserved != sn.total_contributed%}
<span><label>Total reserved:</label> {{sn.total_reserved | oxen}}</span>
{%if 'total_reserved' in sn and sn.total_reserved != sn.total_contributed%}
<span><label>Reserved stakes:</label> {{(sn.total_reserved - sn.total_contributed) | oxen}}</span>
{%endif%}
<span>
@ -94,35 +94,10 @@
</span>
{%if info.service_node%}{# SN tests are only conducted by SNs #}
<span>
<label{%if sn.storage_server_last_unreachable > sn.storage_server_last_reachable%} class="omg warning"{%endif%}>Storage server:</label>
{%if sn.storage_server_last_reachable == 0 and sn.storage_server_last_unreachable == 0%}
Not yet tested
{%elif sn.storage_server_last_reachable >= sn.storage_server_last_unreachable%}
<span title="Last unreachable: {%if sn.storage_server_last_unreachable == 0%}Unknown{%else%}{{sn.storage_server_last_unreachable | from_timestamp | ago}} ago{%endif%}">
Reachable (as of {{sn.storage_server_last_reachable | from_timestamp | ago}} ago
</span>
{%else%}
<span title="Last reachable: {%if sn.storage_server_last_reachable == 0%}Unknown{%else%}{{sn.storage_server_last_reachable | from_timestamp | ago}} ago{%endif%}">
Not reachable (as of {{sn.storage_server_last_unreachable | from_timestamp | ago}} ago)
</span>
{%endif%}
</span>
{% import 'include/reachable.html' as reachable %}
<span>
<label{%if sn.lokinet_last_unreachable > sn.lokinet_last_reachable%} class="omg warning"{%endif%}>Lokinet:</label>
{%if sn.lokinet_last_reachable == 0 and sn.lokinet_last_unreachable == 0%}
Not yet tested
{%elif sn.lokinet_last_reachable >= sn.lokinet_last_unreachable%}
<span title="Last unreachable: {%if sn.lokinet_last_unreachable == 0%}Unknown{%else%}{{sn.lokinet_last_unreachable | from_timestamp | ago}} ago{%endif%}">
Reachable (as of {{sn.lokinet_last_reachable | from_timestamp | ago}} ago
</span>
{%else%}
<span title="Last reachable: {%if sn.lokinet_last_reachable == 0%}Unknown{%else%}{{sn.lokinet_last_reachable | from_timestamp | ago}} ago{%endif%}">
Not reachable (as of {{sn.lokinet_last_unreachable | from_timestamp | ago}} ago)
</span>
{%endif%}
</span>
{{reachable.display(sn)}}
{{reachable.display(sn, true)}}
{%endif%}
{%set pulse_voted = sn.pulse_participation | selectattr("voted") | list | length%}
@ -188,7 +163,8 @@
<p class="sn-awaiting">Awaiting registration. This service node has <span class="oxen required">{{(sn.staking_requirement - sn.total_contributed) | oxen}}</span>
remaining to be contributed.
{%if sn.num_open_spots > 0%}
The minimum required stake contribution is <span class="oxen required">{{((sn.staking_requirement - sn.total_reserved) / sn.num_open_spots) | oxen}}</span>.
{%set total_staked = sn.total_reserved if 'total_reserved' in sn else sn.total_contributed%}
The minimum required stake contribution is <span class="oxen required">{{((sn.staking_requirement - total_staked) / sn.num_open_spots) | oxen}}</span>.
{%endif%}
</p>
{%endif%}
@ -224,7 +200,7 @@
({{c.locked_contributions|length}} contributions)
{%endif-%}
</td>
<td>{{c.reserved | oxen}}</td>
<td>{{(c.reserved if 'reserved' in c else c.amount) | oxen}}</td>
</tr>
{%endfor%}
</tbody>

View File

@ -46,7 +46,7 @@
<span title="Unix timestamp: {{tx.received_timestamp}}"><label>In mempool since:</label> {{tx.received_timestamp | from_timestamp | format_datetime('short')}} UTC
({{tx.received_timestamp | from_timestamp | ago}} ago)</span>
{%endif%}
<span><label>TX Version/Type:</label> {{tx.info.version}}/{{sym.display(tx, text=true)}}</span>
<span><label>TX Version/Type:</label> {{tx.version}}/{{sym.display(tx, text=true)}}</span>
{%if not have_raw_tx%}
{%if 'block_timestamp' in tx%}
<span title="Unix timestamp: {{tx.block_timestamp}}"><label>Timestamp:</label> {{tx.block_timestamp | from_timestamp | format_datetime('short')}} UTC
@ -56,17 +56,17 @@
{# FIXME - if in mempool then link to mempool page instead #}
<span><label>Fee (Per kB):</label>
{%if tx.coinbase or 'rct_signatures' not in tx.info%}
{%if tx.coinbase or 'rct_signatures' not in tx%}
N/A
{%else%}
{{fee.display(tx)}}
({{(tx.info.rct_signatures.txnFee * 1000 / tx.size) | oxen(tag=false, decimals=6)}})
({{(tx.rct_signatures.txnFee * 1000 / tx.size) | oxen(tag=false, decimals=6)}})
{%endif%}
</span>
<span title="{{tx.size}} bytes"><label>TX Size:</label> {{tx.size|si}}B</span>
<span><label>No. Confirmations:</label> {%if 'block_height' in tx%}{{info.height - tx.block_height}}{%else%}None (in mempool){%endif%}</span>
<span><label>RingCT/RingCT Type:</label> {%if tx.info.version >= 2 and 'rct_signatures' in tx.info and not tx.coinbase%}Yes/{{tx.info.rct_signatures.type}}{%else%}No{%endif%}</span>
<span><label>RingCT/RingCT Type:</label> {%if tx.version >= 2 and 'rct_signatures' in tx and not tx.coinbase%}Yes/{{tx.rct_signatures.type}}{%else%}No{%endif%}</span>
{%if tx.coinbase and tx.extra.sn_winner%}
<span><label>Service Node Winner:</label>
@ -78,10 +78,10 @@
</span>
{%endif%}
</h4>
<div class="info-item"><label>Extra: </label>{{tx.info.extra}}</div>
<div class="info-item"><label>Extra: </label>{{tx.tx_extra_raw}}</div>
{% if tx.info.version >= 4 -%}
{% if tx.info.type == 1 and 'sn_state_change' in tx.extra %}
{% if tx.version >= 4 -%}
{% if tx.type == 1 and 'sn_state_change' in tx.extra %}
<h2>
{%- set show_reasons = false -%}
{% if tx.extra.sn_state_change.type == 'decom' -%}
@ -159,12 +159,12 @@ This tx does not includes a vote from this testing service node (only 7 votes ar
</div>
{%endif%}
{% elif tx.info.type == 2 %}
{% elif tx.type == 2 %}
<h2>🔓 Service Node Unlock</h2>
<p class="unlock-pubkey"><label>Service Node Public Key:</label> <a href="/sn/{{tx.extra.sn_pubkey}}">{{tx.extra.sn_pubkey}}</a></p>
<p><label>Unlock key image:</label> {{unlock_key_image}}</p> {# FIXME #}
<p><label>Unlock signature:</label> {{unlock_signature}}</p> {# FIXME #}
{% elif tx.info.type == 4 and 'lns' in tx.extra %}
{% elif tx.type == 4 and 'lns' in tx.extra %}
{% if 'buy' in tx.extra.lns %}
<h2>🎫 Oxen Name Service Registration</h2>
{% elif 'update' in tx.extra.lns %}
@ -216,10 +216,10 @@ This tx does not includes a vote from this testing service node (only 7 votes ar
{% endif %}
{%if tx.info.vout%}
{%if tx.vout%}
<h2>Outputs</h2>
<h4 class="Subtitle">{{tx.info.vout|length}} output(s) for total of
{{tx.info.vout | sum(attribute='amount') | oxen(zero='<span title="Regular LOKI tx amounts are private">???</span>') | safe}}</h4>
<h4 class="Subtitle">{{tx.vout|length}} output(s) for total of
{{tx.vout | sum(attribute='amount') | oxen(zero='<span title="Regular OXEN tx amounts are private">???</span>') | safe}}</h4>
<div class="TitleDivider"></div>
<div class="tx-outputs">
<table class="Table">
@ -232,7 +232,7 @@ This tx does not includes a vote from this testing service node (only 7 votes ar
</thead>
<tbody>
{%for out in tx.info.vout%}
{%for out in tx.vout%}
<tr>
<td><label>{{loop.index0}}:</label> {{out.target.key}}</td>
<td>{{out.amount | oxen(zero='?')}}</td>
@ -549,12 +549,12 @@ This tx does not includes a vote from this testing service node (only 7 votes ar
{%endif%}
#}
{%if not tx.coinbase and tx.info.vin|length > 0 %}
{%if not tx.coinbase and tx.vin|length > 0 %}
<div style="height: 1em"></div>
<h2>Inputs</h2>
<h4 class="Subtitle">{{tx.info.vin|length}} input(s) for total of
{{tx.info.vin | sum(attribute='key.amount') | oxen(zero='<span title="Regular LOKI tx amounts are private">???</span>') | safe}}</h4>
<h4 class="Subtitle">{{tx.vin|length}} input(s) for total of
{{tx.vin | sum(attribute='key.amount') | oxen(zero='<span title="Regular OXEN tx amounts are private">???</span>') | safe}}</h4>
<div class="TitleDivider"></div>
{#FIXME#}
@ -574,7 +574,7 @@ This tx does not includes a vote from this testing service node (only 7 votes ar
<div class="tx-inputs">
<table class="Table">
{%for inp in tx.info.vin if 'key' in inp%}
{%for inp in tx.vin if 'key' in inp%}
<tr class="tx-input-key-image">
<td style="text-align: left;" colspan="2">
<label>Key Image {{loop.index0}}:</label> {{inp.key.k_image}}