diff options
| author | Adam Soltys <[email protected]> | 2026-10-19 22:13:54 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-10-19 22:13:54 -0700 |
| commit | a0829612b8751e85f28d066fbaa0c2308a85fbda (patch) | |
| tree | e9d81ffe0d37023cefbecd1bdf35cf0bf39a5e83 /tools | |
| parent | 1a85347b0a37886e550497eab5ece05410fce923 (diff) | |
| download | coinosv3-a0829612b8751e85f28d066fbaa0c2308a85fbda.tar.gz coinosv3-a0829612b8751e85f28d066fbaa0c2308a85fbda.zip | |
Zaps are signed as the person zapping: the login identity (extension, bunker, passkey, Google) when there is one, else the wallet's key. They were always signed by the wallet's own key, so a signed-in person's zaps showed in stream chats and streamers' overlays under a stranger npub. Zap requests also carry created_at now (a real signer refused them without it).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/zap-signer-test.js | 43 |
1 files changed, 43 insertions, 0 deletions
diff --git a/tools/zap-signer-test.js b/tools/zap-signer-test.js new file mode 100644 index 0000000..13f6008 --- /dev/null +++ b/tools/zap-signer-test.js @@ -0,0 +1,43 @@ +// A zap is signed as the person zapping: with a login identity (extension, +// bunker, passkey…) the zap request's pubkey is that identity, not the +// wallet's own key — a signed-in person's zaps showed in stream chats (and +// streamers' overlays) under the wallet key's stranger npub. Without a +// login, the wallet key signs as before. +// Run: bun tools/zap-signer-test.js +import assert from 'node:assert/strict'; +import { finalizeEvent, generateSecretKey, getPublicKey } from 'nostr-tools/pure'; +import { zapsFeature } from '../src/features/zaps.js'; + +const check = (name, ok, detail = '') => { assert(ok, name + (detail ? ' — ' + detail : '')); console.log(' ✓ ' + name); }; +const walletSk = generateSecretKey(), loginSk = generateSecretKey(), host = getPublicKey(generateSecretKey()); +let asked = null; +globalThis.fetch = async (url) => { + const u = new URL(url); + if (u.pathname.startsWith('/.well-known/lnurlp/')) return Response.json({ tag: 'payRequest', callback: 'https://ln.example/cb', minSendable: 1000, maxSendable: 1e9, allowsNostr: true, nostrPubkey: host }); + if (u.pathname === '/cb') { asked = JSON.parse(u.searchParams.get('nostr')); return Response.json({ pr: 'lnbc210n1fake' }); } + return new Response('no', { status: 404 }); +}; +async function zapWith(login) { + asked = null; + let settled = null; + const ctx = { + h: () => null, ui: {}, render: () => {}, toast: (m) => console.log('toast:', m), fmtAmount: String, unitLabel: () => 'sats', parseAmount: Number, getUnit: () => 'sat', unitTag: () => '', blankSend: () => {}, + wallet: { watchOnly: false, nostrRelays: () => ['wss://relay.example'], nostrProfile: async () => ({ lud16: '[email protected]' }), nostrSign: (e) => finalizeEvent(e, walletSk) }, + hook(name, ...a) { + if (name === 'canLnPay' || name === 'arkReady') return true; + if (name === 'nostrLoginIdentity') return login; + if (name === 'arkPayInvoice') return Promise.resolve({}); + if (name === 'zapSettled') { settled = a; return null; } + return null; + }, + }; + zapsFeature(ctx).lnZapNpub(host, null, 'e'.repeat(64), 21); + for (let i = 0; i < 50 && !settled; i++) await new Promise((r) => setTimeout(r, 20)); + return { req: asked, settled }; +} +const signedIn = await zapWith({ pubkey: getPublicKey(loginSk), signer: { signEvent: async (e) => finalizeEvent(e, loginSk) } }); +check('signed in: the zap goes through', signedIn.settled && signedIn.settled[1] === true, JSON.stringify(signedIn.settled)); +check('...and its request is signed by the login identity', signedIn.req && signedIn.req.kind === 9734 && signedIn.req.pubkey === getPublicKey(loginSk), signedIn.req && signedIn.req.pubkey); +const plain = await zapWith(null); +check('no login: the wallet\'s own key signs it', plain.req && plain.req.pubkey === getPublicKey(walletSk)); +console.log('\n✅ zaps are signed as the person zapping'); |
