Rework logger, improve plaintext caching
This commit is contained in:
@@ -2,7 +2,6 @@ export * from "./app.js"
|
|||||||
export * from "./policy.js"
|
export * from "./policy.js"
|
||||||
export * from "./user.js"
|
export * from "./user.js"
|
||||||
export * from "./session.js"
|
export * from "./session.js"
|
||||||
export * from "./logging.js"
|
|
||||||
export * from "./createApp.js"
|
export * from "./createApp.js"
|
||||||
export * from "./plugins/base.js"
|
export * from "./plugins/base.js"
|
||||||
export * from "./plugins/network.js"
|
export * from "./plugins/network.js"
|
||||||
@@ -12,6 +11,7 @@ export * from "./plugins/relays.js"
|
|||||||
export * from "./plugins/relayStats.js"
|
export * from "./plugins/relayStats.js"
|
||||||
export * from "./plugins/relayLists.js"
|
export * from "./plugins/relayLists.js"
|
||||||
export * from "./plugins/blockedRelayLists.js"
|
export * from "./plugins/blockedRelayLists.js"
|
||||||
|
export * from "./plugins/logger.js"
|
||||||
export * from "./plugins/plaintext.js"
|
export * from "./plugins/plaintext.js"
|
||||||
export * from "./plugins/profiles.js"
|
export * from "./plugins/profiles.js"
|
||||||
export * from "./plugins/follows.js"
|
export * from "./plugins/follows.js"
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
import {randomId} from "@welshman/lib"
|
|
||||||
import {WrappedSigner} from "@welshman/signer"
|
|
||||||
import type {ISigner} from "@welshman/signer"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A structured, extensible log event. The built-in `signer` variant tracks each
|
|
||||||
* signer operation (sign/encrypt/decrypt/getPubkey); the open variant lets
|
|
||||||
* callers emit their own event types — it's not just a string.
|
|
||||||
*/
|
|
||||||
export type LogMessage =
|
|
||||||
| {
|
|
||||||
type: "signer"
|
|
||||||
id: string
|
|
||||||
method: string
|
|
||||||
status: "pending" | "success" | "failure"
|
|
||||||
error?: unknown
|
|
||||||
at: number
|
|
||||||
}
|
|
||||||
| {type: string; at: number; [key: string]: unknown}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An `ISigner` wrapper that emits a structured `LogMessage` (as a "message"
|
|
||||||
* event on itself) for every operation it performs. `User.fromSigner` wraps
|
|
||||||
* signers in this so they're observable; subscribe via `makeAppPolicyLogger`.
|
|
||||||
*/
|
|
||||||
export class LoggingSigner extends WrappedSigner {
|
|
||||||
constructor(signer: ISigner) {
|
|
||||||
super(signer, async (method, thunk) => {
|
|
||||||
const id = randomId()
|
|
||||||
|
|
||||||
this.emit("message", {type: "signer", id, method, status: "pending", at: Date.now()})
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await thunk()
|
|
||||||
|
|
||||||
this.emit("message", {type: "signer", id, method, status: "success", at: Date.now()})
|
|
||||||
|
|
||||||
return result
|
|
||||||
} catch (error) {
|
|
||||||
this.emit("message", {type: "signer", id, method, status: "failure", error, at: Date.now()})
|
|
||||||
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import {writable} from "svelte/store"
|
||||||
|
import {randomId} from "@welshman/lib"
|
||||||
|
import {projection} from "./base.js"
|
||||||
|
import type {IApp} from "../app.js"
|
||||||
|
|
||||||
|
export type LogMessage = {
|
||||||
|
source: string
|
||||||
|
id: string
|
||||||
|
at: number
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A logger which stores messages durably for inspection. Subscribe to `messages`
|
||||||
|
* (a projection) to read the log; append with `log(source, {...})`.
|
||||||
|
*/
|
||||||
|
export class Logger {
|
||||||
|
protected store = writable<LogMessage[]>([])
|
||||||
|
messages = projection(this.store)
|
||||||
|
|
||||||
|
constructor(protected readonly app: IApp) {}
|
||||||
|
|
||||||
|
log(
|
||||||
|
source: string,
|
||||||
|
{id = randomId(), at = Date.now(), ...message}: {id?: string; at?: number; [key: string]: unknown},
|
||||||
|
) {
|
||||||
|
this.store.update($messages => $messages.concat({source, id, at, ...message}).slice(-1000))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,56 +1,20 @@
|
|||||||
import {nthEq} from "@welshman/lib"
|
import {nthEq} from "@welshman/lib"
|
||||||
import {MUTES} from "@welshman/util"
|
import {MUTES} from "@welshman/util"
|
||||||
import type {TrustedEvent} from "@welshman/util"
|
|
||||||
import type {ISigner} from "@welshman/signer"
|
|
||||||
import {MuteList, MuteListBuilder} from "@welshman/domain"
|
import {MuteList, MuteListBuilder} from "@welshman/domain"
|
||||||
import {DerivedPlugin} from "./base.js"
|
import {DerivedPlugin} from "./base.js"
|
||||||
import type {IApp} from "../app.js"
|
import type {IApp} from "../app.js"
|
||||||
import {Network} from "./network.js"
|
import {Network} from "./network.js"
|
||||||
import {Thunks} from "./thunk.js"
|
import {Thunks} from "./thunk.js"
|
||||||
import {Plaintext} from "./plaintext.js"
|
|
||||||
import {User} from "../user.js"
|
import {User} from "../user.js"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A signer that decrypts via the app's plaintext cache (keyed by event), falling
|
* Kind-10000 mute lists, keyed by pubkey.
|
||||||
* back to the real signer. Lets `MuteList.fromEvent(event, signer)` reuse cached
|
|
||||||
* decryptions instead of re-decrypting. Returns undefined when there's no user,
|
|
||||||
* so the reader falls back to public-only.
|
|
||||||
*/
|
|
||||||
const makeCachedSigner = (app: IApp, event: TrustedEvent): ISigner | undefined => {
|
|
||||||
const user = app.user
|
|
||||||
|
|
||||||
if (!user) return undefined
|
|
||||||
|
|
||||||
const {signer} = user
|
|
||||||
const decryptVia =
|
|
||||||
(fallback: (pubkey: string, message: string) => Promise<string>) =>
|
|
||||||
async (pubkey: string, message: string) =>
|
|
||||||
(await app.use(Plaintext).ensure(event)) ?? fallback(pubkey, message)
|
|
||||||
|
|
||||||
return {
|
|
||||||
sign: (event, options) => signer.sign(event, options),
|
|
||||||
getPubkey: () => signer.getPubkey(),
|
|
||||||
nip04: {
|
|
||||||
encrypt: (pubkey, message) => signer.nip04.encrypt(pubkey, message),
|
|
||||||
decrypt: decryptVia((pubkey, message) => signer.nip04.decrypt(pubkey, message)),
|
|
||||||
},
|
|
||||||
nip44: {
|
|
||||||
encrypt: (pubkey, message) => signer.nip44.encrypt(pubkey, message),
|
|
||||||
decrypt: decryptVia((pubkey, message) => signer.nip44.decrypt(pubkey, message)),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kind-10000 mute lists, keyed by pubkey. Mute lists carry private entries in
|
|
||||||
* encrypted content, decoded through the plaintext cache (via a cache-backed
|
|
||||||
* signer passed to the reader).
|
|
||||||
*/
|
*/
|
||||||
export class MuteLists extends DerivedPlugin<MuteList> {
|
export class MuteLists extends DerivedPlugin<MuteList> {
|
||||||
constructor(app: IApp) {
|
constructor(app: IApp) {
|
||||||
super(app, {
|
super(app, {
|
||||||
filters: [{kinds: [MUTES]}],
|
filters: [{kinds: [MUTES]}],
|
||||||
eventToItem: event => MuteList.fromEvent(event, makeCachedSigner(app, event)),
|
eventToItem: MuteList.factory(app.user?.signer),
|
||||||
getKey: mute => mute.author(),
|
getKey: mute => mute.author(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,19 @@
|
|||||||
import {decrypt} from "@welshman/signer"
|
|
||||||
import type {Maybe} from "@welshman/lib"
|
|
||||||
import type {TrustedEvent} from "@welshman/util"
|
|
||||||
import {MapPlugin} from "./base.js"
|
import {MapPlugin} from "./base.js"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A cache of decrypted event content, keyed by event id.
|
* A cache of decrypted content, keyed by the ciphertext. Decryption itself is
|
||||||
|
* supplied by the caller (the signer's underlying decrypt), so the cache stays
|
||||||
|
* independent of which signer produced the plaintext — and `appPolicyCacheDecrypt`
|
||||||
|
* can layer it onto `user.signer` without recursing back into the wrapped signer.
|
||||||
*/
|
*/
|
||||||
export class Plaintext extends MapPlugin<string> {
|
export class Plaintext extends MapPlugin<string> {
|
||||||
ensure = async (event: TrustedEvent): Promise<Maybe<string>> => {
|
ensure = async (ciphertext: string, decrypt: () => Promise<string>): Promise<string> => {
|
||||||
if (this.app.user?.pubkey !== event.pubkey) return
|
let result = this.get(ciphertext)
|
||||||
|
|
||||||
let result = this.get(event.id)
|
if (result === undefined) {
|
||||||
if (event.content && result === undefined) {
|
result = await decrypt()
|
||||||
try {
|
|
||||||
result = await decrypt(this.app.user.signer, event.pubkey, event.content)
|
this.set(ciphertext, result)
|
||||||
this.set(event.id, result)
|
|
||||||
} catch (e: any) {
|
|
||||||
if (!String(e).match(/invalid base64/)) {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
+43
-13
@@ -8,8 +8,8 @@ import type {IApp} from "./app.js"
|
|||||||
import {RelayStats} from "./plugins/relayStats.js"
|
import {RelayStats} from "./plugins/relayStats.js"
|
||||||
import {Wraps} from "./plugins/wraps.js"
|
import {Wraps} from "./plugins/wraps.js"
|
||||||
import {BlockedRelayLists} from "./plugins/blockedRelayLists.js"
|
import {BlockedRelayLists} from "./plugins/blockedRelayLists.js"
|
||||||
import {LoggingSigner} from "./logging.js"
|
import {Plaintext} from "./plugins/plaintext.js"
|
||||||
import type {LogMessage} from "./logging.js"
|
import {Logger} from "./plugins/logger.js"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An app policy is a side effect applied once per app at construction,
|
* An app policy is a side effect applied once per app at construction,
|
||||||
@@ -121,25 +121,55 @@ export const appPolicyWraps: AppPolicy = app => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forwards "message" events from the user's signer to `onMessage`. Opt-in —
|
* Wraps user.signer in a WrappedSigner which checks the plaintext cache before
|
||||||
* add `makeAppPolicyLogger(handler)` to an app's `policies`.
|
* attempting to decrypt something.
|
||||||
*/
|
*/
|
||||||
export const makeAppPolicyLogger =
|
export const appPolicyCacheDecrypt: AppPolicy = app => {
|
||||||
(onMessage: (message: LogMessage) => void): AppPolicy =>
|
if (!app.user) return noop
|
||||||
app => {
|
|
||||||
const unsubscribers: Unsubscriber[] = []
|
|
||||||
const signer = app.user?.signer
|
|
||||||
|
|
||||||
if (signer instanceof LoggingSigner) {
|
return app.user.wrapSigner((method, thunk, args) => {
|
||||||
unsubscribers.push(on(signer, "message", onMessage))
|
if (method === "nip04.decrypt" || method === "nip44.decrypt") {
|
||||||
|
const ciphertext = args[1] as string
|
||||||
|
|
||||||
|
return app.use(Plaintext).ensure(ciphertext, thunk as () => Promise<string>) as ReturnType<
|
||||||
|
typeof thunk
|
||||||
|
>
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => unsubscribers.forEach(call)
|
return thunk()
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps user.signer in a WrappedSigner which logs sign requests to the app logger.
|
||||||
|
*/
|
||||||
|
export const appPolicyLogSignerMethods: AppPolicy = app => {
|
||||||
|
if (!app.user) return noop
|
||||||
|
|
||||||
|
const logger = app.use(Logger)
|
||||||
|
|
||||||
|
return app.user.wrapSigner(async (method, thunk) => {
|
||||||
|
logger.log("signer", {method, status: "pending"})
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await thunk()
|
||||||
|
|
||||||
|
logger.log("signer", {method, status: "success"})
|
||||||
|
|
||||||
|
return result
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("signer", {method, status: "failure", error})
|
||||||
|
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const defaultAppPolicies: AppPolicy[] = [
|
export const defaultAppPolicies: AppPolicy[] = [
|
||||||
appPolicyIngest,
|
appPolicyIngest,
|
||||||
appPolicyRelayStats,
|
appPolicyRelayStats,
|
||||||
appPolicyWraps,
|
appPolicyWraps,
|
||||||
|
appPolicyCacheDecrypt,
|
||||||
|
appPolicyLogSignerMethods,
|
||||||
appPolicyAuthUnlessBlocked,
|
appPolicyAuthUnlessBlocked,
|
||||||
]
|
]
|
||||||
|
|||||||
+19
-10
@@ -1,6 +1,6 @@
|
|||||||
import type {StampedEvent} from "@welshman/util"
|
import type {StampedEvent} from "@welshman/util"
|
||||||
import type {ISigner} from "@welshman/signer"
|
import {WrappedSigner} from "@welshman/signer"
|
||||||
import {LoggingSigner} from "./logging.js"
|
import type {ISigner, SignerMethodWrapper} from "@welshman/signer"
|
||||||
import {getSignerFromSession} from "./session.js"
|
import {getSignerFromSession} from "./session.js"
|
||||||
import type {Session} from "./session.js"
|
import type {Session} from "./session.js"
|
||||||
import type {IApp} from "./app.js"
|
import type {IApp} from "./app.js"
|
||||||
@@ -9,18 +9,18 @@ import type {IApp} from "./app.js"
|
|||||||
* A single identity: a pubkey plus the signer that proves it. An `App` is
|
* A single identity: a pubkey plus the signer that proves it. An `App` is
|
||||||
* centered on (at most) one `User`, since the data a user can access depends
|
* centered on (at most) one `User`, since the data a user can access depends
|
||||||
* entirely on who they are.
|
* entirely on who they are.
|
||||||
|
*
|
||||||
|
* `signer` is mutable so app policies can layer behavior onto it after
|
||||||
|
* construction via `wrapSigner` — e.g. `appPolicyCacheDecrypt` and
|
||||||
|
* `appPolicyLogSignerMethods`.
|
||||||
*/
|
*/
|
||||||
export class User {
|
export class User {
|
||||||
constructor(
|
constructor(
|
||||||
readonly pubkey: string,
|
readonly pubkey: string,
|
||||||
readonly signer: ISigner,
|
public signer: ISigner,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
static async fromSigner(signer: ISigner) {
|
static async fromSigner(signer: ISigner) {
|
||||||
if (!(signer instanceof LoggingSigner)) {
|
|
||||||
signer = new LoggingSigner(signer)
|
|
||||||
}
|
|
||||||
|
|
||||||
const pubkey = await signer.getPubkey()
|
const pubkey = await signer.getPubkey()
|
||||||
|
|
||||||
return new User(pubkey, signer)
|
return new User(pubkey, signer)
|
||||||
@@ -28,9 +28,8 @@ export class User {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reconstruct a signing user from a persisted session, using the registered
|
* Reconstruct a signing user from a persisted session, using the registered
|
||||||
* session handlers to find the one for the session's method. The signer is
|
* session handlers to find the one for the session's method. The pubkey is
|
||||||
* wrapped in a `LoggingSigner` (observe it with `makeAppPolicyLogger`) and the
|
* derived from the signer. Returns undefined when no handler is registered
|
||||||
* pubkey is derived from it. Returns undefined when no handler is registered
|
|
||||||
* for the session's method.
|
* for the session's method.
|
||||||
*/
|
*/
|
||||||
static async fromSession(session: Session): Promise<User | undefined> {
|
static async fromSession(session: Session): Promise<User | undefined> {
|
||||||
@@ -54,4 +53,14 @@ export class User {
|
|||||||
sign = (event: StampedEvent) => this.signer.sign(event)
|
sign = (event: StampedEvent) => this.signer.sign(event)
|
||||||
|
|
||||||
nip44EncryptToSelf = (payload: string) => this.signer.nip44.encrypt(this.pubkey, payload)
|
nip44EncryptToSelf = (payload: string) => this.signer.nip44.encrypt(this.pubkey, payload)
|
||||||
|
|
||||||
|
wrapSigner = (wrap: SignerMethodWrapper) => {
|
||||||
|
const original = this.signer
|
||||||
|
|
||||||
|
this.signer = new WrappedSigner(original, wrap)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
this.signer = original
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ describe("Report", () => {
|
|||||||
|
|
||||||
const report = await Report.fromEvent(event)
|
const report = await Report.fromEvent(event)
|
||||||
|
|
||||||
expect(report.reportedPubkey()).toBe(reported)
|
expect(report.pubkey()).toBe(reported)
|
||||||
expect(report.eventId()).toBe(eventId)
|
expect(report.eventId()).toBe(eventId)
|
||||||
expect(report.reason()).toBe("spam")
|
expect(report.reason()).toBe("spam")
|
||||||
expect(report.content()).toBe("this is spam")
|
expect(report.content()).toBe("this is spam")
|
||||||
@@ -54,7 +54,7 @@ describe("Report", () => {
|
|||||||
|
|
||||||
expect(tmpl.tags.filter(t => t[0] === "p").length).toBe(1)
|
expect(tmpl.tags.filter(t => t[0] === "p").length).toBe(1)
|
||||||
expect(tmpl.tags.filter(t => t[0] === "e").length).toBe(1)
|
expect(tmpl.tags.filter(t => t[0] === "e").length).toBe(1)
|
||||||
expect(tmpl.tags).toContainEqual(["p", reported])
|
expect(tmpl.tags).toContainEqual(["p", reported, "spam"])
|
||||||
expect(tmpl.tags).toContainEqual(["e", eventId, "spam"])
|
expect(tmpl.tags).toContainEqual(["e", eventId, "spam"])
|
||||||
// Unknown passthrough tag survives.
|
// Unknown passthrough tag survives.
|
||||||
expect(tmpl.tags).toContainEqual(["alt", "x"])
|
expect(tmpl.tags).toContainEqual(["alt", "x"])
|
||||||
@@ -63,14 +63,14 @@ describe("Report", () => {
|
|||||||
|
|
||||||
it("builds from a fresh builder", async () => {
|
it("builds from a fresh builder", async () => {
|
||||||
const tmpl = await new ReportBuilder()
|
const tmpl = await new ReportBuilder()
|
||||||
.setReportedPubkey(reported)
|
.setPubkey(reported)
|
||||||
.setEventId(eventId)
|
.setEventId(eventId)
|
||||||
.setReason("impersonation")
|
.setReason("impersonation")
|
||||||
.setContent("bad actor")
|
.setContent("bad actor")
|
||||||
.toTemplate(signer)
|
.toTemplate(signer)
|
||||||
|
|
||||||
expect(tmpl.kind).toBe(REPORT)
|
expect(tmpl.kind).toBe(REPORT)
|
||||||
expect(tmpl.tags).toContainEqual(["p", reported])
|
expect(tmpl.tags).toContainEqual(["p", reported, "impersonation"])
|
||||||
expect(tmpl.tags).toContainEqual(["e", eventId, "impersonation"])
|
expect(tmpl.tags).toContainEqual(["e", eventId, "impersonation"])
|
||||||
expect(tmpl.content).toBe("bad actor")
|
expect(tmpl.content).toBe("bad actor")
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export class ReportBuilder extends EventBuilder<Report> {
|
|||||||
const tags: string[][] = []
|
const tags: string[][] = []
|
||||||
|
|
||||||
if (this.pTag) {
|
if (this.pTag) {
|
||||||
if (this.pTag.length === 2) {
|
if (this.pTag.length === 2 && this.reason) {
|
||||||
this.pTag.push(this.reason)
|
this.pTag.push(this.reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ export class ReportBuilder extends EventBuilder<Report> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.eTag) {
|
if (this.eTag) {
|
||||||
if (this.eTag.length === 2) {
|
if (this.eTag.length === 2 && this.reason) {
|
||||||
this.eTag.push(this.reason)
|
this.eTag.push(this.reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,14 @@ export const decrypt = async (signer: ISigner, pubkey: string, message: string)
|
|||||||
? signer.nip04.decrypt(pubkey, message)
|
? signer.nip04.decrypt(pubkey, message)
|
||||||
: signer.nip44.decrypt(pubkey, message)
|
: signer.nip44.decrypt(pubkey, message)
|
||||||
|
|
||||||
export type SignerMethodWrapper = <T>(method: string, thunk: () => Promise<T>) => Promise<T>
|
// `args` carries the wrapped method's arguments (e.g. [pubkey, message] for
|
||||||
|
// encrypt/decrypt), so wrappers can key off them — a decrypt cache, say. A
|
||||||
|
// wrapper that ignores them can take just (method, thunk).
|
||||||
|
export type SignerMethodWrapper = <T>(
|
||||||
|
method: string,
|
||||||
|
thunk: () => Promise<T>,
|
||||||
|
args: unknown[],
|
||||||
|
) => Promise<T>
|
||||||
|
|
||||||
export class WrappedSigner extends Emitter implements ISigner {
|
export class WrappedSigner extends Emitter implements ISigner {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -64,25 +71,25 @@ export class WrappedSigner extends Emitter implements ISigner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sign(event: StampedEvent, options: SignOptions = {}) {
|
sign(event: StampedEvent, options: SignOptions = {}) {
|
||||||
return this.wrapMethod("sign", () => this.signer.sign(event, options))
|
return this.wrapMethod("sign", () => this.signer.sign(event, options), [event, options])
|
||||||
}
|
}
|
||||||
|
|
||||||
getPubkey() {
|
getPubkey() {
|
||||||
return this.wrapMethod("getPubkey", () => this.signer.getPubkey())
|
return this.wrapMethod("getPubkey", () => this.signer.getPubkey(), [])
|
||||||
}
|
}
|
||||||
|
|
||||||
nip04 = {
|
nip04 = {
|
||||||
encrypt: async (pubkey: string, message: string) =>
|
encrypt: async (pubkey: string, message: string) =>
|
||||||
this.wrapMethod("nip04.encrypt", () => this.signer.nip04.encrypt(pubkey, message)),
|
this.wrapMethod("nip04.encrypt", () => this.signer.nip04.encrypt(pubkey, message), [pubkey, message]),
|
||||||
decrypt: async (pubkey: string, message: string) =>
|
decrypt: async (pubkey: string, message: string) =>
|
||||||
this.wrapMethod("nip04.decrypt", () => this.signer.nip04.decrypt(pubkey, message)),
|
this.wrapMethod("nip04.decrypt", () => this.signer.nip04.decrypt(pubkey, message), [pubkey, message]),
|
||||||
}
|
}
|
||||||
|
|
||||||
nip44 = {
|
nip44 = {
|
||||||
encrypt: async (pubkey: string, message: string) =>
|
encrypt: async (pubkey: string, message: string) =>
|
||||||
this.wrapMethod("nip44.encrypt", () => this.signer.nip44.encrypt(pubkey, message)),
|
this.wrapMethod("nip44.encrypt", () => this.signer.nip44.encrypt(pubkey, message), [pubkey, message]),
|
||||||
decrypt: async (pubkey: string, message: string) =>
|
decrypt: async (pubkey: string, message: string) =>
|
||||||
this.wrapMethod("nip44.decrypt", () => this.signer.nip44.decrypt(pubkey, message)),
|
this.wrapMethod("nip44.decrypt", () => this.signer.nip44.decrypt(pubkey, message), [pubkey, message]),
|
||||||
}
|
}
|
||||||
|
|
||||||
async cleanup() {
|
async cleanup() {
|
||||||
|
|||||||
Reference in New Issue
Block a user