# Where to pick this up Written at the end of the session that built the wallet, so the next one starts from facts. ## The three repositories | path | what it is | | --- | --- | | `~/s/tinygo/nostrino` | the light client, the relay tunnel, the bridge, and the wallet-facing channel | | `~/s/tinygo/tinywallet` | the wallet: seed, keys, scripts, store, domain, transactions, PSBT, workers, page | | `~/s/gjbrr/blake-swap` | the swap; in the browser build it refuses every node wallet RPC | ## What is already proven, and how - BIP143 digest — pinned twice: by the specification's own vector in `tinywallet/pkg/tx/bip143_test.go`, and by verifying Core's signature from `tinywallet/pkg/tx/testdata/bip143.json` over our digest. - Signing — `tinywallet/pkg/tx/sign.go`; the node's fixture signature verifies, and our own does too. Byte equality with Core is *not* expected: the RFC 6979 nonce constructions differ. - The HTLC witness script — pinned in `tinywallet/pkg/script/htlc_test.go` to the script Core compiled, obtained through `utxoupdatepsbt` on the rig. - Block parsing and the merkle proof — `nostrino/pkg/block`, pinned to regtest block 1. - Coin matching — `nostrino/pkg/block`'s `Match`, and `tinywallet/pkg/domain`'s `ScanBlock`. ## What was closed in this session - **Block transport is proven against the node.** `nostrino/pkg/neutrino`'s `Block` asks the peer for the block a match names, `pkg/chaindata`'s `MethodBlock` carries it to the wallet, and `tinywallet/pkg/domain`'s `Sync` proves the merkle root and records the coin. The proof is `NOSTRINO_RIG=1 go test ./internal/rigscan/ -run TestAFilterMatchBecomesACoin -v`: it funds a derived address from the rig wallet, mines one block, syncs, and checks the coin's txid, vout, value and height against the node's `gettxout`. Two protocol bugs had to be fixed first, both named by the node's own log: the frame magic was written little endian (`Wrong MessageStart dab5bffa received`), and `getcfheaders` asked for more than BIP157's 2000-block range (`peer requested too many cfilters/cfheaders`). ## The two things left 1. **The swap.** `pkg/fund`, `pkg/settle` and `pkg/observe` in blake-swap call `Node` interfaces that land in `chain.Node.callWithWallet`, which the browser build refuses (`pkg/chain/nodewallet_js.go`). Re-point them at a local implementation backed by `tinywallet/pkg/tx`, `pkg/psbt` and `pkg/utxo`, then delete the refusal for the paths that have one. `pkg/walletdep` is the vocabulary for the ones that do not yet. 2. **The browser run.** `make web` in tinywallet assembles `bin/web`; serve it and drive connect → create (words shown once, typed back) → address → fund → mine → sync → coins, then reload and resume from the stored filter tip. Nothing in this session exercised the page, and that is the only claim still standing unverified. ## The rig, as it was left Bitcoin Core on `18443` (RPC) and `18444` (P2P), blakecoin on `19443`/`19444`, data under `~/.blake-swap-test`, clients at `~/s/bitcoin-core/bin/bitcoin-cli` and `~/s/bitcoin-blake2b/bin/bitcoin-cli`. The regtest WIF prefix is `0xef`, not `0x80` — that cost an hour once. The node must serve BIP157: `-blockfilterindex=1 -peerblockfilters=1`. Without them it answers `getcfheaders` with `peer requested unsupported block filter type: 0` and disconnects. `scripts/test-nodes` in blake-swap now writes both into the generated `bitcoin.conf`, and the `rig` wallet must be loaded (the script creates it) because the proof funds the wallet's address from it. `-v2transport=0` is **not** required: Core 30 and Knots 29 auto-detect the v1 transport on the same port, and the light client only speaks v1. Earlier notes that blamed BIP324 were wrong — the actual cause of the handshake EOF was the byte-swapped magic. To rebuild the filter index for a chain that ran without it, restart the node with the two flags; the index builds from genesis in seconds on regtest (minutes on the 42k-block blake chain). No relay was running, so torly has to be started on `3334` before the tunnel path can be exercised. ## The browser, as far as it got The page serves from `bin/web` (`python3 -m http.server 8080`) and a real Chromium reaches it. The rig for a two-chain run: ```bash # torly, the relay TORLY_PORT=3334 TORLY_DATA_DIR=/tmp/torly-web ./torly serve # one gateway per node; the advertised chain name is the name the page asks for nostrino-bridge -relay ws://127.0.0.1:3334 -node 127.0.0.1:18444 \ -chain regtest -chains btc -allow-command getdata,block -data /tmp/gw-btc nostrino-bridge -relay ws://127.0.0.1:3334 -node 127.0.0.1:19444 \ -chain regtest -chains blake -allow-command getdata,block -data /tmp/gw-blake ``` Each gateway prints a `nostr+chaindata://` URI for its card. The page's second field is the profile JSON; both chains here are Bitcoin regtest parameters (`{"name":"regtest","magic":"fabfb5da","genesis_header":"0100…02000000",…}`, `genesis_hash 0f9188f1…2206`, `bech32_hrp bcrt`, `no_retarget true`), and the channel asks the gateway for the wallet's chain name, which is why each gateway advertises `btc` or `blake` rather than `regtest`. Bugs found and fixed by driving it, in the order they blocked the page: 1. A `MessagePort` cannot be posted to a worker without a transfer list, and the wallet's ports are nested inside its chain descriptors, so the transfer list is collected recursively. 2. A worker's console was invisible to the page; `worker.js` now forwards it, so a failed connection says why instead of showing nothing. 3. A `MessagePort` received through `postMessage` starts paused, and `msgport.NewMessagePort` attached a listener without calling `start()`. The worker therefore received no frame at all: `open` succeeded, everything after it waited for ever, and no dial was attempted. With `start()` the page's `status` answers in 0 ms where it used to hang. 4. The gateway's whitelist lacked `sendcmpct`, which Core sends unprompted after the handshake; the gateway killed every session about 110 ms in. The bridge also discarded the error that said so — it logs it now. **Where it stands.** In a real Chromium the page connects, creates a wallet, shows the words once, refuses wrong words, accepts them, shows the first address, funds it from the rig, mines, and syncs Core end to end: `from 0 to tip 24109 (filters 24109)`, 201 scripts matched, and the coin it was funded with appears: ``` 100000000 sat · 8ad7b88ef313a09bf97e4391dedb0c08d002d305073c37201531cc817621bd28:0 · height 24109 gettxout: "confirmations": 1, "value": 1.00000000, "coinbase": false getaddressinfo bcrt1qx7qr4ef67v0qcf9hdx4htdl7ym0rrchue2eyld: "ismine": false ``` Screenshots are in `/tmp/webwallet/evidence/` (`btc-02-words-once`, `btc-03-confirmed-address`, `btc-05-synced`, `btc-06-coins`). Two tunnel fixes were needed after the credit accounting: `msgport` never called `start()` on a received port, so no frame arrived at all, and the gateway whitelist lacked `sendcmpct`, so Core's unprompted post-handshake message killed every session. The sync then died at 60 s because `chaindata` applied its answer deadline to `MethodSync`; Sync is now exempt unless a caller sets an explicit deadline. **Not proven:** blakecoin's coin. Its sync was still running with no error when the driver's 900 s wait expired (`syncing…`), which is expected for 42k blocks against btc's 645 s for 24k; it needs a longer wait, not a fix. A second browser run should complete it and then exercise reload → unlock → sync, which resumes from the stored filter tip.