From d2a28ea3c95c8af157e3da53b26f5d2e2a8d8895 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 28 Apr 2024 11:13:54 -0500 Subject: [PATCH] Support the full NIP-46 protocol --- src/api/hooks/nostr/useSignerStream.ts | 89 ++++++++++++++++++++++---- src/schemas/nostr.ts | 6 +- 2 files changed, 80 insertions(+), 15 deletions(-) diff --git a/src/api/hooks/nostr/useSignerStream.ts b/src/api/hooks/nostr/useSignerStream.ts index 374cda57a..c36b49b0d 100644 --- a/src/api/hooks/nostr/useSignerStream.ts +++ b/src/api/hooks/nostr/useSignerStream.ts @@ -5,9 +5,32 @@ import { useNostr } from 'soapbox/contexts/nostr-context'; import { connectRequestSchema, nwcRequestSchema } from 'soapbox/schemas/nostr'; import { jsonSchema } from 'soapbox/schemas/utils'; +/** NIP-46 [response](https://github.com/nostr-protocol/nips/blob/master/46.md#response-events-kind24133) content. */ +interface NostrConnectResponse { + /** Request ID that this response is for. */ + id: string; + /** Result of the call (this can be either a string or a JSON stringified object) */ + result: string; + /** Error in string form, if any. Its presence indicates an error with the request. */ + error?: string; +} + function useSignerStream() { const { relay, pubkey, signer } = useNostr(); + async function sendConnect(response: NostrConnectResponse) { + if (!relay || !pubkey || !signer) return; + + const event = await signer.signEvent({ + kind: 24133, + content: await signer.nip04!.encrypt(pubkey, JSON.stringify(response)), + tags: [['p', pubkey]], + created_at: Math.floor(Date.now() / 1000), + }); + + relay.event(event); + } + async function handleConnectEvent(event: NostrEvent) { if (!relay || !pubkey || !signer) return; const decrypted = await signer.nip04!.decrypt(pubkey, event.content); @@ -19,19 +42,61 @@ function useSignerStream() { return; } - const respMsg = { - id: reqMsg.data.id, - result: JSON.stringify(await signer.signEvent(JSON.parse(reqMsg.data.params[0]))), - }; + const request = reqMsg.data; - const respEvent = await signer.signEvent({ - kind: 24133, - content: await signer.nip04!.encrypt(pubkey, JSON.stringify(respMsg)), - tags: [['p', pubkey]], - created_at: Math.floor(Date.now() / 1000), - }); - - relay.event(respEvent); + switch (request.method) { + case 'connect': + return sendConnect({ + id: request.id, + result: 'ack', + }); + case 'sign_event': + return sendConnect({ + id: request.id, + result: JSON.stringify(await signer.signEvent(JSON.parse(request.params[0]))), + }); + case 'ping': + return sendConnect({ + id: request.id, + result: 'pong', + }); + case 'get_relays': + return sendConnect({ + id: request.id, + result: JSON.stringify(await signer.getRelays?.() ?? []), + }); + case 'get_public_key': + return sendConnect({ + id: request.id, + result: await signer.getPublicKey(), + }); + case 'nip04_encrypt': + return sendConnect({ + id: request.id, + result: await signer.nip04!.encrypt(request.params[0], request.params[1]), + }); + case 'nip04_decrypt': + return sendConnect({ + id: request.id, + result: await signer.nip04!.decrypt(request.params[0], request.params[1]), + }); + case 'nip44_encrypt': + return sendConnect({ + id: request.id, + result: await signer.nip44!.encrypt(request.params[0], request.params[1]), + }); + case 'nip44_decrypt': + return sendConnect({ + id: request.id, + result: await signer.nip44!.decrypt(request.params[0], request.params[1]), + }); + default: + return sendConnect({ + id: request.id, + result: '', + error: `Unrecognized method: ${request.method}`, + }); + } } async function handleWalletEvent(event: NostrEvent) { diff --git a/src/schemas/nostr.ts b/src/schemas/nostr.ts index 8288e9791..1f4e0f64b 100644 --- a/src/schemas/nostr.ts +++ b/src/schemas/nostr.ts @@ -24,11 +24,11 @@ const eventSchema = eventTemplateSchema.extend({ /** Nostr event schema that also verifies the event's signature. */ const signedEventSchema = eventSchema.refine(verifyEvent); -/** NIP-46 signer request. */ +/** NIP-46 request content schema. */ const connectRequestSchema = z.object({ id: z.string(), - method: z.literal('sign_event'), - params: z.tuple([z.string()]), + method: z.string(), + params: z.string().array(), }); /** NIP-47 signer response. */