argus/src/seed-tld-beacon.mjs
Local Dev fd6f112cab sirius/registrar: add .hub, .os TLDs; portal: fix getUtxosForAddresses call
.hub and .os seeded on the chipnet TLD beacon (~2,600 sats) and added to the
public registrar. .dea (already on-chain via a concurrent session) also
surfaced in the TLDS array so its second-level names become registerable —
consistent with the "if it's on-chain, offer it" default.

portal.html: fix "t is not iterable" by passing (electrum, addresses) to
BNS.getUtxosForAddresses instead of (addresses); open one BNS.connect() and
share it across the UTXO scan and the buildIndex. Sign-in itself already
worked (wallet unlocks, address populates) — the crash was in the subsequent
names load. Ships a clearer network-blocked error too when the browser can't
reach chipnet electrum ports (50004/50011/62002).
2026-08-31 03:25:28 +02:00

105 lines
4.8 KiB
JavaScript

// One-off (idempotent): seed the chipnet TLD-certificate beacon with the
// operator-confirmed initial TLD set. Reads what's already there via the wallet's
// provider (so re-runs skip anything present), then mints a TREG for each
// missing TLD. Verifies at the end by rebuilding the snapshot from beacon
// history — the same code path the /tld-list.json endpoint uses.
//
// node src/seed-tld-beacon.mjs
//
// Design context: Decentralized.DNS/DESIGN-tld-registry.md §9 item 1. Chipnet
// only — mainnet is a separate switch and needs the TLD covenant first.
import { loadWallet } from "./lib/wallet.js";
import { registerTld, parseOpReturn } from "./lib/bns.js";
import { buildTldSnapshot } from "./lib/tld-index.js";
import { TLD_BEACON } from "./config.js";
// Operator-confirmed seed set (2026-08-20): shipped defaults minus
// deviant/bcnr/silentmode, plus x for the coinspectrum.x work already in flight.
// Extended 2026-08-29: asm, neo, gt, sc, sia, com; then dex, cex, nt; then dea.
// The operator holds the countersignature key that would otherwise gate
// ICANN-adjacent TLDs (.com, .sc, .gt, .neo) on mainnet; on chipnet no covenant
// enforces the gate, so these are seeded directly under operator authority.
// `.com` is HELD by the operator (excluded from the public registrar UI) so
// nobody else can claim names under it — the TLD cert stays on-chain, but no
// second-level registrations are offered. Future TUPD may set policy:"frozen"
// once the TLD covenant lands and enforces it.
const SEED_TLDS = [
"bch", "p2p", "bit", "nav", "test", "x",
"asm", "neo", "gt", "sc", "sia", "com",
"dex", "cex", "nt", "dea",
"hub", "os",
];
// Empty records for the seed — the TLD covenant work will populate
// policy/fee_bps/reserved later. Empty means "defaults apply everywhere",
// which per the design doc §3 is {policy:"open", fee_bps:500, min_len:1}.
const SEED_RECORDS = {};
const wallet = await loadWallet("main");
console.log(`from wallet : ${wallet.cashaddr}`);
console.log(`TLD beacon : ${TLD_BEACON}`);
console.log(`target TLD set : ${SEED_TLDS.join(" ")}`);
// Fetch what's already on the beacon so re-runs skip it.
console.log("\nfetching current TLD-beacon state …");
const history = await wallet.provider.getHistory(TLD_BEACON);
const txs = new Map();
for (const { tx_hash } of history) {
try { txs.set(tx_hash, await wallet.provider.getRawTransactionObject(tx_hash)); }
catch { /* unreadable — skip */ }
}
const before = buildTldSnapshot({ history, txs });
console.log(`present : ${before.tlds.length ? before.tlds.map((t) => t.tld).join(" ") : "(none)"}`);
const existing = new Set(before.tlds.map((t) => t.tld));
const toMint = SEED_TLDS.filter((t) => !existing.has(t));
if (!toMint.length) {
console.log("\nnothing to do — all target TLDs already registered.");
process.exit(0);
}
console.log(`will register : ${toMint.join(" ")} (${toMint.length} tx)`);
const bal = await wallet.getBalance("sat");
console.log(`wallet balance : ${bal} sat`);
const results = [];
for (const tld of toMint) {
process.stdout.write(` TREG ${tld} … `);
try {
const res = await registerTld(wallet, tld, SEED_RECORDS);
results.push({ tld, ok: true, ...res });
console.log(`ok txid=${res.txid} category=${res.category?.slice(0, 16)}…`);
} catch (e) {
results.push({ tld, ok: false, error: String(e && e.message || e) });
console.log(`FAIL ${e && e.message || e}`);
}
// A short pause so a chain query between two REGs sees the previous tx.
await new Promise((r) => setTimeout(r, 500));
}
const failed = results.filter((r) => !r.ok);
console.log(`\n${results.length - failed.length}/${results.length} TREG(s) broadcast${failed.length ? ` (${failed.length} failed)` : ""}`);
// Verify by rebuilding the snapshot from the beacon.
console.log("\nverifying via TLD-beacon snapshot rebuild …");
await new Promise((r) => setTimeout(r, 2000)); // mempool propagation
const historyAfter = await wallet.provider.getHistory(TLD_BEACON);
const txsAfter = new Map();
for (const { tx_hash } of historyAfter) {
try { txsAfter.set(tx_hash, await wallet.provider.getRawTransactionObject(tx_hash)); }
catch { /* skip */ }
}
const after = buildTldSnapshot({ history: historyAfter, txs: txsAfter });
console.log(`snapshot root : ${after.root}`);
console.log(`snapshot tlds :`);
for (const t of after.tlds) {
console.log(` - ${t.tld.padEnd(6)} cat=${t.category.slice(0, 16)}… regHeight=${t.regHeight ?? "(mempool)"} txid=${t.regTxid.slice(0, 16)}…`);
}
const missing = SEED_TLDS.filter((t) => !after.tlds.some((s) => s.tld === t));
if (missing.length) {
console.log(`\nWARNING: still missing after broadcast: ${missing.join(" ")}`);
process.exit(1);
}
console.log(`\ndone — all ${SEED_TLDS.length} target TLDs present on the TLD beacon.`);
process.exit(0);