Loki -> Oxen
This commit is contained in:
parent
868fd7dc0b
commit
007baea846
12
README.md
12
README.md
|
@ -1,6 +1,6 @@
|
|||
# Loki Observer OMG block explorer
|
||||
# Oxen Observer OMG block explorer
|
||||
|
||||
Block explorer using Loki 8+ LMQ RPC interface that does everything through RPC requests. Sexy,
|
||||
Block explorer using Oxen 8+ LMQ RPC interface that does everything through RPC requests. Sexy,
|
||||
awesome, safe.
|
||||
|
||||
## Prerequisite packages
|
||||
|
@ -23,7 +23,7 @@ Quick and dirty setup instructions for now:
|
|||
(Note that we require a very recent python3-jinja package (2.11+), which may not be installed by the
|
||||
above.)
|
||||
|
||||
You'll also need to run lokid with `--lmq-local-control ipc:///path/to/loki-observer/mainnet.sock`.
|
||||
You'll also need to run oxend with `--lmq-local-control ipc:///path/to/loki-observer/mainnet.sock`.
|
||||
|
||||
## Running in debug mode
|
||||
|
||||
|
@ -60,13 +60,13 @@ Create a "vassal" config for loki-observer, `/etc/uwsgi-emperor/vassals/loki-obs
|
|||
logger = file:logfile=/path/to/loki-observer/mainnet.log
|
||||
|
||||
Set ownership of this user to whatever use you want it to run as, and set the group to `_loki` (so
|
||||
that it can open the lokid unix socket):
|
||||
that it can open the oxend unix socket):
|
||||
|
||||
chown MYUSERNAME:_loki /etc/uwsgi-emperor/vassals/loki-observer.ini
|
||||
|
||||
In the loki-observer/mainnet.py, set:
|
||||
|
||||
config.lokid_rpc = 'ipc:///var/lib/loki/lokid.sock'
|
||||
config.oxend_rpc = 'ipc:///var/lib/loki/oxend.sock'
|
||||
|
||||
and finally, proxy requests from the webserver to the wsgi socket. For Apache I do this with:
|
||||
|
||||
|
@ -88,4 +88,4 @@ make uwsgi restart (for example because you are changing things) then it is suff
|
|||
apache2/uwsgi-emperor layers).
|
||||
|
||||
If you want to set up a testnet or devnet observer the procedure is essentially the same, but
|
||||
using testnet.py or devnet.py pointing to a lokid.sock from a testnet or devnet lokid.
|
||||
using testnet.py or devnet.py pointing to a oxend.sock from a testnet or devnet oxend.
|
||||
|
|
10
config.py
10
config.py
|
@ -7,10 +7,10 @@
|
|||
# into `mainnet.py`/`testnet.py`/etc.
|
||||
|
||||
|
||||
# LMQ RPC endpoint of lokid; can be a unix socket 'ipc:///path/to/lokid.sock' (preferred) or a tcp
|
||||
# LMQ RPC endpoint of oxend; can be a unix socket 'ipc:///path/to/oxend.sock' (preferred) or a tcp
|
||||
# socket 'tcp://127.0.0.1:5678'. Typically you want this running with admin permission.
|
||||
# Leave this as None here, and set it for each observer in the mainnet.py/testnet.py/etc. script.
|
||||
lokid_rpc = None
|
||||
oxend_rpc = None
|
||||
|
||||
# Default blocks per page for the index.
|
||||
blocks_per_page=20
|
||||
|
@ -25,9 +25,9 @@ autorefresh_option=True
|
|||
enable_mixins_details=True
|
||||
|
||||
# URLs to networks other than the one we are on:
|
||||
mainnet_url='https://blocks.lokinet.dev'
|
||||
testnet_url='https://testnet.lokinet.dev'
|
||||
devnet_url='https://devnet.lokinet.dev'
|
||||
mainnet_url='https://oxen.observer'
|
||||
testnet_url='https://testnet.oxen.observer'
|
||||
devnet_url='https://devnet.oxen.observer'
|
||||
|
||||
# Same as above, but these apply if we are on a .loki URL:
|
||||
lokinet_mainnet_url='http://blocks.loki'
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
from observer import app, config
|
||||
|
||||
config.lokid_rpc = 'ipc://lokid/devnet.sock'
|
||||
config.oxend_rpc = 'ipc://oxend/devnet.sock'
|
||||
|
|
16
lmq.py
16
lmq.py
|
@ -4,16 +4,16 @@ import json
|
|||
import sys
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
lmq, lokid = None, None
|
||||
lmq, oxend = None, None
|
||||
def lmq_connection():
|
||||
global lmq, lokid
|
||||
global lmq, oxend
|
||||
if lmq is None:
|
||||
lmq = pylokimq.LokiMQ(pylokimq.LogLevel.warn)
|
||||
lmq.max_message_size = 200*1024*1024
|
||||
lmq.start()
|
||||
if lokid is None:
|
||||
lokid = lmq.connect_remote(config.lokid_rpc)
|
||||
return (lmq, lokid)
|
||||
if oxend is None:
|
||||
oxend = lmq.connect_remote(config.oxend_rpc)
|
||||
return (lmq, oxend)
|
||||
|
||||
cached = {}
|
||||
cached_args = {}
|
||||
|
@ -31,7 +31,7 @@ class FutureJSON():
|
|||
result in unbounded memory growth.
|
||||
|
||||
lmq - the lmq object
|
||||
lokid - the lokid lmq connection id object
|
||||
oxend - the oxend lmq connection id object
|
||||
endpoint - the lmq endpoint, e.g. 'rpc.get_info'
|
||||
cache_seconds - how long to cache the response; can be None to not cache it at all
|
||||
cache_key - fixed string to enable different caches of the same endpoint
|
||||
|
@ -40,7 +40,7 @@ class FutureJSON():
|
|||
timeout - maximum time to spend waiting for a reply
|
||||
"""
|
||||
|
||||
def __init__(self, lmq, lokid, endpoint, cache_seconds=3, *, cache_key='', args=None, fail_okay=False, timeout=10):
|
||||
def __init__(self, lmq, oxend, endpoint, cache_seconds=3, *, cache_key='', args=None, fail_okay=False, timeout=10):
|
||||
self.endpoint = endpoint
|
||||
self.cache_key = self.endpoint + cache_key
|
||||
self.fail_okay = fail_okay
|
||||
|
@ -53,7 +53,7 @@ class FutureJSON():
|
|||
else:
|
||||
self.json = None
|
||||
self.args = args
|
||||
self.future = lmq.request_future(lokid, self.endpoint, [] if self.args is None else [self.args], timeout=timeout)
|
||||
self.future = lmq.request_future(oxend, self.endpoint, [] if self.args is None else [self.args], timeout=timeout)
|
||||
self.cache_seconds = cache_seconds
|
||||
|
||||
def get(self):
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
from observer import app, config
|
||||
|
||||
config.lokid_rpc = 'ipc://lokid/mainnet.sock'
|
||||
config.oxend_rpc = 'ipc://oxend/mainnet.sock'
|
||||
|
|
160
observer.py
160
observer.py
|
@ -115,10 +115,10 @@ def format_si(value):
|
|||
i += 1
|
||||
return filter_round(value) + '{}'.format(si_suffix[i])
|
||||
|
||||
@app.template_filter('loki')
|
||||
def format_loki(atomic, tag=True, fixed=False, decimals=9, zero=None):
|
||||
@app.template_filter('oxen')
|
||||
def format_oxen(atomic, tag=True, fixed=False, decimals=9, zero=None):
|
||||
"""Formats an atomic current value as a human currency value.
|
||||
tag - if False then don't append " LOKI"
|
||||
tag - if False then don't append " OXEN"
|
||||
fixed - if True then don't strip insignificant trailing 0's and '.'
|
||||
decimals - at how many decimal we should round; the default is full precision
|
||||
fixed - if specified, replace 0 with this string
|
||||
|
@ -130,7 +130,7 @@ def format_loki(atomic, tag=True, fixed=False, decimals=9, zero=None):
|
|||
if not fixed and decimals > 0:
|
||||
disp = disp.rstrip('0').rstrip('.')
|
||||
if tag:
|
||||
disp += ' LOKI'
|
||||
disp += ' OXEN'
|
||||
return disp
|
||||
|
||||
# For some inexplicable reason some hex fields are provided as array of byte integer values rather
|
||||
|
@ -169,8 +169,8 @@ def css():
|
|||
return flask.send_from_directory('static', 'style.css')
|
||||
|
||||
|
||||
def get_sns_future(lmq, lokid):
|
||||
return FutureJSON(lmq, lokid, 'rpc.get_service_nodes', 5,
|
||||
def get_sns_future(lmq, oxend):
|
||||
return FutureJSON(lmq, oxend, 'rpc.get_service_nodes', 5,
|
||||
args={
|
||||
'all': False,
|
||||
'fields': { x: True for x in ('service_node_pubkey', 'requested_unlock_height', 'last_reward_block_height',
|
||||
|
@ -200,8 +200,8 @@ def get_sns(sns_future, info_future):
|
|||
return awaiting_sns, active_sns, inactive_sns
|
||||
|
||||
|
||||
def get_quorums_future(lmq, lokid, height):
|
||||
return FutureJSON(lmq, lokid, 'rpc.get_quorum_state', 30,
|
||||
def get_quorums_future(lmq, oxend, height):
|
||||
return FutureJSON(lmq, oxend, 'rpc.get_quorum_state', 30,
|
||||
args={ 'start_height': height-55, 'end_height': height })
|
||||
|
||||
|
||||
|
@ -218,8 +218,8 @@ def get_quorums(quorums_future):
|
|||
print("Something getting wrong in quorums: found unknown quorum_type={}".format(q['quorum_type']), file=sys.stderr)
|
||||
return quo
|
||||
|
||||
def get_mempool_future(lmq, lokid):
|
||||
return FutureJSON(lmq, lokid, 'rpc.get_transaction_pool', 5, args={"tx_extra":True, "stake_info":True})
|
||||
def get_mempool_future(lmq, oxend):
|
||||
return FutureJSON(lmq, oxend, 'rpc.get_transaction_pool', 5, args={"tx_extra":True, "stake_info":True})
|
||||
|
||||
def parse_mempool(mempool_future):
|
||||
# mempool RPC return values are about as nasty as can be. For each mempool tx, we get back
|
||||
|
@ -257,19 +257,19 @@ def template_globals():
|
|||
@app.route('/autorefresh/<int:refresh>')
|
||||
@app.route('/')
|
||||
def main(refresh=None, page=0, per_page=None, first=None, last=None):
|
||||
lmq, lokid = lmq_connection()
|
||||
inforeq = FutureJSON(lmq, lokid, 'rpc.get_info', 1)
|
||||
stake = FutureJSON(lmq, lokid, 'rpc.get_staking_requirement', 10)
|
||||
base_fee = FutureJSON(lmq, lokid, 'rpc.get_fee_estimate', 10)
|
||||
hfinfo = FutureJSON(lmq, lokid, 'rpc.hard_fork_info', 10)
|
||||
mempool = get_mempool_future(lmq, lokid)
|
||||
sns = get_sns_future(lmq, lokid)
|
||||
checkpoints = FutureJSON(lmq, lokid, 'rpc.get_checkpoints', args={"count": 3})
|
||||
lmq, oxend = lmq_connection()
|
||||
inforeq = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
stake = FutureJSON(lmq, oxend, 'rpc.get_staking_requirement', 10)
|
||||
base_fee = FutureJSON(lmq, oxend, 'rpc.get_fee_estimate', 10)
|
||||
hfinfo = FutureJSON(lmq, oxend, 'rpc.hard_fork_info', 10)
|
||||
mempool = get_mempool_future(lmq, oxend)
|
||||
sns = get_sns_future(lmq, oxend)
|
||||
checkpoints = FutureJSON(lmq, oxend, 'rpc.get_checkpoints', args={"count": 3})
|
||||
|
||||
# This call is slow the first time it gets called in lokid but will be fast after that, so call
|
||||
# This call is slow the first time it gets called in oxend but will be fast after that, so call
|
||||
# it with a very short timeout. It's also an admin-only command, so will always fail if we're
|
||||
# using a restricted RPC interface.
|
||||
coinbase = FutureJSON(lmq, lokid, 'admin.get_coinbase_tx_sum', 10, timeout=1, fail_okay=True,
|
||||
coinbase = FutureJSON(lmq, oxend, 'admin.get_coinbase_tx_sum', 10, timeout=1, fail_okay=True,
|
||||
args={"height":0, "count":2**31-1})
|
||||
|
||||
custom_per_page = ''
|
||||
|
@ -298,7 +298,7 @@ def main(refresh=None, page=0, per_page=None, first=None, last=None):
|
|||
end_height = max(0, height - per_page*page - 1)
|
||||
start_height = max(0, end_height - per_page + 1)
|
||||
|
||||
blocks = FutureJSON(lmq, lokid, 'rpc.get_block_headers_range', cache_key='main', args={
|
||||
blocks = FutureJSON(lmq, oxend, 'rpc.get_block_headers_range', cache_key='main', args={
|
||||
'start_height': start_height,
|
||||
'end_height': end_height,
|
||||
'get_tx_hashes': True,
|
||||
|
@ -313,7 +313,7 @@ def main(refresh=None, page=0, per_page=None, first=None, last=None):
|
|||
txids.append(b['miner_tx_hash'])
|
||||
if 'tx_hashes' in b:
|
||||
txids += b['tx_hashes']
|
||||
txs = parse_txs(tx_req(lmq, lokid, txids, cache_key='mempool').get())
|
||||
txs = parse_txs(tx_req(lmq, oxend, txids, cache_key='mempool').get())
|
||||
i = 0
|
||||
for tx in txs:
|
||||
# TXs should come back in the same order so we can just skip ahead one when the block
|
||||
|
@ -353,9 +353,9 @@ def main(refresh=None, page=0, per_page=None, first=None, last=None):
|
|||
|
||||
@app.route('/txpool')
|
||||
def mempool():
|
||||
lmq, lokid = lmq_connection()
|
||||
info = FutureJSON(lmq, lokid, 'rpc.get_info', 1)
|
||||
mempool = get_mempool_future(lmq, lokid)
|
||||
lmq, oxend = lmq_connection()
|
||||
info = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
mempool = get_mempool_future(lmq, oxend)
|
||||
|
||||
return flask.render_template('mempool.html',
|
||||
info=info.get(),
|
||||
|
@ -364,9 +364,9 @@ def mempool():
|
|||
|
||||
@app.route('/service_nodes')
|
||||
def sns():
|
||||
lmq, lokid = lmq_connection()
|
||||
info = FutureJSON(lmq, lokid, 'rpc.get_info', 1)
|
||||
awaiting, active, inactive = get_sns(get_sns_future(lmq, lokid), info)
|
||||
lmq, oxend = lmq_connection()
|
||||
info = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
awaiting, active, inactive = get_sns(get_sns_future(lmq, oxend), info)
|
||||
|
||||
return flask.render_template('service_nodes.html',
|
||||
info=info.get(),
|
||||
|
@ -375,8 +375,8 @@ def sns():
|
|||
inactive_sns=inactive,
|
||||
)
|
||||
|
||||
def tx_req(lmq, lokid, txids, cache_key='single', **kwargs):
|
||||
return FutureJSON(lmq, lokid, 'rpc.get_transactions', cache_seconds=10, cache_key=cache_key,
|
||||
def tx_req(lmq, oxend, txids, cache_key='single', **kwargs):
|
||||
return FutureJSON(lmq, oxend, 'rpc.get_transactions', cache_seconds=10, cache_key=cache_key,
|
||||
args={
|
||||
"txs_hashes": txids,
|
||||
"decode_as_json": True,
|
||||
|
@ -386,38 +386,38 @@ def tx_req(lmq, lokid, txids, cache_key='single', **kwargs):
|
|||
},
|
||||
**kwargs)
|
||||
|
||||
def sn_req(lmq, lokid, pubkey, **kwargs):
|
||||
return FutureJSON(lmq, lokid, 'rpc.get_service_nodes', 5, cache_key='single',
|
||||
def sn_req(lmq, oxend, pubkey, **kwargs):
|
||||
return FutureJSON(lmq, oxend, 'rpc.get_service_nodes', 5, cache_key='single',
|
||||
args={"service_node_pubkeys": [pubkey]}, **kwargs
|
||||
)
|
||||
|
||||
|
||||
def block_header_req(lmq, lokid, hash_or_height, **kwargs):
|
||||
def block_header_req(lmq, oxend, hash_or_height, **kwargs):
|
||||
if isinstance(hash_or_height, int) or (len(hash_or_height) <= 10 and hash_or_height.isdigit()):
|
||||
return FutureJSON(lmq, lokid, 'rpc.get_block_header_by_height', cache_key='single',
|
||||
return FutureJSON(lmq, oxend, 'rpc.get_block_header_by_height', cache_key='single',
|
||||
args={ "height": int(hash_or_height) }, **kwargs)
|
||||
else:
|
||||
return FutureJSON(lmq, lokid, 'rpc.get_block_header_by_hash', cache_key='single',
|
||||
return FutureJSON(lmq, oxend, 'rpc.get_block_header_by_hash', cache_key='single',
|
||||
args={ 'hash': hash_or_height }, **kwargs)
|
||||
|
||||
|
||||
def block_with_txs_req(lmq, lokid, hash_or_height, **kwargs):
|
||||
def block_with_txs_req(lmq, oxend, hash_or_height, **kwargs):
|
||||
args = { 'get_tx_hashes': True }
|
||||
if isinstance(hash_or_height, int) or (len(hash_or_height) <= 10 and hash_or_height.isdigit()):
|
||||
args['height'] = int(hash_or_height)
|
||||
else:
|
||||
args['hash'] = hash_or_height
|
||||
|
||||
return FutureJSON(lmq, lokid, 'rpc.get_block', cache_key='single', args=args, **kwargs)
|
||||
return FutureJSON(lmq, oxend, 'rpc.get_block', cache_key='single', args=args, **kwargs)
|
||||
|
||||
|
||||
@app.route('/service_node/<hex64:pubkey>') # For backwards compatibility with old explorer URLs
|
||||
@app.route('/sn/<hex64:pubkey>')
|
||||
def show_sn(pubkey):
|
||||
lmq, lokid = lmq_connection()
|
||||
info = FutureJSON(lmq, lokid, 'rpc.get_info', 1)
|
||||
hfinfo = FutureJSON(lmq, lokid, 'rpc.hard_fork_info', 10)
|
||||
sn = sn_req(lmq, lokid, pubkey).get()
|
||||
lmq, oxend = lmq_connection()
|
||||
info = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
hfinfo = FutureJSON(lmq, oxend, 'rpc.hard_fork_info', 10)
|
||||
sn = sn_req(lmq, oxend, pubkey).get()
|
||||
|
||||
if 'service_node_states' not in sn or not sn['service_node_states']:
|
||||
return flask.render_template('not_found.html',
|
||||
|
@ -452,7 +452,7 @@ def parse_txs(txs_rpc):
|
|||
|
||||
for tx in txs_rpc['txs']:
|
||||
if 'info' not in tx:
|
||||
# We have serialized JSON data inside a field in the JSON, because of lokid's
|
||||
# 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"])
|
||||
del tx['as_json']
|
||||
|
@ -462,7 +462,7 @@ def parse_txs(txs_rpc):
|
|||
return txs_rpc['txs']
|
||||
|
||||
|
||||
def get_block_txs_future(lmq, lokid, block):
|
||||
def get_block_txs_future(lmq, oxend, block):
|
||||
hashes = []
|
||||
if 'tx_hashes' in block:
|
||||
hashes += block['tx_hashes']
|
||||
|
@ -475,7 +475,7 @@ def get_block_txs_future(lmq, lokid, block):
|
|||
except Exception as e:
|
||||
print("Something getting wrong: cannot parse block json for block {}: {}".format(block_height, e), file=sys.stderr)
|
||||
|
||||
return tx_req(lmq, lokid, hashes, cache_key='block')
|
||||
return tx_req(lmq, oxend, hashes, cache_key='block')
|
||||
|
||||
|
||||
@app.route('/block/<int:height>')
|
||||
|
@ -483,15 +483,15 @@ def get_block_txs_future(lmq, lokid, block):
|
|||
@app.route('/block/<hex64:hash>')
|
||||
@app.route('/block/<hex64:hash>/<int:more_details>')
|
||||
def show_block(height=None, hash=None, more_details=False):
|
||||
lmq, lokid = lmq_connection()
|
||||
info = FutureJSON(lmq, lokid, 'rpc.get_info', 1)
|
||||
hfinfo = FutureJSON(lmq, lokid, 'rpc.hard_fork_info', 10)
|
||||
lmq, oxend = lmq_connection()
|
||||
info = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
hfinfo = FutureJSON(lmq, oxend, 'rpc.hard_fork_info', 10)
|
||||
if height is not None:
|
||||
val = height
|
||||
elif hash is not None:
|
||||
val = hash
|
||||
|
||||
block = None if val is None else block_with_txs_req(lmq, lokid, val).get()
|
||||
block = None if val is None else block_with_txs_req(lmq, oxend, val).get()
|
||||
if block is None:
|
||||
return flask.render_template("not_found.html",
|
||||
info=info.get(),
|
||||
|
@ -503,10 +503,10 @@ def show_block(height=None, hash=None, more_details=False):
|
|||
|
||||
next_block = None
|
||||
block_height = block['block_header']['height']
|
||||
txs = get_block_txs_future(lmq, lokid, block)
|
||||
txs = get_block_txs_future(lmq, oxend, block)
|
||||
|
||||
if info.get()['height'] > 1 + block_height:
|
||||
next_block = block_header_req(lmq, lokid, '{}'.format(block_height + 1))
|
||||
next_block = block_header_req(lmq, oxend, '{}'.format(block_height + 1))
|
||||
|
||||
if more_details:
|
||||
formatter = HtmlFormatter(cssclass="syntax-highlight", style="native")
|
||||
|
@ -534,17 +534,17 @@ def show_block(height=None, hash=None, more_details=False):
|
|||
|
||||
@app.route('/block/latest')
|
||||
def show_block_latest():
|
||||
lmq, lokid = lmq_connection()
|
||||
height = FutureJSON(lmq, lokid, 'rpc.get_info', 1).get()['height'] - 1
|
||||
lmq, oxend = lmq_connection()
|
||||
height = FutureJSON(lmq, oxend, 'rpc.get_info', 1).get()['height'] - 1
|
||||
return flask.redirect(flask.url_for('show_block', height=height), code=302)
|
||||
|
||||
|
||||
@app.route('/tx/<hex64:txid>')
|
||||
@app.route('/tx/<hex64:txid>/<int:more_details>')
|
||||
def show_tx(txid, more_details=False):
|
||||
lmq, lokid = lmq_connection()
|
||||
info = FutureJSON(lmq, lokid, 'rpc.get_info', 1)
|
||||
txs = tx_req(lmq, lokid, [txid]).get()
|
||||
lmq, oxend = lmq_connection()
|
||||
info = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
txs = tx_req(lmq, oxend, [txid]).get()
|
||||
|
||||
if 'txs' not in txs or not txs['txs']:
|
||||
return flask.render_template('not_found.html',
|
||||
|
@ -557,7 +557,7 @@ 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']:
|
||||
testing_quorum = FutureJSON(lmq, lokid, 'rpc.get_quorum_state', 60, cache_key='tx_state_change',
|
||||
testing_quorum = FutureJSON(lmq, 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} } }
|
||||
|
@ -581,14 +581,14 @@ def show_tx(txid, more_details=False):
|
|||
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']]
|
||||
outputs = FutureJSON(lmq, lokid, 'rpc.get_outs', args={
|
||||
outputs = FutureJSON(lmq, oxend, 'rpc.get_outs', args={
|
||||
'get_txid': True,
|
||||
'outputs': outs_req,
|
||||
}).get()
|
||||
if outputs and 'outs' in outputs and len(outputs['outs']) == len(outs_req):
|
||||
outputs = outputs['outs']
|
||||
# Also load block details for all of those outputs:
|
||||
block_info_req = FutureJSON(lmq, lokid, 'rpc.get_block_header_by_height', args={
|
||||
block_info_req = FutureJSON(lmq, oxend, 'rpc.get_block_header_by_height', args={
|
||||
'heights': [o["height"] for o in outputs]
|
||||
})
|
||||
i = 0
|
||||
|
@ -637,9 +637,9 @@ 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'])
|
||||
lmq, oxend = lmq_connection()
|
||||
info = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
quos = get_quorums_future(lmq, oxend, info.get()['height'])
|
||||
|
||||
return flask.render_template('quorums.html',
|
||||
info=info.get(),
|
||||
|
@ -649,8 +649,8 @@ def show_quorums():
|
|||
|
||||
@app.route('/search')
|
||||
def search():
|
||||
lmq, lokid = lmq_connection()
|
||||
info = FutureJSON(lmq, lokid, 'rpc.get_info', 1)
|
||||
lmq, oxend = lmq_connection()
|
||||
info = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
val = (flask.request.args.get('value') or '').strip()
|
||||
|
||||
if val and len(val) < 10 and val.isdigit(): # Block height
|
||||
|
@ -664,9 +664,9 @@ def search():
|
|||
)
|
||||
|
||||
# Initiate all the lookups at once, then redirect to whichever one responds affirmatively
|
||||
snreq = sn_req(lmq, lokid, val)
|
||||
blreq = block_header_req(lmq, lokid, val, fail_okay=True)
|
||||
txreq = tx_req(lmq, lokid, [val])
|
||||
snreq = sn_req(lmq, oxend, val)
|
||||
blreq = block_header_req(lmq, oxend, val, fail_okay=True)
|
||||
txreq = tx_req(lmq, oxend, [val])
|
||||
|
||||
sn = snreq.get()
|
||||
if 'service_node_states' in sn and sn['service_node_states']:
|
||||
|
@ -686,9 +686,9 @@ def search():
|
|||
|
||||
@app.route('/api/networkinfo')
|
||||
def api_networkinfo():
|
||||
lmq, lokid = lmq_connection()
|
||||
info = FutureJSON(lmq, lokid, 'rpc.get_info', 1)
|
||||
hfinfo = FutureJSON(lmq, lokid, 'rpc.hard_fork_info', 10)
|
||||
lmq, oxend = lmq_connection()
|
||||
info = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
hfinfo = FutureJSON(lmq, oxend, 'rpc.hard_fork_info', 10)
|
||||
|
||||
info = info.get()
|
||||
data = {**info}
|
||||
|
@ -700,9 +700,9 @@ def api_networkinfo():
|
|||
|
||||
@app.route('/api/emission')
|
||||
def api_emission():
|
||||
lmq, lokid = lmq_connection()
|
||||
info = FutureJSON(lmq, lokid, 'rpc.get_info', 1)
|
||||
coinbase = FutureJSON(lmq, lokid, 'admin.get_coinbase_tx_sum', 10, timeout=1, fail_okay=True,
|
||||
lmq, oxend = lmq_connection()
|
||||
info = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
coinbase = FutureJSON(lmq, oxend, 'admin.get_coinbase_tx_sum', 10, timeout=1, fail_okay=True,
|
||||
args={"height":0, "count":2**31-1}).get()
|
||||
if not coinbase:
|
||||
return flask.jsonify(None)
|
||||
|
@ -722,8 +722,8 @@ def api_emission():
|
|||
|
||||
@app.route('/api/circulating_supply')
|
||||
def api_circulating_supply():
|
||||
lmq, lokid = lmq_connection()
|
||||
coinbase = FutureJSON(lmq, lokid, 'admin.get_coinbase_tx_sum', 10, timeout=1, fail_okay=True,
|
||||
lmq, oxend = lmq_connection()
|
||||
coinbase = FutureJSON(lmq, oxend, 'admin.get_coinbase_tx_sum', 10, timeout=1, fail_okay=True,
|
||||
args={"height":0, "count":2**31-1}).get()
|
||||
return flask.jsonify((coinbase["emission_amount"] - coinbase["burn_amount"]) // 1000000000 if coinbase else None)
|
||||
|
||||
|
@ -731,8 +731,8 @@ def api_circulating_supply():
|
|||
# FIXME: need better error handling here
|
||||
@app.route('/api/transaction/<hex64:txid>')
|
||||
def api_tx(txid):
|
||||
lmq, lokid = lmq_connection()
|
||||
tx = tx_req(lmq, lokid, [txid]).get()
|
||||
lmq, oxend = lmq_connection()
|
||||
tx = tx_req(lmq, oxend, [txid]).get()
|
||||
txs = parse_txs(tx)
|
||||
return flask.jsonify({
|
||||
"status": tx['status'],
|
||||
|
@ -742,9 +742,9 @@ def api_tx(txid):
|
|||
@app.route('/api/block/<int:height>')
|
||||
@app.route('/api/block/<hex64:blkid>')
|
||||
def api_block(blkid=None, height=None):
|
||||
lmq, lokid = lmq_connection()
|
||||
block = block_with_txs_req(lmq, lokid, blkid if blkid is not None else height).get()
|
||||
txs = get_block_txs_future(lmq, lokid, block)
|
||||
lmq, oxend = lmq_connection()
|
||||
block = block_with_txs_req(lmq, oxend, blkid if blkid is not None else height).get()
|
||||
txs = get_block_txs_future(lmq, oxend, block)
|
||||
|
||||
if 'block_header' in block:
|
||||
data = block['block_header'].copy()
|
||||
|
@ -761,7 +761,7 @@ ticker_cache, ticker_cache_expires = {}, None
|
|||
@app.route('/api/price/<fiat>')
|
||||
def api_price(fiat=None):
|
||||
global ticker_cache, ticker_cache_expires, ticker_vs, ticker_vs_expires
|
||||
# TODO: will need to change to 'oxen' when the ticker changes:
|
||||
# TODO: will need to change to 'oxen' when/if the ticker changes:
|
||||
ticker = 'loki-network'
|
||||
|
||||
if not ticker_cache or not ticker_cache_expires or ticker_cache_expires < time.time():
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
{% block head %}
|
||||
<title>{% block title %}{% endblock %}Loki{{' TESTNET' if info and info.testnet else ' DEVNET' if info and info.devnet else ''}}
|
||||
<title>{% block title %}{% endblock %}Oxen{{' TESTNET' if info and info.testnet else ' DEVNET' if info and info.devnet else ''}}
|
||||
Blockchain Explorer</title>
|
||||
<link rel="stylesheet" type="text/css" href="/style.css">
|
||||
{% if refresh %}
|
||||
|
@ -16,7 +16,7 @@
|
|||
<div id="header" class="Wrapper">
|
||||
{% block header %}
|
||||
<div id="header-content">
|
||||
<h1 class="Header"><a href="/">Loki
|
||||
<h1 class="Header"><a href="/">Oxen
|
||||
{%if info and info.testnet%}
|
||||
<span style="color:#ff6b62">Testnet</span>
|
||||
{%elif info and info.devnet%}
|
||||
|
@ -41,7 +41,7 @@
|
|||
<p style="margin-top:10px">
|
||||
<a href="https://github.com/loki-project/loki-observer">Source Code</a>
|
||||
| Explorer Revision: {{server.revision}}
|
||||
| Loki Version: {{info.version}}
|
||||
| Oxen Version: {{info.version}}
|
||||
</p>
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
|
|
@ -52,19 +52,19 @@ Validator bits: {{"{:011b}".format(block.info.pulse.validator_bitset)}}"><label>
|
|||
{%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%}
|
||||
|
||||
<span title="{{(block_header.reward - sum_fees) | loki(fixed=True)}} created in this block.{%if sum_fees > 0%}
|
||||
<span title="{{(block_header.reward - sum_fees) | oxen(fixed=True)}} created in this block.{%if sum_fees > 0%}
|
||||
|
||||
Note that this value does not include earned transaction fees ({{sum_fees | loki(fixed=True, decimals=4)}}){%endif%}"><label>Block reward:</label>
|
||||
{{(block_header.reward - sum_fees) | loki(decimals=4)}}</span>
|
||||
Note that this value does not include earned transaction fees ({{sum_fees | oxen(fixed=True, decimals=4)}}){%endif%}"><label>Block reward:</label>
|
||||
{{(block_header.reward - sum_fees) | oxen(decimals=4)}}</span>
|
||||
|
||||
{%if sum_fees > 0%}
|
||||
<span title="Earned TX fees: {{sum_fees | loki(fixed=True)}}"><label>Block TX fees:</label> {{ sum_fees | loki(fixed=True, decimals=4) }}</span>
|
||||
<span title="Earned TX fees: {{sum_fees | oxen(fixed=True)}}"><label>Block TX fees:</label> {{ sum_fees | oxen(fixed=True, decimals=4) }}</span>
|
||||
{%endif%}
|
||||
|
||||
{%if sum_burned > 0%}
|
||||
<span title="{{sum_burned | loki(fixed=True)}} burned in the transactions included in block">
|
||||
<span title="{{sum_burned | oxen(fixed=True)}} burned in the transactions included in block">
|
||||
<label>Burned fees:</label>
|
||||
{{sum_burned | loki(decimals=4)}} <span class="icon">🔥</span>
|
||||
{{sum_burned | oxen(decimals=4)}} <span class="icon">🔥</span>
|
||||
</span>
|
||||
{%endif%}
|
||||
|
||||
|
@ -90,7 +90,7 @@ Note that this value does not include earned transaction fees ({{sum_fees | loki
|
|||
</tr>
|
||||
<tr>
|
||||
<td><a href="/tx/{{miner_tx.tx_hash}}">{{miner_tx.tx_hash}}</a></td>
|
||||
<td>{{miner_tx.info.vout | sum(attribute='amount') | loki}}</td>
|
||||
<td>{{miner_tx.info.vout | sum(attribute='amount') | oxen}}</td>
|
||||
<td>{{miner_tx.size}}</td>
|
||||
<td>{{miner_tx.info.version}}</td>
|
||||
</tr>
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<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) | loki(tag=false, decimals=4)}}
|
||||
{{fee.display(tx)}} / {{(tx.info.rct_signatures.txnFee * 1000 / tx.blob_size) | oxen(tag=false, decimals=4)}}
|
||||
{%else%}
|
||||
N/A
|
||||
{%endif%}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
<tr>
|
||||
{%include 'include/sn_kcf.html'%}
|
||||
<td>{{sn.staking_requirement | loki(tag=false, fixed=true)}}</td>
|
||||
<td>{{sn.staking_requirement | oxen(tag=false, fixed=true)}}</td>
|
||||
<td>{{sn.last_reward_block_height}}</td>
|
||||
<td>{{sn.last_uptime_proof | from_timestamp | ago if sn.last_uptime_proof > 0 else "Not Received"}}</td>
|
||||
<td>
|
||||
|
|
|
@ -16,15 +16,15 @@
|
|||
{%for sn in (awaiting_sns | sort(attribute='portions_for_operator,contribution_open,contribution_required,service_node_pubkey'))[:limit_awaiting]%}
|
||||
<tr>
|
||||
{%include 'include/sn_kcf.html'%}
|
||||
<td>{{sn.total_contributed | loki(tag=false, fixed=true)}}</td>
|
||||
<td>{{sn.total_contributed | oxen(tag=false, fixed=true)}}</td>
|
||||
{%if sn.total_reserved >= sn.staking_requirement%}
|
||||
<td title="All remaining contribution room is reserved for specific contributors">
|
||||
⛔ {{sn.contribution_open | loki(tag=false, fixed=true)}}
|
||||
⛔ {{sn.contribution_open | oxen(tag=false, fixed=true)}}
|
||||
</td>
|
||||
{%else%}
|
||||
<td>{{sn.contribution_open | loki(tag=false, fixed=true)}}</td>
|
||||
<td>{{sn.contribution_open | oxen(tag=false, fixed=true)}}</td>
|
||||
{%endif%}
|
||||
<td>{{ (0 if sn.num_contributions >= 4 else (sn.contribution_open / (4 - sn.num_contributions)) | round(method='ceil')) | loki(tag=false, fixed=true) }}</td>
|
||||
<td>{{ (0 if sn.num_contributions >= 4 else (sn.contribution_open / (4 - sn.num_contributions)) | round(method='ceil')) | oxen(tag=false, fixed=true) }}</td>
|
||||
<td>
|
||||
{%if sn.requested_unlock_height%}
|
||||
<span title="Service Node unlock in progress (unlocks at block {{sn.requested_unlock_height}})">🔓</span>
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
|
||||
<td><a href="/sn/{{sn.service_node_pubkey}}">{{sn.service_node_pubkey}}</a></td>
|
||||
<td title="
|
||||
{%-for c in sn.contributors%}{%for lc in c.locked_contributions%}{{c.address | truncate(15)}} ({{lc.amount | loki(decimals=0)}} = {{(lc.amount / sn.staking_requirement * 100) | round(1) | chop0}}%)
|
||||
{%-for c in sn.contributors%}{%for lc in c.locked_contributions%}{{c.address | truncate(15)}} ({{lc.amount | oxen(decimals=0)}} = {{(lc.amount / sn.staking_requirement * 100) | round(1) | chop0}}%)
|
||||
{%endfor%}{%endfor%}"><span class="icon">{{sn.contributors | length}}/4</span></td>
|
||||
<td>{%if sn.portions_for_operator != portions_base%}{{ (sn.portions_for_operator / portions_base * 100) | round(3) | chop0 }}{%endif%}</td>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
(tx.info.rct_signatures.txnFee if 'rct_signatures' in tx.info and 'txnFee' in tx.info.rct_signatures else 0)
|
||||
-%}
|
||||
{% if fee > 0 or show_zero -%}
|
||||
{{ fee | loki(tag=False, fixed=True, decimals=4) }}
|
||||
{{ fee | oxen(tag=False, fixed=True, decimals=4) }}
|
||||
{%- if 'burn_amount' in tx.extra %}
|
||||
<span class="icon" title="= {{(fee - tx.extra.burn_amount) | loki}} TX fee
|
||||
+ {{tx.extra.burn_amount | loki}} burned">🔥</span>
|
||||
<span class="icon" title="= {{(fee - tx.extra.burn_amount) | oxen}} TX fee
|
||||
+ {{tx.extra.burn_amount | oxen}} burned">🔥</span>
|
||||
{%-endif-%}
|
||||
{%endif-%}
|
||||
{% endmacro %}
|
||||
|
|
|
@ -74,14 +74,14 @@
|
|||
<span><label>Network difficulty:</label> {{info.difficulty}}</span>
|
||||
<span><label>Hash rate:</label> ~{{(info.difficulty / info.target) | si }}H/s</span>
|
||||
{% endif %}
|
||||
<span><label>Staking requirement:</label> {{stake.staking_requirement | loki}}</span>
|
||||
<span title="{{(2500 * fees.fee_per_byte + 2*fees.fee_per_output) | loki}} for a typical simple transaction (~2.5kB, 2 outputs)">
|
||||
<span><label>Staking requirement:</label> {{stake.staking_requirement | oxen}}</span>
|
||||
<span title="{{(2500 * fees.fee_per_byte + 2*fees.fee_per_output) | oxen}} for a typical simple transaction (~2.5kB, 2 outputs)">
|
||||
<label>Base fee:</label>
|
||||
{{fees.fee_per_output | loki}}/output + {{(fees.fee_per_byte * 1000) | loki}}/kB
|
||||
{{fees.fee_per_output | oxen}}/output + {{(fees.fee_per_byte * 1000) | oxen}}/kB
|
||||
</span>
|
||||
<span title="{{(2500 * fees.blink_fee_per_byte + 2*fees.blink_fee_per_output) | loki}} for a typical simple blink transaction (~2.5kB, 2 outputs)">
|
||||
<span title="{{(2500 * fees.blink_fee_per_byte + 2*fees.blink_fee_per_output) | oxen}} for a typical simple blink transaction (~2.5kB, 2 outputs)">
|
||||
<label>Blink fee:</label>
|
||||
{{fees.blink_fee_per_output | loki}}/output + {{(fees.blink_fee_per_byte * 1000) | loki}}/kB
|
||||
{{fees.blink_fee_per_output | oxen}}/output + {{(fees.blink_fee_per_byte * 1000) | oxen}}/kB
|
||||
</span>
|
||||
<span title="{{(info.block_size_limit / 2) | si}}B soft limit, {{info.block_size_limit | si}}B hard limit. Blocks may include TXes up to the soft limit without penalty and incur increasing reward penalties as they approach the hard limit.">
|
||||
<label>Block size limit:</label>
|
||||
|
@ -96,10 +96,10 @@
|
|||
{% if not emission or emission.status == 'BUSY' %}
|
||||
(still calculating...)</span>
|
||||
{% elif emission.status == 'OK' %}
|
||||
{{(emission.emission_amount - emission.burn_amount) | loki}}</span>
|
||||
<span><label>(Coinbase:</label> {{emission.emission_amount | loki}}</span>
|
||||
<span><label>Fees:</label> {{emission.fee_amount | loki}}</span>
|
||||
<span><label>Burned:</label> {{emission.burn_amount | loki}}<label>).</label></span>
|
||||
{{(emission.emission_amount - emission.burn_amount) | oxen}}</span>
|
||||
<span><label>(Coinbase:</label> {{emission.emission_amount | oxen}}</span>
|
||||
<span><label>Fees:</label> {{emission.fee_amount | oxen}}</span>
|
||||
<span><label>Burned:</label> {{emission.burn_amount | oxen}}<label>).</label></span>
|
||||
{%endif%}
|
||||
<p style="padding: 0px; margin-top: 2px; font-size: 0.9em">
|
||||
* — Circulating supply may exclude any currently, publicised locked tokens, otherwise it is equal to the Coinbase minus burned coins.
|
||||
|
@ -117,7 +117,7 @@
|
|||
<span>🚫 Deregistration</span>
|
||||
<span>📋 IP Change Penalty</span>
|
||||
<span>🔓 Stake Unlock</span>
|
||||
<span>🎫 Loki Name System Purchase</span>
|
||||
<span>🎫 Oxen Name System Purchase</span>
|
||||
<span>💾 LNS Update</span>
|
||||
</h4>
|
||||
</div>
|
||||
|
@ -172,7 +172,7 @@
|
|||
<td>{{symbol.display(b.txs[0])}}</td>
|
||||
<td><a href="/tx/{{b.miner_tx_hash}}">{{b.miner_tx_hash}}</a></td>
|
||||
<td>{{fee.display(b.txs[0])}}</td>
|
||||
<td>{{b.reward | loki(tag=False, fixed=True, decimals=2)}}</td>
|
||||
<td>{{b.reward | oxen(tag=False, fixed=True, decimals=2)}}</td>
|
||||
<td>0/{{b.txs[0].info.vout | length}}</td>
|
||||
<td>{{b.txs[0].size | si}}</td>
|
||||
</tr>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<span>🚫 Deregistration</span>
|
||||
<span>📋 IP Change Penalty</span>
|
||||
<span>🔓 Stake Unlock</span>
|
||||
<span>🎫 Loki Name System Purchase</span>
|
||||
<span>🎫 Oxen Name System Purchase</span>
|
||||
<span>💾 LNS Update</span>
|
||||
</h4>
|
||||
</div>
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
<a href="/block/{{sn.state_height}}">{{sn.state_height}}</a>
|
||||
</span>
|
||||
|
||||
<span><label>Staking requirement:</label> {{sn.staking_requirement | loki}}</span>
|
||||
<span><label>Staking requirement:</label> {{sn.staking_requirement | oxen}}</span>
|
||||
|
||||
{%if not solo_node%}
|
||||
<span><label>Operator fee:</label> {{(sn.portions_for_operator / portions_base * 100) | round(3) | chop0}}%</span>
|
||||
|
@ -50,12 +50,12 @@
|
|||
<span><label>Total contributed:</label>
|
||||
{%if sn.total_contributed >= sn.staking_requirement%}100%
|
||||
{%else%}
|
||||
{{sn.total_contributed | loki}} ({{(sn.total_contributed / sn.staking_requirement * 100) | round(2) | chop0}}%)
|
||||
{{sn.total_contributed | oxen}} ({{(sn.total_contributed / sn.staking_requirement * 100) | round(2) | chop0}}%)
|
||||
{%endif%}
|
||||
</span>
|
||||
|
||||
{%if sn.total_reserved != sn.total_contributed%}
|
||||
<span><label>Total reserved:</label> {{sn.total_reserved | loki}}</span>
|
||||
<span><label>Total reserved:</label> {{sn.total_reserved | oxen}}</span>
|
||||
{%endif%}
|
||||
|
||||
<span>
|
||||
|
@ -132,10 +132,10 @@
|
|||
{%endif%}
|
||||
</p>
|
||||
{%else%}
|
||||
<p class="sn-awaiting">Awaiting registration. This service node has <span class="loki required">{{(sn.staking_requirement - sn.total_contributed) | loki}}</span>
|
||||
<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="loki required">{{((sn.staking_requirement - sn.total_reserved) / sn.num_open_spots) | loki}}</span>.
|
||||
The minimum required stake contribution is <span class="oxen required">{{((sn.staking_requirement - sn.total_reserved) / sn.num_open_spots) | oxen}}</span>.
|
||||
{%endif%}
|
||||
</p>
|
||||
{%endif%}
|
||||
|
@ -166,12 +166,12 @@
|
|||
{%for c in sn.contributors%}
|
||||
<tr>
|
||||
<td>{{c.address}}</td>
|
||||
<td>{{c.amount | loki}}
|
||||
<td>{{c.amount | oxen}}
|
||||
{%-if c.locked_contributions and c.locked_contributions|length > 1%}
|
||||
({{c.locked_contributions|length}} contributions)
|
||||
{%endif-%}
|
||||
</td>
|
||||
<td>{{c.reserved | loki}}</td>
|
||||
<td>{{c.reserved | oxen}}</td>
|
||||
</tr>
|
||||
{%endfor%}
|
||||
</tbody>
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
{%else%}
|
||||
{% import 'include/tx_fee.html' as fee %}
|
||||
{{fee.display(tx)}}
|
||||
({{(tx.info.rct_signatures.txnFee * 1000 / tx.size) | loki(tag=false, decimals=6)}})
|
||||
({{(tx.info.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>
|
||||
|
@ -138,9 +138,9 @@ This tx does not includes a vote from this testing service node (only 7 votes ar
|
|||
<p><label>Unlock signature:</label> {{unlock_signature}}</p> {# FIXME #}
|
||||
{% elif tx.info.type == 4 and 'lns' in tx.extra %}
|
||||
{% if 'buy' in tx.extra.lns %}
|
||||
<h2>🎫 Loki Name Service Registration</h2>
|
||||
<h2>🎫 Oxen Name Service Registration</h2>
|
||||
{% elif 'update' in tx.extra.lns %}
|
||||
<h2>💾 Loki Name Service Update</h2>
|
||||
<h2>💾 Oxen Name Service Update</h2>
|
||||
{% endif %}
|
||||
{#FIXME - show some metadata?#}
|
||||
{% elif 'sn_registration' in tx.extra %}
|
||||
|
@ -187,7 +187,7 @@ This tx does not includes a vote from this testing service node (only 7 votes ar
|
|||
{%if tx.info.vout%}
|
||||
<h2>Outputs</h2>
|
||||
<h4 class="Subtitle">{{tx.info.vout|length}} output(s) for total of
|
||||
{{tx.info.vout | sum(attribute='amount') | loki(zero='<span title="Regular LOKI tx amounts are private">???</span>') | safe}}</h4>
|
||||
{{tx.info.vout | sum(attribute='amount') | oxen(zero='<span title="Regular LOKI tx amounts are private">???</span>') | safe}}</h4>
|
||||
<div class="TitleDivider"></div>
|
||||
<div class="tx-outputs">
|
||||
<table class="Table">
|
||||
|
@ -203,7 +203,7 @@ This tx does not includes a vote from this testing service node (only 7 votes ar
|
|||
{%for out in tx.info.vout%}
|
||||
<tr>
|
||||
<td><label>{{loop.index0}}:</label> {{out.target.key}}</td>
|
||||
<td>{{out.amount | loki(zero='?')}}</td>
|
||||
<td>{{out.amount | oxen(zero='?')}}</td>
|
||||
<td>{%if 'output_indices' in tx%}{{tx.output_indices[loop.index0]}}{# FIXME: of {{num_outputs}}#}{%endif%}</td>
|
||||
</tr>
|
||||
{%endfor%}
|
||||
|
@ -256,7 +256,7 @@ This tx does not includes a vote from this testing service node (only 7 votes ar
|
|||
<p style="margin: 0px">Prove to someone that you have sent them Loki in this transaction</p>
|
||||
<p style="margin: 0px">
|
||||
TX private key can be obtained using <i>get_tx_key</i>
|
||||
command in <i>loki-wallet-cli</i> command line tool
|
||||
command in <i>oxen-wallet-cli</i> command line tool
|
||||
<br/>
|
||||
{%if enable_js%}
|
||||
Note: Address/Subaddress and TX private key are NOT sent to the server, as the calculations are done on the client side
|
||||
|
@ -522,7 +522,7 @@ This tx does not includes a vote from this testing service node (only 7 votes ar
|
|||
|
||||
<h2>Inputs</h2>
|
||||
<h4 class="Subtitle">{{tx.info.vin|length}} input(s) for total of
|
||||
{{tx.info.vin | sum(attribute='key.amount') | loki(zero='<span title="Regular LOKI tx amounts are private">???</span>') | safe}}</h4>
|
||||
{{tx.info.vin | sum(attribute='key.amount') | oxen(zero='<span title="Regular LOKI tx amounts are private">???</span>') | safe}}</h4>
|
||||
<div class="TitleDivider"></div>
|
||||
|
||||
{#FIXME#}
|
||||
|
@ -556,7 +556,7 @@ This tx does not includes a vote from this testing service node (only 7 votes ar
|
|||
{%endif%}
|
||||
{%endif%}
|
||||
</td>
|
||||
<td style="text-align: right">Amount: {{inp.key.amount | loki(zero='?')}}</td>
|
||||
<td style="text-align: right">Amount: {{inp.key.amount | oxen(zero='?')}}</td>
|
||||
</tr>
|
||||
{%if config.enable_mixins_details%}
|
||||
<tr class="TableHeader">
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
from observer import app, config
|
||||
|
||||
config.lokid_rpc = 'ipc://lokid/testnet.sock'
|
||||
config.oxend_rpc = 'ipc://oxend/testnet.sock'
|
||||
|
|
Loading…
Reference in New Issue