Remove promenade and pomade stuff from signer package

This commit is contained in:
Jon Staab
2026-01-13 10:25:29 -08:00
parent ac7190acc5
commit 09e687ab05
16 changed files with 92 additions and 268 deletions
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/app",
"version": "0.7.1",
"version": "0.8.0-pre.1",
"author": "hodlbod",
"license": "MIT",
"description": "A collection of svelte stores for use in building nostr client applications.",
@@ -32,6 +32,7 @@
"@welshman/signer": "workspace:*",
"@welshman/store": "workspace:*",
"@welshman/util": "workspace:*",
"nostr-tools": "^2.19.4",
"svelte": "^4.2.18"
},
"devDependencies": {
+76 -5
View File
@@ -1,6 +1,7 @@
import * as nt44 from "nostr-tools/nip44"
import {Client, ClientOptions} from "@pomade/core"
import {derived, writable} from "svelte/store"
import {cached, randomId, append, omit, equals, assoc} from "@welshman/lib"
import {cached, randomId, append, omit, equals, assoc, thrower, hexToBytes} from "@welshman/lib"
import {withGetter} from "@welshman/store"
import {
Wallet,
@@ -14,17 +15,81 @@ import {
import {
Nip59,
WrappedSigner,
PomadeSigner,
Nip46Broker,
Nip46Signer,
Nip07Signer,
Nip01Signer,
Nip55Signer,
ISigner,
SignOptions,
signWithOptions,
} from "@welshman/signer"
import {WrapManager} from "@welshman/net"
import {tracker, repository} from "./core.js"
class PomadeSigner implements ISigner {
#pubkey: string
#sharedSecretCache = new Map<string, Uint8Array<ArrayBuffer>>()
constructor(readonly client: Client) {
this.#pubkey = client.userPubkey
}
private getSharedSecret = async (pubkey: string) => {
let sharedSecret = this.#sharedSecretCache.get(pubkey)
if (!sharedSecret) {
const hexSharedSecret = await this.client.getConversationKey(pubkey)
if (hexSharedSecret) {
sharedSecret = hexToBytes(hexSharedSecret)
this.#sharedSecretCache.set(pubkey, sharedSecret)
}
}
return sharedSecret
}
getPubkey = async () => this.#pubkey
sign = (event: StampedEvent, options: SignOptions = {}) => {
const promise = this.client.sign(event).then(r => {
if (!r.event) {
throw new Error(r.messages[0]?.payload.message || "Failed to sign event")
}
return r.event
})
return signWithOptions(promise, options)
}
nip04 = {
encrypt: thrower("PomadeSigner does not support nip44"),
decrypt: thrower("PomadeSigner does not support nip44"),
}
nip44 = {
encrypt: async (pubkey: string, message: string) => {
const sharedSecret = await this.getSharedSecret(pubkey)
if (!sharedSecret) {
throw new Error("Failed to get shared secret")
}
return nt44.v2.encrypt(message, sharedSecret)
},
decrypt: async (pubkey: string, message: string) => {
const sharedSecret = await this.getSharedSecret(pubkey)
if (!sharedSecret) {
throw new Error("Failed to get shared secret")
}
return nt44.v2.decrypt(message, sharedSecret)
},
}
}
export enum SessionMethod {
Nip01 = "nip01",
Nip07 = "nip07",
@@ -66,6 +131,7 @@ export type SessionPomade = {
method: SessionMethod.Pomade
pubkey: string
clientOptions: ClientOptions
email: string
}
export type SessionPubkey = {
@@ -164,10 +230,15 @@ export const makeNip55Session = (pubkey: string, signer: string): SessionNip55 =
signer,
})
export const makePomadeSession = (pubkey: string, clientOptions: ClientOptions): SessionPomade => ({
export const makePomadeSession = (
pubkey: string,
email: string,
clientOptions: ClientOptions,
): SessionPomade => ({
method: SessionMethod.Pomade,
pubkey,
clientOptions,
email,
})
export const makePubkeySession = (pubkey: string): SessionPubkey => ({
@@ -211,8 +282,8 @@ export const loginWithNip46 = (
export const loginWithNip55 = (pubkey: string, signer: string) =>
addSession(makeNip55Session(pubkey, signer))
export const loginWithPomade = (pubkey: string, clientOptions: ClientOptions) =>
addSession(makePomadeSession(pubkey, clientOptions))
export const loginWithPomade = (pubkey: string, email: string, clientOptions: ClientOptions) =>
addSession(makePomadeSession(pubkey, email, clientOptions))
export const loginWithPubkey = (pubkey: string) => addSession(makePubkeySession(pubkey))
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/content",
"version": "0.7.1",
"version": "0.8.0-pre.1",
"author": "hodlbod",
"license": "MIT",
"description": "A collection of utilities for parsing nostr note content.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/editor",
"version": "0.7.1",
"version": "0.8.0-pre.1",
"author": "hodlbod",
"license": "MIT",
"description": "A batteries-included nostr editor.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/feeds",
"version": "0.7.1",
"version": "0.8.0-pre.1",
"author": "hodlbod",
"license": "MIT",
"description": "Utilities for building dynamic nostr feeds.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/lib",
"version": "0.7.1",
"version": "0.8.0-pre.1",
"author": "hodlbod",
"license": "MIT",
"description": "A collection of utilities.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/net",
"version": "0.7.1",
"version": "0.8.0-pre.1",
"author": "hodlbod",
"license": "MIT",
"description": "Utilities for connecting with nostr relays.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/router",
"version": "0.7.1",
"version": "0.8.0-pre.1",
"author": "hodlbod",
"license": "MIT",
"description": "A collection of utilities for nostr relay selection.",
+1 -6
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/signer",
"version": "0.7.1",
"version": "0.8.0-pre.1",
"author": "hodlbod",
"license": "MIT",
"description": "A nostr signer implemenation supporting several login methods.",
@@ -19,13 +19,9 @@
"compile": "tsc -b tsconfig.build.json",
"prepublishOnly": "pnpm run build"
},
"dependencies": {
"@jsr/fiatjaf__promenade-trusted-dealer": "^0.4.1"
},
"peerDependencies": {
"@noble/curves": "^1.9.7",
"@noble/hashes": "^2.0.1",
"@pomade/core": "^0.0.5",
"@welshman/lib": "workspace:*",
"@welshman/net": "workspace:*",
"@welshman/util": "workspace:*",
@@ -36,7 +32,6 @@
"@capacitor/core": "^7.2.0",
"@noble/curves": "^1.9.7",
"@noble/hashes": "^2.0.1",
"@pomade/core": "^0.0.5",
"@welshman/lib": "workspace:*",
"@welshman/net": "workspace:*",
"@welshman/util": "workspace:*",
-1
View File
@@ -4,4 +4,3 @@ export * from "./signers/nip01.js"
export * from "./signers/nip07.js"
export * from "./signers/nip46.js"
export * from "./signers/nip55.js"
export * from "./signers/pomade.js"
+1 -144
View File
@@ -1,14 +1,5 @@
import {
trustedKeyDeal,
hexShard,
hexPubShard,
KeyShard,
} from "@jsr/fiatjaf__promenade-trusted-dealer"
import {
Emitter,
uniq,
spec,
inc,
throttle,
makePromise,
defer,
@@ -16,24 +7,19 @@ import {
tryCatch,
randomId,
MaybeAsync,
shuffle,
} from "@welshman/lib"
import {
getPubkey,
HashedEvent,
makeEvent,
makeSecret,
normalizeRelayUrl,
NOSTR_CONNECT,
prep,
PROMENADE_REGISTER_ACCOUNT,
PROMENADE_SHARD_ACK,
PROMENADE_SHARD_SHARE,
RelayMode,
StampedEvent,
TrustedEvent,
} from "@welshman/util"
import {publish, request, PublishStatus, AdapterContext} from "@welshman/net"
import {publish, request, AdapterContext} from "@welshman/net"
import {ISigner, EncryptionImplementation, signWithOptions, SignOptions, decrypt} from "../util.js"
import {Nip01Signer} from "./nip01.js"
@@ -346,135 +332,6 @@ export class Nip46Broker extends Emitter {
})
}
static fromPromenade = async (options: PromenadeOptions) => {
const [m, n] = options.policy
if (options.signerPubkeys.length < n) {
throw new Error("Not enough signers to create all shards")
}
const deal = trustedKeyDeal(BigInt("0x" + options.secret), m, n)
const signer = Nip01Signer.fromSecret(options.secret)
const ourPubkey = await signer.getPubkey()
const ackRelays = await options.getPubkeyRelays(ourPubkey, RelayMode.Read)
const remainingSignerPubkeys = shuffle(uniq(options.signerPubkeys))
const errorsBySignerPubkey = new Map<string, string>()
const shardsBySignerPubkey = new Map<string, KeyShard>()
if (ackRelays.length === 0) {
throw new Error("No read relays returned for user pubkey")
}
nip46Log(`generated promenade shards for user ${ourPubkey}`, deal)
await Promise.all(
deal.shards.map(async (shard, i) => {
while (remainingSignerPubkeys.length > 0) {
const signerPubkey = remainingSignerPubkeys.shift()!
nip46Log(`generating proof of work for shard ${i}`)
const shardTemplate = makeEvent(PROMENADE_SHARD_SHARE, {
content: await signer.nip44.encrypt(signerPubkey, hexShard(shard)),
tags: [
["p", signerPubkey],
["coordinator", options.coordinatorUrl],
...ackRelays.map(url => ["reply", url]),
],
})
const shardTemplateWithWork = await tryCatch(() =>
options.generatePow(prep(shardTemplate, ourPubkey), 20),
)
if (!shardTemplateWithWork) {
errorsBySignerPubkey.set(signerPubkey, "Failed to generate work")
continue
}
const shardEvent = await signer.sign(shardTemplateWithWork)
const shardRelays = await options.getPubkeyRelays(signerPubkey, RelayMode.Read)
const publishResults = await publish({relays: shardRelays, event: shardEvent})
nip46Log(`published shard ${i} to signer ${signerPubkey}`, shardRelays, publishResults)
if (!Object.values(publishResults).some(spec({status: PublishStatus.Success}))) {
errorsBySignerPubkey.set(signerPubkey, "Failed to publish shard")
continue
}
const controller = new AbortController()
const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(30_000)])
await request({
signal,
relays: ackRelays,
filters: [
{
kinds: [PROMENADE_SHARD_ACK],
authors: [signerPubkey],
"#p": [ourPubkey],
"#e": [shardEvent.id],
},
],
onEvent: (event: TrustedEvent, url: string) => {
nip46Log(`received ack for shard ${i} from signer ${signerPubkey} on ${url}`)
shardsBySignerPubkey.set(signerPubkey, shard)
options.onProgress?.(shardsBySignerPubkey.size / inc(n))
controller.abort()
},
})
if (shardsBySignerPubkey.has(signerPubkey)) {
break
} else {
errorsBySignerPubkey.set(signerPubkey, "Failed to receive shard ACK")
nip46Log(`failed to receive ack for shard ${i} from signer ${signerPubkey}`)
}
}
}),
)
if (shardsBySignerPubkey.size < deal.shards.length) {
throw new PromenadeShardError("Failed to publish all shards", errorsBySignerPubkey)
}
const connectSecret = randomId()
const signerSecret = makeSecret()
const signerPubkey = getPubkey(signerSecret)
const tags = [
["h", signerPubkey],
["threshold", String(m)],
["handlersecret", signerSecret],
["profile", "MAIN", connectSecret, ""],
]
for (const [pubkey, shard] of shardsBySignerPubkey) {
tags.push(["p", pubkey, hexPubShard(shard.pubShard)])
}
nip46Log(`registering coordinator account`, tags)
const relays = [options.coordinatorUrl]
const event = await signer.sign(makeEvent(PROMENADE_REGISTER_ACCOUNT, {tags}))
const accountResults = await publish({relays, event})
if (!Object.values(accountResults).some(spec({status: PublishStatus.Success}))) {
throw new Error("Failed to publish accounts to coordinator")
}
nip46Log(`successfully created promenade broker`)
const clientSecret = makeSecret()
return new Nip46Broker({
relays,
clientSecret,
signerPubkey,
connectSecret,
})
}
// Getters for helper objects
makeSigner = () => new Nip01Signer(this.params.clientSecret)
-68
View File
@@ -1,68 +0,0 @@
import * as nt44 from "nostr-tools/nip44"
import type {Client} from "@pomade/core"
import type {StampedEvent} from "@welshman/util"
import {thrower, hexToBytes} from "@welshman/lib"
import {ISigner, SignOptions, signWithOptions} from "../util.js"
export class PomadeSigner implements ISigner {
#pubkey: string
#sharedSecretCache = new Map<string, Uint8Array<ArrayBuffer>>()
constructor(readonly client: Client) {
this.#pubkey = client.userPubkey
}
private getSharedSecret = async (pubkey: string) => {
let sharedSecret = this.#sharedSecretCache.get(pubkey)
if (!sharedSecret) {
const hexSharedSecret = await this.client.getConversationKey(pubkey)
if (hexSharedSecret) {
sharedSecret = hexToBytes(hexSharedSecret)
this.#sharedSecretCache.set(pubkey, sharedSecret)
}
}
return sharedSecret
}
getPubkey = async () => this.#pubkey
sign = (event: StampedEvent, options: SignOptions = {}) => {
const promise = this.client.sign(event).then(r => {
if (!r.event) {
throw new Error(r.messages[0]?.payload.message || "Failed to sign event")
}
return r.event
})
return signWithOptions(promise, options)
}
nip04 = {
encrypt: thrower("PomadeSigner does not support nip44"),
decrypt: thrower("PomadeSigner does not support nip44"),
}
nip44 = {
encrypt: async (pubkey: string, message: string) => {
const sharedSecret = await this.getSharedSecret(pubkey)
if (!sharedSecret) {
throw new Error("Failed to get shared secret")
}
return nt44.v2.encrypt(message, sharedSecret)
},
decrypt: async (pubkey: string, message: string) => {
const sharedSecret = await this.getSharedSecret(pubkey)
if (!sharedSecret) {
throw new Error("Failed to get shared secret")
}
return nt44.v2.decrypt(message, sharedSecret)
},
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/store",
"version": "0.7.1",
"version": "0.8.0-pre.1",
"author": "hodlbod",
"license": "MIT",
"description": "A collection of utilities based on svelte/store for use with welshman",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@welshman/util",
"version": "0.7.1",
"version": "0.8.0-pre.1",
"author": "hodlbod",
"license": "MIT",
"description": "A collection of nostr-related utilities.",