Add wrapped signer and signer log; fix adapter unsubscribing in app storage; add more details to thunk signing error, handle signing errors in auth
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
import {derived, writable} from "svelte/store"
|
||||
import {cached, omit, equals, assoc} from "@welshman/lib"
|
||||
import {cached, randomId, append, omit, equals, assoc} from "@welshman/lib"
|
||||
import {withGetter} from "@welshman/store"
|
||||
import {
|
||||
WrappedSigner,
|
||||
Nip46Broker,
|
||||
Nip46Signer,
|
||||
Nip07Signer,
|
||||
Nip01Signer,
|
||||
Nip55Signer,
|
||||
getPubkey,
|
||||
ISigner,
|
||||
} from "@welshman/signer"
|
||||
|
||||
export enum SessionMethod {
|
||||
@@ -175,13 +177,62 @@ export const loginWithPubkey = (pubkey: string) => addSession(makePubkeySession(
|
||||
|
||||
export const nip46Perms = "sign_event:22242,nip04_encrypt,nip04_decrypt,nip44_encrypt,nip44_decrypt"
|
||||
|
||||
export enum SignerLogEntryStatus {
|
||||
Pending = "pending",
|
||||
Success = "success",
|
||||
Failure = "failure",
|
||||
}
|
||||
|
||||
export type SignerLogEntry = {
|
||||
id: string
|
||||
method: string
|
||||
status: SignerLogEntryStatus
|
||||
duration: number
|
||||
}
|
||||
|
||||
export const signerLog = withGetter(writable<SignerLogEntry[]>([]))
|
||||
|
||||
export const wrapSigner = (signer: ISigner) =>
|
||||
new WrappedSigner(signer, async <T>(method: string, thunk: () => Promise<T>) => {
|
||||
const id = randomId()
|
||||
const now = Date.now()
|
||||
|
||||
signerLog.update(log =>
|
||||
append({id, method, status: SignerLogEntryStatus.Pending, duration: 0}, log),
|
||||
)
|
||||
|
||||
try {
|
||||
const result = await thunk()
|
||||
|
||||
signerLog.update(log =>
|
||||
log.map(x =>
|
||||
x.id === id
|
||||
? {...x, status: SignerLogEntryStatus.Success, duration: Date.now() - now}
|
||||
: x,
|
||||
),
|
||||
)
|
||||
|
||||
return result
|
||||
} catch (error: any) {
|
||||
signerLog.update(log =>
|
||||
log.map(x =>
|
||||
x.id === id
|
||||
? {...x, status: SignerLogEntryStatus.Failure, duration: Date.now() - now}
|
||||
: x,
|
||||
),
|
||||
)
|
||||
|
||||
throw error
|
||||
}
|
||||
})
|
||||
|
||||
export const getSigner = cached({
|
||||
maxSize: 100,
|
||||
getKey: ([session]: [Session | undefined]) => `${session?.method}:${session?.pubkey}`,
|
||||
getValue: ([session]: [Session | undefined]) => {
|
||||
if (isNip07Session(session)) return new Nip07Signer()
|
||||
if (isNip01Session(session)) return new Nip01Signer(session.secret)
|
||||
if (isNip55Session(session)) return new Nip55Signer(session.signer)
|
||||
if (isNip07Session(session)) return wrapSigner(new Nip07Signer())
|
||||
if (isNip01Session(session)) return wrapSigner(new Nip01Signer(session.secret))
|
||||
if (isNip55Session(session)) return wrapSigner(new Nip55Signer(session.signer))
|
||||
if (isNip46Session(session)) {
|
||||
const {
|
||||
secret: clientSecret,
|
||||
@@ -190,7 +241,7 @@ export const getSigner = cached({
|
||||
const broker = new Nip46Broker({clientSecret, signerPubkey, relays})
|
||||
const signer = new Nip46Signer(broker)
|
||||
|
||||
return signer
|
||||
return wrapSigner(signer)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -22,7 +22,7 @@ export const ready = defer<void>()
|
||||
|
||||
export const dead = withGetter(writable(false))
|
||||
|
||||
export const subs: Unsubscriber[] = []
|
||||
export const unsubscribers: Unsubscriber[] = []
|
||||
|
||||
export const getAll = async (name: string) => {
|
||||
await ready
|
||||
@@ -100,16 +100,18 @@ export const initStorage = async (
|
||||
|
||||
ready.resolve()
|
||||
|
||||
await Promise.all(Object.values(adapters).map(adapter => adapter.init()))
|
||||
await Promise.all(
|
||||
Object.values(adapters).map(async adapter => {
|
||||
await adapter.init()
|
||||
|
||||
const unsubscribers = Object.values(adapters).map(adapter => adapter.sync())
|
||||
|
||||
return () => unsubscribers.forEach(call)
|
||||
unsubscribers.push(adapter.sync())
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
export const closeStorage = async () => {
|
||||
dead.set(true)
|
||||
subs.forEach(unsub => unsub())
|
||||
unsubscribers.forEach(call)
|
||||
await db?.close()
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ export class Thunk {
|
||||
try {
|
||||
event = await signer.sign(event)
|
||||
} catch (e: any) {
|
||||
return this._fail(String(e.error || e))
|
||||
return this._fail(`Failed to sign event: ${String(e.error || e)}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import EventEmitter from "events"
|
||||
import {on, poll, call} from "@welshman/lib"
|
||||
import {on, poll, call, tryCatch} from "@welshman/lib"
|
||||
import {SignedEvent, StampedEvent} from "@welshman/util"
|
||||
import {makeRelayAuth} from "@welshman/util"
|
||||
import {isRelayAuth, isClientAuth, isRelayOk, RelayMessage} from "./message.js"
|
||||
@@ -101,7 +101,7 @@ export class AuthState extends EventEmitter {
|
||||
this.setStatus(AuthStatus.PendingSignature)
|
||||
|
||||
const template = makeRelayAuth(this.socket.url, this.challenge)
|
||||
const event = await sign(template)
|
||||
const event = await tryCatch(() => sign(template))
|
||||
|
||||
if (event) {
|
||||
this.request = event.id
|
||||
|
||||
@@ -3,7 +3,7 @@ import {bytesToHex, hexToBytes} from "@noble/hashes/utils"
|
||||
import * as nt04 from "nostr-tools/nip04"
|
||||
import * as nt44 from "nostr-tools/nip44"
|
||||
import {generateSecretKey, getPublicKey, getEventHash} from "nostr-tools/pure"
|
||||
import {cached, now} from "@welshman/lib"
|
||||
import {Emitter, cached, now} from "@welshman/lib"
|
||||
import {SignedEvent, HashedEvent, EventTemplate, StampedEvent, OwnedEvent} from "@welshman/util"
|
||||
|
||||
export const makeSecret = () => bytesToHex(generateSecretKey())
|
||||
@@ -64,3 +64,36 @@ export const decrypt = async (signer: ISigner, pubkey: string, message: string)
|
||||
nip04.detect(message)
|
||||
? signer.nip04.decrypt(pubkey, message)
|
||||
: signer.nip44.decrypt(pubkey, message)
|
||||
|
||||
export type SignerMethodWrapper = <T>(method: string, thunk: () => Promise<T>) => Promise<T>
|
||||
|
||||
export class WrappedSigner extends Emitter implements ISigner {
|
||||
constructor(
|
||||
private signer: ISigner,
|
||||
private wrapMethod: SignerMethodWrapper,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
sign(event: StampedEvent) {
|
||||
return this.wrapMethod("sign", () => this.signer.sign(event))
|
||||
}
|
||||
|
||||
getPubkey() {
|
||||
return this.wrapMethod("getPubkey", () => this.signer.getPubkey())
|
||||
}
|
||||
|
||||
nip04 = {
|
||||
encrypt: async (pubkey: string, message: string) =>
|
||||
this.wrapMethod("nip04.encrypt", () => this.signer.nip04.encrypt(pubkey, message)),
|
||||
decrypt: async (pubkey: string, message: string) =>
|
||||
this.wrapMethod("nip04.decrypt", () => this.signer.nip04.decrypt(pubkey, message)),
|
||||
}
|
||||
|
||||
nip44 = {
|
||||
encrypt: async (pubkey: string, message: string) =>
|
||||
this.wrapMethod("nip44.encrypt", () => this.signer.nip44.encrypt(pubkey, message)),
|
||||
decrypt: async (pubkey: string, message: string) =>
|
||||
this.wrapMethod("nip44.decrypt", () => this.signer.nip44.decrypt(pubkey, message)),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user