diff --git a/docs/signer/index.md b/docs/signer/index.md index 6a20d8c..4a6192c 100644 --- a/docs/signer/index.md +++ b/docs/signer/index.md @@ -20,8 +20,11 @@ import { makeEvent } from '@welshman/util' import { ISigner, Nip01Signer, makeSecret } from '@welshman/signer' const signer: ISigner = new Nip01Signer(makeSecret()) +const options = { + signal: AbortSignal.timeout(10_000), +} -signer.sign(makeEvent(1)).then(signedEvent => console.log(signedEvent)) +signer.sign(makeEvent(1), options).then(signedEvent => console.log(signedEvent)) ``` ## Installation diff --git a/docs/signer/isigner.md b/docs/signer/isigner.md index 1499f63..74899a3 100644 --- a/docs/signer/isigner.md +++ b/docs/signer/isigner.md @@ -5,9 +5,15 @@ It includes methods for signing messages, verifying signatures, and encrypting/d ```typescript +export type SignOptions = { + signal?: AbortSignal +} + +export type SignWithOptions = (event: StampedEvent, options?: SignOptions) => Promise + interface ISigner { // Core signing functionality - sign: (event: StampedEvent) => Promise + sign: SignWithOptions getPubkey: () => Promise // Encryption capabilities diff --git a/docs/signer/nip-01.md b/docs/signer/nip-01.md index 3df92f3..00238ee 100644 --- a/docs/signer/nip-01.md +++ b/docs/signer/nip-01.md @@ -8,7 +8,7 @@ class Nip01Signer implements ISigner { constructor(private secret: string) // ISigner implementation - sign: (event: StampedEvent) => Promise + sign: SignWithOptions getPubkey: () => Promise nip04: { encrypt, decrypt } nip44: { encrypt, decrypt } diff --git a/docs/signer/nip-55.md b/docs/signer/nip-55.md index 5715367..9191635 100644 --- a/docs/signer/nip-55.md +++ b/docs/signer/nip-55.md @@ -55,7 +55,7 @@ class Nip55Signer implements ISigner { constructor(private secret: string) // ISigner implementation - sign: (event: StampedEvent) => Promise + sign: SignWithOptions getPubkey: () => Promise nip04: { encrypt, decrypt } nip44: { encrypt, decrypt } diff --git a/packages/app/src/thunk.ts b/packages/app/src/thunk.ts index ca38f5c..de4f113 100644 --- a/packages/app/src/thunk.ts +++ b/packages/app/src/thunk.ts @@ -132,9 +132,11 @@ export class Thunk { } try { - event = await signer.sign(event) + event = await signer.sign(event, { + signal: AbortSignal.timeout(15_000), + }) } catch (e: any) { - return this._fail(`Failed to sign event: ${String(e.error || e)}`) + return this._fail(String(e || "Failed to sign event")) } } diff --git a/packages/signer/src/signers/nip01.ts b/packages/signer/src/signers/nip01.ts index c89c11f..70318c1 100644 --- a/packages/signer/src/signers/nip01.ts +++ b/packages/signer/src/signers/nip01.ts @@ -1,5 +1,16 @@ import {StampedEvent} from "@welshman/util" -import {nip04, nip44, own, hash, sign, getPubkey, ISigner, makeSecret} from "../util.js" +import { + nip04, + nip44, + own, + hash, + sign, + getPubkey, + ISigner, + SignOptions, + signWithOptions, + makeSecret, +} from "../util.js" export class Nip01Signer implements ISigner { #pubkey: string @@ -14,7 +25,8 @@ export class Nip01Signer implements ISigner { getPubkey = async () => this.#pubkey - sign = async (event: StampedEvent) => sign(hash(own(event, this.#pubkey)), this.secret) + sign = (event: StampedEvent, options: SignOptions = {}) => + signWithOptions(sign(hash(own(event, this.#pubkey)), this.secret), options) nip04 = { encrypt: async (pubkey: string, message: string) => nip04.encrypt(pubkey, this.secret, message), diff --git a/packages/signer/src/signers/nip07.ts b/packages/signer/src/signers/nip07.ts index 7bde3d6..f506055 100644 --- a/packages/signer/src/signers/nip07.ts +++ b/packages/signer/src/signers/nip07.ts @@ -1,5 +1,13 @@ import {StampedEvent} from "@welshman/util" -import {hash, own, Sign, ISigner, EncryptionImplementation} from "../util.js" +import { + hash, + own, + signWithOptions, + SignOptions, + Sign, + ISigner, + EncryptionImplementation, +} from "../util.js" export type Nip07 = { signEvent: Sign @@ -33,11 +41,11 @@ export class Nip07Signer implements ISigner { getPubkey = async () => this.#then(ext => ext.getPublicKey() as string) - sign = async (template: StampedEvent) => { - const event = hash(own(template, await this.getPubkey())) - - return this.#then(ext => ext.signEvent(event)) - } + sign = (template: StampedEvent, options: SignOptions = {}) => + signWithOptions( + this.#then(async ext => ext.signEvent(hash(own(template, await ext.getPublicKey()!)))), + options, + ) nip04 = { encrypt: (pubkey: string, message: string) => diff --git a/packages/signer/src/signers/nip46.ts b/packages/signer/src/signers/nip46.ts index f118a65..b1ce1bc 100644 --- a/packages/signer/src/signers/nip46.ts +++ b/packages/signer/src/signers/nip46.ts @@ -7,7 +7,15 @@ import { NOSTR_CONNECT, } from "@welshman/util" import {publish, request, AdapterContext} from "@welshman/net" -import {ISigner, EncryptionImplementation, decrypt, hash, own} from "../util.js" +import { + ISigner, + EncryptionImplementation, + signWithOptions, + SignOptions, + decrypt, + hash, + own, +} from "../util.js" import {Nip01Signer} from "./nip01.js" export type Nip46Context = { @@ -463,6 +471,9 @@ export class Nip46Signer implements ISigner { return this.pubkey } - sign = async (template: StampedEvent) => - this.broker.signEvent(hash(own(template, await this.getPubkey()))) + sign = (template: StampedEvent, options: SignOptions = {}) => + signWithOptions( + this.getPubkey().then(pubkey => this.broker.signEvent(hash(own(template, pubkey)))), + options, + ) } diff --git a/packages/signer/src/signers/nip55.ts b/packages/signer/src/signers/nip55.ts index 201ce89..1a6089b 100644 --- a/packages/signer/src/signers/nip55.ts +++ b/packages/signer/src/signers/nip55.ts @@ -1,7 +1,7 @@ import {NostrSignerPlugin, AppInfo} from "nostr-signer-capacitor-plugin" import {decode} from "nostr-tools/nip19" import {SignedEvent, StampedEvent} from "@welshman/util" -import {hash, own, ISigner} from "../util.js" +import {hash, own, signWithOptions, SignOptions, ISigner} from "../util.js" export const getNip55 = async (): Promise => { const {apps} = await NostrSignerPlugin.getInstalledSignerApps() @@ -64,21 +64,23 @@ export class Nip55Signer implements ISigner { }) } - sign = async (template: StampedEvent): Promise => { - const pubkey = await this.getPubkey() // hex-encoded public key - const npub = this.#npub! // Bech32-encoded public key - const event = {sig: "", ...hash(own(template, pubkey))} + sign = (template: StampedEvent, options: SignOptions = {}): Promise => + signWithOptions( + this.getPubkey().then(pubkey => { + const hashedEvent = hash(own(template, pubkey)) - return this.#then(async signer => { - const {event: signedEventJson} = await signer.signEvent({ - eventJson: JSON.stringify(event), - eventId: event.id, - npub: npub, - }) - const signedEvent = JSON.parse(signedEventJson) as SignedEvent - return signedEvent - }) - } + return this.#then(async signer => { + const {event: json} = await signer.signEvent({ + eventJson: JSON.stringify({sig: "", ...hashedEvent}), + eventId: hashedEvent.id, + npub: this.#npub!, + }) + + return JSON.parse(json) as SignedEvent + }) + }), + options, + ) nip04 = { encrypt: async (recipientPubKey: string, message: string): Promise => { diff --git a/packages/signer/src/util.ts b/packages/signer/src/util.ts index b2430a8..7ed265d 100644 --- a/packages/signer/src/util.ts +++ b/packages/signer/src/util.ts @@ -44,6 +44,12 @@ export const nip44 = { export type Sign = (event: StampedEvent) => Promise +export type SignOptions = { + signal?: AbortSignal +} + +export type SignWithOptions = (event: StampedEvent, options?: SignOptions) => Promise + export type Encrypt = (pubkey: string, message: string) => Promise export type Decrypt = (pubkey: string, message: string) => Promise @@ -54,7 +60,7 @@ export type EncryptionImplementation = { } export interface ISigner { - sign: Sign + sign: SignWithOptions nip04: EncryptionImplementation nip44: EncryptionImplementation getPubkey: () => Promise @@ -75,8 +81,8 @@ export class WrappedSigner extends Emitter implements ISigner { super() } - sign(event: StampedEvent) { - return this.wrapMethod("sign", () => this.signer.sign(event)) + sign(event: StampedEvent, options: SignOptions = {}) { + return this.wrapMethod("sign", () => this.signer.sign(event, options)) } getPubkey() { @@ -97,3 +103,12 @@ export class WrappedSigner extends Emitter implements ISigner { this.wrapMethod("nip44.decrypt", () => this.signer.nip44.decrypt(pubkey, message)), } } + +export const signWithOptions = ( + promise: Promise | SignedEvent, + options: SignOptions, +) => + new Promise((resolve, reject) => { + Promise.resolve(promise).then(resolve) + options.signal?.addEventListener("abort", () => reject("Signing was aborted")) + })