Add ONS Lookup capability
This commit is contained in:
parent
9dbbd57255
commit
d1c2d23270
159
observer.py
159
observer.py
|
@ -9,6 +9,7 @@ import statistics
|
|||
import string
|
||||
import requests
|
||||
import time
|
||||
import base64
|
||||
from base64 import b32encode, b16decode
|
||||
from werkzeug.routing import BaseConverter
|
||||
from pygments import highlight
|
||||
|
@ -17,7 +18,9 @@ from pygments.formatters import HtmlFormatter
|
|||
import subprocess
|
||||
import qrcode
|
||||
from io import BytesIO
|
||||
|
||||
import pysodium
|
||||
import nacl.encoding
|
||||
import nacl.hash
|
||||
import config
|
||||
import local_config
|
||||
from lmq import FutureJSON, lmq_connection
|
||||
|
@ -414,6 +417,109 @@ def block_with_txs_req(lmq, oxend, hash_or_height, **kwargs):
|
|||
|
||||
return FutureJSON(lmq, oxend, 'rpc.get_block', cache_key='single', args=args, **kwargs)
|
||||
|
||||
def ons_info(lmq, 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={
|
||||
"entries": [{'name_hash':name_hash.decode('ascii'),'types':[ons_type]}]})
|
||||
|
||||
|
||||
@app.route('/ons/<string:name>')
|
||||
@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)
|
||||
if len(name) > 64 or not all(c.isalnum() or c in '_-' for c in name):
|
||||
return flask.render_template('not_found.html',
|
||||
info=info.get(),
|
||||
type='bad_search',
|
||||
id=name,
|
||||
)
|
||||
|
||||
ons_types = {'session':0,'wallet':1,'lokinet':2}
|
||||
ons_data = {'name':name}
|
||||
SESSION_ENCRYPTED_LENGTH = 146 # If the encrypted value is not of expected character
|
||||
WALLET_ENCRYPTED_LENGTH = 210 # length it is of HF15 and before.
|
||||
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()
|
||||
if 'entries' in onsinfo:
|
||||
onsinfo = onsinfo['entries'][0]
|
||||
ons_data[ons_type] = onsinfo
|
||||
|
||||
if len(onsinfo['encrypted_value']) in [SESSION_ENCRYPTED_LENGTH, WALLET_ENCRYPTED_LENGTH, LOKINET_ENCRYPTED_LENGTH]:
|
||||
# RPC returns encrypted_value as ciphertext and nonce concatenated.
|
||||
# The nonce is the last 48 characters of the encrypted value and the remainder of characters is the encrypted_value.
|
||||
nonce_received = onsinfo['encrypted_value'][-48:]
|
||||
nonce = bytes.fromhex(nonce_received)
|
||||
|
||||
# The ciphertext is the encrypted_value with the nonce taken away.
|
||||
ciphertext = bytes.fromhex(onsinfo['encrypted_value'][:-48])
|
||||
|
||||
# If ons type is lokinet we need to add .loki to the name before hashing.
|
||||
if ons_types[ons_type] == 2:
|
||||
name+='.loki'
|
||||
|
||||
# Calculate the blake2b hash of the lower-case full name
|
||||
name_hash = nacl.hash.blake2b(name.encode(),encoder = nacl.encoding.RawEncoder)
|
||||
|
||||
# Decryption key: another blake2b hash, but this time a keyed blake2b hash where the first hash is the key
|
||||
decryption_key = nacl.hash.blake2b(name.encode(), key=name_hash, encoder = nacl.encoding.RawEncoder)
|
||||
|
||||
# XChaCha20+Poly1305 decryption
|
||||
val = pysodium.crypto_aead_xchacha20poly1305_ietf_decrypt(ciphertext=ciphertext, ad=b'', nonce=nonce, key=decryption_key)
|
||||
|
||||
# lokinet check
|
||||
print(ons_types[ons_type])
|
||||
if ons_types[ons_type] == 2:
|
||||
# val will currently be the raw lokinet ed25519 pubkey (32 bytes). We can convert it to the more
|
||||
# common lokinet address (which is the same value but encoded in z-base-32) and convert the bytes to
|
||||
# a string:
|
||||
val = b32encode(val).decode()
|
||||
|
||||
# Python's regular base32 uses a different alphabet, so translate from base32 to z-base-32:
|
||||
val = val.translate(str.maketrans("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",
|
||||
"ybndrfg8ejkmcpqxot1uwisza345h769"))
|
||||
|
||||
# Base32 is also padded with '=', which isn't used in z-base-32:
|
||||
val = val.rstrip('=')
|
||||
|
||||
# Finally slap ".loki" on the end:
|
||||
val += ".loki"
|
||||
|
||||
ons_data[ons_type]['mapping'] = val
|
||||
else:
|
||||
ons_data[ons_type]['mapping'] = val.hex()
|
||||
else:
|
||||
# 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]['mapping'] = 'Owner needs to update their ID for mapping info.'
|
||||
else:
|
||||
# If returned with no data from the RPC
|
||||
ons_data[ons_type] = True
|
||||
|
||||
if more_details:
|
||||
formatter = HtmlFormatter(cssclass="syntax-highlight", style="paraiso-dark")
|
||||
more_details = {
|
||||
'details_css': formatter.get_style_defs('.syntax-highlight'),
|
||||
'details_html': highlight(json.dumps(ons_data, indent="\t"), JsonLexer(), formatter),
|
||||
}
|
||||
else:
|
||||
more_details = {}
|
||||
|
||||
|
||||
return flask.render_template('ons.html',
|
||||
info=info.get(),
|
||||
ons=ons_data,
|
||||
**more_details,
|
||||
)
|
||||
|
||||
|
||||
@app.route('/sn/<hex64:pubkey>')
|
||||
@app.route('/sn/<hex64:pubkey>/<int:more_details>')
|
||||
|
@ -687,12 +793,12 @@ def show_quorums():
|
|||
base32z_dict = 'ybndrfg8ejkmcpqxot1uwisza345h769'
|
||||
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)
|
||||
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)
|
||||
|
||||
|
@ -704,34 +810,43 @@ def search():
|
|||
v >>= 4
|
||||
val = "{:64x}".format(v)
|
||||
|
||||
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])
|
||||
|
||||
sn = snreq.get()
|
||||
if sn and 'service_node_states' in sn and sn['service_node_states']:
|
||||
return flask.redirect(flask.url_for('show_sn', pubkey=val), code=301)
|
||||
|
||||
bl = blreq.get()
|
||||
if bl and 'block_header' in bl and bl['block_header']:
|
||||
return flask.redirect(flask.url_for('show_block', hash=val), code=301)
|
||||
|
||||
tx = txreq.get()
|
||||
if tx and 'txs' in tx and tx['txs']:
|
||||
return flask.redirect(flask.url_for('show_tx', txid=val), code=301)
|
||||
|
||||
# ONS can be of length 64 however with txids, and sn pubkey's being of length 64
|
||||
# I have removed it from the possible searches.
|
||||
if len(val) < 64 and all(c.isalnum() or c in '_-' for c in val):
|
||||
return flask.redirect(flask.url_for('show_ons', name=val), code=301)
|
||||
|
||||
elif not val or len(val) != 64 or any(c not in string.hexdigits for c in val):
|
||||
return flask.render_template('not_found.html',
|
||||
info=info.get(),
|
||||
type='bad_search',
|
||||
id=val,
|
||||
)
|
||||
|
||||
# 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])
|
||||
|
||||
sn = snreq.get()
|
||||
if sn and 'service_node_states' in sn and sn['service_node_states']:
|
||||
return flask.redirect(flask.url_for('show_sn', pubkey=val), code=301)
|
||||
bl = blreq.get()
|
||||
if bl and 'block_header' in bl and bl['block_header']:
|
||||
return flask.redirect(flask.url_for('show_block', hash=val), code=301)
|
||||
tx = txreq.get()
|
||||
if tx and 'txs' in tx and tx['txs']:
|
||||
return flask.redirect(flask.url_for('show_tx', txid=val), code=301)
|
||||
info=info.get(),
|
||||
type='bad_search',
|
||||
id=val,
|
||||
)
|
||||
|
||||
return flask.render_template('not_found.html',
|
||||
info=info.get(),
|
||||
type='search',
|
||||
type='bad_search',
|
||||
id=val,
|
||||
)
|
||||
|
||||
|
||||
@app.route('/api/networkinfo')
|
||||
def api_networkinfo():
|
||||
lmq, oxend = lmq_connection()
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
{% extends "_basic.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
|
||||
<div class="sn-details Wrapper">
|
||||
<div class="sn-details-main">
|
||||
<div class="details">
|
||||
<h2>Oxen Name Server Lookup <b>"{{ons.name}}"</b></h2>
|
||||
<div class="TitleUnderliner"></div>
|
||||
<h2><label>Session</label> </h2>
|
||||
<div class="TitleUnderliner"></div>
|
||||
{%if ons.session == True%}
|
||||
<h4 style="margin:5px"><label>Available:</label> Yes</h4>
|
||||
{%else%}
|
||||
<h4 style="margin:5px"><label>Available:</label> No</h4>
|
||||
<h4 style="margin:5px"><label>Owner:</label> {{ons.session.owner}}</h4>
|
||||
<h4 style="margin:5px"><label>TX Hash:</label> <a href="/tx/{{ons.session.txid}}">{{ons.session.txid}}</a></h4>
|
||||
|
||||
<h4 style="margin:5px"><label>Update Height:</label> <a href="/block/{{ons.session.update_height}}">{{ons.session.update_height}}</a></h4>
|
||||
<h4 style="margin:5px"><label>Name Hash:</label> {{ons.session.name_hash}}</h4>
|
||||
<h4 style="margin:5px"><label>Mapping:</label> {{ons.session.mapping}}</h4>
|
||||
{%endif%}
|
||||
|
||||
<h2><label>Wallet</label></h2>
|
||||
<div class="TitleUnderliner"></div>
|
||||
{%if ons.wallet == True%}
|
||||
{%if ons.session == True%}
|
||||
<h4 style="margin:5px"><label>Available:</label> Yes</h4>
|
||||
{%else%}
|
||||
<h4 style="margin:5px"><label>Available:</label> Only available for above Session ID Owner</h4>
|
||||
{%endif%}
|
||||
{%else%}
|
||||
<h4 style="margin:5px"><label>Available:</label> No</h4>
|
||||
<h4 style="margin:5px"><label>Owner:</label> {{ons.wallet.owner}}</h4>
|
||||
<h4 style="margin:5px"><label>TX Hash:</label> <a href="/tx/{{ons.wallet.txid}}">{{ons.wallet.txid}}</a></h4>
|
||||
<h4 style="margin:5px"><label>Update Height:</label> <a href="/block/{{ons.wallet.update_height}}">{{ons.wallet.update_height}}</a></h4>
|
||||
<h4 style="margin:5px"><label>Name Hash:</label> {{ons.wallet.name_hash}}</h4>
|
||||
<h4 style="margin:5px"><label>Mapping:</label> {{ons.wallet.mapping}}</h4>
|
||||
{%endif%}
|
||||
|
||||
<h2><label>Lokinet</label></h2>
|
||||
<div class="TitleUnderliner"></div>
|
||||
{%if ons.lokinet == True%}
|
||||
<h4 style="margin:5px"><label>Available:</label> Yes</h4>
|
||||
{%else%}
|
||||
<h4 style="margin:5px"><label>Available:</label> No</h4>
|
||||
<h4 style="margin:5px"><label>Owner:</label> {{ons.lokinet.owner}}</h4>
|
||||
<h4 style="margin:5px"><label>TX Hash:</label> <a href="/tx/{{ons.session.txid}}">{{ons.lokinet.txid}}</a></h4>
|
||||
<h4 style="margin:5px"><label>Update Height:</label> <a href="/block/{{ons.lokinet.update_height}}">{{ons.lokinet.update_height}}</a></h4>
|
||||
<h4 style="margin:5px"><label>Expiration Height:</label> {{ons.lokinet.expiration_height}}</h4>
|
||||
<h4 style="margin:5px"><label>Name Hash:</label> {{ons.lokinet.name_hash}}</h4>
|
||||
<h4 style="margin:5px"><label>Mapping:</label> {{ons.lokinet.mapping}}</h4>
|
||||
{%endif%}
|
||||
</div>
|
||||
</div>
|
||||
{%if details_html%}
|
||||
<style type="text/css">
|
||||
{{details_css | safe}}
|
||||
</style>
|
||||
<div class="TitleDivider" id="more_details"></div>
|
||||
{{details_html | safe}}
|
||||
{%else%}
|
||||
<h5>
|
||||
<a href="/ons/{{ons.name}}/1#more_details">Show raw details</a>
|
||||
</h5>
|
||||
{%endif%}
|
||||
</div>
|
||||
{% endblock %}
|
Loading…
Reference in New Issue