CONTINUE.md raw

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

pathwhat it is
~/s/tinygo/nostrinothe light client, the relay tunnel, the bridge, and the wallet-facing channel
~/s/tinygo/tinywalletthe wallet: seed, keys, scripts, store, domain, transactions, PSBT, workers, page
~/s/gjbrr/blake-swapthe swap; in the browser build it refuses every node wallet RPC

What is already proven, and how

tinywallet/pkg/tx/bip143_test.go, and by verifying Core's signature from tinywallet/pkg/tx/testdata/bip143.json over our digest.

too. Byte equality with Core is not expected: the RFC 6979 nonce constructions differ.

compiled, obtained through utxoupdatepsbt on the rig.

What was closed in this session

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.

  1. 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:

# 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.

  1. A worker's console was invisible to the page; worker.js now forwards it, so a failed

connection says why instead of showing nothing.

  1. 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.

  1. 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.