| name | bitcoin-kernel/browser-node | |||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| version | 0.0.1 | |||||||||||||||||||||||||||||||||||
| description | A Bitcoin testnet4 node's validation core running in a browser tab. | |||||||||||||||||||||||||||||||||||
| network | testnet4 | |||||||||||||||||||||||||||||||||||
| homepage | http://bitcoin-kernel.com/browser-node/ | |||||||||||||||||||||||||||||||||||
| license | AGPL-3.0-or-later | |||||||||||||||||||||||||||||||||||
| machine_readable_manifest | ./manifest.json | |||||||||||||||||||||||||||||||||||
| agent_guide | ./AGENTS.md | |||||||||||||||||||||||||||||||||||
| built_on |
|
|||||||||||||||||||||||||||||||||||
| acts |
|
A Bitcoin testnet4 node's validation core, running in a browser tab.
Bootstrap a UTXO set from a torrented or Bitcoin Core assumeUTXO snapshot, validate blocks
forward against it (scripts, signatures, fees, all consensus rules), follow the chain by applying
each block to the UTXO set, and live-sync the header chain from a real peer over a WebSocket-to-TCP
bridge — all in the tab. The pure-JS consensus engine (@bitcoin-desktop/schema)
and the browser-node design (bitcoin-kernel/node) do the work.
▶ Live demo: http://bitcoin-kernel.com/browser-node/ (use buttons ③ ④ ⑥; ① ⑤ need a local server) ▶ Run the node (capstone): http://bitcoin-kernel.com/browser-node/node.html — a live dashboard that runs the whole pipeline (validation works on Pages; live header sync needs the local bridge)
Status: demo / proof-of-concept. Machine-readable manifest:
manifest.json. Agent guide:AGENTS.md.
| # | Act | Proves | Runs on | Verified |
|---|---|---|---|---|
| ① | Bootstrap UTXO from a torrented snapshot | a snapshot fetched over WebTorrent/HTTP streams into a sharded coin view | local server | 250k coins in 0.11s; full scale 14.1M = 3.23 GB / 9.7s |
| ③ | Validate a block forward | one real block fully validated (scripts, BIP143 sigs, fees, maturity, witness commitment) | GitHub Pages | block #26000, 16/16 rules, 45ms; rejects a 1‑sat tamper |
| ④ | Follow the chain | consecutive blocks validated, applying UTXO updates so later blocks spend earlier outputs; linkage checked | GitHub Pages | blocks 26000–26020, UTXO 249→1,361, 1.1s |
| ⑤ | Live feed over a WS↔TCP bridge | the tab speaks p2p over a bridge to a real peer, syncs + fully validates the header chain (PoW, BIP94, reorg), tails the tip, and persists to OPFS | local server | 141,671 headers genesis→tip in 5.8s; reload resumes from OPFS in 1.7s |
| ⑥ | Parse Core's dumptxoutset snapshot |
Core's real assumeUTXO snapshot (v2 compressed format) parsed in-tab and used as a validating coin view | GitHub Pages | full 811 MB file → 13,870,119 coins, 40/40 match gettxout; block #120001 validated, 85ms |
| ⑦ | Persist the UTXO set (OPFS) | the RAM-resident coin view is checkpointed to OPFS and resumes from disk on reload | GitHub Pages | 16,913 coins → 2.5 MB checkpoint (17ms); reload resumes in 11ms |
| ⑨ | WASM signature verification | a WASM libsecp256k1 backend swapped in via setVerifyBackend, gated by verdict-equivalence |
GitHub Pages | block #26000: ~1,161 → ~4,895 verifies/s (4.2× at block level), verdicts identical |
| ⑩ | Run the node in a Web Worker (scale) | engine + WASM secp + UTXO store + OPFS sync-handles run off the main thread, so validation/checkpoints don't freeze the UI | GitHub Pages | follow 21 blocks in a Worker: UI responsive (16 ms frame gap) vs frozen on the main thread (~1063 ms) |
| ⑪ | SwiftSync — stateless validation | set-consistency via a 32-byte accumulator (add created, subtract spent) instead of the ~25 GB UTXO set | GitHub Pages | blocks 26000–26020 cancel to ZERO with 32 bytes of state; a fabricated spend is detected; 72 ms |
| ⑫ | SwiftSync at scale | the full real UTXO set committed via the accumulator with set-state held at 32 bytes, not 25 GB | GitHub Pages | Node: 14.1M coins → 0.9 GB RSS (the file), 32-byte commitment; tab: 3M coins streamed in the worker → 32 bytes, UI responsive |
| ⑬ | SwiftSync hints | reconstruct the UTXO set from blocks + a tiny hints file (no spend processing), verified by the accumulator | GitHub Pages | 26000–26020: 516-byte hints (~25 B/block; whole chain ≈ 3.3 MB) → 1,361-coin set rebuilt → accumulator ZERO ✓ |
Acts ③ ④ ⑥ are fully static and work on GitHub Pages. Acts ① (torrent seeding) and ⑤ (the bridge) need a local server — see below.
Open http://bitcoin-kernel.com/browser-node/ and click ③, ④, ⑥.
node serve.mjs # static server + HTTP Range on http://localhost:8088 (zero deps)
# open http://localhost:8088 -> acts ②③④⑥ workAct ① (WebTorrent):
node tools/gen-snapshot.mjs 250000 snapshot.ndjson # generate a synthetic UTXO snapshot
npm install # webtorrent
node serve.mjs & # webseed host
node seed.mjs # seeds + writes magnet.txt, then click ①Act ⑤ (live feed) — needs a testnet4 peer; a local bitcoind -testnet4 (P2P :48333) works:
npm install # ws
node bridge.mjs # ws://localhost:8334 -> 127.0.0.1:48333
# or point elsewhere: PEER_HOST=seed.testnet4.bitcoin.sprovoost.nl node bridge.mjs
# then click ⑤npm test # validate #26000, adversarial tamper, follow 26000–26020, parse + validate #120001index.html the ten acts, explained (the showcase)
node.html the capstone: a running-node dashboard (orchestrates the worker + live feed)
fullchain.html full-chain SwiftSync run: stream blocks from genesis through the accumulator (32-byte state)
sharded-utxo-browser.js ShardedUtxo — coin view sharded past V8's 16.7M Map cap
validate-forward.js load engine + coin view, validate one block forward
follow-chain.js applyBlock() + followChain() — validate a run, update the UTXO set
dumptxoutset.js parser for Bitcoin Core's dumptxoutset v2 compressed snapshot format
live-feed.js connect + syncToTip + tail (header sync over the bridge)
opfs-header-store.js persist the header chain to OPFS (main-thread async) — resume on reload
opfs-coins-store.js checkpoint the UTXO set (ShardedUtxo) to OPFS — resume on reload
wasm-secp.js WASM libsecp256k1 backend (tiny-secp256k1 wasm) for setVerifyBackend
secp256k1.wasm the libsecp256k1 binary (1.2 MB — the one large file in the repo)
node-worker.js the validation core in a Web Worker (engine + WASM secp + UTXO + OPFS sync handles)
swiftsync/ the repo's SwiftSync accumulator (vendored) — stateless set-consistency, 32-byte state
peer-ws.js WsPeer — Bitcoin p2p over a WebSocket (browser transport)
engine/ vendored @bitcoin-desktop/schema: consensus engine + schemas + stores
serve.mjs zero-dep static server (local)
seed.mjs WebTorrent seeder for act ① (optional, needs webtorrent)
bridge.mjs WebSocket-to-TCP bridge for act ⑤ (optional, needs ws)
tools/gen-snapshot.mjs generate a synthetic UTXO snapshot for act ①
data/ small committed fixtures (see Data & provenance)
All fixtures are small and derived from real testnet4 data via a Bitcoin Core node (no third-party APIs):
| File | What | Origin |
|---|---|---|
data/block-26000.hex, data/block-120001.hex |
raw blocks | bitcoin-cli getblock <hash> 0 |
data/snapshot-26000.ndjson |
block 26000's prevouts | getblock <hash> 3 (verbosity-3 prevouts) |
data/range.json, data/range-seed.ndjson |
blocks 26000–26020 + pre-run prevouts | getblock raw + verbosity-3 |
data/utxo-prefix.dat |
first 1 MB of utxo-testnet4-120000.dat |
a real Core dumptxoutset snapshot (the full file is ~811 MB; only a prefix is committed) |
data/snapshot-120001.ndjson |
block 120001's pre-snapshot prevouts | extracted by parsing the Core .dat with dumptxoutset.js |
data/testnet4.json |
genesis header + retarget vectors | from @bitcoin-desktop/schema |
The synthetic snapshot for act ① is not committed; generate it with tools/gen-snapshot.mjs.
The bridge and the torrent/HTTP transports are untrusted — they cannot forge valid blocks or headers, because the tab validates everything (PoW, difficulty, scripts, signatures, fees). Their only powers are withholding and eclipsing. assumeUTXO trusts the snapshot's UTXO set until a background re-validation from genesis (not included in this demo) confirms it.
- Full hints-based stateless IBD — the memory ceiling is gone (act ⑫ /
tools/swiftsync-commit.mjs: the full 14.1M-coin UTXO set commits with 32 bytes of state), and SwiftSync is wired into the running node (act ⑪ +node.html). The deepest remaining piece is a complete stateless forward-validation over a large range / genesis→tip: generate a SwiftSync hints file (<100 MB, WebTorrent-distributed), stream blocks + prevout data, and validate holding only the accumulator + a bounded working set — a full IBD that never materializes the UTXO set. Large snapshots/hints go via WebTorrent, never git.
AGPL-3.0-or-later. Built on bitcoin-kernel/node
and @bitcoin-desktop/schema (vendored under engine/,
unmodified except import-path rewrites). See NOTICE.