Repository navigation
Roadmap: browser-native Bitcoin node (epic) #1
Description
The tracking issue for building a browser-native Bitcoin node on the bitcoin-kernel engine. Background and the full architecture/perf model: bitcoin-kernel.github.io#1.
Principle: every step is gated by a benchmark
We do not build on guesses. Each milestone ships with a measured number, and the whole plan is re-checked against M0's results before we commit to the expensive parts.
Usable first, trustless later (resource tiers)
Opt into more resource to trust less. Tiers are designed in from day one, not bolted on.
| Tier | Does | Cost | Device |
|---|---|---|---|
| 0 Lite | headers + Neutrino wallet (BIP 157/158) | ~100 MB, minutes | phone |
| 1 Validating | assumeutxo snapshot, validate new blocks live | a few GB | laptop |
| 2 Full | backfill genesis to tip, fully trustless | ~650 GB, lots of RAM | desktop |
Trust ladder: headers (PoW) -> filters/SPV (light) -> assumeutxo (one hash) -> backfill complete (trustless).
Three pillars
- Storage: OPFS, synchronous access handles in a Worker. (OPFS works on gh-pages with no special headers.)
- Compute: WASM-SIMD secp256k1 + Web Workers; UTXO set in RAM, no DB in the hot path.
- Networking: a small local WebSocket-to-TCP bridge. A browser cannot open raw TCP. WebRTC/WebTorrent block-swarming is a later decentralization layer, not the MVP.
Milestones
- M0 — Benchmarks. Measure WASM-secp, SHA256, OPFS, and SharedArrayBuffer on real data before any app code. These numbers decide everything.
- M1 — Headers + the bridge (Tier 0 foundation). Local TCP shim, headers-first sync to tip with full PoW/difficulty validation, persisted and resumable in OPFS.
- M2 — Light wallet (Tier 0). BIP 158 filter scan backwards from tip; build a wallet's UTXO/history; verify inclusion via SPV.
- M3 — assumeutxo (Tier 1). Load a hash-checked UTXO snapshot; validate new blocks forward (WASM workers, UTXO in RAM).
- M4 — Backfill (Tier 2). Validate genesis to snapshot in the background; flip the UI from trusted snapshot to fully validated.
- M5 — Durability / PWA. Persistent storage, installable, survives restart, resumes.
Known gh-pages gotchas to resolve along the way: SharedArrayBuffer needs COOP/COEP (service-worker workaround, or skip SAB initially); the TCP bridge is a separate local download, not hosted.
Sub-issues are filed per milestone, starting with M0. Non-goals: archival serving, mining, mempool/relay policy, matching native speed.
Activity
melvincarvalho commentedon Jun 18, 2026
Progress: Tier 0 core runs in Node (testnet4).
src/peer.js) is done; the browser WS bridge wrapper remains.npm test/npm run bench.Decisions locked along the way: ESM
.jsonly (no.mjs); built for mainnet + testnet4 via the network param, testnet4 first; the tip cross-check uses p2p peers, never a third-party explorer API.Next: M1.3 (OPFS store + resume + reorg/most-work), then M0.1 (the WASM-secp benchmark) before any block-validation work.
melvincarvalho commentedon Jun 18, 2026
Model correction from M0.1 (#2), measured not estimated: WASM ECDSA verify is ~3,840/s (tiny-secp256k1), 16x the pure-JS engine but ~2.6x slower than the ~10k/s I'd assumed. Per-call JS↔WASM overhead dominates. Revised full-signature-verification estimate: ~7.5h on 24 cores (~8-15h realistic), not ~3h. The honest headline becomes "fully validated overnight," and batch verification + SIMD move to the critical path (each needs its own M0-style benchmark before block validation).
Progress so far: M1.2 (#8) and M1.3 (#9) done — live testnet4 header sync, full validation, resume, most-work reorg, all headless-tested behind
HeaderStore/HeaderSyncinterfaces (two swaps to the browser). M0.1 (#2) done. 14 unit tests green.melvincarvalho commentedon Jun 18, 2026
M2 (light wallet) core done, headless-proven.
src/wallet/wallet-scan.js—WalletScan: watch scriptPubKeys, scan a height range by BIP 158 compact filters, download only matching blocks (match-then-fetch, so filter false positives cost a fetch not correctness), build UTXO set + balance + history. Platform-agnostic likeHeaderSync: depends only on codec +GcsFilter+ three transport-agnostic fetchers.src/scan-testnet4.js— live Neutrino scan wired (sync headers → getcfilters → match → getdata matched blocks → balance/history).Honest finding (measured): 0/10 reachable public testnet4 peers advertise
NODE_COMPACT_FILTERS(services=3081). Public testnet4 nodes rarely runblockfilterindex, so the live filter scan needs a filter-serving source (own node with-blockfilterindex=1 -peerblockfilters=1, or another filter source). The logic is proven against the official vectors regardless. This is an M0-style discovery worth a dedicated sub-issue.Session tally: M1.2, M1.3, M0.1, and M2-core done; 18 headless tests; live testnet4 header sync + resume + reorg working. The two open realities to weigh: (1) M0.1 says full validation is ~overnight not ~afternoon (batch/SIMD secp is the lever); (2) M2 live needs a filter source.