Switch to using new python3-oxenmq
This commit is contained in:
parent
e2bff03fcf
commit
cd214df1b2
|
@ -1,3 +0,0 @@
|
|||
[submodule "pylokimq"]
|
||||
path = pylokimq
|
||||
url = https://github.com/majestrate/pylokimq.git
|
36
README.md
36
README.md
|
@ -5,25 +5,9 @@ awesome, safe.
|
|||
|
||||
## Prerequisite packages
|
||||
|
||||
sudo apt install build-essential pkg-config libsodium-dev libzmq3-dev python3-dev python3-flask python3-babel python3-pygments
|
||||
sudo apt install build-essential pkg-config libsodium-dev libzmq3-dev python3-dev python3-flask python3-babel python3-pygments python3-oxenmq
|
||||
|
||||
## Building and running
|
||||
|
||||
Quick and dirty setup instructions for now:
|
||||
|
||||
git submodule update --init --recursive
|
||||
cd pylokimq
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make -j6
|
||||
cd ../..
|
||||
ln -s pylokimq/build/pylokimq/pylokimq.cpython-*.so .
|
||||
|
||||
(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 oxend with `--lmq-local-control ipc:///path/to/loki-observer/mainnet.sock`.
|
||||
Note that the last requirement (python3-oxenmq) comes from the Oxen repository (https://deb.oxen.io).
|
||||
|
||||
## Running in debug mode
|
||||
|
||||
|
@ -32,11 +16,11 @@ To run it in debug mode (production requires setting up a WSGI server, see below
|
|||
FLASK_APP=observer flask run --reload --debugger
|
||||
|
||||
This mode seems to be a bit flakey, though -- reloading, in particular, seems to break things and
|
||||
make it just silently exit after a while.
|
||||
make it just silently exit after a while, so only do this for quick and dirty testing.
|
||||
|
||||
## Setting up for production with uwsgi-emperor:
|
||||
|
||||
Do all of the above, but instead of running it with flask, set up uwsgi-emperor as follows:
|
||||
Do the above, but instead of running it with flask directly, set up uwsgi-emperor as follows:
|
||||
|
||||
apt install uwsgi-emperor uwsgi-plugin-python3
|
||||
|
||||
|
@ -47,24 +31,24 @@ in `/etc/uwsgi-emperor/emperor.ini` add configuration of:
|
|||
cap = setgid,setuid
|
||||
emperor-tyrant = true
|
||||
|
||||
Create a "vassal" config for loki-observer, `/etc/uwsgi-emperor/vassals/loki-observer.ini`, containing:
|
||||
Create a "vassal" config for oxen-observer, `/etc/uwsgi-emperor/vassals/oxen-observer.ini`, containing:
|
||||
|
||||
[uwsgi]
|
||||
chdir = /path/to/loki-observer
|
||||
chdir = /path/to/oxen-observer
|
||||
socket = mainnet.wsgi
|
||||
plugins = python3,logfile
|
||||
processes = 4
|
||||
manage-script-name = true
|
||||
mount = /=mainnet:app
|
||||
|
||||
logger = file:logfile=/path/to/loki-observer/mainnet.log
|
||||
logger = file:logfile=/path/to/oxen-observer/mainnet.log
|
||||
|
||||
Set ownership of this user to whatever use you want it to run as, and set the group to `_loki` (so
|
||||
Set ownership of this user to whatever user you want it to run as, and set the group to `_loki` (so
|
||||
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:
|
||||
In the oxen-observer/mainnet.py, set:
|
||||
|
||||
config.oxend_rpc = 'ipc:///var/lib/loki/oxend.sock'
|
||||
|
||||
|
@ -88,4 +72,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 oxend.sock from a testnet or devnet oxend.
|
||||
using testnet.py or devnet.py pointing to the oxend.sock from a testnet or devnet oxend.
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from observer import app, config
|
||||
import oxenmq
|
||||
|
||||
config.oxend_rpc = 'ipc://oxend/devnet.sock'
|
||||
config.oxend_rpc = oxenmq.Address('ipc://oxend/devnet.sock')
|
||||
|
|
30
lmq.py
30
lmq.py
|
@ -1,19 +1,19 @@
|
|||
import pylokimq
|
||||
import oxenmq
|
||||
import config
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
lmq, oxend = None, None
|
||||
def lmq_connection():
|
||||
global lmq, oxend
|
||||
if lmq is None:
|
||||
lmq = pylokimq.LokiMQ(pylokimq.LogLevel.warn)
|
||||
lmq.max_message_size = 200*1024*1024
|
||||
lmq.start()
|
||||
omq, oxend = None, None
|
||||
def omq_connection():
|
||||
global omq, oxend
|
||||
if omq is None:
|
||||
omq = oxenmq.OxenMQ(log_level=oxenmq.LogLevel.warn)
|
||||
omq.max_message_size = 200*1024*1024
|
||||
omq.start()
|
||||
if oxend is None:
|
||||
oxend = lmq.connect_remote(config.oxend_rpc)
|
||||
return (lmq, oxend)
|
||||
oxend = omq.connect_remote(config.oxend_rpc)
|
||||
return (omq, oxend)
|
||||
|
||||
cached = {}
|
||||
cached_args = {}
|
||||
|
@ -30,9 +30,9 @@ class FutureJSON():
|
|||
Cache entries are *not* purged, they are only replaced, so using dynamic data in the key would
|
||||
result in unbounded memory growth.
|
||||
|
||||
lmq - the lmq object
|
||||
oxend - the oxend lmq connection id object
|
||||
endpoint - the lmq endpoint, e.g. 'rpc.get_info'
|
||||
omq - the omq object
|
||||
oxend - the oxend omq connection id object
|
||||
endpoint - the omq 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
|
||||
args - if not None, a value to pass (after converting to JSON) as the request parameter. Typically a dict.
|
||||
|
@ -40,7 +40,7 @@ class FutureJSON():
|
|||
timeout - maximum time to spend waiting for a reply
|
||||
"""
|
||||
|
||||
def __init__(self, lmq, oxend, endpoint, cache_seconds=3, *, cache_key='', args=None, fail_okay=False, timeout=10):
|
||||
def __init__(self, omq, 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(oxend, self.endpoint, [] if self.args is None else [self.args], timeout=timeout)
|
||||
self.future = omq.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,4 @@
|
|||
from observer import app, config
|
||||
import oxenmq
|
||||
|
||||
config.oxend_rpc = 'ipc://oxend/mainnet.sock'
|
||||
config.oxend_rpc = oxenmq.Address('ipc://oxend/mainnet.sock')
|
||||
|
|
173
observer.py
173
observer.py
|
@ -25,7 +25,7 @@ import base58
|
|||
import sha3
|
||||
import config
|
||||
import local_config
|
||||
from lmq import FutureJSON, lmq_connection
|
||||
from lmq import FutureJSON, omq_connection
|
||||
|
||||
# Make a dict of config.* to pass to templating
|
||||
conf = {x: getattr(config, x) for x in dir(config) if not x.startswith('__')}
|
||||
|
@ -178,8 +178,8 @@ def css():
|
|||
return flask.send_from_directory('static', 'style.css')
|
||||
|
||||
|
||||
def get_sns_future(lmq, oxend):
|
||||
return FutureJSON(lmq, oxend, 'rpc.get_service_nodes', 5,
|
||||
def get_sns_future(omq, oxend):
|
||||
return FutureJSON(omq, 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',
|
||||
|
@ -209,8 +209,8 @@ def get_sns(sns_future, info_future):
|
|||
return awaiting_sns, active_sns, inactive_sns
|
||||
|
||||
|
||||
def get_quorums_future(lmq, oxend, height):
|
||||
return FutureJSON(lmq, oxend, 'rpc.get_quorum_state', 30,
|
||||
def get_quorums_future(omq, oxend, height):
|
||||
return FutureJSON(omq, oxend, 'rpc.get_quorum_state', 30,
|
||||
args={ 'start_height': height-55, 'end_height': height })
|
||||
|
||||
|
||||
|
@ -227,8 +227,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, oxend):
|
||||
return FutureJSON(lmq, oxend, 'rpc.get_transaction_pool', 5, args={"tx_extra":True, "stake_info":True})
|
||||
def get_mempool_future(omq, oxend):
|
||||
return FutureJSON(omq, 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
|
||||
|
@ -264,21 +264,22 @@ def template_globals():
|
|||
@app.route('/page/<int:page>/<int:per_page>')
|
||||
@app.route('/range/<int:first>/<int:last>')
|
||||
@app.route('/autorefresh/<int:refresh>')
|
||||
@app.route('/v<int:style>') # debug while mucking with stylesheets
|
||||
@app.route('/')
|
||||
def main(refresh=None, page=0, per_page=None, first=None, last=None):
|
||||
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})
|
||||
def main(refresh=None, page=0, per_page=None, first=None, last=None, style=None):
|
||||
omq, oxend = omq_connection()
|
||||
inforeq = FutureJSON(omq, oxend, 'rpc.get_info', 1)
|
||||
stake = FutureJSON(omq, oxend, 'rpc.get_staking_requirement', 10)
|
||||
base_fee = FutureJSON(omq, oxend, 'rpc.get_fee_estimate', 10)
|
||||
hfinfo = FutureJSON(omq, oxend, 'rpc.hard_fork_info', 10)
|
||||
mempool = get_mempool_future(omq, oxend)
|
||||
sns = get_sns_future(omq, oxend)
|
||||
checkpoints = FutureJSON(omq, oxend, 'rpc.get_checkpoints', args={"count": 3})
|
||||
|
||||
# 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, oxend, 'admin.get_coinbase_tx_sum', 10, timeout=1, fail_okay=True,
|
||||
coinbase = FutureJSON(omq, oxend, 'admin.get_coinbase_tx_sum', 10, timeout=1, fail_okay=True,
|
||||
args={"height":0, "count":2**31-1})
|
||||
|
||||
custom_per_page = ''
|
||||
|
@ -307,7 +308,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, oxend, 'rpc.get_block_headers_range', cache_key='main', args={
|
||||
blocks = FutureJSON(omq, oxend, 'rpc.get_block_headers_range', cache_key='main', args={
|
||||
'start_height': start_height,
|
||||
'end_height': end_height,
|
||||
'get_tx_hashes': True,
|
||||
|
@ -324,7 +325,7 @@ def main(refresh=None, page=0, per_page=None, first=None, last=None):
|
|||
if 'tx_hashes' in b:
|
||||
txids += b['tx_hashes']
|
||||
if txids:
|
||||
txs = parse_txs(tx_req(lmq, oxend, txids, cache_key='mempool').get())
|
||||
txs = parse_txs(tx_req(omq, oxend, txids, cache_key='mempool').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]:
|
||||
|
@ -366,9 +367,9 @@ def main(refresh=None, page=0, per_page=None, first=None, last=None):
|
|||
|
||||
@app.route('/txpool')
|
||||
def mempool():
|
||||
lmq, oxend = lmq_connection()
|
||||
info = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
mempool = get_mempool_future(lmq, oxend)
|
||||
omq, oxend = omq_connection()
|
||||
info = FutureJSON(omq, oxend, 'rpc.get_info', 1)
|
||||
mempool = get_mempool_future(omq, oxend)
|
||||
|
||||
return flask.render_template('mempool.html',
|
||||
info=info.get(),
|
||||
|
@ -377,9 +378,9 @@ def mempool():
|
|||
|
||||
@app.route('/service_nodes')
|
||||
def sns():
|
||||
lmq, oxend = lmq_connection()
|
||||
info = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
awaiting, active, inactive = get_sns(get_sns_future(lmq, oxend), info)
|
||||
omq, oxend = omq_connection()
|
||||
info = FutureJSON(omq, oxend, 'rpc.get_info', 1)
|
||||
awaiting, active, inactive = get_sns(get_sns_future(omq, oxend), info)
|
||||
|
||||
return flask.render_template('service_nodes.html',
|
||||
info=info.get(),
|
||||
|
@ -388,8 +389,8 @@ def sns():
|
|||
inactive_sns=inactive,
|
||||
)
|
||||
|
||||
def tx_req(lmq, oxend, txids, cache_key='single', **kwargs):
|
||||
return FutureJSON(lmq, oxend, 'rpc.get_transactions', cache_seconds=10, cache_key=cache_key,
|
||||
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,
|
||||
|
@ -399,36 +400,36 @@ def tx_req(lmq, oxend, txids, cache_key='single', **kwargs):
|
|||
},
|
||||
**kwargs)
|
||||
|
||||
def sn_req(lmq, oxend, pubkey, **kwargs):
|
||||
return FutureJSON(lmq, oxend, 'rpc.get_service_nodes', 5, cache_key='single',
|
||||
def sn_req(omq, oxend, pubkey, **kwargs):
|
||||
return FutureJSON(omq, oxend, 'rpc.get_service_nodes', 5, cache_key='single',
|
||||
args={"service_node_pubkeys": [pubkey]}, **kwargs
|
||||
)
|
||||
|
||||
|
||||
def block_header_req(lmq, oxend, hash_or_height, **kwargs):
|
||||
def block_header_req(omq, 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, oxend, 'rpc.get_block_header_by_height', cache_key='single',
|
||||
return FutureJSON(omq, oxend, 'rpc.get_block_header_by_height', cache_key='single',
|
||||
args={ "height": int(hash_or_height) }, **kwargs)
|
||||
else:
|
||||
return FutureJSON(lmq, oxend, 'rpc.get_block_header_by_hash', cache_key='single',
|
||||
return FutureJSON(omq, oxend, 'rpc.get_block_header_by_hash', cache_key='single',
|
||||
args={ 'hash': hash_or_height }, **kwargs)
|
||||
|
||||
|
||||
def block_with_txs_req(lmq, oxend, hash_or_height, **kwargs):
|
||||
def block_with_txs_req(omq, 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, oxend, 'rpc.get_block', cache_key='single', args=args, **kwargs)
|
||||
return FutureJSON(omq, oxend, 'rpc.get_block', cache_key='single', args=args, **kwargs)
|
||||
|
||||
def ons_info(lmq, oxend, name,ons_type,**kwargs):
|
||||
def ons_info(omq, oxend, name,ons_type,**kwargs):
|
||||
if ons_type == 2:
|
||||
name=name+'.loki'
|
||||
name_hash = nacl.hash.blake2b(name.encode(), encoder = nacl.encoding.Base64Encoder)
|
||||
|
||||
return FutureJSON(lmq, oxend, 'rpc.ons_names_to_owners', args={
|
||||
return FutureJSON(omq, oxend, 'rpc.ons_names_to_owners', args={
|
||||
"entries": [{'name_hash':name_hash.decode('ascii'),'types':[ons_type]}]})
|
||||
|
||||
|
||||
|
@ -436,8 +437,8 @@ def ons_info(lmq, oxend, name,ons_type,**kwargs):
|
|||
@app.route('/ons/<string:name>/<int:more_details>')
|
||||
def show_ons(name, more_details=False):
|
||||
name = name.lower()
|
||||
lmq, oxend = lmq_connection()
|
||||
info = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
omq, oxend = omq_connection()
|
||||
info = FutureJSON(omq, oxend, 'rpc.get_info', 1)
|
||||
|
||||
if len(name) > 64 or not all(c.isalnum() or c in '_-' for c in name):
|
||||
return flask.render_template('not_found.html',
|
||||
|
@ -453,7 +454,7 @@ def show_ons(name, more_details=False):
|
|||
LOKINET_ENCRYPTED_LENGTH = 144 # The user must update their session mapping.
|
||||
|
||||
for ons_type in ons_types:
|
||||
onsinfo = ons_info(lmq, oxend, name, ons_types[ons_type]).get()
|
||||
onsinfo = ons_info(omq, oxend, name, ons_types[ons_type]).get()
|
||||
|
||||
if 'entries' not in onsinfo:
|
||||
# If returned with no data from the RPC
|
||||
|
@ -469,7 +470,7 @@ def show_ons(name, more_details=False):
|
|||
if len(onsinfo['encrypted_value']) not in [SESSION_ENCRYPTED_LENGTH, WALLET_ENCRYPTED_LENGTH, LOKINET_ENCRYPTED_LENGTH]:
|
||||
# Encryption involves a much more expensive argon2-based calculation for HF15 registrations.
|
||||
# Owners should be notified they should update to the new encryption format.
|
||||
ons_data[ons_type] = ons_info(lmq, oxend, name,ons_types[ons_type]).get()['entries'][0]
|
||||
ons_data[ons_type] = ons_info(omq, oxend, name,ons_types[ons_type]).get()['entries'][0]
|
||||
ons_data[ons_type]['mapping'] = 'Owner needs to update their ID for mapping info.'
|
||||
|
||||
else:
|
||||
|
@ -560,11 +561,11 @@ def show_ons(name, more_details=False):
|
|||
@app.route('/sn/<hex64:pubkey>')
|
||||
@app.route('/sn/<hex64:pubkey>/<int:more_details>')
|
||||
def show_sn(pubkey, more_details=False):
|
||||
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()
|
||||
quos = get_quorums_future(lmq, oxend, info.get()['height'])
|
||||
omq, oxend = omq_connection()
|
||||
info = FutureJSON(omq, oxend, 'rpc.get_info', 1)
|
||||
hfinfo = FutureJSON(omq, oxend, 'rpc.hard_fork_info', 10)
|
||||
sn = sn_req(omq, oxend, pubkey).get()
|
||||
quos = get_quorums_future(omq, oxend, info.get()['height'])
|
||||
|
||||
|
||||
if 'service_node_states' not in sn or not sn['service_node_states']:
|
||||
|
@ -640,7 +641,7 @@ def parse_txs(txs_rpc):
|
|||
return txs_rpc['txs']
|
||||
|
||||
|
||||
def get_block_txs_future(lmq, oxend, block):
|
||||
def get_block_txs_future(omq, oxend, block):
|
||||
hashes = []
|
||||
if 'tx_hashes' in block:
|
||||
hashes += block['tx_hashes']
|
||||
|
@ -653,7 +654,7 @@ def get_block_txs_future(lmq, oxend, 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, oxend, hashes, cache_key='block')
|
||||
return tx_req(omq, oxend, hashes, cache_key='block')
|
||||
|
||||
|
||||
@app.route('/block/<int:height>')
|
||||
|
@ -661,15 +662,15 @@ def get_block_txs_future(lmq, oxend, 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, oxend = lmq_connection()
|
||||
info = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
hfinfo = FutureJSON(lmq, oxend, 'rpc.hard_fork_info', 10)
|
||||
omq, oxend = omq_connection()
|
||||
info = FutureJSON(omq, oxend, 'rpc.get_info', 1)
|
||||
hfinfo = FutureJSON(omq, 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, oxend, val).get()
|
||||
block = None if val is None else block_with_txs_req(omq, oxend, val).get()
|
||||
if block is None:
|
||||
return flask.render_template("not_found.html",
|
||||
info=info.get(),
|
||||
|
@ -681,10 +682,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, oxend, block)
|
||||
txs = get_block_txs_future(omq, oxend, block)
|
||||
|
||||
if info.get()['height'] > 1 + block_height:
|
||||
next_block = block_header_req(lmq, oxend, '{}'.format(block_height + 1))
|
||||
next_block = block_header_req(omq, oxend, '{}'.format(block_height + 1))
|
||||
|
||||
if more_details:
|
||||
formatter = HtmlFormatter(cssclass="syntax-highlight", style="native")
|
||||
|
@ -712,17 +713,17 @@ def show_block(height=None, hash=None, more_details=False):
|
|||
|
||||
@app.route('/block/latest')
|
||||
def show_block_latest():
|
||||
lmq, oxend = lmq_connection()
|
||||
height = FutureJSON(lmq, oxend, 'rpc.get_info', 1).get()['height'] - 1
|
||||
omq, oxend = omq_connection()
|
||||
height = FutureJSON(omq, 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, oxend = lmq_connection()
|
||||
info = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
txs = tx_req(lmq, oxend, [txid]).get()
|
||||
omq, oxend = omq_connection()
|
||||
info = FutureJSON(omq, oxend, 'rpc.get_info', 1)
|
||||
txs = tx_req(omq, oxend, [txid]).get()
|
||||
|
||||
if 'txs' not in txs or not txs['txs']:
|
||||
return flask.render_template('not_found.html',
|
||||
|
@ -735,7 +736,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, oxend, 'rpc.get_quorum_state', 60, cache_key='tx_state_change',
|
||||
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} } }
|
||||
|
@ -759,14 +760,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, oxend, 'rpc.get_outs', args={
|
||||
outputs = FutureJSON(omq, 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, oxend, 'rpc.get_block_header_by_height', args={
|
||||
block_info_req = FutureJSON(omq, oxend, 'rpc.get_block_header_by_height', args={
|
||||
'heights': [o["height"] for o in outputs]
|
||||
})
|
||||
i = 0
|
||||
|
@ -816,9 +817,9 @@ def show_tx(txid, more_details=False):
|
|||
|
||||
@app.route('/quorums')
|
||||
def show_quorums():
|
||||
lmq, oxend = lmq_connection()
|
||||
info = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
quos = get_quorums_future(lmq, oxend, info.get()['height'])
|
||||
omq, oxend = omq_connection()
|
||||
info = FutureJSON(omq, oxend, 'rpc.get_info', 1)
|
||||
quos = get_quorums_future(omq, oxend, info.get()['height'])
|
||||
|
||||
return flask.render_template('quorums.html',
|
||||
info=info.get(),
|
||||
|
@ -832,8 +833,8 @@ base32z_map = {base32z_dict[i]: i for i in range(len(base32z_dict))}
|
|||
|
||||
@app.route('/search')
|
||||
def search():
|
||||
lmq, oxend = lmq_connection()
|
||||
info = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
omq, oxend = omq_connection()
|
||||
info = FutureJSON(omq, oxend, 'rpc.get_info', 1)
|
||||
val = (flask.request.args.get('value') or '').strip()
|
||||
if val and len(val) < 10 and val.isdigit(): # Block height
|
||||
return flask.redirect(flask.url_for('show_block', height=val), code=301)
|
||||
|
@ -848,9 +849,9 @@ def search():
|
|||
|
||||
if len(val) == 64:
|
||||
# Initiate all the lookups at once, then redirect to whichever one responds affirmatively
|
||||
snreq = sn_req(lmq, oxend, val)
|
||||
blreq = block_header_req(lmq, oxend, val, fail_okay=True)
|
||||
txreq = tx_req(lmq, oxend, [val])
|
||||
snreq = sn_req(omq, oxend, val)
|
||||
blreq = block_header_req(omq, oxend, val, fail_okay=True)
|
||||
txreq = tx_req(omq, oxend, [val])
|
||||
|
||||
sn = snreq.get()
|
||||
if sn and 'service_node_states' in sn and sn['service_node_states']:
|
||||
|
@ -881,9 +882,9 @@ def search():
|
|||
|
||||
@app.route('/api/networkinfo')
|
||||
def api_networkinfo():
|
||||
lmq, oxend = lmq_connection()
|
||||
info = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
hfinfo = FutureJSON(lmq, oxend, 'rpc.hard_fork_info', 10)
|
||||
omq, oxend = omq_connection()
|
||||
info = FutureJSON(omq, oxend, 'rpc.get_info', 1)
|
||||
hfinfo = FutureJSON(omq, oxend, 'rpc.hard_fork_info', 10)
|
||||
|
||||
info = info.get()
|
||||
data = {**info}
|
||||
|
@ -895,9 +896,9 @@ def api_networkinfo():
|
|||
|
||||
@app.route('/api/emission')
|
||||
def api_emission():
|
||||
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,
|
||||
omq, oxend = omq_connection()
|
||||
info = FutureJSON(omq, oxend, 'rpc.get_info', 1)
|
||||
coinbase = FutureJSON(omq, 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)
|
||||
|
@ -917,10 +918,10 @@ def api_emission():
|
|||
|
||||
@app.route('/api/service_node_stats')
|
||||
def api_service_node_stats():
|
||||
lmq, oxend = lmq_connection()
|
||||
info = FutureJSON(lmq, oxend, 'rpc.get_info', 1)
|
||||
stakinginfo = FutureJSON(lmq, oxend, 'rpc.get_staking_requirement', 30)
|
||||
sns = get_sns_future(lmq, oxend)
|
||||
omq, oxend = omq_connection()
|
||||
info = FutureJSON(omq, oxend, 'rpc.get_info', 1)
|
||||
stakinginfo = FutureJSON(omq, oxend, 'rpc.get_staking_requirement', 30)
|
||||
sns = get_sns_future(omq, oxend)
|
||||
sns = sns.get()
|
||||
if 'service_node_states' not in sns:
|
||||
return flask.jsonify({"status": "Error retrieving SN stats"}), 500
|
||||
|
@ -952,8 +953,8 @@ def api_service_node_stats():
|
|||
|
||||
@app.route('/api/circulating_supply')
|
||||
def api_circulating_supply():
|
||||
lmq, oxend = lmq_connection()
|
||||
coinbase = FutureJSON(lmq, oxend, 'admin.get_coinbase_tx_sum', 10, timeout=1, fail_okay=True,
|
||||
omq, oxend = omq_connection()
|
||||
coinbase = FutureJSON(omq, 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"]) // 1_000_000_000 if coinbase else None)
|
||||
|
||||
|
@ -961,8 +962,8 @@ def api_circulating_supply():
|
|||
# FIXME: need better error handling here
|
||||
@app.route('/api/transaction/<hex64:txid>')
|
||||
def api_tx(txid):
|
||||
lmq, oxend = lmq_connection()
|
||||
tx = tx_req(lmq, oxend, [txid]).get()
|
||||
omq, oxend = omq_connection()
|
||||
tx = tx_req(omq, oxend, [txid]).get()
|
||||
txs = parse_txs(tx)
|
||||
return flask.jsonify({
|
||||
"status": tx['status'],
|
||||
|
@ -972,9 +973,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, 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)
|
||||
omq, oxend = omq_connection()
|
||||
block = block_with_txs_req(omq, oxend, blkid if blkid is not None else height).get()
|
||||
txs = get_block_txs_future(omq, oxend, block)
|
||||
|
||||
if 'block_header' in block:
|
||||
data = block['block_header'].copy()
|
||||
|
|
1
pylokimq
1
pylokimq
|
@ -1 +0,0 @@
|
|||
Subproject commit 558c679c55c5a366b57110a55b6cbc68c447925d
|
|
@ -1,3 +1,4 @@
|
|||
from observer import app, config
|
||||
import oxenmq
|
||||
|
||||
config.oxend_rpc = 'ipc://oxend/testnet.sock'
|
||||
config.oxend_rpc = oxenmq.Address('ipc://oxend/testnet.sock')
|
||||
|
|
Loading…
Reference in New Issue