f648349e82
* Diatixisify! * First pass at Typedoc generation for TS SDK * Remove overview pages * Fix typos and remove codebase references from docs Fix typos across network and developer docs: Quorum, available, cryptosystem, transaction, proportional, Standalone. Remove TODO placeholder from dVPN protocol page. Strip GitHub source links from network docs to decouple documentation from repo structure. * Expand thin landing pages across network and developer docs - Add intro content to network overview, infrastructure, and reference landing pages - Expand developer index with "where to start" guide - Add usage instructions and explanations to all five TS playground pages - Expand WebSocket client page with setup and message format examples * Restructure Rust SDK developer docs - Delete redundant mixnet example, message-helpers, and message-types subpages - Delete client-pool architecture and example subpages (content folded into landing) - Delete tcpproxy troubleshooting (folded into landing page) - Add deprecation notices to TcpProxy pages, pointing to Stream module - Add stream module docs: landing page, architecture, tutorial, and 4 example pages - Add mixnet and client-pool tutorials - Add SDK tour page - Update navigation and landing pages with docs.rs links * Restructure TS SDK developer docs - Merge overview, installation, and getting started into TS SDK landing page - Fold FAQ content into bundling/troubleshooting section - Delete redundant overview, installation, start, and FAQ pages - Update internal links in browsers.mdx and native.mdx - Update navigation and example page imports * Flatten and expand APIs section - Collapse nested API subpages into single pages with inline Redoc embeds - Rewrite introduction as landing page with decision table - Add endpoint categories, quick curl examples to each API page - Mark Explorer API as deprecated - Move NS API deployment guide to operators/performance-and-testing - Fix dangling /apis/nym-api/mainnet link in network-components - Remove sandbox endpoints from all API pages * Add redirects for moved and deleted pages - Add 25 redirects covering TS SDK, Rust SDK, APIs, and network sections - Fix dangling /developers/typescript/start link in operators changelog * Replace individual example doc pages with GitHub-linked tables, expand tutorials - replace individual example doc pages with GitHub-linked tables - expand mixnet tutorial with persistent identity and split_sender sections - add tcpproxy tutorial - rename "API Reference" to "TypeDoc Reference" in TS SDK sidebar - rename "Misc" to "Extras" in developer sidebar, move VPN CLI up - remove echo server from tools - update message-queue callout to reference actual modules - fix mixnet/examples redirect collision * Add SEO frontmatter, validate encryption standards, clean up URLs - add title/description/schemaType/section/lastUpdated frontmatter to 48 pages across developers, network, and APIs sections - remove network/.archive/ directory (compare against develop instead) - update nymtech.net → nym.com for website/blog links (keep infra URLs) - add native proxy "in progress" callout for Rust/C/Go * API-scraper update (#6598) * read nodes and locations * update python-prebuild.sh * Address PR #6494 review feedback - Use "mode" consistently instead of "role" on nym-nodes page - Replace "staking" with "bonding" for NYM token collateral - Wire up auto-scraped node counts via TimeNow + nodes-count.json - Fix broken licensing images: download CC icons locally, replace inline HTML - Fix 9 stale redirects pointing through deleted /network/architecture path * Fix linkcheck errors - Fix stale cross-links: /network/concepts/ → /network/mixnet-mode/ - Replace README.md references with globals.md in TypeDoc output - Add entryFileName: globals to typedoc.json configs to prevent recurrence * Fix remaining stale /network/architecture links - zk-nym-overview: architecture/nyx#nym-api → /network/infrastructure/nyx#nym-api - setup: network/architecture → /network/overview * Remove accidentally re-included architecture.md file from rebase * Standardize tutorials, document examples, add llms.txt, apply tone fixes - Expand Rust SDK tutorials with step-by-step structure; document all SDK examples across mixnet, client-pool, and tcpproxy pages - Add llms.txt generation script, wire into build and CI workflows - Apply tone/style fixes: deduplicate callouts, vary sentence structure, standardize voice consistency across changed pages * Consolidate redundant network overview docs * Trim dev docs: git-first imports, stream notice, collapse TcpProxy * Update tutorial * Refresh auto-generated API and command outputs * Update network section docs * Update developer and API docs: reusable components, stream protocol, conventions, tutorial fixes * Fix Rust SDK tutorial bugs: setup_env, port conflicts, logging, open_stream race condition * Update stream.mdx * Remove docs.rs link from Stream overview for the moment * add llms.txt and llms-full.txt note to readme --------- Co-authored-by: import this <97586125+serinko@users.noreply.github.com>
457 lines
13 KiB
Python
457 lines
13 KiB
Python
#!/usr/bin/python3
|
|
|
|
import argparse
|
|
import os
|
|
import requests
|
|
import json
|
|
import sys
|
|
import pandas as pd
|
|
from collections import namedtuple
|
|
from time import gmtime, strftime
|
|
import time
|
|
|
|
############################################
|
|
############## GENERAL FNs #################
|
|
############################################
|
|
|
|
def get_url(args, **kwargs):
|
|
config_file = "./api_targets_config.json"
|
|
with open(config_file, "r") as f:
|
|
config = json.load(f)
|
|
env = args.api
|
|
endpoint = args.endpoint
|
|
if env == "github":
|
|
url = f"{config[env]}/{endpoint}"
|
|
else:
|
|
url = f"{config[env]}/api/v1/{endpoint}"
|
|
return url
|
|
|
|
def subparser_read(args):
|
|
url = get_url(args)
|
|
r = requests.get(url)
|
|
response = r.json()
|
|
return response
|
|
|
|
|
|
def print_time_now(args):
|
|
#now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
#now = time.ctime()
|
|
day = strftime("%d", gmtime())
|
|
if day[0] == "0":
|
|
day = day[1]
|
|
if day == "1" or day == "21" or day == "31":
|
|
suffix = "st"
|
|
elif day == "2" or day == "22":
|
|
suffix = "nd"
|
|
elif day == "3" or day == "23":
|
|
suffix = "rd"
|
|
else:
|
|
suffix = "th"
|
|
now = strftime(f"%A, %B {day}{suffix} %Y, %X UTC", gmtime())
|
|
print(now)
|
|
|
|
############################################
|
|
########### NYX RELATED FNs ################
|
|
############################################
|
|
|
|
def convert_u_nym(unym):
|
|
unym = float(unym)
|
|
nym = unym / 1000000
|
|
nym = int(nym)
|
|
return nym
|
|
|
|
def thousand_separator(n, separator):
|
|
if separator == " ":
|
|
n = f'{n:_}'
|
|
n = remove_underscore(n)
|
|
else:
|
|
n = f'{n:{separator}}'
|
|
return n
|
|
|
|
def remove_underscore(arg):
|
|
string = arg.replace("_", " ")
|
|
string = string.title()
|
|
return string
|
|
|
|
def display_supply_table(response, args):
|
|
separator = args.separator
|
|
df = pd.DataFrame(response)
|
|
df = df.T
|
|
del df['denom']
|
|
# df.set_axis(['**Item**', '**Amount in NYM**'], axis=1, inplace=True)
|
|
df = df.rename_axis('index1').reset_index()
|
|
df = df.rename(columns={'index1': '**Item**', 'amount': '**Amount in NYM**'})
|
|
df['**Item**'] = df['**Item**'].apply(remove_underscore)
|
|
df['**Amount in NYM**'] = df['**Amount in NYM**'].apply(convert_u_nym)
|
|
desc_column = _get_desc_column()
|
|
df.insert(1, '**Description**', desc_column, True)
|
|
stake_saturation = _get_stake_saturation()
|
|
df.loc[len(df.index)] = ['Stake Saturation', 'Optimal size of node self-bond + delegation', stake_saturation]
|
|
df['**Amount in NYM**'] = df['**Amount in NYM**'].apply(thousand_separator, args=(separator, ))
|
|
table = df.to_markdown(index=False,colalign=("left","left","right"))
|
|
print(table)
|
|
|
|
def _get_stake_saturation():
|
|
reward_params = get_api("https://validator.nymtech.net/api/v1/epoch/reward_params")
|
|
stake_saturation = get_dict_value(reward_params,["interval","stake_saturation_point"])
|
|
stake_saturation = convert_u_nym(stake_saturation)
|
|
return stake_saturation
|
|
|
|
def _get_desc_column():
|
|
supply = "Maximum amount of NYM token in existence"
|
|
reserve = "Tokens releasing for operators rewards"
|
|
vesting = "Tokens locked outside of circulation for future claim"
|
|
circulating = "Amount of unlocked tokens"
|
|
desc_column = [supply, reserve, vesting, circulating]
|
|
return desc_column
|
|
|
|
def read_supply(args):
|
|
separator = args.separator
|
|
response = subparser_read(args)
|
|
if args.endpoint == "circulating-supply":
|
|
if args.value:
|
|
value = get_nested_value(response, args)
|
|
value = convert_u_nym(value)
|
|
value = thousand_separator(value, separator)
|
|
print(value)
|
|
elif args.format == "markdown":
|
|
display_supply_table(response, args)
|
|
else:
|
|
value = response
|
|
print(value)
|
|
elif args.endpoint == "epoch/reward_params":
|
|
value = get_reward_params(response, args, separator)
|
|
print(value)
|
|
|
|
def get_reward_params(response, args, separator):
|
|
value = get_nested_value(response, args)
|
|
if args.format == "percent":
|
|
value = _return_percent_annotation(value)
|
|
else:
|
|
value = convert_u_nym(value)
|
|
value = thousand_separator(value, separator)
|
|
return value
|
|
|
|
def get_nested_value(response, args):
|
|
value = response
|
|
for key in args.value:
|
|
value = value[key]
|
|
return value
|
|
|
|
def _return_percent_annotation(value):
|
|
value = float(value) * 100
|
|
value = round(value, 2)
|
|
value = f"{value}%"
|
|
return value
|
|
|
|
###########################################
|
|
############# CALCULATE FNs ###############
|
|
###########################################
|
|
|
|
def calculate(args):
|
|
separator = args.separator
|
|
reward_params = get_api("https://validator.nymtech.net/api/v1/epoch/reward_params")
|
|
circulating_supply = get_api("https://validator.nymtech.net/api/v1/circulating-supply")
|
|
if args.staking_target:
|
|
display_staking_target(args, reward_params, circulating_supply, separator)
|
|
|
|
def get_api(url):
|
|
r = requests.get(url)
|
|
response = r.json()
|
|
return response
|
|
|
|
|
|
def display_staking_target(args, reward_params, circulating_supply, separator):
|
|
keys = ["interval", "staking_supply_scale_factor"]
|
|
staking_supply_scale_factor = get_dict_value(reward_params, keys)
|
|
keys = ["circulating_supply", "amount"]
|
|
circulating_supply = get_dict_value(circulating_supply, keys)
|
|
staking_target = float(staking_supply_scale_factor) * float(circulating_supply)
|
|
staking_target = convert_u_nym(staking_target)
|
|
if args.separator:
|
|
staking_target = thousand_separator(staking_target, separator)
|
|
print(staking_target)
|
|
|
|
def get_dict_value(json, keys):
|
|
value = json
|
|
for key in keys:
|
|
value = value[key]
|
|
return value
|
|
|
|
|
|
###########################################
|
|
############ GH RELATED FNs ###############
|
|
###########################################
|
|
|
|
def get_nym_vpn_version(args):
|
|
response = subparser_read(args)
|
|
if args.client == "desktop":
|
|
version = current_desktop_version(args, response)
|
|
elif args.client == "cli":
|
|
version = current_cli_version(args, response)
|
|
else:
|
|
print("Incorrect argument for -c, --client")
|
|
sys.exit(-1)
|
|
|
|
def current_cli_version(args, response):
|
|
df = pd.DataFrame(response)
|
|
print(df)
|
|
|
|
# NEEDS THIS IN PYTHON:
|
|
# current_cli_version=$(curl -s $release_url | jq -r '.[].tag_name' | grep '^nym-vpn-cli-v' | sort -Vr | head -n 1 | awk -F'-v' '{print $NF}')
|
|
|
|
|
|
def current_desktop_version(args, response):
|
|
# NEEDS THIS IN PYTHON:
|
|
df = pd.DataFrame(response)
|
|
print(df)
|
|
# version=$(curl -s $release_url | jq -r '.[].tag_name' | grep '^nym-vpn-desktop-v' | sort -Vr | head -n 1 | awk -F'-v' '{print $NF}')
|
|
|
|
###########################################
|
|
########## NODES DESCRIBED FNs ############
|
|
###########################################
|
|
|
|
|
|
def get_all_paginated_data(url, page_size=1000, timeout=30):
|
|
session = requests.Session()
|
|
page = 0
|
|
all_data = []
|
|
total = None
|
|
|
|
while True:
|
|
r = session.get(
|
|
url,
|
|
params={"page": page, "size": page_size},
|
|
timeout=timeout
|
|
)
|
|
r.raise_for_status()
|
|
response = r.json()
|
|
|
|
page_data = response.get("data", [])
|
|
pagination = response.get("pagination", {})
|
|
|
|
if total is None:
|
|
total = pagination.get("total")
|
|
|
|
if not page_data:
|
|
break
|
|
|
|
all_data.extend(page_data)
|
|
|
|
if total is not None and len(all_data) >= int(total):
|
|
break
|
|
|
|
page += 1
|
|
|
|
return all_data
|
|
|
|
|
|
def summarize_described_nodes(nodes):
|
|
unique_locations = set()
|
|
mixnodes = 0
|
|
exit_gateways = 0
|
|
|
|
for node in nodes:
|
|
description = node.get("description", {})
|
|
declared_role = description.get("declared_role", {})
|
|
auxiliary_details = description.get("auxiliary_details", {})
|
|
|
|
# location (2-letter country code like "FI")
|
|
location = auxiliary_details.get("location")
|
|
if location:
|
|
unique_locations.add(str(location).strip())
|
|
|
|
# roles
|
|
if declared_role.get("mixnode") is True:
|
|
mixnodes += 1
|
|
|
|
if declared_role.get("exit_nr") is True or declared_role.get("exit_ipr") is True:
|
|
exit_gateways += 1
|
|
|
|
summary = {
|
|
"nodes": len(nodes),
|
|
"locations": len(unique_locations),
|
|
"mixnodes": mixnodes,
|
|
"exit_gateways": exit_gateways,
|
|
}
|
|
|
|
return summary
|
|
|
|
|
|
def read_described_nodes(args):
|
|
url = get_url(args)
|
|
nodes = get_all_paginated_data(url, page_size=args.page_size)
|
|
summary = summarize_described_nodes(nodes)
|
|
|
|
if args.value:
|
|
value = summary
|
|
for key in args.value:
|
|
value = value[key]
|
|
print(value)
|
|
else:
|
|
print(json.dumps(summary, indent=2))
|
|
|
|
|
|
###########################################
|
|
############### MAIN PARSER ###############
|
|
###########################################
|
|
|
|
def parser_main():
|
|
parser = argparse.ArgumentParser(
|
|
prog="Nym API scraper",
|
|
description='''Get any live data from Nyx validator''',
|
|
epilog=''
|
|
)
|
|
subparsers = parser.add_subparsers(help="")
|
|
parser_supply = subparsers.add_parser('validator',
|
|
help='Reads validaor API enpoints',
|
|
aliases=['v']
|
|
)
|
|
|
|
parser_supply.add_argument(
|
|
"-a","--api",
|
|
type=str,
|
|
default="mainnet",
|
|
help="choose: mainnet, perf, sandbox"
|
|
)
|
|
parser_supply.add_argument(
|
|
"-e","--endpoint",
|
|
type=str,
|
|
help="choose from: https://validator.nymtech.net/api/swagger/index.html"
|
|
)
|
|
parser_supply.add_argument(
|
|
"-v","--value",
|
|
type=str,
|
|
help="dictionary keys to get needed value separated by a space",
|
|
nargs = '+'
|
|
)
|
|
|
|
parser_supply.add_argument(
|
|
"-f","--format",
|
|
type=str,
|
|
help="'markdown' formats the output for documentation purpose; 'percent' returns a number with % annotation",
|
|
)
|
|
|
|
parser_supply.add_argument(
|
|
"-s", "--separator",
|
|
type=str,
|
|
default=" ",
|
|
help="Add custom thousand separator to --format flag (default is none)"
|
|
)
|
|
|
|
parser_supply.set_defaults(func=read_supply)
|
|
|
|
|
|
|
|
|
|
parser_calculate = subparsers.add_parser('calculate',
|
|
help='Calculate and print the values of optional args',
|
|
aliases=['c']
|
|
)
|
|
|
|
parser_calculate.add_argument(
|
|
"--staking_target",
|
|
action="store_true",
|
|
help="A multiplier of staking supply scale factor and circulating supply"
|
|
)
|
|
|
|
parser_calculate.add_argument(
|
|
"-s", "--separator",
|
|
type=str,
|
|
default=" ",
|
|
help="Add custom thousand separator to --format flag (default is none)"
|
|
)
|
|
# parser_calculate.add_argument(
|
|
# "--api",
|
|
# default="mainnet",
|
|
# )
|
|
|
|
parser_calculate.set_defaults(func=calculate)
|
|
|
|
parser_time_now = subparsers.add_parser('time_now',
|
|
help='Prints UTC time now',
|
|
aliases=['time', 't']
|
|
)
|
|
|
|
parser_time_now.set_defaults(func=print_time_now)
|
|
|
|
|
|
parser_nym_vpn = subparsers.add_parser('nym_vpn',
|
|
help='reads NymVPN latest version',
|
|
aliases=['n']
|
|
)
|
|
|
|
parser_nym_vpn.add_argument(
|
|
"-c","--client",
|
|
type=str,
|
|
default="desktop",
|
|
help="choose: desktop, cli - default: desktop"
|
|
)
|
|
|
|
parser_nym_vpn.add_argument(
|
|
"-a","--api",
|
|
type=str,
|
|
default="github",
|
|
help="choose: mainnet, perf, sandbox"
|
|
)
|
|
|
|
parser_nym_vpn.add_argument(
|
|
"-e","--endpoint",
|
|
type=str,
|
|
help="add the url suffix",
|
|
default="repos/nymtech/nym-vpn-client/releases"
|
|
)
|
|
|
|
|
|
parser_nym_vpn.set_defaults(func=get_nym_vpn_version)
|
|
|
|
|
|
parser_described_nodes = subparsers.add_parser('described_nodes',
|
|
help='Summarise validator api/v1/nym-nodes/described',
|
|
aliases=['dn']
|
|
)
|
|
|
|
parser_described_nodes.add_argument(
|
|
"-a", "--api",
|
|
type=str,
|
|
default="mainnet",
|
|
help="choose: mainnet, perf, sandbox"
|
|
)
|
|
|
|
parser_described_nodes.add_argument(
|
|
"-e", "--endpoint",
|
|
type=str,
|
|
default="nym-nodes/described",
|
|
help="validator endpoint"
|
|
)
|
|
|
|
parser_described_nodes.add_argument(
|
|
"-v", "--value",
|
|
type=str,
|
|
help="optional summary key to print: nodes locations isps mixnodes exit_gateways",
|
|
nargs='+'
|
|
)
|
|
|
|
parser_described_nodes.add_argument(
|
|
"--page-size",
|
|
type=int,
|
|
default=1000,
|
|
help="pagination size per request; code keeps paging until all entries are fetched"
|
|
)
|
|
|
|
parser_described_nodes.set_defaults(func=read_described_nodes)
|
|
|
|
|
|
args = parser.parse_args()
|
|
try:
|
|
args.func(args)
|
|
except AttributeError as e:
|
|
print("Error on argparser")
|
|
sys.exit(-1)
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser_main()
|