import { schnorr } from '@noble/curves/secp256k1' import { sha256 } from '@noble/hashes/sha256' import { bytesToHex } from '@noble/hashes/utils' import { getPublicKey } from './keys.ts' import { utf8Encoder } from './utils.ts' /** @deprecated Use numbers instead. */ /* eslint-disable no-unused-vars */ export enum Kind { Metadata = 0, Text = 1, RecommendRelay = 2, Contacts = 3, EncryptedDirectMessage = 4, EventDeletion = 5, Repost = 6, Reaction = 7, BadgeAward = 8, ChannelCreation = 40, ChannelMetadata = 41, ChannelMessage = 42, ChannelHideMessage = 43, ChannelMuteUser = 44, Blank = 255, Report = 1984, ZapRequest = 9734, Zap = 9735, RelayList = 10002, ClientAuth = 22242, HttpAuth = 27235, ProfileBadge = 30008, BadgeDefinition = 30009, Article = 30023, FileMetadata = 1063, } export type EventTemplate = { kind: K tags: string[][] content: string created_at: number } export type UnsignedEvent = EventTemplate & { pubkey: string } export type Event = UnsignedEvent & { id: string sig: string } export function getBlankEvent(): EventTemplate export function getBlankEvent(kind: K): EventTemplate export function getBlankEvent(kind: K | Kind.Blank = Kind.Blank) { return { kind, content: '', tags: [], created_at: 0, } } export function finishEvent(t: EventTemplate, privateKey: string): Event { let event = t as Event event.pubkey = getPublicKey(privateKey) event.id = getEventHash(event) event.sig = getSignature(event, privateKey) return event } export function serializeEvent(evt: UnsignedEvent): string { if (!validateEvent(evt)) throw new Error("can't serialize event with wrong or missing properties") return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]) } export function getEventHash(event: UnsignedEvent): string { let eventHash = sha256(utf8Encoder.encode(serializeEvent(event))) return bytesToHex(eventHash) } const isRecord = (obj: unknown): obj is Record => obj instanceof Object export function validateEvent(event: T): event is T & UnsignedEvent { if (!isRecord(event)) return false if (typeof event.kind !== 'number') return false if (typeof event.content !== 'string') return false if (typeof event.created_at !== 'number') return false if (typeof event.pubkey !== 'string') return false if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false if (!Array.isArray(event.tags)) return false for (let i = 0; i < event.tags.length; i++) { let tag = event.tags[i] if (!Array.isArray(tag)) return false for (let j = 0; j < tag.length; j++) { if (typeof tag[j] === 'object') return false } } return true } export function verifySignature(event: Event): boolean { try { return schnorr.verify(event.sig, getEventHash(event), event.pubkey) } catch (err) { return false } } /** @deprecated Use `getSignature` instead. */ export function signEvent(event: UnsignedEvent, key: string): string { console.warn( 'nostr-tools: `signEvent` is deprecated and will be removed or changed in the future. Please use `getSignature` instead.', ) return getSignature(event, key) } /** Calculate the signature for an event. */ export function getSignature(event: UnsignedEvent, key: string): string { return bytesToHex(schnorr.sign(getEventHash(event), key)) }