Skip to content

hclivess/nado

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2,120 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NADO

DiscordTwitter/X

NADO

A phone-mineable, fair-launch, post-quantum, lightweight blockchain.

NADO is built around a seamless, one-click experience: every node serves a single zero-install browser pagewallet, block explorer, miner, and alias manager in one — at its root URL, so full interaction with the chain is one tap away from any device, with no app, no sync, and no account. Under that surface, NADO lets an ordinary phone — running nothing but that browser tab — take part in block production for zero capital, on a fair launch with no premine, secured by post-quantum signatures. It replaces the Proof-of-Work hash race with a deterministic, beacon-keyed weighted draw: one hash decides each block's producer, so faster hardware (ASICs, GPUs) confers no advantage and there is nothing to grind. Coins enter circulation only as block rewards.

Status: testnet-stage alpha, NOT yet mainnet-launched. The fair-mining economics and the full consensus-security hardening plan (objective fork-choice, enforced finality, grind-proof chain weight, detached winner signatures + equivocation slashing (block-authorship + FFG attestation), enforced FFG stake-attested finality, commit-reveal RANDAO) are now implemented; the multi-node, epoch-crossing behaviour of the last three is still only lightly exercised empirically (see Security). What genuinely remains is a subset of eclipse hardening (ASN-level peer diversity, pinned multi-seed bootstrap, snapshot- bootstrap binding to a finalized signed checkpoint). On top of consensus, a STARK-proven execution layer (a field-native zkVM) is live and carries real dApps — ~20 on-chain games, shielded transfers, and end-to-end-encrypted on-chain messaging. Run it on testnet / at your own risk; do not secure value of consequence with it yet. The alphanet rerolls often (fresh genesis, balances carried forward) as consensus changes land strictly with no backward-compat; the current chain id is alphanet-6.


Why NADO

Most "anyone-can-mine" coins fail in one of four ways: mining gets captured by specialized hardware; the launch isn't fair (premines, insider allocations); the cryptography isn't quantum-resistant; or a "light" client still leans on trusted infrastructure. NADO targets all four at once, and adds a fifth goal — that re-joining the network should never get harder as more people join.

It is inspired by NANO, IDENA, NYZO and Vertcoin, and pushes the barrier to entry lower than any of them: no puzzles to keep solving, no efficient rig to keep running, and no requirement to own coins.

Key features

  • Seamless — one client, any device, one tap. Every node serves a single browser page (/) that is at once the wallet, block explorer, miner, and alias manager — and, via a Rollup tab, a window onto the execution layer that browses, deploys and calls contracts. No install, no browser extension, no full node, no signup, no seed-phrase ceremony. Unlike a MetaMask-style extension wallet — which only holds keys, can't mine, and means "install the extension, back up a seed, buy gas, connect to a dApp" before you do anything — NADO is just a URL. Open it and you're already a full participant: generate a post-quantum wallet, mine, send/receive, register a human-readable alias, and browse the chain, all from one link on any phone or laptop.

  • A real execution layer, already live. Beyond payments, NADO runs a field-native zkVM whose state is settled to L1 under STARK proofs (objective, stake-backed finality — L1 verifies a Merkle proof against a bonded-quorum-settled root, not a re-execution). On it today: ~20 provably-fair on-chain games (dice, roulette, poker/hold'em, blackjack, mines, an original settlers-genre strategy game, an auto-battler, a deck-builder, tamagotchi-style NFT pets, and more — each with its own subdomain), shielded transfers (a private payments pool), and end-to-end-encrypted on-chain messaging (ML-KEM-768) — so you can DM any address with no off-chain server. Randomness for games is pinned to future block hashes (nobody can rig a roll); hidden information (hole cards, fog-of-war) uses commit-reveal. Games self-serve from the wallet's background signer — no per-move wallet round-trip.

  • Everyone mining earns — a presence dividend, not a lottery. Winner-take-all blocks mean most miners see nothing for long stretches. NADO redistributes most of the open lane's block reward to everyone present, weighted by how steadily they show up — a steady stream instead of a rare jackpot. It accrues off-chain while you mine (so a million miners cost the chain nothing and there's no dust bloat), and you sweep it into your spendable balance with one Collect tap — claimed trust-minimised against a bonded-quorum-settled state root. Many people getting a little, continuously: what an open, populace-scale chain should actually feel like. (Design + mechanism: doc/presence-dividend.md.)

  • Mine from your pocket — and forever if you keep it open. Presence is a PoSW lease: one ~1 s proof buys ~a day of eligibility, so a locked, asleep phone keeps mining on its own — no relay, no per-epoch traffic — and the wallet shows the exact "mining while locked" countdown. Leave the page open and mining never stops: it auto-renews the proof just before it lapses, auto-bonds your rewards if you want, and auto-resumes across a browser refresh — so direct mining runs indefinitely with zero babysitting. Open the link once and walk away.

Share the link, and the barrier to entry collapses. Because the whole experience is one page with no install step, onboarding is sending a link. Drop it in a school group chat and whoever opens it is instantly mining — and each of them shares it again. No app store, no wallet setup, no gas to buy first: the distance between "hears about NADO" and "is mining and transacting on NADO" is a single tap. Lower the barrier to entry to a shared URL and let it spread — one classroom becomes the whole school. That is the growth thesis.

  • Phone-mineable. Block production is one hash per slot over a public beacon, not a race — a phone competes on equal terms with a datacenter. Winners are credited by address, so a phone can win a block while its tab is closed and a relay assembles the block on its behalf.
  • Fair launch, no premine. Genesis mints zero coins (TREASURY_GENESIS = 0). Every coin in existence was minted as a block reward. A flat base subsidy lets a brand-new, zero-coin miner earn spendable coins from block 1.
  • Governed, self-burning treasury. The 10% treasury is a keyless reserved account — no founder key, no multisig — spent only by a 2/3 bonded-stake vote in the wallet's Quorum tab, and any idle balance is burned each period. Emission holders would get anyway is forced into the ecosystem by their own vote, or destroyed — never hoarded (doc/treasury.md). Even the maintainer's reward is a votable, revocable quorum grant (guideline ~1% of treasury inflow) — not a hard-coded founder cut.
  • Consensus anti-Sybil registration. Registration is a non-parallelizable sequential PoSW whose difficulty scales with recent registration volume (enforced in validation off the finalized anchor epoch), so an identity flood gets progressively more expensive while a normal network stays at 1× (doc/registration-difficulty.md).
  • Two-lane "diligence" mining. A free OPEN lane anyone can win with no coins (capped at ~30% of blocks, a population-independent Sybil ceiling) plus a BONDED lane won with refundable, whale-capped stake. Bonding is optional and only boosts the bonded lane — never required.
  • Post-quantum signatures. ML-DSA-44 (NIST FIPS 204 / Dilithium) via pure-Python dilithium-py — no native build, in keeping with the lightweight goal. Cross-validated against the browser's @noble/post-quantum so a phone and a full node verify each other.
  • Lightweight & reproducible. Consensus hashing is over canonical JSON, so a browser client reproduces every address, transaction id, and verification byte-for-byte. State is a single memory-mapped key-value store; block bodies are compact zstd-compressed blobs.
  • First-party clients. A browser/mobile NADO Interface that is also a full wallet, a PySide6 desktop wallet, and browsable explorer endpoints on every node.

How mining works

Time is divided into epochs of EPOCH_LENGTH = 60 slots, each keyed by a per-epoch randomness beacon. For each slot the protocol deterministically draws exactly one producer.

Draw, not race

For a given slot the winner is a single computation over the public beacon:

draw   = int( blake2b([beacon, slot]) ) % total_weight
winner = the address whose cumulative-weight band contains `draw`
         (walking eligible addresses in canonical sorted order)

There is no multi-attempt hash race and no nonce grinding — one hash decides each slot. Faster hashing hardware therefore confers no advantage, and any full node or browser client reproduces the same winner from public chain state. Because the winner is chosen by address, an offline phone can win a slot and a relay can build and broadcast the crediting block for it.

Two lanes per epoch

Each epoch's 60 slots are split by a beacon-keyed permutation of slot indices into two lanes:

  • OPEN laneK_OPEN = 18 slots (~30%, OPEN_BPS = 3000), winnable by any registered, present identity for zero coins.
  • BONDED lane — the remaining 48 slots, won in proportion to locked, refundable stake.

The split is over slot indices, not per-identity weight, so there are always exactly K_OPEN open slots no matter how many identities register. A zero-capital botnet of a million identities still cannot win more than OPEN_BPS (30%) of blocks. This population-independent structural ceiling — not a puzzle difficulty or an economic cost — is NADO's central Sybil defense. (Empty-lane policy is one-directional and fail-closed: an empty open slot falls back to the bonded lane, but an empty bonded slot is skipped, never the reverse, so the free lane can never absorb bonded slots.)

The OPEN lane (free)

  1. Register by computing a sequential Proof-of-Work (PoSW) — a non-parallelizable hash chain (POSW_T steps, ~1 s in-browser, fee-exempt, post-quantum: it assumes only blake2b — no trusted setup, no elliptic curve, nothing Shor-breakable). Unlike the old parallelizable hashcash, a GPU/ASIC can't mint identities in bulk, and the proof is validated by every node in consensus (validate_transaction, the block-validation path) — not just the relay you connect to, so a bogus registration is rejected network-wide. Registration is a renewable presence lease (POSW_LEASE_EPOCHS, ≈ 1 day): to stay in the open lane you renew with a fresh PoSW, turning "pay once, farm forever" into "pay continuously per identity." The structural ~30 % lane cap is still the hard Sybil bound; the PoSW lease prices identity creation and upkeep in real sequential time on top. The recert is the single presence signal — there is no separate heartbeat. You're eligible iff you have a recert within POSW_LEASE_EPOCHS, so AFK mining is trivial: one ~1 s PoSW buys a full lease of eligibility, locked phone or not — no relay, no pre-signed heartbeats, no per-epoch traffic. The miner auto-renews at ~80 % of the lease; kept open, it mines forever.

Why no separate per-epoch heartbeat? An earlier design had one. But once the lease covers the whole ~1-day AFK window (and can be pre-signed), a per-epoch heartbeat is co-terminal with the lease and carries no information the recert doesn't — redundant. Collapsing to one signal is strictly simpler: the recert prices the identity and marks presence. The ~30 % lane cap stays the hard Sybil bound regardless.

Open-lane selection weight is capital-free: a flat floor (OPEN_BASE_FLOOR = 2) every present identity always gets, plus a diligence ramp to OPEN_FID_BONUS = 8 over FIDELITY_CAP = 30 consecutive recerts (overall range 2..10). Fidelity is continuity over recerts (apply_register, revert-symmetric): a continuous recert adds a step, a lapse resets the streak — so a rotated/churned identity can't keep a ramp it stopped paying for. The single most effective thing you can do is stay present. Mine to one address — splitting across addresses gains nothing, and onboarding many addresses from one machine is throttled (below).

Progressive IP-diversity onboarding cap. Registering (onboarding) new OPEN-lane addresses is rate-limited per source IP by subnet proximity: a new address's "crowding cost" is full for a same-exact-IP peer and halves for each broader shared prefix (same /24 = ½, /16 = ¼, /8 = ⅛), unrelated networks cost nothing (IPv4 /32·/24·/16·/8; IPv6 /128·/64·/48·/32). So a datacenter /24 gets one bounded shared budget while distinct networks aren't penalised — stopping "one box scripts 10 000 miners" at the entry point. This is relay admission control, not consensus (an IP can't be a consensus input without forking); the hard Sybil bound is still the structural ~30 % lane cap. Budget is max_registrations_per_ip (default 64/hr, NADO_MAX_REG_PER_IP, 0 = off).

The BONDED lane (optional stake)

A bond transaction moves spendable balance into a non-spendable bonded column; an unbond/withdraw pair moves it back out after a timelock (see below). Bonded selection weight is min(bonded, BOND_CAP) // B_MIN, capped at MAX_SHARES = 100:

  • Split-neutral — weight depends only on total bonded capital, so sharding across many addresses gains nothing.
  • Whale-capped — a single identity tops out at BOND_CAP = 1,000 NADO (B_MIN = 10 NADO per share), so no whale can monopolise the lane. The bond is refundable — you keep your coins.

Bonded lane + FFG finality — now active. At the 10-NADO entry the bonded registry is populated and blocks began producing on the bonded lane the moment B_MIN dropped. At the old 1,000-NADO entry nobody on a fair launch could grind enough to bond, so the registry sat empty and every block fell back to the zero-stake OPEN lane — no economic weight behind blocks. Now each lane carries distinct security: the OPEN lane is Sybil-capped presence, the BONDED lane is stake-weighted capital that both draws its share of producers and backs finality. A validator that signs two blocks at the same height+parent — or two conflicting FFG attestations for one epoch — is slashable via a portable proof (below), and FFG (Casper-style stake-attested epoch checkpoints, justifying at a strict >2/3 quorum) now enforces finality: a justified→finalized checkpoint is folded into the persisted rollback floor, so a >2/3-attested checkpoint is objectively un-reorgable (remote sync can't adopt a conflicting heavier chain) — it is no longer merely observed. Honest scope: FFG does not speed up confirmations — confirmation finality is still the depth-based floor at tip − FINALITY_DEPTH (~12 blocks), and FFG (epoch-granular, 60 blocks) normally trails that floor. FFG is the objective, accountable, long-range finality layer on top of the fast subjective depth floor, never a latency reduction, and it can never stall the chain: the quorum denominator is the active bonded set (validators that attested within INACTIVITY_WINDOW = 3 epochs), so a bonded validator that goes dark is leaked from the finality quorum (its vote lapses, its bond untouched) and a live attesting majority can always finalize.

Bonded mining is passive — no work, no need to be online. Once you hold enough to bond (B_MIN = 10 NADO), you can stop actively mining: the bonded lane is staking, not proof-of-work. There is no PoW/PoSW to compute, no periodic recert, and no requirement to keep the app or a node open — the beacon draws you in proportion to your stake, and because winners are credited by address, a relay builds your winning block even while you're offline. With auto-bond on, rewards compound straight back into stake, so it grows hands-free. Two honest caveats: (1) a freshly bonded stake ramps to full selection weight over BOND_RAMP_EPOCHS (~30 epochs) — an automatic anti-sudden-whale delay, no action needed — after which it earns at full rate; (2) your share is competitive (proportional to your slice of total bonded stake), and this lane is rich-get-richer by design (stake = yield, as in any Proof-of-Stake). The capital-free path — the OPEN lane + presence dividend — is the counterweight for anyone without coins, and it's the part that actually requires (light, phone-doable) mining.

Unbond is now timelocked (enforced). unbond is a release request, not an instant refund: the stake stays in the bonded column — still slashable — and a maturity block release_block = current + BOND_UNLOCK_DELAY (1440) is recorded. A separate fee-exempt withdraw transaction moves the matured amount to spendable balance only at/after release_block. Keeping the stake bonded through the delay is what keeps a caught equivocator's stake slashable while the unbond is in flight. One unbond may be pending at a time.


Economics

protocol.py is the economic source of truth. All on-chain amounts are integers in raw units, where 1 NADO = DENOMINATION = 10,000,000,000 raw (the smallest unit is 0.0000000001 NADO).

  • No premine. Genesis mints zero coins; the chain bootstraps purely through the open mining lane.
  • Per-block reward = a FLAT base subsidy scaled by bondingreward = BASE_SUBSIDY (0.1 NADO) · m(r). No fee-weighted term and no ceiling (the old REWARD_CAP is removed): fees are destroyed, so minting more when fees rise would only soften the deflation. Since m(r) ≤ 1, 0.1 NADO is the max emission per block (~1,440 NADO/day at the 6 s block_time), and m·BASE ≈ 0.0166 the min (perpetual tail, ~87,000 NADO/yr forever). Emission is per-block, so block_time (default 6 s) is the emission-rate lever.
  • Bond-elastic emission → super hard money (doc/bond-elastic-emission.md). m(r) = 0.15 + 0.85·e^(−4r) (tuned), where r is the bonded ratio (bonded ÷ total supply): the more the network locks up, the less it mints. Combined with fee destruction this makes NADO net-deflationary under real usage, while the perpetual tail (m never reaches 0) means block production is always rewarded — no hard cap, no security cliff (Monero's reasoning). It self-limits: the open lane siphons OPEN_BPS=30 %, so bonding past ~70 % is real-negative and the ratio settles ~40 %. m(r) is a hardcoded integer table in protocol.py (never a runtime float — a last-ULP difference could fork consensus), read from committed parent state exactly like cumulative_weight.
  • 90 / 10 split. The producer keeps 90 %; 10 % accrues to the treasury (TREASURY_BPS = 1000). The treasury is a reserved, keyless treasury account (no private key exists for it) that starts empty and fills only from this per-block cut.
  • Quorum-governed, self-burning treasury (doc/treasury.md). The treasury is spent only by a 2/3 bonded-stake vote — no founder key, no multisig; the bonded lane is the multisig, reusing the same settlement_justified quorum as finality. A treasury_spend proposal is voted with fee-bearing treasury_votes (each approval's weight snapshotted at vote time; newly-bonded stake must age before it counts), capped at TREASURY_MAX_SPEND_BPS (25 %) of the balance per proposal, and paid by a treasury_execute once the quorum is met. Idle treasury above a floor is burned every TREASURY_SPEND_PERIOD (TREASURY_BURN_BPS = 1 %/period), so emission that holders would receive anyway is forced into the ecosystem by their own vote — or destroyed, never hoarded. Stakers propose and vote from the wallet's Quorum tab.
  • Fees are destroyed, not paid to producers — that is what drives the elastic reward (it is a fee mechanic, not a "burn"; the old burn-to-bribe mechanic was removed entirely). A deterministic floor MIN_TX_FEE = 1000 raw applies to ordinary transfers and bond; register, unbond, and withdraw are fee-exempt (they move no coins out — unbond/withdraw only retime the sender's own stake).
  • No free→capital faucet. Open-lane presence can never mint bonded stake; the only path from free to capital is the block subsidy an open miner actually earns — itself capped at OPEN_BPS (30 %).

Quickstart — run a node

NADO runs on Python 3.10+. The entrypoint is nado.py; the node serves its API and web UI on port 9173.

Install

This one line is the ONLY official, supported way to install or run a NADO node.

There is no manual setup, no hand-rolled venv, no pip install -r requirements.txt, no nohup, no Docker image. Those paths are unsupported — a node installed any other way falls off the update path (/update refuses a directory that is not a git checkout), drifts out of consensus as protocol changes ship, and will not be helped. If you already run a node that was installed some other way, run this command on it: it repairs the install in place.

curl -sSfL https://raw.githubusercontent.com/hclivess/nado/main/scripts/install.sh | sudo bash -s -- --service --user nado

No checkout needed — piped standalone, the script installs git if missing, clones the repo itself, builds the venv, installs the node dependencies, and registers a systemd service that boots on start and restarts on failure.

--user nado runs the node as a dedicated non-root system account instead of root: the checkout and chain data live at /srv/nado-home/nado, the units get a hardening block (NoNewPrivileges, ProtectSystem=strict, no capabilities), and a root-owned restart bridge keeps self-update working end to end. An existing root install is migrated in place — services stopped cleanly, directory moved, a compatibility symlink left at the old path — and later re-runs auto-adopt the account, so nothing ever quietly hands the node back to root. Omit the flag only if you have a reason to keep the node running as the invoking user.

It is also the universal upgrade / repair command. Re-running is always safe (idempotent). If it finds a node laid down by an older, git-less installer it converts that directory into a real checkout in place and fast-forwards it to the latest code — your private/ keys and blocks/ / index/ chain data are gitignored, so nothing it does can touch them. Any node, however it was first installed, gets current by running this again.

Check the path first. If your node lives somewhere non-standard, pass --dir /path/to/your/nado — otherwise you install a second node and the original stays stale. (With --user nado the standard locations are found automatically: an install at ~/nado is migrated to /srv/nado-home/nado and a symlink keeps the old path working.)

Flags: --user <name> runs everything as a dedicated non-root account (recommended, see above); --exec also runs the execution / shielded-pool node on :9273; --wallet adds the desktop-wallet deps; --auto-bond <pct> auto-compounds mined rewards (see below); --home <dir> keeps chain data under <dir>/nado instead of ~/nado. Run scripts/install.sh --help for all options.

Once running, open http://127.0.0.1:9173 for the node's web interface and JSON endpoints. To join a network, announce your node to a peer:

http://127.0.0.1:9173/announce_peer?ip=<peer-ip>

For public reachability and rewards, forward port 9173. Always stop the node cleanly — systemctl stop nado, or http://127.0.0.1:9173/terminate — never kill -9, which can corrupt the database. To wipe local data and resync from scratch, stop the service and run nado_venv/bin/python purge.py from the checkout: it clears the chain state (blocks/, index/, logs/) under your data home while keeping your keys and peers; on the next start the node rebuilds genesis (re-seeding the public bootstrap) and resyncs from the network.

Operate the service

The install above already runs the node unattended — it mines on its own, starts on boot, restarts on crash, and needs no open terminal. To manage it:

systemctl status nado        # health
journalctl -u nado -f        # live logs
systemctl restart nado       # pick up new code after a manual pull
systemctl stop nado          # clean shutdown (never kill -9 — it can corrupt the DB)

Common variants (re-run the one-liner with these appended, or sudo scripts/install.sh from the checkout):

--service --user nado               # run as a dedicated non-root account (recommended; migrates in place)
--service --auto-bond 25            # auto-bond 25% of mined rewards
--service --home /srv/nado-data     # chain data in /srv/nado-data/nado
--service --exec                    # also run the execution / shielded-pool node on :9273

Auto-bond — compound mined rewards into stake, hands-free

A miner routes a percentage of newly-mined rewards straight into bonded stake, auto-compounding their weight in the bonded lane without any manual bond transactions. It is on by default at AUTO_BOND_DEFAULT_PERCENT = 80% (a fresh node / browser / wallet with no saved preference joins the bonded lane hands-free) and fully overridable — set 0 to keep all rewards spendable; an explicit 0 is remembered and never reverts to the default. It is throttled to at most one bond per epoch, only fires once the accrued amount clears a small dust floor (so each bond dwarfs its tiny fee), and stops automatically at BOND_CAP (1,000 NADO — bonding past it buys no extra selection weight, so it never needlessly freezes coins). It is available in all three clients:

  • Node (unattended): set auto_bond_percent in private/config.json, or the NADO_AUTO_BOND_PERCENT environment variable (which the --service installer wires into the unit). The node bonds the configured share of its own block rewards each epoch — ideal for a headless miner.
  • Browser interface: the Stake tab has an "Auto-bond mining rewards" field; while the tab is mining it compounds that share of your rewards (persisted in the browser).
  • Desktop wallet: the Mining tab has an "Auto-bond mining rewards" control (persisted in ~/.nado_wallet/wallet.json).

It is a client/operator convenience and is never validated on-chain — every auto-bond is just an ordinary signed bond transaction.

Keeping a node up to date

NADO nodes update themselves. Two mechanisms:

  • Integrated updater (/update). Any node running current code exposes a harmless /update endpoint — hitting it makes the node fast-forward-pull the official main (pinned to github.com/hclivess/nado, ff-only, never an arbitrary branch), then relaunch its services. It also waves the request to its peers, checks on its own every 15 minutes, and — the part that makes fleet versioning near-real-time — reacts to peer hints: the moment any peer's status advertises a commit a node does not recognize, that node checks origin immediately. So the first node to pick up a push (timer or nudge) pulls the entire mesh current within seconds; a hostile peer controls only when a node glances at the canonical repo, never what it pulls. A node's latest applied commit is visible in /status (running_commit).

  • Self-healing updatability (startup check + auto-repair). A node that cannot update is not merely stale — since consensus changes ship with no backward compatibility, it eventually diverges and forks. So at boot (and again daily) the node checks whether it could update at all: is git installed, is this a real checkout, does origin point at the official repo, and is there a systemd unit so an applied update actually restarts the process. If any of those fail it logs the defect loudly and repairs itself by running its own scripts/install.sh --service — the local installer that ships with the node, never a download. The verdict is published in /status as update_capable, so a node that will drift is visible from the outside instead of discoverable only by calling /update. An unreachable origin is a warning, never a failure — treating a GitHub outage as fatal would take the whole fleet down at once.

    Why this exists: a fleet sweep found 21 of 25 peers reporting running_commit: null — installed by hand or by an old installer, so they could never self-update and could not even be version-checked. Three more had current repos but stale running processes, because nothing existed to restart them. Note the limit: a node already in that state cannot receive this fix (that is the defect). It stops the fleet rotting into it again; the stranded ones still need their operator to run the one-liner once.

  • The installer one-liner (above) is the fallback for a node too old to have /update, or one laid down by an old git-less installer: re-running it installs git, converts the directory to a real checkout, and fast-forwards it — keys and chain data preserved. Old nodes with no /update and no git cannot be reached remotely; their operator must run this one line once to rejoin the update path.

  • Fork resolution — one measurement, one decision. Recovery used to be inferred from chain weights, donor behaviour and peer benching across five overlapping paths. Those mis-fired together on 2026-07-20: a node that was simply ~500 blocks behind on the correct chain read that as "forked", rolled back into a snapshot with no history beneath it, and looped for 40+ minutes. The inference is gone, replaced by a single measured number — the highest height where our block hash equals the majority's:

    measurement state action
    ancestor == our tip BEHIND ordinary forward sync — never a rollback
    ancestor ≥ finalized REORG roll back to the ancestor, then sync
    ancestor < finalized DEAD_FORK finality forbids the rollback → purge chain data + resync
    no majority / peers silent UNKNOWN do nothing

    Hash equality at a height is a fact, not a heuristic, and the binary search costs ~log₂(depth) probes. It asks peers directly, so it cannot be blinded by a collapsed peer set or a wrong bench — both of which happened. DEAD_FORK is the case that previously required a human: it is now automatic, but deliberately hard to trigger (frozen tip for 15 min, two independent probes must agree, any peer agreeing with us vetoes it), because the remedy destroys chain-derived data. private/ is never touched.

Genesis rerolls. Because consensus changes ship strictly, with no backward compatibility, the alphanet occasionally rerolls to a fresh genesis (balances carried forward from the prior chain). This is signalled in the code by a bumped CHAIN_GENERATION: on the next update a current node automatically wipes its old chain data and boots the new genesis — no manual step. (Keys in private/ are never touched.) A node that has fallen too far behind to auto-detect the reroll clears its old state with scripts/purge_resync.sh and rejoins.

Two more unattended behaviors round out a hands-free headless node (both best-effort, once per epoch, never disrupting consensus):

  • Auto-collect the presence dividendon by default (auto_collect_dividend / NADO_AUTO_COLLECT): sweeps your accrued dividend into a provable collection. Skipped unless the node is an open-lane member (a bonded-only node accrues none, so it never burns a wasted fee).
  • Auto-register the open laneopt-in (auto_register / NADO_AUTO_REGISTER=1): keeps the PoSW presence lease alive (registers when absent, renews inside the lease tail), so a server can mine the free lane 24/7 unattended. Off by default so a headless node never silently joins — and Sybil-loads — the open lane. Full reference: doc/cli.md.

Local multi-node testnet

A self-contained harness spins up N nodes on 127.0.0.x loopback IPs, meshes them, and reports whether they converge and produce blocks:

python scripts/testnet/run_testnet.py [num_nodes=3] [run_seconds=240]

It uses throwaway temp dirs and sets NADO_TESTNET=1 per child (relaxes the SSRF guard for loopback). Never set NADO_TESTNET on a real node.

Ubuntu notes

For a production-style node, raise the open-file limit (/etc/security/limits.conf: root soft/hard nofile 65535, fs.file-max = 100000 via sysctl). The installer handles Python and the venv itself; update with /update or by re-running the one-liner — never by hand-pulling, which leaves the running process on the old code until you systemctl restart nado.

Windows

Not a supported node platform — the installer targets Linux with systemd. To mine from Windows, open any running node's light-miner in a browser (see Mine from a phone); it needs no install at all. For a full node, use a Linux VM or WSL2 and run the one-liner inside it.


Mine from a phone

Open the running node's light-miner in any browser:

http://<node-ip>:9173/static/interface.html

The NADO Interface (static/interface.html + static/interface.js) is also a full wallet: it generates or imports a key, computes the sequential registration PoSW in pure JS (byte-identical to the node's verifier), registers/renews its PoSW lease against the node (no heartbeats), and keeps winning blocks even while the phone is locked — presence is a ~1-day PoSW lease (no per-epoch traffic), and a relay assembles the crediting block. It can send/receive with QR payment links and #pay deep links, bond/unbond, browse the chain, and runs in 16 languages (browser-locale default) — all from a phone. It also shows how busy each lane is right now — live OPEN and BONDED participant counts (from /mining_status open_registry_size / bonded_registry_size) alongside your own bonded shares — so a miner can see the field it is competing against. Crypto is vendored (static/vendor/nado-crypto.js: blake2b + ML-DSA-44) so it works offline, and an in-page self-test asserts byte-equality of its canonical encoding against the live repo on boot.

The NADO Interface keeps its private key in browser localStorage in plaintext (disclosed in the UI). Treat it like a hot wallet.

Clients

  • Command line (scripts/nado_cli.py) — every interface operation from the terminal, signed by your local keys.dat: info, send, register (computes the sequential PoSW), bond/unbond, alias, propose/vote/execute (treasury governance), collect (presence dividend), bridge-deposit. It builds the same signed transaction the browser does and POSTs it to the node's existing /submit_transaction — no new signing endpoint, no new trust surface. Full reference: doc/cli.md.
  • Browser / mobile NADO Interface (wallet)static/interface.html (see above).
  • Desktop walletpython3.10 pyside_wallet.py (PySide6): overview, send, bond/unbond, register & mine, expected-time-to-mine, an auto-bond control (compound a % of mined rewards into stake), and a live selection-lane visualization. PySide6 is wallet-only; the node itself does not need it.
  • Block explorer — folded into the NADO Interface as an Explore tab (static/interface.html + static/interface.js): search by address / alias / block number / block hash / txid, browse recent blocks, and see live network + mining-lane stats — all reading the node's own public JSON API in the browser. The node serves the wallet/explorer at /. (The raw JSON endpoints — /get_account, /get_block, /get_transaction, /get_supply, /status, /resolve_alias, … with readable=true — remain available directly.)

Security

NADO's security rests on the two-lane selection design plus anti-DoS/anti-Sybil hygiene. The split between implemented and planned below is the difference between testnet-safe and mainnet-safe — read it before running anything of value.

Security audit (all exploitable findings fixed)

A deep adversarial audit was run across six surfaces (fork-choice/51%/rollback/finality; Sybil/two-lane/selection; slashing/equivocation/unbond; RANDAO/FFG/beacon; tx-validation/pubkey-once; KV atomicity/eclipse/DoS), against a chain that was testnet-stage alpha with no value at stake. Every exploitable finding it surfaced is now fixed and unit-tested — full writeup in doc/security-audit.md. In brief:

  • In-block duplicate reserved-tx bugs (CRITICAL/HIGH). Uniqueness was checked only against parent state and block assembly did no dedup, so duplicates of a reserved tx in one block could drain a single unbond via repeated withdraws (slash-escape / chain-halt), over-burn on a duplicate slash (which two honest reporters trigger organically), or collapse duplicate heartbeat/reveal rows so a reorg over-deletes the shared row → registry/beacon desync fork. Fixed by per-reserved-tx in-block uniqueness (reserved_uniqueness_key + dedupe_reserved in assembly + assert_unique_reserved in verify_block), plus cross-block heartbeat/reveal-secret guards.
  • Same-length fork-choice wedge (CRITICAL, liveness). Two equal-weight honest tips at one height could wedge forever because the switch was strictly-greater-weight only. Fixed by the deterministic lowest-hash tie-break: every node now switches to the global-best tip by (weight DESC, hash ASC), so they converge.
  • quick_sync validation bypass (HIGH). Old-block sync skipped signature + spending checks. verify_block now always runs validate_transactions_in_block — the bypass is gone.
  • Unauthenticated advertised-weight DoS (HIGH). A single Sybil peer advertising a huge latest_block_weight forced honest nodes into emergency rollbacks. Fixed by a bounded, auto-clearing rejected_tips exclusion so a bogus weight can't loop a node.
  • Plus: per-IP rate limits on the heavy unauthenticated read endpoints (/mining_status, /get_transactions_of_account, /get_blocks_after//get_blocks_before); an honest-signer guard (a node only ever signs a strictly higher height, so an honest re-signer can't be slashed for its own reorg); the per-/16 subnet cap now also gates the disk-reload path; and a dead /get_blocks_before was fixed.

The audit also confirmed the safety core sound with no change needed: the atomic incorporate/rollback window, the monotonic finality floor, equivocation-proof unforgeability (and the no-innocent-victim address binding), the detached-signature-outside-the-hash property, and pubkey-once key→sender binding. The remaining items are documented residuals / future hardening (below and in doc/security-audit.md), none of which is a theft or fork vector in the current code.

Implemented (live in production and verification paths)

  • Structural Sybil bound — the open lane is exactly K_OPEN slots/epoch regardless of identity count, so a free botnet can never exceed 30 % of blocks. One-directional fail-closed empty-lane policy preserves the ceiling.
  • Fail-closed deterministic authorshipvalidate_block_producer, called inside verify_block before incorporation, recomputes the two-lane winner from parent state + the epoch beacon and rejects any block whose producer isn't that winner (block integrity is by deterministic recomputation, optionally authenticated by the detached winner signature below).
  • Objective stake-weighted heaviest-chain fork-choice — the canonical tip is argmax cumulative_weight among tips whose chain contains the node's finalized block, switching only on strictly-greater weight (lowest-hash tie-break). Peer IPs, trust, and uptime carry exactly zero weight, so a Sybil fleet of zero-bond IPs cannot reorg honest nodes. Replaces the old peer-IP plurality fork-choice.
  • Grind-proof cumulative_weight header — committed inside the block-hash preimage as parent.cumulative_weight + total_bonded_shares(as-of-parent) + 1. It is the total bonded registry weight (not the slot winner's share), so it is beacon-independent: a proposer can't grind the beacon to inflate fork weight. Recomputed in rebuild_block and verified as-of-parent. The +1 height term guarantees the weight is strictly increasing even when the bonded registry is empty: without it, a no-stake network advertises one frozen weight forever, fork-choice degenerates to the lowest-hash tie-break, and a stalled node whose tip hash happens to sort low considers itself canonical and never resyncs (observed live 2026-07-05; fixed at the relaunch-3 genesis). Pure longest-chain while nothing is bonded, stake-dominated the moment anything is (shares ≫ 1).
  • Advertised-tip (weight) DoS hardening — a peer's advertised latest_block_weight is only ever a hint: acting on it means fetching the blocks, which verify_block re-derives and enforces. A tip that was advertised heavier but could not be backed by a valid chain — the peer serves nothing, garbage, or forged blocks — is excluded (rejected_tips, bounded + auto-cleared ~30 s so a real tip that merely blipped is retried). The exclusion is honoured everywhere it matters: the emergency sync loop rejects the tip on every failure path and re-evaluates being-behind each pass, and the caught-up production gate skips rejected advertisements — so two forked-away or lying clients can cost the network ~one failed sync per 30 s, never a production stall or an emergency-mode wedge. Peer admission is additionally gated on an exact chain_id match in /status (a missing field counts as a mismatch), so nodes from a different chain/relaunch never enter the consensus pools at all — a foreign chain's frozen weight can't trip the caught-up gate.
  • Enforced finality floor — a block at height H finalizes everything at/below H - FINALITY_DEPTH (FINALITY_DEPTH = 12); rollback refuses to cross the persisted, monotonic finalized height (raises FinalityViolation). The ordering max_rollbacks (10) < FINALITY_DEPTH (12) < EPOCH_LENGTH (60) means honest reorgs never hit the floor while a long-range reorg is capped below one epoch.
  • Corroborated finality + escalated re-anchor (partition-wedge recovery) — the depth floor now advances only while the peer-majority tip lies on our canonical chain (_depth_floor_corroborated); a node that briefly loses the majority (e.g. under load) and keeps producing on its own branch no longer self-finalizes that minority fork, so partition forks stay reorgable and heal through the normal weight-based reorg (a Sybil can only withhold corroboration — delay the floor, the safe direction). And if a node is ever wedged anyway (its snapshot/finality floor stuck on a minority fork below the heaviest chain's snapshots), after REANCHOR_ESCALATE = 3 cooldown-spaced failed re-anchors it drops the snapshot-above-floor restriction and re-anchors to the strictly-heaviest chain (every tail block still re-verified). Observed live and fixed; both are node-local safety/fork-choice rules (they change only when a node advances its own floor, never block validity). Unit-tested (tests/test_wedge_recovery.py).
  • Fail-loud epoch beaconepoch_beacon chains from the hash of the first block of the previous epoch (a finalized, non-parent anchor), and now raises instead of silently substituting GENESIS_BEACON when the anchor is missing (a missing anchor means this node is under-synced).
  • Detached winner block signature — when the selected winner is online it attaches an optional ML-DSA signature outside the hash preimage (so it never affects the hash, weight, validity, or reward); verifiers reject a present-but-forged or wrong-signer signature. An offline winner's relay-built block is simply unsigned and still valid — "win-while-offline" is preserved.
  • Equivocation slashing (both proof types) — slashing punishes double-signing, and covers two distinct offences under one resolve_slash path: (1) block-authorship equivocation — two valid winner signatures over different blocks at the same height+parent; and (2) FFG-attestation equivocation — the same validator signs two conflicting attest transactions for one target_epoch (same epoch, different target_hash — a finality double-vote). Either forms a portable, unforgeable proof (only the key-holder can sign either message). A fee-exempt slash transaction carrying the proof burns SLASH_BOND_PENALTY (= B_MIN, one bonded share) of the offender's bonded stake. Anyone may report it (the proof is the anti-spam); it is replay-guarded to one slash per (offender, height) — attestation slashes are namespaced above real block heights so the two proof types never collide — revert-symmetric on rollback, and the coins are destroyed (the deterrent is the loss, not a bounty). Validation requires the offender still hold the penalty so the dock never floors. This punishes equivocation, not Sybil-ness — Sybil resistance is a separate mechanism (open lane capped at OPEN_BPS = 30% + PoSW cost; bonded lane split-neutral with 10-NADO-locked shares).
  • FFG stake-attested finality (enforced) — bonded validators emit one attest transaction per epoch for that epoch's checkpoint (its first block). A checkpoint justifies when attesting bonded shares strictly exceed >2/3 (FFG_NUM/FFG_DEN = 2/3) of the active quorum, and finalizes on two-consecutive-justified; on-chain UNIQUE(validator, epoch) prevents on-chain double-voting (and a cross-fork double-vote is now slashable — above). The finalized checkpoint is folded into the persisted rollback floor (finalized_height = max(prev, tip − FINALITY_DEPTH, ffg_finalized)), so a

    2/3-attested checkpoint is objectively un-reorgable — remote sync cannot adopt a heavier conflicting chain. Inactivity leak: the quorum denominator is active_shares — the selection shares of bonded validators that attested some checkpoint within the last INACTIVITY_WINDOW = 3 epochs — not all bonded stake. A validator that goes dark is leaked from the finality quorum (its vote lapses; its bond is untouched), so a live attesting majority always finalizes instead of being wedged by bonded-but-absent stake. This is what let ffg_finalized start advancing despite many bonded-but-non-attesting dust accounts. Honest scope: FFG is epoch-granular and normally trails the depth floor, so confirmation latency remains the depth floor (~12 blocks) — FFG is the objective/long-range finality layer on top of the fast subjective floor, never a speedup, and (being layered on the always-advancing floor) it can never stall the chain. Exposed at /status.ffg_finalized.

  • Commit-reveal RANDAO (voluntary participation) — bonded validators commit a secret's hash in epoch E−2 and reveal it in E−1's finalized window; epoch_beacon mixes the finalized prior-epoch anchor with the revealed secrets, so no single anchor-producer controls the beacon. With zero reveals it falls back to the anchor-only value (liveness). It keeps the anchor (non-recursive), so the beacon stays snapshot-safe and the reveals are immutable by the time the beacon is needed. Participation is voluntary (RANDAO_ENFORCED = False in protocol.py): every reveal that lands strengthens the beacon, but skipping the duty costs nothing and the bonded-lane draw runs over the full registry. The enforcement machinery (randao_eligible_bonded — no reveal for epoch E, no production rights in E — applied identically at production, relay rebuild, and verification) is implemented, unit-tested, and kept behind the flag; it was switched off because mandatory participation forces O(validators) commit+reveal txs every epoch and ties rewards to tx-inclusion latency, which scales poorly. Fork weight and the FFG/settlement quorums stay on the full registry in either mode, so withholding can't move fork-choice or stall finality. Both the node (maybe_randao) and the browser interface (for bonded wallets, while the tab is open) still contribute automatically.
  • Pubkey-once — the 1312-byte ML-DSA public_key is excluded from the txid and stored once in account state on an address's first tx, so later txs (notably every-epoch heartbeats) omit it; validators recover it from committed state. Store/clear is byte-identically revert-symmetric.
  • Reward recompute-and-enforce, registration-PoW enforcement, canonical in-block tx ordering (txid-sorted before hashing, so honest nodes selecting the same tx set produce an identical block hash).
  • Anti-DoS / eclipse throttles — per-IP sliding-window rate limits on /submit_transaction (30 req/60 s) and /announce_peer (10 req/60 s), plus the heavy unauthenticated read endpoints (/mining_status, /get_transactions_of_account, /get_blocks_after//get_blocks_before, added in the audit), a per-/16 peer-diversity cap (at most MAX_PEERS_PER_SUBNET = 4 peers per /16 — now enforced on the disk-reload path too, so one network can't fill a victim's peer view), a progressive per-range IP registration cap on OPEN-lane onboarding (crowding cost scales with subnet proximity, so a datacenter /24 gets one bounded budget — max_registrations_per_ip), a hard mempool cap (150,000), heartbeat-index GC, and an SSRF guard (check_ip rejects own-IP and all non-globally-routable addresses).

Lightly exercised (implemented + unit-tested, but not yet hardened on a live multi-node net)

The equivocation slashing (both proof types), FFG finality, and commit-reveal RANDAO above are wired and unit-tested for correctness, and FFG now finalizes live (ffg_finalized advances past 0 once the active bonded set attests two consecutive checkpoints). Their multi-node, adversarial, epoch-crossing behaviour is still only lightly exercised empirically: the core loop's ~6 s/block cadence makes exercising a full justify→finalize under contention and a complete commit→reveal cycle slow. Treat their cross-epoch adversarial dynamics as not-yet-battle-tested.

Planned (designed, NOT yet implemented — do not rely on these)

  • Broader eclipse hardening — beyond the per-/16 subnet cap and the /announce_peer rate-limit (both already live): ASN-level (vs /16) peer-diversity caps, pinned anchor outbound slots, a multi-seed bootstrap list (replacing the single genesis seed), and snapshot-bootstrap binding to a finalized signed checkpoint. These are post-launch items.

Honest statement of current limits

Objective fork-choice, enforced finality, equivocation slashing (block-authorship + FFG attestation), enforced FFG stake-attested finality, and the commit-reveal RANDAO make a zero-bond Sybil/IP reorg ineffective, bound the disagreement window below one epoch, and layer objective finality and a non-grindable beacon on top — a substantial hardening over the previous peer-count fork-choice. Beyond the lightly-exercised cross-epoch behaviour of FFG/RANDAO and the outstanding eclipse hardening above, the documented residuals from the audit (see doc/security-audit.md) — none a theft or fork vector — are:

  • RANDAO withholder penalty: CLOSED (2026-07-05). Reveal-for-the-epoch is now a hard eligibility condition for the bonded-lane draw (see above), so suppression costs the withholder its entire epoch of production. The residual is only the classic last-revealer bit: with m colluding withholders, up to 2^m beacon outcomes — each priced at m epochs of forfeited rewards, and defeated whenever ≥1 honest secret is revealed after the anchor.
  • FFG slashable-stake backing: CLOSED. FFG is no longer observational — the finalized checkpoint is enforced in the rollback floor (objectively un-reorgable), and attestation-equivocation slashing is live: a validator that signs two conflicting attestations for one epoch loses SLASH_BOND_PENALTY of bonded stake via the same slash path as block-authorship double-signing. On-chain double-voting is still blocked by the per-epoch UNIQUE(validator, epoch) marker; cross-fork double-voting is now punished rather than merely prevented.
  • The bonded MAX_SHARES cap is per-identity, not aggregate — sharding capital above BOND_CAP across addresses recovers full proportional weight. The bonded lane is capital-proportional by design; the cap only limits single-address variance, not aggregate stake.
  • Registration / fee-exempt state growthregister writes an account doc; idle-account GC is implemented (ops/gc_ops.py): long-lapsed empty docs and ancient recert rows are swept deterministically in-block at epoch boundaries (revert-safe, snapshot-root-identical on every node). Also bounded by the lane cap, per-IP rate limit, mempool cap, and the in-block one-register-per-sender dedup; idle-account GC is future work.
  • Fidelity is continuity over recertsapply_register adds a step for each continuous recert (gap ≤ the lease) and resets the streak on a lapse; it ramps the open bonus over FIDELITY_CAP recerts.
  • Snapshot bootstrap trusts an 80%-of-peers quorum with no hardcoded finalized checkpoint cross-check (weak-subjectivity); a pinned checkpoint is future eclipse hardening.

All mining/economic parameters are provisional and flagged simulate-before-lock-in in code. NADO remains a testnet-stage alpha, not open-value-mainnet-safe. (No hardfork concern: mainnet is not live.)


Cryptography & determinism

  • Signatures — ML-DSA-44 (FIPS 204, post-quantum) via dilithium-py. Keys are a 32-byte seed from which the 1312-byte public key and ~2420-byte signatures are deterministically regenerated. Consensus only ever checks verify(sig, pk, msg) == True, never signature-byte equality, so hedged signatures interoperate across implementations. Signatures authenticate transactions/heartbeats and are deliberately never the randomness source (a malleable signature would be grindable).
  • Addresses"ndo" + 42-hex public-key prefix + a 4-hex blake2b checksum (49 chars). The keyless reserved recipients {bond, unbond, withdraw, register, slash, attest, commit, reveal, alias} are valid as a recipient/target only, never as a sender.
  • Aliases — a human-readable name → owner address, so you can send to a short name instead of the 49-char ndo… address. Register / transfer / unregister are on-chain ops (reserved recipient alias); an ordinary transfer whose recipient is a registered alias credits the alias's current owner. See doc/aliases.md; the NADO Interface's Send field accepts an alias and its Receive tab manages them.
  • Multisig (opt-in) — native M-of-N shared accounts with no script language and no on-chain setup: the address is the policy — make_address(blake2b(["nado-msig-v1", M, members])), the way a P2SH hash commits a script. Fund it like any address; a spend carries the descriptor in the signed body plus a list of member signatures over the txid, so co-signers sign independently in any order and exchange the proposal by any channel (the Interface's Multisig tab and the CLI's msig-propose / msig-sign / msig-submit implement the relay). Multisig accounts are payment accounts only — they can't bond, mine or vote, so every one-key-one-identity validator assumption stays intact. ops/multisig_ops.py.
  • Hashing & serialization — BLAKE2b over canonical_bytes() (compact, sorted-key, ASCII JSON, float-free). Every consensus integer is a raw integer, so a browser reproduces identical bytes with BigInt-aware serialization. Transaction ids and blocks bind CHAIN_ID = "alphanet-1", blocking cross-chain / pre-relaunch replay.
  • Wire — transactions submit over HTTP POST + msgpack (an ML-DSA-44 tx is too large for a GET URL); msgpack is wire/transport only and never the hashed preimage.

Storage

State lives in a single schemaless, memory-mapped, ACID key-value store (LMDB)ops/kv_ops.py, which replaced the prior SQLite index. Account/state records are schemaless msgpack documents (no columns, no DDL), so adding a field needs no migration. A whole block's mutations (account docs, tx index, block index, totals, heartbeats) commit in one write transaction, so a crash leaves a block either fully applied or not at all, and replay is idempotent. Block bodies are zstd(codec) records in append-only segment files under blocks/ (ops/segment_store.py — ~300 files/year instead of one per block; crc-guarded, fsynced before their LMDB locator commits, torn tails repaired at startup), and consensus hashing stays canonical JSON — neither is touched by the index.

Archive vs rolling nodes (opt-in history pruning). By default a node is an archive node (config.archive = true) that keeps every block body forever. Set archive = false (or NADO_ARCHIVE=0) to run a rolling/pruned node that drops finalized block bodies older than HISTORY_RETENTION_BLOCKS (default 100 800 ≈ 1 week) while always keeping state and the number↔hash indexes — so it stays a full validator and still serves the beacon/FFG lookbacks, with bounded disk. Retention is floored internally at REWARD_WINDOW + FINALITY_DEPTH so pruning can never corrupt the reward calc or a legal rollback. This keeps phones viable under adoption; see doc/rolling-mode-and-da.md.

Private key storage

Keys are post-quantum ML-DSA-44 (FIPS 204); what is stored is the 32-byte seed. Your ndo… address shape is unchanged (49 chars).

  • Linux: ~/nado/private/keys.dat
  • Windows: C:\Users\<username>\nado\private

Learn more

  • Whitepaperdoc/whitepaper.md: the authoritative, accuracy-reviewed overview of the mechanism, with a full constants table and an explicit implemented-vs-planned split.
  • RoadmapROADMAP.md: where the app layer goes next. An honest gap analysis against the chains that actually generate app revenue (assets → AMM → launchpad → router → wallet swap → terminal), the on-ramp/fair-ordering/dev-surface tracks that run alongside it, and the rule that keeps it NADO-shaped: the protocol takes nothing, apps declare their fees on-chain, and the default sink is burn.
  • Consensus hardening plandoc/consensus-hardening-plan.md: the locked, ordered design for the remaining security milestones.
  • Storage designdoc/storage-kv-migration.md.
  • Execution-layer instructionsdoc/exec-instructions.md: every blob op (deploy/call/upgrade/lock with value escrow, bridge/dividend, emit, privacy) with params + how to submit and read. Contracts run on a STARK-provable zkVM (the only runtime), authored in zkasm (execnode/zkvmasm.py); every call is provable (doc/zk-execution-proofs.md). Settlement is currently a bonded-stake quorum (settlement_justified, the same >2/3 shape as finality); a validity-proof path exists as capability but is not yet the authoritative settlement rule (see the recursion note below). 15 games ship as live zkVM contracts — coin flip, dice, roulette, slots, mines, blackjack, tic-tac-toe, Connect Four, reversi, chess, farkle, parimutuel sports betting, battleship, tamagotchi-NFT pets, and multiplayer Texas hold'em with an on-chain 7-card hand evaluator — each an ordinary upgradable contract (opt-in immutability via lock) with no game-specific API.
  • STARK recursion — succinct, K→1, O(1) verifydoc/zk-recursion.md: the path to verifying an epoch of execution in constant time. Built + tested (off the L1 consensus path): a verifier-authoritative in-circuit STARK verifier over an algebraic hash (alghash2); T-independent ("succinct") verification (structured periodic columns — no per-proof O(T) work); a K→1 collapse (one recursion bundle re-verifies K chained segment proofs from their public parts alone); an in-circuit Fiat-Shamir transcript; and recursion depth (fold-of-folds) whose root verifies in ~0.2 s regardless of tree size — the O(1)-verification goal, realized. Honest status: the verify side is O(1) and the mechanisms are validated at small scale, but proving at the full execution-AIR scale is memory/throughput- bound in pure Python (the native Rust prover is the throughput prerequisite), and the pipeline is capability — not wired as the authoritative settlement verifier (the bonded quorum still governs). None of this touches L1 block validity.
  • Previous-network snapshotdoc/previous-network-snapshot.md: the prior NADO network's final account balances (2,801 holders), exported from its ledger index — the dev-fund premine excluded, in keeping with the no-premine relaunch.
  • Release notesRELEASE_NOTES.md.
  • Project site: https://nadochain.com

protocol.py and the ops/ modules are the source of truth; where an older companion doc disagrees, the code wins.

Related repositories


For developers

Design philosophy

New functionality should be driven by the existing routines/loops rather than instant invocation of functions — every function should have its place in the routine responsible for it. Functions should be small, independent, and named after the small task they perform; prefer returning values to mutating objects passed as arguments. Use the existing compounder for multi-target loops rather than synchronous loops.

How NADO is structured

  • Level IIInado.py runs all loops and governs API endpoints.
  • Level II — a central memory element, memserver.py, holds shared state accessed by the main loops (consensus_loop.py, core_loop.py, message_loop.py, peer_loop.py).
  • Level I*_ops.py modules (block_ops.py, account_ops.py, transaction_ops.py, mining_ops.py, kv_ops.py, peer_ops.py, …) hold minimal low-level operations.

Block production

A block is built with construct_block(), then produced via produce_block()verify_block() (with rebuild_block() to recompute hashes/weights for remotely received blocks) → incorporate_block(). There is ONE mempool (transaction_pool): a submitted tx is validated and enters it directly, and peers keep pools converged by set reconciliation — when advertised pool hashes differ, a node fetches the peer's txid list (/transaction_ids, ~64 B/tx) and downloads only the bodies it is actually missing (POST /transactions_by_id), never the whole pool.

Contributing

Fork the repository, make your changes, and open a merge request.

License

Copyright (C) 2022-2026 Jan Kučera (hclivess).

NADO is free software licensed under the GNU Affero General Public License v3.0 (see LICENSE). The AGPL's network clause (§13) means that anyone who runs a modified version of NADO as a network service must make their modified source available to its users — you may fork and build on it, but derivative networks must stay open. This replaces the project's earlier MIT license going forward; copies already distributed under MIT remain under their MIT grant.

About

NADO is a phone-mineable, fair-launch, post-quantum blockchain. Signatures are quantum-resistant (ML-DSA-44), and transfers can be made fully private with in-browser zero-knowledge STARK proofs — no rig, no install. Open one link and you're a full participant: wallet, miner, explorer and shielded pool in a single web page.

Topics

Resources

License

Stars

18 stars

Watchers

3 watching

Forks

Contributors