Written at the end of the session that built the wallet, so the next one starts from facts.
| 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 |
tinywallet/pkg/tx/bip143_test.go, and by verifying Core's signature from
tinywallet/pkg/tx/testdata/bip143.json over our digest.
tinywallet/pkg/tx/sign.go; the node's fixture signature verifies, and our own doestoo. Byte equality with Core is not expected: the RFC 6979 nonce constructions differ.
tinywallet/pkg/script/htlc_test.go to the script Core compiled, obtained through utxoupdatepsbt on the rig.
nostrino/pkg/block, pinned to regtest block 1.nostrino/pkg/block's Match, and tinywallet/pkg/domain's ScanBlock.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`).
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.
make web in tinywallet assembles bin/web; serve it and driveconnect → 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.
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 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:
MessagePort cannot be posted to a worker without a transfer list, and the wallet's portsare nested inside its chain descriptors, so the transfer list is collected recursively.
worker.js now forwards it, so a failedconnection says why instead of showing nothing.
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.
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.