feat: optional Bitcoin node + BTC wallet (bitcoind + BDK sidecar) - #4
Open
erubboli wants to merge 5 commits into
Open
feat: optional Bitcoin node + BTC wallet (bitcoind + BDK sidecar)#4erubboli wants to merge 5 commits into
erubboli wants to merge 5 commits into
Conversation
Infra (profile: bitcoin, indexer-style enable): - bitcoind service pinned to Core 25 (bdk 0.29 rpc backend cannot parse the Core 26+ `warnings` array format), txindex on, no host ports - bdk-wallet sidecar: BIP84 light wallet holding the BTC keys; axum HTTP API (status/balance/address/txs/send/fee-estimate) with basic auth; seed persisted 0600, amounts as satoshi strings throughout - init.sh optional step, .env.example vars, .gitignore entries Web GUI client: - src/lib/bitcoin-wallet.ts: server-side typed client for the sidecar with timeout + basic auth; BITCOIN_ENABLED feature flag - 8 unit tests; verified end-to-end on regtest (create/restore, 101-block fund, sync, send with change + fee, tx history)
- /bitcoin page: create/restore wallet with one-time seed backup, balance
cards (confirmed/pending/immature, BigInt math), QR receive, send form,
transaction history, node status strip with enable instructions
- /api/bitcoin/{overview,wallet,send,sync,fee-estimate}: session-gated
proxy routes with server-side amount validation; partial sidecar
outages degrade independently
- Settings → Bitcoin section: show/hide sidebar page pref + live node
status panel; nav item gated on BITCOIN_ENABLED + pref
- 13 API route tests; make bitcoin / make bitcoin-cli; README section
Verified: 523/523 tests, tsc clean, astro build ok, clippy clean,
compose config valid, regtest end-to-end (create, fund, sync, send).
- bdk-wallet Dockerfile: install libsqlite3-dev in the builder stage; bdk's rusqlite links the system library, so the build failed on machines without it (worked locally only because the dev host had it) - bdk-wallet: vendor jsonrpc 0.13.0 with the per-request timeout raised 15s -> 120s. bdk 0.29 hardcodes a 100-per-keychain address cache and imports all ~200 watch-only descriptors in one `importdescriptors` call; on modest hardware that exceeds 15s, the sync aborts before persisting its checkpoint, and every retry repeats the slow import - BitcoinWallet UI: satsToBtc appended a stray trailing zero for integer BTC amounts (50 BTC rendered as "500 BTC") - Makefile bitcoin-cli: pass -rpcport=8332 (compose binds the node RPC to 8332 for all networks; regtest's default 18443 is not in use) All found by installing the stack in a fresh Ubuntu/QEMU VM (regtest): create -> fund -> sync -> send, verified against bitcoind ground truth.
Member
Author
Live VM verification (fresh Ubuntu 24.04 / QEMU)Installed the full stack from scratch inside a throwaway Ubuntu VM (regtest, 8 GB RAM, 4 vCPU) — not the fast dev host — and drove it end-to-end through the browser. This surfaced 3 real bugs, all fixed in
Also: Verified flow (regtest)init.sh (bitcoin=yes) → login (password+TOTP) → create wallet → 12-word seed backup screen → receive address + QR → |
- init.sh: create bitcoin-data/ and bitcoin-wallet-data/ right after the .env write instead of inside the "start services" block. Previously a user declining auto-start got no dirs, and Docker then created the bind mounts as root, breaking the sidecar's seed persistence (found live in the VM; worked around with chown 1000:1000 there). - compose: run bdk-wallet as ML_USER_ID/ML_GROUP_ID like web-gui, so hosts with a UID other than 1000 also match the bind-mount owner.
…me 151
- Setup wizard ("create new" and "import from seed") now initializes the
optional BTC wallet from the same mnemonic as the ML wallet, so a single
seed controls both chains. Non-fatal when the bitcoin profile is off or
the sidecar is unreachable; the BTC page keeps its own fallback flow.
- middleware: Referrer-Policy no-referrer -> strict-origin-when-cross-origin.
Chrome 151 elides the Origin header (sends "Origin: null") on form POSTs
when the referrer policy strips referrers; Astro's same-origin CSRF check
then rejects every form with 403, making login/setup unusable. Verified
by header capture: minimal page -> real Origin sent; app page (no-referrer)
-> "Origin: null". The browser default policy keeps cross-origin requests
origin-only, so no referrer leakage is introduced.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an opt-in Bitcoin stack (
--profile bitcoin, same pattern as the indexer) and a BTC wallet page to the web GUI. Per the agreed plan: enable/disable stays on the host (no Docker socket in web-gui); keys are held by a light-wallet sidecar, bitcoind only provides chain data and broadcasts; network follows MintlayerNETWORKwith aBITCOIN_NETWORKoverride.Architecture
What's included
Infrastructure (profile: bitcoin)
bitcoind— pinned to Core 25.x (the BDK 0.29 rpc backend cannot parse thewarningsarray format that Core 26+ returns; hit and verified during integration testing).txindex=1default (needed for wallet history), no host ports, no host RPC exposure.bdk-wallet(bdk-wallet/, Rust/axum) — BIP84 wallet holding the keys; endpoints: create/restore wallet (mnemonic returned once), status, sync, balance, address (new/current), txs, send, fee estimates. Basic auth, internal-network only, seed persisted0600, all amounts as satoshi strings (no floats), sync horizon = wallet creation time (avoids genesis scans).init.shoptional step (passwords, network, disk/hot-wallet warnings),.env.example,make bitcoin,make bitcoin-cli, README section.Web GUI
/bitcoinpage — create/restore wallet with one-time seed backup step, balance cards (confirmed/pending/immature, BigInt math), QR receive, send form, transaction history, node status strip with enable instructions when offline./api/bitcoin/*— session-gated (middleware) proxy routes with server-side amount validation; sidecar outages degrade per-call instead of failing the whole page.BITCOIN_ENABLED+ pref.Security notes
dumpwallet/importwallet/stop).Test plan
tsc --noEmitclean,astro buildclean,cargo clippyclean,docker compose configvalid/bitcoinand/api/bitcoin/*redirect unauthenticated requestsKnown limitations (documented)
fee-estimatereturns empty on regtest (insufficient data) — expected.