mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-utils.git
synced 2026-10-20 18:56:26 +00:00
Nova's configs carry Nova's own Infura project keys inside node URLs, and
every sync copied them into ours: Ethereum had four keyed Infura nodes and
Base one, across 43 published files. Our Android build already fills
`{INFURA_API_KEY}` from our own secret; the keyed URLs used another
organisation's quota and would break for every user when Nova rotates them.
- sync_from_nova.py strips nodes whose URL embeds an Infura key, and drops a
chain left with no node (only Ethereum - Goerli, a retired testnet, in the
dev files); merge-chains.py applies the same filter
- the published chain files are rewritten with that filter; nothing else in
them changes (each file round-trips byte for byte through json.dump)
- check_pezkuwi_integrity.py refuses a keyed Infura URL under chains/
136 lines
5.1 KiB
Python
Executable File
136 lines
5.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Refuse the five regressions this repo has already had.
|
|
|
|
Each check below exists because the thing it forbids happened, was fixed, and in two
|
|
cases came back. They are cheap; the failures they catch are not.
|
|
|
|
Run from the repository root. Exits non-zero on the first category that fails, after
|
|
reporting every problem it found.
|
|
"""
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
APP_CHAINS = ROOT / "chains" / "v22" / "android" / "chains.json"
|
|
AH = "Pezkuwi Asset Hub"
|
|
|
|
errors: list[str] = []
|
|
|
|
|
|
def load(path: Path):
|
|
with path.open(encoding="utf-8") as fh:
|
|
return json.load(fh)
|
|
|
|
|
|
def asset_hub_assets(doc):
|
|
for chain in doc:
|
|
if chain.get("name") == AH:
|
|
yield from chain.get("assets", [])
|
|
|
|
|
|
def extras(asset):
|
|
return asset.get("typeExtras") or {}
|
|
|
|
|
|
# ── 1. Only assets that exist on chain may be declared ───────────────────────
|
|
#
|
|
# 1001/1002/1003 were declared as DOT/ETH/BTC with icons pointing at Nova's own repo —
|
|
# copied from a template, never created on Pezkuwi Asset Hub. Queried 2026-08-11 against
|
|
# wss://asset-hub-rpc.pezkuwichain.io, assets.asset() returns None for all three; only
|
|
# 1 (PEZ) and 1000 (wUSDT) are Live. Removed twice, back twice.
|
|
GHOST = {"1001", "1002", "1003"}
|
|
for name in ("chains/chains.json",
|
|
"chains/v22/android/chains.json",
|
|
"chains/v22/android/chains_minimal.json"):
|
|
path = ROOT / name
|
|
if not path.exists():
|
|
continue
|
|
bad = [a.get("symbol") for a in asset_hub_assets(load(path))
|
|
if str(extras(a).get("assetId", "")) in GHOST]
|
|
if bad:
|
|
errors.append(
|
|
f"{name}: declares Asset Hub assets that do not exist on chain: {bad}"
|
|
)
|
|
|
|
# ── 2. Sufficient assets must say so ─────────────────────────────────────────
|
|
#
|
|
# Absent, LocalToDomainChainMapper resolves isSufficient to false, the wallet's
|
|
# DeadRecipientValidation runs at ERROR level, and every transfer to an account holding
|
|
# no HEZ is refused — which is every fresh deposit address, so nobody can fund an
|
|
# exchange account. Both assets are sufficient=true on chain.
|
|
if APP_CHAINS.exists():
|
|
declared = {
|
|
str(extras(a).get("assetId"))
|
|
for a in asset_hub_assets(load(APP_CHAINS))
|
|
if extras(a).get("isSufficient") is True
|
|
}
|
|
missing = {"1", "1000"} - declared
|
|
if missing:
|
|
errors.append(
|
|
f"{APP_CHAINS.name}: Asset Hub assets {sorted(missing)} must declare "
|
|
f"isSufficient: true — the chain says they are sufficient"
|
|
)
|
|
|
|
# ── 3. Assets are served from a branch that is actually served ───────────────
|
|
#
|
|
# 252 icon references once pointed at pending/post-fix-release. Every file they named
|
|
# also existed on master, so the dependency bought nothing — and would have broken
|
|
# silently the day that branch was tidied away.
|
|
grep = subprocess.run(
|
|
[
|
|
"grep",
|
|
"-rlE",
|
|
r"pezkuwi-wallet-utils/(pending|feature|fix|test)/",
|
|
str(ROOT / "chains"),
|
|
],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
if grep.stdout.strip():
|
|
files = grep.stdout.strip().splitlines()
|
|
errors.append(
|
|
f"{len(files)} file(s) under chains/ reference a working branch for "
|
|
f"assets; use master, e.g. {Path(files[0]).name}"
|
|
)
|
|
|
|
# ── 4. A published fixture matches its source ────────────────────────────────
|
|
#
|
|
# sync_from_nova.py does not copy tests/, so these two are kept in step by hand and
|
|
# nothing noticed if they drifted.
|
|
for fixture in ("pezkuwi_assets_for_testBalance.json",):
|
|
src = ROOT / "pezkuwi-overlay" / "tests" / fixture
|
|
pub = ROOT / "tests" / fixture
|
|
if src.exists() and pub.exists() and src.read_bytes() != pub.read_bytes():
|
|
errors.append(
|
|
f"tests/{fixture} has drifted from pezkuwi-overlay/tests/{fixture}"
|
|
)
|
|
|
|
# ── 5. No third-party API key in a node URL ──────────────────────────────────
|
|
#
|
|
# Nova's configs shipped Nova's own Infura project keys inside node URLs, and
|
|
# every sync copied them into ours. Our builds fill {INFURA_API_KEY} from our
|
|
# own secret; a keyed URL is someone else's quota and dies when they rotate it.
|
|
grep = subprocess.run(
|
|
["grep", "-rlE", r"infura\.io/.*/v3/[0-9a-f]{32}", str(ROOT / "chains")],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
if grep.stdout.strip():
|
|
files = grep.stdout.strip().splitlines()
|
|
errors.append(
|
|
f"{len(files)} file(s) under chains/ embed an Infura key in a node URL; "
|
|
f"use {{INFURA_API_KEY}}, e.g. {Path(files[0]).relative_to(ROOT)}"
|
|
)
|
|
|
|
if errors:
|
|
for e in errors:
|
|
print(f"::error::{e}")
|
|
print(f"\n{len(errors)} integrity problem(s).", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
print("Pezkuwi integrity: on-chain assets only, sufficiency declared, "
|
|
"assets served from master, fixture in step with its source, "
|
|
"no third-party key in a node URL.")
|