diff --git a/README.md b/README.md index 8e03269..68c62bd 100644 --- a/README.md +++ b/README.md @@ -19,36 +19,31 @@ If using TypeScript, this package requires TypeScript >= 5.0. ### Generating a private key and a public key ```js -import { generatePrivateKey, getPublicKey } from 'nostr-tools' +import { generateSecretKey, getPublicKey } from 'nostr-tools' -let sk = generatePrivateKey() // `sk` is a hex string +let sk = generateSecretKey() // `sk` is a hex string let pk = getPublicKey(sk) // `pk` is a hex string ``` ### Creating, signing and verifying events ```js -import { validateEvent, verifySignature, getSignature, getEventHash, getPublicKey } from 'nostr-tools' +import { finalizeEvent, verifyEvent } from 'nostr-tools' -let event = { +let event = finalizeEvent({ kind: 1, created_at: Math.floor(Date.now() / 1000), tags: [], content: 'hello', - pubkey: getPublicKey(privateKey), -} +}, privateKey) -event.id = getEventHash(event) -event.sig = getSignature(event, privateKey) - -let ok = validateEvent(event) -let veryOk = verifySignature(event) +let isGood = verifyEvent(event) ``` ### Interacting with a relay ```js -import { relayConnect, finishEvent, generatePrivateKey, getPublicKey } from 'nostr-tools' +import { relayConnect, finalizeEvent, generateSecretKey, getPublicKey } from 'nostr-tools' const relay = await relayConnect('wss://relay.example.com') console.log(`connected to ${relay.url}`) @@ -68,7 +63,7 @@ const sub = relay.subscribe([ }) // let's publish a new event while simultaneously monitoring the relay for it -let sk = generatePrivateKey() +let sk = generateSecretKey() let pk = getPublicKey(sk) let sub = relay.sub([ @@ -90,7 +85,7 @@ let event = { } // this assigns the pubkey, calculates the event id and signs the event in a single step -const signedEvent = finishEvent(event, sk) +const signedEvent = finalizeEvent(event, sk) await relay.publish(signedEvent) let events = await relay.list([{ kinds: [0, 1] }]) @@ -184,21 +179,21 @@ nip05.useFetchImplementation(require('node-fetch')) ### Encoding and decoding NIP-19 codes ```js -import { nip19, generatePrivateKey, getPublicKey } from 'nostr-tools' +import { nip19, generateSecretKey, getPublicKey } from 'nostr-tools' -let sk = generatePrivateKey() +let sk = generateSecretKey() let nsec = nip19.nsecEncode(sk) let { type, data } = nip19.decode(nsec) assert(type === 'nsec') assert(data === sk) -let pk = getPublicKey(generatePrivateKey()) +let pk = getPublicKey(generateSecretKey()) let npub = nip19.npubEncode(pk) let { type, data } = nip19.decode(npub) assert(type === 'npub') assert(data === pk) -let pk = getPublicKey(generatePrivateKey()) +let pk = getPublicKey(generateSecretKey()) let relays = ['wss://relay.nostr.example.mydomain.example.com', 'wss://nostr.banana.com'] let nprofile = nip19.nprofileEncode({ pubkey: pk, relays }) let { type, data } = nip19.decode(nprofile) @@ -212,7 +207,7 @@ assert(data.relays.length === 2) ```html ``` diff --git a/core.test.ts b/core.test.ts index 2b9aba3..72f2153 100644 --- a/core.test.ts +++ b/core.test.ts @@ -11,10 +11,10 @@ import { generateSecretKey, } from './pure.ts' import { ShortTextNote } from './kinds.ts' -import { hexToBytes } from '@noble/hashes/utils' +import { bytesToHex, hexToBytes } from '@noble/hashes/utils' test('private key generation', () => { - expect(generateSecretKey()).toMatch(/[a-f0-9]{64}/) + expect(bytesToHex(generateSecretKey())).toMatch(/[a-f0-9]{64}/) }) test('public key generation', () => { @@ -30,7 +30,7 @@ test('public key from private key deterministic', () => { } }) -describe('finishEvent', () => { +describe('finalizeEvent', () => { test('should create a signed event from a template', () => { const privateKey = hexToBytes('d217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf') const publicKey = getPublicKey(privateKey) @@ -211,7 +211,7 @@ describe('validateEvent', () => { }) }) -describe('verifySignature', () => { +describe('verifyEvent', () => { test('should return true for a valid event signature', () => { const privateKey = hexToBytes('d217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf') const event = finalizeEvent( diff --git a/filter.ts b/filter.ts index 203f6ec..4cda3b6 100644 --- a/filter.ts +++ b/filter.ts @@ -1,4 +1,4 @@ -import { Event } from './event.ts' +import { Event } from './pure.ts' export type Filter = { ids?: string[] diff --git a/index.ts b/index.ts index ae85ccb..76d5626 100644 --- a/index.ts +++ b/index.ts @@ -1,6 +1,6 @@ -export * from './keys.ts' +export * from './pure.ts' export * from './relay.ts' -export * from './event.ts' +export * from './pure.ts' export * from './filter.ts' export * from './pool.ts' export * from './references.ts' diff --git a/nip04.test.ts b/nip04.test.ts index 4e744ac..67cada3 100644 --- a/nip04.test.ts +++ b/nip04.test.ts @@ -2,7 +2,8 @@ import { test, expect } from 'bun:test' import crypto from 'node:crypto' import { encrypt, decrypt } from './nip04.ts' -import { getPublicKey, generatePrivateKey } from './keys.ts' +import { getPublicKey, generateSecretKey } from './pure.ts' +import { bytesToHex, hexToBytes } from '@noble/hashes/utils' try { // @ts-ignore @@ -13,20 +14,20 @@ try { } test('encrypt and decrypt message', async () => { - let sk1 = generatePrivateKey() - let sk2 = generatePrivateKey() + let sk1 = generateSecretKey() + let sk2 = generateSecretKey() let pk1 = getPublicKey(sk1) let pk2 = getPublicKey(sk2) - let ciphertext = await encrypt(sk1, pk2, 'hello') + let ciphertext = await encrypt(bytesToHex(sk1), pk2, 'hello') - expect(await decrypt(sk2, pk1, ciphertext)).toEqual('hello') + expect(await decrypt(bytesToHex(sk2), pk1, ciphertext)).toEqual('hello') }) test('decrypt message from go-nostr', async () => { let sk1 = '91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe' let sk2 = '96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220' - let pk1 = getPublicKey(sk1) + let pk1 = getPublicKey(hexToBytes(sk1)) let ciphertext = 'zJxfaJ32rN5Dg1ODjOlEew==?iv=EV5bUjcc4OX2Km/zPp4ndQ==' @@ -36,7 +37,7 @@ test('decrypt message from go-nostr', async () => { test('decrypt big payload from go-nostr', async () => { let sk1 = '91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe' let sk2 = '96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220' - let pk1 = getPublicKey(sk1) + let pk1 = getPublicKey(hexToBytes(sk1)) let ciphertext = '6f8dMstm+udOu7yipSn33orTmwQpWbtfuY95NH+eTU1kArysWJIDkYgI2D25EAGIDJsNd45jOJ2NbVOhFiL3ZP/NWsTwXokk34iyHyA/lkjzugQ1bHXoMD1fP/Ay4hB4al1NHb8HXHKZaxPrErwdRDb8qa/I6dXb/1xxyVvNQBHHvmsM5yIFaPwnCN1DZqXf2KbTA/Ekz7Hy+7R+Sy3TXLQDFpWYqykppkXc7Fs0qSuPRyxz5+anuN0dxZa9GTwTEnBrZPbthKkNRrvZMdTGJ6WumOh9aUq8OJJWy9aOgsXvs7qjN1UqcCqQqYaVnEOhCaqWNDsVtsFrVDj+SaLIBvCiomwF4C4nIgngJ5I69tx0UNI0q+ZnvOGQZ7m1PpW2NYP7Yw43HJNdeUEQAmdCPnh/PJwzLTnIxHmQU7n7SPlMdV0SFa6H8y2HHvex697GAkyE5t8c2uO24OnqIwF1tR3blIqXzTSRl0GA6QvrSj2p4UtnWjvF7xT7RiIEyTtgU/AsihTrXyXzWWZaIBJogpgw6erlZqWjCH7sZy/WoGYEiblobOAqMYxax6vRbeuGtoYksr/myX+x9rfLrYuoDRTw4woXOLmMrrj+Mf0TbAgc3SjdkqdsPU1553rlSqIEZXuFgoWmxvVQDtekgTYyS97G81TDSK9nTJT5ilku8NVq2LgtBXGwsNIw/xekcOUzJke3kpnFPutNaexR1VF3ohIuqRKYRGcd8ADJP2lfwMcaGRiplAmFoaVS1YUhQwYFNq9rMLf7YauRGV4BJg/t9srdGxf5RoKCvRo+XM/nLxxysTR9MVaEP/3lDqjwChMxs+eWfLHE5vRWV8hUEqdrWNZV29gsx5nQpzJ4PARGZVu310pQzc6JAlc2XAhhFk6RamkYJnmCSMnb/RblzIATBi2kNrCVAlaXIon188inB62rEpZGPkRIP7PUfu27S/elLQHBHeGDsxOXsBRo1gl3te+raoBHsxo6zvRnYbwdAQa5taDE63eh+fT6kFI+xYmXNAQkU8Dp0MVhEh4JQI06Ni/AKrvYpC95TXXIphZcF+/Pv/vaGkhG2X9S3uhugwWK?iv=2vWkOQQi0WynNJz/aZ4k2g==' diff --git a/nip04.ts b/nip04.ts index 334f3a3..bbb9447 100644 --- a/nip04.ts +++ b/nip04.ts @@ -1,4 +1,4 @@ -import { randomBytes } from '@noble/hashes/utils' +import { bytesToHex, randomBytes } from '@noble/hashes/utils' import { secp256k1 } from '@noble/curves/secp256k1' import { base64 } from '@scure/base' @@ -10,7 +10,8 @@ if (typeof crypto !== 'undefined' && !crypto.subtle && crypto.webcrypto) { crypto.subtle = crypto.webcrypto.subtle } -export async function encrypt(privkey: string, pubkey: string, text: string): Promise { +export async function encrypt(secretKey: string | Uint8Array, pubkey: string, text: string): Promise { + const privkey: string = secretKey instanceof Uint8Array ? bytesToHex(secretKey) : secretKey const key = secp256k1.getSharedSecret(privkey, '02' + pubkey) const normalizedKey = getNormalizedX(key) @@ -24,7 +25,8 @@ export async function encrypt(privkey: string, pubkey: string, text: string): Pr return `${ctb64}?iv=${ivb64}` } -export async function decrypt(privkey: string, pubkey: string, data: string): Promise { +export async function decrypt(secretKey: string | Uint8Array, pubkey: string, data: string): Promise { + const privkey: string = secretKey instanceof Uint8Array ? bytesToHex(secretKey) : secretKey let [ctb64, ivb64] = data.split('?iv=') let key = secp256k1.getSharedSecret(privkey, '02' + pubkey) let normalizedKey = getNormalizedX(key) diff --git a/nip10.ts b/nip10.ts index 87c4cb4..d01c00a 100644 --- a/nip10.ts +++ b/nip10.ts @@ -1,4 +1,4 @@ -import type { Event } from './event.ts' +import type { Event } from './pure.ts' import type { EventPointer, ProfilePointer } from './nip19.ts' export type NIP10Result = { diff --git a/nip13.ts b/nip13.ts index c3ef403..8f1bfc2 100644 --- a/nip13.ts +++ b/nip13.ts @@ -1,4 +1,4 @@ -import { type UnsignedEvent, type Event, getEventHash } from './event.ts' +import { type UnsignedEvent, type Event, getEventHash } from './pure.ts' /** Get POW difficulty from a Nostr hex ID. */ export function getPow(hex: string): number { diff --git a/nip18.test.ts b/nip18.test.ts index 0374c4b..d424090 100644 --- a/nip18.test.ts +++ b/nip18.test.ts @@ -1,6 +1,6 @@ import { describe, test, expect } from 'bun:test' -import { finishEvent } from './event.ts' -import { getPublicKey } from './keys.ts' +import { hexToBytes } from '@noble/hashes/utils' +import { finalizeEvent, getPublicKey } from './pure.ts' import { Repost, ShortTextNote } from './kinds.ts' import { finishRepostEvent, getRepostedEventPointer, getRepostedEvent } from './nip18.ts' import { buildEvent } from './test-helpers.ts' @@ -8,11 +8,10 @@ import { buildEvent } from './test-helpers.ts' const relayUrl = 'https://relay.example.com' describe('finishRepostEvent + getRepostedEventPointer + getRepostedEvent', () => { - const privateKey = 'd217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf' - + const privateKey = hexToBytes('d217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf') const publicKey = getPublicKey(privateKey) - const repostedEvent = finishEvent( + const repostedEvent = finalizeEvent( { kind: ShortTextNote, tags: [ diff --git a/nip18.ts b/nip18.ts index df5e963..a721bb8 100644 --- a/nip18.ts +++ b/nip18.ts @@ -1,4 +1,4 @@ -import { Event, finishEvent, verifySignature } from './event.ts' +import { Event, finalizeEvent, verifyEvent } from './pure.ts' import { Repost } from './kinds.ts' import { EventPointer } from './nip19.ts' @@ -23,9 +23,9 @@ export function finishRepostEvent( t: RepostEventTemplate, reposted: Event, relayUrl: string, - privateKey: string, + privateKey: Uint8Array, ): Event { - return finishEvent( + return finalizeEvent( { kind: Repost, tags: [...(t.tags ?? []), ['e', reposted.id, relayUrl], ['p', reposted.pubkey]], @@ -89,7 +89,7 @@ export function getRepostedEvent(event: Event, { skipVerification }: GetReposted return undefined } - if (!skipVerification && !verifySignature(repostedEvent)) { + if (!skipVerification && !verifyEvent(repostedEvent)) { return undefined } diff --git a/nip19.test.ts b/nip19.test.ts index 1d5e9a7..fe38e28 100644 --- a/nip19.test.ts +++ b/nip19.test.ts @@ -1,5 +1,5 @@ import { test, expect } from 'bun:test' -import { generatePrivateKey, getPublicKey } from './keys.ts' +import { generateSecretKey, getPublicKey } from './pure.ts' import { decode, naddrEncode, @@ -14,7 +14,7 @@ import { } from './nip19.ts' test('encode and decode nsec', () => { - let sk = generatePrivateKey() + let sk = generateSecretKey() let nsec = nsecEncode(sk) expect(nsec).toMatch(/nsec1\w+/) let { type, data } = decode(nsec) @@ -23,7 +23,7 @@ test('encode and decode nsec', () => { }) test('encode and decode npub', () => { - let pk = getPublicKey(generatePrivateKey()) + let pk = getPublicKey(generateSecretKey()) let npub = npubEncode(pk) expect(npub).toMatch(/npub1\w+/) let { type, data } = decode(npub) @@ -32,7 +32,7 @@ test('encode and decode npub', () => { }) test('encode and decode nprofile', () => { - let pk = getPublicKey(generatePrivateKey()) + let pk = getPublicKey(generateSecretKey()) let relays = ['wss://relay.nostr.example.mydomain.example.com', 'wss://nostr.banana.com'] let nprofile = nprofileEncode({ pubkey: pk, relays }) expect(nprofile).toMatch(/nprofile1\w+/) @@ -56,7 +56,7 @@ test('decode nprofile without relays', () => { }) test('encode and decode naddr', () => { - let pk = getPublicKey(generatePrivateKey()) + let pk = getPublicKey(generateSecretKey()) let relays = ['wss://relay.nostr.example.mydomain.example.com', 'wss://nostr.banana.com'] let naddr = naddrEncode({ pubkey: pk, @@ -76,7 +76,7 @@ test('encode and decode naddr', () => { }) test('encode and decode nevent', () => { - let pk = getPublicKey(generatePrivateKey()) + let pk = getPublicKey(generateSecretKey()) let relays = ['wss://relay.nostr.example.mydomain.example.com', 'wss://nostr.banana.com'] let naddr = neventEncode({ id: pk, @@ -93,7 +93,7 @@ test('encode and decode nevent', () => { }) test('encode and decode nevent with kind 0', () => { - let pk = getPublicKey(generatePrivateKey()) + let pk = getPublicKey(generateSecretKey()) let relays = ['wss://relay.nostr.example.mydomain.example.com', 'wss://nostr.banana.com'] let naddr = neventEncode({ id: pk, diff --git a/nip19.ts b/nip19.ts index d817865..64d0471 100644 --- a/nip19.ts +++ b/nip19.ts @@ -48,7 +48,7 @@ type Prefixes = { nrelay: string nevent: EventPointer naddr: AddressPointer - nsec: string + nsec: Uint8Array npub: string note: string } @@ -130,6 +130,8 @@ export function decode(nip19: string): DecodeResult { } case 'nsec': + return { type: prefix, data } + case 'npub': case 'note': return { type: prefix, data: bytesToHex(data) } @@ -157,16 +159,16 @@ function parseTLV(data: Uint8Array): TLV { return result } -export function nsecEncode(hex: string): `nsec1${string}` { - return encodeBytes('nsec', hex) +export function nsecEncode(key: Uint8Array): `nsec1${string}` { + return encodeBytes('nsec', key) } export function npubEncode(hex: string): `npub1${string}` { - return encodeBytes('npub', hex) + return encodeBytes('npub', hexToBytes(hex)) } export function noteEncode(hex: string): `note1${string}` { - return encodeBytes('note', hex) + return encodeBytes('note', hexToBytes(hex)) } function encodeBech32(prefix: Prefix, data: Uint8Array): `${Prefix}1${string}` { @@ -174,9 +176,8 @@ function encodeBech32(prefix: Prefix, data: Uint8Array): return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}` } -function encodeBytes(prefix: Prefix, hex: string): `${Prefix}1${string}` { - let data = hexToBytes(hex) - return encodeBech32(prefix, data) +function encodeBytes(prefix: Prefix, bytes: Uint8Array): `${Prefix}1${string}` { + return encodeBech32(prefix, bytes) } export function nprofileEncode(profile: ProfilePointer): `nprofile1${string}` { diff --git a/nip25.test.ts b/nip25.test.ts index 61fef0d..f1d86bc 100644 --- a/nip25.test.ts +++ b/nip25.test.ts @@ -1,15 +1,14 @@ import { describe, test, expect } from 'bun:test' -import { finishEvent } from './event.ts' -import { getPublicKey } from './keys.ts' +import { hexToBytes } from '@noble/hashes/utils' +import { finalizeEvent, getPublicKey } from './pure.ts' import { Reaction, ShortTextNote } from './kinds.ts' import { finishReactionEvent, getReactedEventPointer } from './nip25.ts' describe('finishReactionEvent + getReactedEventPointer', () => { - const privateKey = 'd217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf' - + const privateKey = hexToBytes('d217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf') const publicKey = getPublicKey(privateKey) - const reactedEvent = finishEvent( + const reactedEvent = finalizeEvent( { kind: ShortTextNote, tags: [ diff --git a/nip25.ts b/nip25.ts index ffd97b7..92d1699 100644 --- a/nip25.ts +++ b/nip25.ts @@ -1,4 +1,4 @@ -import { Event, finishEvent } from './event.ts' +import { Event, finalizeEvent } from './pure.ts' import { Reaction } from './kinds.ts' import type { EventPointer } from './nip19.ts' @@ -17,10 +17,10 @@ export type ReactionEventTemplate = { created_at: number } -export function finishReactionEvent(t: ReactionEventTemplate, reacted: Event, privateKey: string): Event { +export function finishReactionEvent(t: ReactionEventTemplate, reacted: Event, privateKey: Uint8Array): Event { const inheritedTags = reacted.tags.filter(tag => tag.length >= 2 && (tag[0] === 'e' || tag[0] === 'p')) - return finishEvent( + return finalizeEvent( { ...t, kind: Reaction, diff --git a/nip28.test.ts b/nip28.test.ts index a458fe2..446d28d 100644 --- a/nip28.test.ts +++ b/nip28.test.ts @@ -1,5 +1,6 @@ import { describe, test, expect } from 'bun:test' -import { getPublicKey } from './keys.ts' +import { hexToBytes } from '@noble/hashes/utils' +import { getPublicKey } from './pure.ts' import * as Kind from './kinds.ts' import { channelCreateEvent, @@ -11,7 +12,7 @@ import { ChannelMessageEventTemplate, } from './nip28.ts' -const privateKey = 'd217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf' +const privateKey = hexToBytes('d217c1ff2f8a65c3e3a1740db3b9f58b8c848bb45e26d00ed4714e4a0f4ceecf') const publicKey = getPublicKey(privateKey) describe('NIP-28 Functions', () => { diff --git a/nip28.ts b/nip28.ts index 38383c9..cfe281d 100644 --- a/nip28.ts +++ b/nip28.ts @@ -1,4 +1,4 @@ -import { Event, finishEvent } from './event.ts' +import { Event, finalizeEvent } from './pure.ts' import { ChannelCreation, ChannelHideMessage, ChannelMessage, ChannelMetadata, ChannelMuteUser } from './kinds.ts' export interface ChannelMetadata { @@ -45,7 +45,7 @@ export interface ChannelMuteUserEventTemplate { tags?: string[][] } -export const channelCreateEvent = (t: ChannelCreateEventTemplate, privateKey: string): Event | undefined => { +export const channelCreateEvent = (t: ChannelCreateEventTemplate, privateKey: Uint8Array): Event | undefined => { let content: string if (typeof t.content === 'object') { content = JSON.stringify(t.content) @@ -55,7 +55,7 @@ export const channelCreateEvent = (t: ChannelCreateEventTemplate, privateKey: st return undefined } - return finishEvent( + return finalizeEvent( { kind: ChannelCreation, tags: [...(t.tags ?? [])], @@ -66,7 +66,7 @@ export const channelCreateEvent = (t: ChannelCreateEventTemplate, privateKey: st ) } -export const channelMetadataEvent = (t: ChannelMetadataEventTemplate, privateKey: string): Event | undefined => { +export const channelMetadataEvent = (t: ChannelMetadataEventTemplate, privateKey: Uint8Array): Event | undefined => { let content: string if (typeof t.content === 'object') { content = JSON.stringify(t.content) @@ -76,7 +76,7 @@ export const channelMetadataEvent = (t: ChannelMetadataEventTemplate, privateKey return undefined } - return finishEvent( + return finalizeEvent( { kind: ChannelMetadata, tags: [['e', t.channel_create_event_id], ...(t.tags ?? [])], @@ -87,14 +87,14 @@ export const channelMetadataEvent = (t: ChannelMetadataEventTemplate, privateKey ) } -export const channelMessageEvent = (t: ChannelMessageEventTemplate, privateKey: string): Event => { +export const channelMessageEvent = (t: ChannelMessageEventTemplate, privateKey: Uint8Array): Event => { const tags = [['e', t.channel_create_event_id, t.relay_url, 'root']] if (t.reply_to_channel_message_event_id) { tags.push(['e', t.reply_to_channel_message_event_id, t.relay_url, 'reply']) } - return finishEvent( + return finalizeEvent( { kind: ChannelMessage, tags: [...tags, ...(t.tags ?? [])], @@ -106,7 +106,10 @@ export const channelMessageEvent = (t: ChannelMessageEventTemplate, privateKey: } /* "e" tag should be the kind 42 event to hide */ -export const channelHideMessageEvent = (t: ChannelHideMessageEventTemplate, privateKey: string): Event | undefined => { +export const channelHideMessageEvent = ( + t: ChannelHideMessageEventTemplate, + privateKey: Uint8Array, +): Event | undefined => { let content: string if (typeof t.content === 'object') { content = JSON.stringify(t.content) @@ -116,7 +119,7 @@ export const channelHideMessageEvent = (t: ChannelHideMessageEventTemplate, priv return undefined } - return finishEvent( + return finalizeEvent( { kind: ChannelHideMessage, tags: [['e', t.channel_message_event_id], ...(t.tags ?? [])], @@ -127,7 +130,7 @@ export const channelHideMessageEvent = (t: ChannelHideMessageEventTemplate, priv ) } -export const channelMuteUserEvent = (t: ChannelMuteUserEventTemplate, privateKey: string): Event | undefined => { +export const channelMuteUserEvent = (t: ChannelMuteUserEventTemplate, privateKey: Uint8Array): Event | undefined => { let content: string if (typeof t.content === 'object') { content = JSON.stringify(t.content) @@ -137,7 +140,7 @@ export const channelMuteUserEvent = (t: ChannelMuteUserEventTemplate, privateKey return undefined } - return finishEvent( + return finalizeEvent( { kind: ChannelMuteUser, tags: [['p', t.pubkey_to_mute], ...(t.tags ?? [])], diff --git a/nip42.ts b/nip42.ts index 18862a8..f8fd1ec 100644 --- a/nip42.ts +++ b/nip42.ts @@ -1,4 +1,4 @@ -import { EventTemplate } from './event.ts' +import { EventTemplate } from './pure.ts' import { ClientAuth } from './kinds.ts' /** diff --git a/nip47.test.ts b/nip47.test.ts index d7c571d..6c9f1b5 100644 --- a/nip47.test.ts +++ b/nip47.test.ts @@ -1,7 +1,8 @@ +import crypto from 'node:crypto' import { describe, test, expect } from 'bun:test' +import { hexToBytes } from '@noble/hashes/utils' import { makeNwcRequestEvent, parseConnectionString } from './nip47' import { decrypt } from './nip04.ts' -import crypto from 'node:crypto' import { NWCWalletRequest } from './kinds.ts' // @ts-ignore @@ -44,14 +45,10 @@ describe('parseConnectionString', () => { describe('makeNwcRequestEvent', () => { test('returns a valid NWC request event', async () => { const pubkey = 'b889ff5b1513b641e2a139f661a661364979c5beee91842f8f0ef42ab558e9d4' - const secret = '71a8c14c1407c113601079c4302dab36460f0ccd0ad506f1f2dc73b5100e4f3c' + const secret = hexToBytes('71a8c14c1407c113601079c4302dab36460f0ccd0ad506f1f2dc73b5100e4f3c') const invoice = 'lnbc210n1pjdgyvupp5x43awdarnfd4mdlsklelux0nyckwfu5c708ykuet8vcjnjp3rnpqdqu2askcmr9wssx7e3q2dshgmmndp5scqzzsxqyz5vqsp52l7y9peq9pka3vd3j7aps7gjnalsmy46ndj2mlkz00dltjgqfumq9qyyssq5fasr5dxed8l4qjfnqq48a02jzss3asf8sly7sfaqtr9w3yu2q9spsxhghs3y9aqdf44zkrrg9jjjdg6amade4h0hulllkwk33eqpucp6d5jye' - const result = await makeNwcRequestEvent({ - pubkey, - secret, - invoice, - }) + const result = await makeNwcRequestEvent(pubkey, secret, invoice) expect(result.kind).toBe(NWCWalletRequest) expect(await decrypt(secret, pubkey, result.content)).toEqual( JSON.stringify({ diff --git a/nip47.ts b/nip47.ts index f8915a5..cab1b1b 100644 --- a/nip47.ts +++ b/nip47.ts @@ -1,4 +1,4 @@ -import { finishEvent } from './event.ts' +import { finalizeEvent } from './pure.ts' import { NWCWalletRequest } from './kinds.ts' import { encrypt } from './nip04.ts' @@ -15,22 +15,14 @@ export function parseConnectionString(connectionString: string) { return { pubkey, relay, secret } } -export async function makeNwcRequestEvent({ - pubkey, - secret, - invoice, -}: { - pubkey: string - secret: string - invoice: string -}) { +export async function makeNwcRequestEvent(pubkey: string, secretKey: Uint8Array, invoice: string) { const content = { method: 'pay_invoice', params: { invoice, }, } - const encryptedContent = await encrypt(secret, pubkey, JSON.stringify(content)) + const encryptedContent = await encrypt(secretKey, pubkey, JSON.stringify(content)) const eventTemplate = { kind: NWCWalletRequest, created_at: Math.round(Date.now() / 1000), @@ -38,5 +30,5 @@ export async function makeNwcRequestEvent({ tags: [['p', pubkey]], } - return finishEvent(eventTemplate, secret) + return finalizeEvent(eventTemplate, secretKey) } diff --git a/nip57.test.ts b/nip57.test.ts index d34acdf..465ff2c 100644 --- a/nip57.test.ts +++ b/nip57.test.ts @@ -1,6 +1,6 @@ import { describe, test, expect, mock } from 'bun:test' -import { finishEvent } from './event.ts' -import { getPublicKey, generatePrivateKey } from './keys.ts' +import { finalizeEvent } from './pure.ts' +import { getPublicKey, generateSecretKey } from './pure.ts' import { getZapEndpoint, makeZapReceipt, makeZapRequest, useFetchImplementation, validateZapRequest } from './nip57.ts' import { buildEvent } from './test-helpers.ts' @@ -122,7 +122,7 @@ describe('validateZapRequest', () => { }) test('returns an error message if the signature on the Zap request is invalid', () => { - const privateKey = generatePrivateKey() + const privateKey = generateSecretKey() const publicKey = getPublicKey(privateKey) const zapRequest = { @@ -141,9 +141,8 @@ describe('validateZapRequest', () => { }) test('returns an error message if the Zap request does not have a "p" tag', () => { - const privateKey = generatePrivateKey() - - const zapRequest = finishEvent( + const privateKey = generateSecretKey() + const zapRequest = finalizeEvent( { kind: 9734, created_at: Date.now() / 1000, @@ -160,9 +159,8 @@ describe('validateZapRequest', () => { }) test('returns an error message if the "p" tag on the Zap request is not valid hex', () => { - const privateKey = generatePrivateKey() - - const zapRequest = finishEvent( + const privateKey = generateSecretKey() + const zapRequest = finalizeEvent( { kind: 9734, created_at: Date.now() / 1000, @@ -180,10 +178,10 @@ describe('validateZapRequest', () => { }) test('returns an error message if the "e" tag on the Zap request is not valid hex', () => { - const privateKey = generatePrivateKey() + const privateKey = generateSecretKey() const publicKey = getPublicKey(privateKey) - const zapRequest = finishEvent( + const zapRequest = finalizeEvent( { kind: 9734, created_at: Date.now() / 1000, @@ -202,10 +200,10 @@ describe('validateZapRequest', () => { }) test('returns an error message if the Zap request does not have a relays tag', () => { - const privateKey = generatePrivateKey() + const privateKey = generateSecretKey() const publicKey = getPublicKey(privateKey) - const zapRequest = finishEvent( + const zapRequest = finalizeEvent( { kind: 9734, created_at: Date.now() / 1000, @@ -222,10 +220,10 @@ describe('validateZapRequest', () => { }) test('returns null for a valid Zap request', () => { - const privateKey = generatePrivateKey() + const privateKey = generateSecretKey() const publicKey = getPublicKey(privateKey) - const zapRequest = finishEvent( + const zapRequest = finalizeEvent( { kind: 9734, created_at: Date.now() / 1000, @@ -245,11 +243,11 @@ describe('validateZapRequest', () => { describe('makeZapReceipt', () => { test('returns a valid Zap receipt with a preimage', () => { - const privateKey = generatePrivateKey() + const privateKey = generateSecretKey() const publicKey = getPublicKey(privateKey) const zapRequest = JSON.stringify( - finishEvent( + finalizeEvent( { kind: 9734, created_at: Date.now() / 1000, @@ -283,11 +281,11 @@ describe('makeZapReceipt', () => { }) test('returns a valid Zap receipt without a preimage', () => { - const privateKey = generatePrivateKey() + const privateKey = generateSecretKey() const publicKey = getPublicKey(privateKey) const zapRequest = JSON.stringify( - finishEvent( + finalizeEvent( { kind: 9734, created_at: Date.now() / 1000, diff --git a/nip57.ts b/nip57.ts index cdebd72..0770007 100644 --- a/nip57.ts +++ b/nip57.ts @@ -1,6 +1,6 @@ import { bech32 } from '@scure/base' -import { validateEvent, verifySignature, type Event, type EventTemplate } from './event.ts' +import { validateEvent, verifyEvent, type Event, type EventTemplate } from './pure.ts' import { utf8Decoder } from './utils.ts' var _fetch: any @@ -86,7 +86,7 @@ export function validateZapRequest(zapRequestString: string): string | null { if (!validateEvent(zapRequest)) return 'Zap request is not a valid Nostr event.' - if (!verifySignature(zapRequest)) return 'Invalid signature on zap request.' + if (!verifyEvent(zapRequest)) return 'Invalid signature on zap request.' let p = zapRequest.tags.find(([t, v]) => t === 'p' && v) if (!p) return "Zap request doesn't have a 'p' tag." diff --git a/nip98.test.ts b/nip98.test.ts index db7944b..0a21463 100644 --- a/nip98.test.ts +++ b/nip98.test.ts @@ -1,17 +1,17 @@ import { describe, test, expect } from 'bun:test' import { getToken, unpackEventFromToken, validateEvent, validateToken } from './nip98.ts' -import { Event, finishEvent } from './event.ts' -import { generatePrivateKey, getPublicKey } from './keys.ts' +import { Event, finalizeEvent } from './pure.ts' +import { generateSecretKey, getPublicKey } from './pure.ts' import { sha256 } from '@noble/hashes/sha256' import { utf8Encoder } from './utils.ts' import { bytesToHex } from '@noble/hashes/utils' import { HTTPAuth } from './kinds.ts' -const sk = generatePrivateKey() +const sk = generateSecretKey() describe('getToken', () => { test('getToken GET returns without authorization scheme', async () => { - let result = await getToken('http://test.com', 'get', e => finishEvent(e, sk)) + let result = await getToken('http://test.com', 'get', e => finalizeEvent(e, sk)) const decodedResult: Event = await unpackEventFromToken(result) @@ -26,7 +26,7 @@ describe('getToken', () => { }) test('getToken POST returns token without authorization scheme', async () => { - let result = await getToken('http://test.com', 'post', e => finishEvent(e, sk)) + let result = await getToken('http://test.com', 'post', e => finalizeEvent(e, sk)) const decodedResult: Event = await unpackEventFromToken(result) @@ -43,7 +43,7 @@ describe('getToken', () => { test('getToken GET returns token WITH authorization scheme', async () => { const authorizationScheme = 'Nostr ' - let result = await getToken('http://test.com', 'post', e => finishEvent(e, sk), true) + let result = await getToken('http://test.com', 'post', e => finalizeEvent(e, sk), true) expect(result.startsWith(authorizationScheme)).toBe(true) @@ -62,7 +62,7 @@ describe('getToken', () => { test('getToken returns token with a valid payload tag when payload is present', async () => { const payload = { test: 'payload' } const payloadHash = bytesToHex(sha256(utf8Encoder.encode(JSON.stringify(payload)))) - let result = await getToken('http://test.com', 'post', e => finishEvent(e, sk), true, payload) + let result = await getToken('http://test.com', 'post', e => finalizeEvent(e, sk), true, payload) const decodedResult: Event = await unpackEventFromToken(result) @@ -80,14 +80,14 @@ describe('getToken', () => { describe('validateToken', () => { test('validateToken returns true for valid token without authorization scheme', async () => { - const validToken = await getToken('http://test.com', 'get', e => finishEvent(e, sk)) + const validToken = await getToken('http://test.com', 'get', e => finalizeEvent(e, sk)) const result = await validateToken(validToken, 'http://test.com', 'get') expect(result).toBe(true) }) test('validateToken returns true for valid token with authorization scheme', async () => { - const validToken = await getToken('http://test.com', 'get', e => finishEvent(e, sk), true) + const validToken = await getToken('http://test.com', 'get', e => finalizeEvent(e, sk), true) const result = await validateToken(validToken, 'http://test.com', 'get') expect(result).toBe(true) @@ -104,21 +104,21 @@ describe('validateToken', () => { }) test('validateToken throws an error for a wrong url', async () => { - const validToken = await getToken('http://test.com', 'get', e => finishEvent(e, sk)) + const validToken = await getToken('http://test.com', 'get', e => finalizeEvent(e, sk)) const result = validateToken(validToken, 'http://wrong-test.com', 'get') expect(result).rejects.toThrow(Error) }) test('validateToken throws an error for a wrong method', async () => { - const validToken = await getToken('http://test.com', 'get', e => finishEvent(e, sk)) + const validToken = await getToken('http://test.com', 'get', e => finalizeEvent(e, sk)) const result = validateToken(validToken, 'http://test.com', 'post') expect(result).rejects.toThrow(Error) }) test('validateEvent returns true for valid decoded token with authorization scheme', async () => { - const validToken = await getToken('http://test.com', 'get', e => finishEvent(e, sk), true) + const validToken = await getToken('http://test.com', 'get', e => finalizeEvent(e, sk), true) const decodedResult: Event = await unpackEventFromToken(validToken) const result = await validateEvent(decodedResult, 'http://test.com', 'get') @@ -126,7 +126,7 @@ describe('validateToken', () => { }) test('validateEvent throws an error for a wrong url', async () => { - const validToken = await getToken('http://test.com', 'get', e => finishEvent(e, sk), true) + const validToken = await getToken('http://test.com', 'get', e => finalizeEvent(e, sk), true) const decodedResult: Event = await unpackEventFromToken(validToken) const result = validateEvent(decodedResult, 'http://wrong-test.com', 'get') @@ -134,7 +134,7 @@ describe('validateToken', () => { }) test('validateEvent throws an error for a wrong method', async () => { - const validToken = await getToken('http://test.com', 'get', e => finishEvent(e, sk), true) + const validToken = await getToken('http://test.com', 'get', e => finalizeEvent(e, sk), true) const decodedResult: Event = await unpackEventFromToken(validToken) const result = validateEvent(decodedResult, 'http://test.com', 'post') @@ -142,7 +142,7 @@ describe('validateToken', () => { }) test('validateEvent returns true for valid payload tag hash', async () => { - const validToken = await getToken('http://test.com', 'post', e => finishEvent(e, sk), true, { test: 'payload' }) + const validToken = await getToken('http://test.com', 'post', e => finalizeEvent(e, sk), true, { test: 'payload' }) const decodedResult: Event = await unpackEventFromToken(validToken) const result = await validateEvent(decodedResult, 'http://test.com', 'post', { test: 'payload' }) @@ -150,7 +150,7 @@ describe('validateToken', () => { }) test('validateEvent returns false for invalid payload tag hash', async () => { - const validToken = await getToken('http://test.com', 'post', e => finishEvent(e, sk), true, { test: 'a-payload' }) + const validToken = await getToken('http://test.com', 'post', e => finalizeEvent(e, sk), true, { test: 'a-payload' }) const decodedResult: Event = await unpackEventFromToken(validToken) const result = validateEvent(decodedResult, 'http://test.com', 'post', { test: 'a-different-payload' }) diff --git a/nip98.ts b/nip98.ts index d0dff18..5dd195c 100644 --- a/nip98.ts +++ b/nip98.ts @@ -1,9 +1,9 @@ import { bytesToHex } from '@noble/hashes/utils' import { sha256 } from '@noble/hashes/sha256' import { base64 } from '@scure/base' -import { Event, EventTemplate, verifySignature } from './event' -import { utf8Decoder, utf8Encoder } from './utils' -import { HTTPAuth } from './kinds' +import { Event, EventTemplate, verifyEvent } from './pure.ts' +import { utf8Decoder, utf8Encoder } from './utils.ts' +import { HTTPAuth } from './kinds.ts' const _authorizationScheme = 'Nostr ' @@ -83,7 +83,7 @@ export async function validateEvent(event: Event, url: string, method: string, b if (!event) { throw new Error('Invalid nostr event') } - if (!verifySignature(event)) { + if (!verifyEvent(event)) { throw new Error('Invalid nostr event, signature invalid') } if (event.kind !== HTTPAuth) { diff --git a/pool.test.ts b/pool.test.ts index 49e3e0f..c7c003f 100644 --- a/pool.test.ts +++ b/pool.test.ts @@ -1,7 +1,7 @@ import { test, expect, afterAll } from 'bun:test' -import { finishEvent, type Event } from './event.ts' -import { generatePrivateKey, getPublicKey } from './keys.ts' +import { finalizeEvent, type Event } from './pure.ts' +import { generateSecretKey, getPublicKey } from './pure.ts' import { SimplePool } from './pool.ts' let pool = new SimplePool() @@ -13,7 +13,7 @@ afterAll(() => { }) test('removing duplicates when querying', async () => { - let priv = generatePrivateKey() + let priv = generateSecretKey() let pub = getPublicKey(priv) pool.subscribeMany(relays, [{ authors: [pub] }], { @@ -26,7 +26,7 @@ test('removing duplicates when querying', async () => { }) let received: Event[] = [] - let event = finishEvent( + let event = finalizeEvent( { created_at: Math.round(Date.now() / 1000), content: 'test', @@ -44,7 +44,7 @@ test('removing duplicates when querying', async () => { }) test('same with double querying', async () => { - let priv = generatePrivateKey() + let priv = generateSecretKey() let pub = getPublicKey(priv) pool.subscribeMany(relays, [{ authors: [pub] }], { @@ -60,7 +60,7 @@ test('same with double querying', async () => { let received: Event[] = [] - let event = finishEvent( + let event = finalizeEvent( { created_at: Math.round(Date.now() / 1000), content: 'test2', diff --git a/pool.ts b/pool.ts index 3cf39c5..c8687d1 100644 --- a/pool.ts +++ b/pool.ts @@ -1,7 +1,7 @@ import { Relay, SubscriptionParams, Subscription } from './relay.ts' import { normalizeURL } from './utils.ts' -import type { Event } from './event.ts' +import type { Event } from './pure.ts' import { type Filter } from './filter.ts' export type SubCloser = { close: () => void } @@ -103,7 +103,7 @@ export class SimplePool { return } - let subscription = await relay.subscribe(filters, { + let subscription = relay.subscribe(filters, { ...params, oneose: () => handleEose(i), onclose: reason => handleClose(i, reason), diff --git a/pure.ts b/pure.ts index e8af48d..e29b3e8 100644 --- a/pure.ts +++ b/pure.ts @@ -14,7 +14,7 @@ class JS implements Nostr { } finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent { const event = t as VerifiedEvent - event.pubkey = this.getPublicKey(secretKey) + event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey)) event.id = getEventHash(event) event.sig = bytesToHex(schnorr.sign(getEventHash(event), secretKey)) event[verifiedSymbol] = true diff --git a/references.ts b/references.ts index 2ceb511..9d62537 100644 --- a/references.ts +++ b/references.ts @@ -1,6 +1,6 @@ import { decode, type AddressPointer, type ProfilePointer, type EventPointer } from './nip19.ts' -import type { Event } from './event.ts' +import type { Event } from './pure.ts' type Reference = { text: string diff --git a/relay.test.ts b/relay.test.ts index 6c3bb42..4376b79 100644 --- a/relay.test.ts +++ b/relay.test.ts @@ -1,7 +1,7 @@ import { test, expect, afterEach, beforeEach } from 'bun:test' -import { finishEvent } from './event.ts' -import { generatePrivateKey, getPublicKey } from './keys.ts' +import { finalizeEvent } from './pure.ts' +import { generateSecretKey, getPublicKey } from './pure.ts' import { Relay } from './relay.ts' let relay = new Relay('wss://public.relaying.io') @@ -57,7 +57,7 @@ test('querying', async () => { }, 10000) test('listening and publishing and closing', async () => { - let sk = generatePrivateKey() + let sk = generateSecretKey() let pk = getPublicKey(sk) var resolve1: (_: void) => void var resolve2: (_: void) => void @@ -91,7 +91,7 @@ test('listening and publishing and closing', async () => { }, ) - let event = finishEvent( + let event = finalizeEvent( { kind: 23571, created_at: Math.floor(Date.now() / 1000), diff --git a/relay.ts b/relay.ts index f007b34..212d910 100644 --- a/relay.ts +++ b/relay.ts @@ -1,6 +1,6 @@ /* global WebSocket */ -import { verifySignature, validateEvent, type Event, EventTemplate } from './event.ts' +import { verifyEvent, validateEvent, type Event, EventTemplate } from './pure.ts' import { matchFilters, type Filter } from './filter.ts' import { getHex64, getSubscriptionId } from './fakejson.ts' import { Queue, normalizeURL } from './utils.ts' @@ -161,7 +161,7 @@ export class Relay { case 'EVENT': { const so = this.openSubs.get(data[1] as string) as Subscription const event = data[2] as Event - if ((this.trusted || (validateEvent(event) && verifySignature(event))) && matchFilters(so.filters, event)) { + if ((this.trusted || (validateEvent(event) && verifyEvent(event))) && matchFilters(so.filters, event)) { so.onevent(event) } return diff --git a/test-helpers.ts b/test-helpers.ts index 6169aa6..b75f510 100644 --- a/test-helpers.ts +++ b/test-helpers.ts @@ -1,4 +1,4 @@ -import type { Event } from './event.ts' +import type { Event } from './pure.ts' /** Build an event for testing purposes. */ export function buildEvent(params: Partial): Event { diff --git a/utils.test.ts b/utils.test.ts index ff9ba57..7f9cd25 100644 --- a/utils.test.ts +++ b/utils.test.ts @@ -2,7 +2,7 @@ import { describe, test, expect } from 'bun:test' import { buildEvent } from './test-helpers.ts' import { Queue, insertEventIntoAscendingList, insertEventIntoDescendingList, binarySearch } from './utils.ts' -import type { Event } from './event.ts' +import type { Event } from './pure.ts' describe('inserting into a desc sorted list of events', () => { test('insert into an empty list', async () => { diff --git a/utils.ts b/utils.ts index 33d6d70..b22134d 100644 --- a/utils.ts +++ b/utils.ts @@ -1,4 +1,4 @@ -import type { Event } from './event.ts' +import type { Event } from './pure.ts' export const utf8Decoder = new TextDecoder('utf-8') export const utf8Encoder = new TextEncoder()