163 lines
4.5 KiB
Python
163 lines
4.5 KiB
Python
from __future__ import unicode_literals
|
|
from ipaddress import ip_address
|
|
from json import loads
|
|
import socket
|
|
import urllib.request
|
|
from sys import exit, stderr
|
|
|
|
|
|
def discover_peers(ep, discover):
|
|
"""
|
|
From first node, discover his known nodes.
|
|
Remove from know nodes if nodes are down.
|
|
If discover option: scan all network to know all nodes.
|
|
display percentage discovering.
|
|
"""
|
|
endpoints = parse_endpoints(get_request(ep, "network/peers")["peers"])
|
|
if discover:
|
|
print("Discovering network")
|
|
for i, ep in enumerate(endpoints):
|
|
if discover:
|
|
print("{0:.0f}%".format(i / len(endpoints) * 100))
|
|
if best_node(ep, 0) is None:
|
|
endpoints.remove(ep)
|
|
elif discover:
|
|
endpoints = recursive_discovering(endpoints, ep)
|
|
return endpoints
|
|
|
|
|
|
def recursive_discovering(endpoints, ep):
|
|
"""
|
|
Discover recursively new nodes.
|
|
If new node found add it and try to found new node from his known nodes.
|
|
"""
|
|
news = parse_endpoints(get_request(ep, "network/peers")["peers"])
|
|
for new in news:
|
|
if best_node(new, 0) is not None and new not in endpoints:
|
|
endpoints.append(new)
|
|
recursive_discovering(endpoints, new)
|
|
return endpoints
|
|
|
|
|
|
def parse_endpoints(rep):
|
|
"""
|
|
endpoints[]{"domain", "ip4", "ip6", "pubkey"}
|
|
list of dictionaries
|
|
reps: raw endpoints
|
|
"""
|
|
i, j, endpoints = 0, 0, []
|
|
while (i < len(rep)):
|
|
if (rep[i]["status"] == "UP"):
|
|
while j < len(rep[i]["endpoints"]):
|
|
ep = parse_endpoint(rep[i]["endpoints"][j])
|
|
j += 1
|
|
if ep is None:
|
|
break
|
|
ep["pubkey"] = rep[i]["pubkey"]
|
|
endpoints.append(ep)
|
|
i += 1
|
|
j = 0
|
|
return endpoints
|
|
|
|
|
|
def parse_endpoint(rep):
|
|
"""
|
|
rep: raw endpoint, sep: split endpoint
|
|
domain, ip4 or ip6 could miss on raw endpoint
|
|
"""
|
|
ep, sep = {}, rep.split(" ")
|
|
if sep[0] == "BASIC_MERKLED_API":
|
|
if check_port(sep[-1]):
|
|
ep["port"] = sep[-1]
|
|
if len(sep) == 5 and check_ip(sep[1]) == 0 and check_ip(sep[2]) == 4 and check_ip(sep[3]) == 6:
|
|
ep["domain"], ep["ip4"], ep["ip6"] = sep[1], sep[2], sep[3]
|
|
if len(sep) == 4:
|
|
ep = endpoint_type(sep[1], ep)
|
|
ep = endpoint_type(sep[2], ep)
|
|
if len(sep) == 3:
|
|
ep = endpoint_type(sep[1], ep)
|
|
return ep
|
|
else:
|
|
return None
|
|
|
|
|
|
def endpoint_type(sep, ep):
|
|
typ = check_ip(sep)
|
|
if typ == 0:
|
|
ep["domain"] = sep
|
|
elif typ == 4:
|
|
ep["ip4"] = sep
|
|
elif typ == 6:
|
|
ep["ip6"] = sep
|
|
return ep
|
|
|
|
|
|
def check_ip(address):
|
|
try:
|
|
return ip_address(address).version
|
|
except:
|
|
return 0
|
|
|
|
|
|
def get_request(ep, path):
|
|
address = best_node(ep, 0)
|
|
if address is None:
|
|
return address
|
|
url = "http://" + ep[address] + ":" + ep["port"] + "/" + path
|
|
if ep["port"] == "443":
|
|
url = "https://" + ep[address] + "/" + path
|
|
request = urllib.request.Request(url)
|
|
response = urllib.request.urlopen(request)
|
|
encoding = response.info().get_content_charset('utf8')
|
|
return loads(response.read().decode(encoding))
|
|
|
|
|
|
def post_request(ep, path, postdata):
|
|
address = best_node(ep, 0)
|
|
if address is None:
|
|
return address
|
|
url = "http://" + ep[address] + ":" + ep["port"] + "/" + path
|
|
if ep["port"] == "443":
|
|
url = "https://" + ep[address] + "/" + path
|
|
request = urllib.request.Request(url, bytes(postdata, 'utf-8'))
|
|
try:
|
|
response = urllib.request.urlopen(request)
|
|
except urllib.error.URLError as e:
|
|
print(e, file=stderr)
|
|
exit(1)
|
|
encoding = response.info().get_content_charset('utf8')
|
|
return loads(response.read().decode(encoding))
|
|
|
|
|
|
def best_node(ep, main):
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
addresses, port = {"ip6", "ip4", "domain"}, int(ep["port"])
|
|
for address in addresses:
|
|
if address in ep:
|
|
try:
|
|
s.connect((ep[address], port))
|
|
s.close()
|
|
return address
|
|
except:
|
|
pass
|
|
if main:
|
|
print("Wrong node gived as argument", file=stderr)
|
|
exit(1)
|
|
return None
|
|
|
|
|
|
def check_port(port):
|
|
try:
|
|
port = int(port)
|
|
except:
|
|
print("Port must be an integer", file=stderr)
|
|
exit(1)
|
|
if (port < 0 or port > 65536):
|
|
print("Wrong port number", file=stderr)
|
|
exit(1)
|
|
return 1
|
|
|
|
|
|
def get_current_block(ep):
|
|
return get_request(ep, "blockchain/current")
|