Throttle nip46 signer requests

This commit is contained in:
Jon Staab
2024-10-29 08:48:32 -07:00
parent dc62248b7c
commit 5babdd8143
+48 -9
View File
@@ -1,4 +1,4 @@
import {Emitter, tryCatch, randomId, equals, now} from "@welshman/lib" import {Emitter, sleep, tryCatch, randomId, equals, now} from "@welshman/lib"
import {createEvent, TrustedEvent, StampedEvent, NOSTR_CONNECT} from "@welshman/util" import {createEvent, TrustedEvent, StampedEvent, NOSTR_CONNECT} from "@welshman/util"
import {subscribe, publish, Subscription} from "@welshman/net" import {subscribe, publish, Subscription} from "@welshman/net"
import {ISigner, decrypt, hash, own} from '../util' import {ISigner, decrypt, hash, own} from '../util'
@@ -24,6 +24,12 @@ export type Nip46Response = {
result?: string result?: string
} }
type Request = {
method: string
params: string[]
resolve: (result: string) => void
}
let singleton: Nip46Broker let singleton: Nip46Broker
export class Nip46Broker extends Emitter { export class Nip46Broker extends Emitter {
@@ -31,7 +37,9 @@ export class Nip46Broker extends Emitter {
#handler: Nip46Handler #handler: Nip46Handler
#algorithm: Nip46Algorithm #algorithm: Nip46Algorithm
#closed = false #closed = false
#processing = false
#connectResult?: string #connectResult?: string
#queue: Request[] = []
#sub?: Subscription #sub?: Subscription
static get(params: Nip46BrokerParams) { static get(params: Nip46BrokerParams) {
@@ -91,6 +99,31 @@ export class Nip46Broker extends Emitter {
}) })
} }
#processQueue = async () => {
if (this.#processing) {
return
}
this.#processing = true
try {
while (this.#queue.length > 0) {
const [{method, params, resolve}] = this.#queue.splice(0, 1)
// Throttle requests to the signer so the user isn't overwhelmed by dialogs, but time
// out and move on to other requests if they're ignored
// Note: currenlty throttle is too low to help with dialogs, but blocking prevents
// important user actions
await Promise.race([
this.request(method, params).then(resolve),
sleep(100),
])
}
} finally {
this.#processing = false
}
}
request = async (method: string, params: string[]) => { request = async (method: string, params: string[]) => {
if (this.#closed) { if (this.#closed) {
throw new Error("Attempted to make a nip46 request with a closed broker") throw new Error("Attempted to make a nip46 request with a closed broker")
@@ -126,44 +159,50 @@ export class Nip46Broker extends Emitter {
}) })
} }
enqueue = (method: string, params: string[]) =>
new Promise<string>(resolve => {
this.#queue.push({method, params, resolve})
this.#processQueue()
})
createAccount = (username: string, perms = "") => { createAccount = (username: string, perms = "") => {
if (!this.#handler.domain) { if (!this.#handler.domain) {
throw new Error("Unable to create an account without a handler domain") throw new Error("Unable to create an account without a handler domain")
} }
return this.request("create_account", [username, this.#handler.domain, "", perms]) return this.enqueue("create_account", [username, this.#handler.domain, "", perms])
} }
connect = async (token = "", perms = "") => { connect = async (token = "", perms = "") => {
if (!this.#connectResult) { if (!this.#connectResult) {
const params = ["", token, perms] const params = ["", token, perms]
this.#connectResult = await this.request("connect", params) this.#connectResult = await this.enqueue("connect", params)
} }
return this.#connectResult === "ack" return this.#connectResult === "ack"
} }
getPublicKey = () => this.request("get_public_key", []) getPublicKey = () => this.enqueue("get_public_key", [])
signEvent = async (event: StampedEvent) => { signEvent = async (event: StampedEvent) => {
return JSON.parse(await this.request("sign_event", [JSON.stringify(event)]) as string) return JSON.parse(await this.enqueue("sign_event", [JSON.stringify(event)]) as string)
} }
nip04Encrypt = (pk: string, message: string) => { nip04Encrypt = (pk: string, message: string) => {
return this.request("nip04_encrypt", [pk, message]) return this.enqueue("nip04_encrypt", [pk, message])
} }
nip04Decrypt = (pk: string, message: string) => { nip04Decrypt = (pk: string, message: string) => {
return this.request("nip04_decrypt", [pk, message]) return this.enqueue("nip04_decrypt", [pk, message])
} }
nip44Encrypt = (pk: string, message: string) => { nip44Encrypt = (pk: string, message: string) => {
return this.request("nip44_encrypt", [pk, message]) return this.enqueue("nip44_encrypt", [pk, message])
} }
nip44Decrypt = (pk: string, message: string) => { nip44Decrypt = (pk: string, message: string) => {
return this.request("nip44_decrypt", [pk, message]) return this.enqueue("nip44_decrypt", [pk, message])
} }
teardown = () => { teardown = () => {