Skip to content

Commit

Permalink
feat!: remove nip-26 delegation support (#350)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: NIP-26 support has been removed. Delegated events will not be handled differently.
  • Loading branch information
cameri authored Jan 12, 2024
1 parent ed30823 commit 6760ab0
Show file tree
Hide file tree
Showing 16 changed files with 35 additions and 561 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ NIPs with a relay-specific implementation are listed here.
- [x] NIP-16: Event Treatment
- [x] NIP-20: Command Results
- [x] NIP-22: Event `created_at` Limits
- [x] NIP-26: Delegated Event Signing
- [ ] NIP-26: Delegated Event Signing (REMOVED)
- [x] NIP-28: Public Chat
- [x] NIP-33: Parameterized Replaceable Events
- [x] NIP-40: Expiration Timestamp
Expand Down
18 changes: 18 additions & 0 deletions migrations/20240111204900_remove_delegator_from_events_table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
exports.up = async function (knex) {
await knex.schema
.raw('DROP INDEX IF EXISTS pubkey_delegator_kind_idx;')
await knex.schema.alterTable('events', function (table) {
table.dropColumn('event_delegator')
})
}

exports.down = async function (knex) {
await knex.schema.alterTable('events', function (table) {
table.binary('event_delegator').nullable().index()
})
await knex.schema
.raw(
`CREATE UNIQUE INDEX pubkey_delegator_kind_idx
ON events ( event_pubkey, event_delegator, event_kind );`,
)
}
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
16,
20,
22,
26,
28,
33,
40
Expand Down
7 changes: 1 addition & 6 deletions src/@types/event.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ContextMetadata, EventId, Pubkey, Tag } from './base'
import { ContextMetadataKey, EventDeduplicationMetadataKey, EventDelegatorMetadataKey, EventExpirationTimeMetadataKey, EventKinds } from '../constants/base'
import { ContextMetadataKey, EventDeduplicationMetadataKey, EventExpirationTimeMetadataKey, EventKinds } from '../constants/base'

export interface BaseEvent {
id: EventId
Expand All @@ -21,10 +21,6 @@ export type UnsignedEvent = Omit<Event, 'sig'>

export type UnidentifiedEvent = Omit<UnsignedEvent, 'id'>

export interface DelegatedEvent extends Event {
[EventDelegatorMetadataKey]?: Pubkey
}

export interface ExpiringEvent extends Event {
[EventExpirationTimeMetadataKey]?: number
}
Expand All @@ -42,7 +38,6 @@ export interface DBEvent {
event_content: string
event_tags: Tag[]
event_signature: Buffer
event_delegator?: Buffer | null
event_deduplication?: string | null
first_seen: Date
deleted_at?: Date
Expand Down
2 changes: 1 addition & 1 deletion src/@types/subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export type SubscriptionId = string

export interface SubscriptionFilter {
ids?: EventId[]
kinds?: EventKinds[]
kinds?: (EventKinds | number)[]
since?: number
until?: number
authors?: Pubkey[]
Expand Down
2 changes: 0 additions & 2 deletions src/constants/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ export enum EventTags {
Event = 'e',
Pubkey = 'p',
// Multicast = 'm',
Delegation = 'delegation',
Deduplication = 'd',
Expiration = 'expiration',
Invoice = 'bolt11',
Expand All @@ -49,7 +48,6 @@ export enum PaymentsProcessors {
LNBITS = 'lnbits',
}

export const EventDelegatorMetadataKey = Symbol('Delegator')
export const EventDeduplicationMetadataKey = Symbol('Deduplication')
export const ContextMetadataKey = Symbol('Context')
export const EventExpirationTimeMetadataKey = Symbol('Expiration')
21 changes: 0 additions & 21 deletions src/factories/delegated-event-strategy-factory.ts

This file was deleted.

15 changes: 1 addition & 14 deletions src/factories/message-handler-factory.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { IEventRepository, IUserRepository } from '../@types/repositories'
import { IncomingMessage, MessageType } from '../@types/messages'
import { createSettings } from './settings-factory'
import { DelegatedEventMessageHandler } from '../handlers/delegated-event-message-handler'
import { delegatedEventStrategyFactory } from './delegated-event-strategy-factory'
import { EventMessageHandler } from '../handlers/event-message-handler'
import { eventStrategyFactory } from './event-strategy-factory'
import { isDelegatedEvent } from '../utils/event'
import { IWebSocketAdapter } from '../@types/adapters'
import { slidingWindowRateLimiterFactory } from './rate-limiter-factory'
import { SubscribeMessageHandler } from '../handlers/subscribe-message-handler'
Expand All @@ -18,16 +15,6 @@ export const messageHandlerFactory = (
switch (message[0]) {
case MessageType.EVENT:
{
if (isDelegatedEvent(message[1])) {
return new DelegatedEventMessageHandler(
adapter,
delegatedEventStrategyFactory(eventRepository),
userRepository,
createSettings,
slidingWindowRateLimiterFactory,
)
}

return new EventMessageHandler(
adapter,
eventStrategyFactory(eventRepository),
Expand All @@ -39,7 +26,7 @@ export const messageHandlerFactory = (
case MessageType.REQ:
return new SubscribeMessageHandler(adapter, eventRepository, createSettings)
case MessageType.CLOSE:
return new UnsubscribeMessageHandler(adapter,)
return new UnsubscribeMessageHandler(adapter)
default:
throw new Error(`Unknown message type: ${String(message[0]).substring(0, 64)}`)
}
Expand Down
75 changes: 0 additions & 75 deletions src/handlers/delegated-event-message-handler.ts

This file was deleted.

14 changes: 2 additions & 12 deletions src/repositories/event-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
toPairs,
} from 'ramda'

import { ContextMetadataKey, EventDeduplicationMetadataKey, EventDelegatorMetadataKey, EventExpirationTimeMetadataKey } from '../constants/base'
import { ContextMetadataKey, EventDeduplicationMetadataKey, EventExpirationTimeMetadataKey } from '../constants/base'
import { DatabaseClient, EventId } from '../@types/base'
import { DBEvent, Event } from '../@types/event'
import { IEventRepository, IQueryResult } from '../@types/repositories'
Expand Down Expand Up @@ -105,7 +105,7 @@ export class EventRepository implements IEventRepository {
])(currentFilter[filterName] as string[])
})
})({
authors: ['event_pubkey', 'event_delegator'],
authors: ['event_pubkey'],
ids: ['event_id'],
})

Expand Down Expand Up @@ -180,11 +180,6 @@ export class EventRepository implements IEventRepository {
event_tags: pipe(prop('tags'), toJSON),
event_content: prop('content'),
event_signature: pipe(prop('sig'), toBuffer),
event_delegator: ifElse(
propSatisfies(is(String), EventDelegatorMetadataKey),
pipe(prop(EventDelegatorMetadataKey as any), toBuffer),
always(null),
),
remote_address: path([ContextMetadataKey as any, 'remoteAddress', 'address']),
expires_at: ifElse(
propSatisfies(is(Number), EventExpirationTimeMetadataKey),
Expand Down Expand Up @@ -212,11 +207,6 @@ export class EventRepository implements IEventRepository {
event_tags: pipe(prop('tags'), toJSON),
event_content: prop('content'),
event_signature: pipe(prop('sig'), toBuffer),
event_delegator: ifElse(
propSatisfies(is(String), EventDelegatorMetadataKey),
pipe(prop(EventDelegatorMetadataKey as any), toBuffer),
always(null),
),
event_deduplication: ifElse(
propSatisfies(isNil, EventDeduplicationMetadataKey),
pipe(paths([['pubkey'], ['kind']]), toJSON),
Expand Down
61 changes: 2 additions & 59 deletions src/utils/event.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as secp256k1 from '@noble/secp256k1'

import { applySpec, converge, curry, mergeLeft, nth, omit, pipe, prop, reduceBy } from 'ramda'
import { applySpec, pipe, prop } from 'ramda'
import { CanonicalEvent, DBEvent, Event, UnidentifiedEvent, UnsignedEvent } from '../@types/event'
import { createCipheriv, getRandomValues } from 'crypto'
import { EventId, Pubkey, Tag } from '../@types/base'
Expand All @@ -12,7 +12,6 @@ import { EventKindsRange } from '../@types/settings'
import { fromBuffer } from './transform'
import { getLeadingZeroBits } from './proof-of-work'
import { isGenericTagQuery } from './filter'
import { RuneLike } from './runes/rune-like'
import { SubscriptionFilter } from '../@types/subscription'
import { WebSocketServerAdapterEvent } from '../constants/adapter'

Expand Down Expand Up @@ -68,18 +67,7 @@ export const isEventMatchingFilter = (filter: SubscriptionFilter) => (event: Eve
if (
!filter.authors.some(startsWith(event.pubkey))
) {
if (isDelegatedEvent(event)) {
const delegation = event.tags.find((tag) => tag[0] === EventTags.Delegation)
if (typeof delegation === 'undefined') {
return false
}

if (!filter.authors.some(startsWith(delegation[1]))) {
return false
}
} else {
return false
}
return false
}
}

Expand Down Expand Up @@ -116,51 +104,6 @@ export const isEventMatchingFilter = (filter: SubscriptionFilter) => (event: Eve
return true
}

export const isDelegatedEvent = (event: Event): boolean => {
return event.tags.some((tag) => tag.length === 4 && tag[0] === EventTags.Delegation)
}

export const isDelegatedEventValid = async (event: Event): Promise<boolean> => {
const delegation = event.tags.find((tag) => tag.length === 4 && tag[0] === EventTags.Delegation)
if (!delegation) {
return false
}


// Validate rune
const runifiedEvent = (converge(
curry(mergeLeft),
[
omit(['tags']),
pipe(
prop('tags') as any,
reduceBy<EventTags, string[]>(
(acc, tag) => ([...acc, tag[1]]),
[],
nth(0) as any,
),
),
],
) as any)(event)

let result: boolean
try {
[result] = RuneLike.from(delegation[2]).test(runifiedEvent)
} catch (error) {
result = false
}

if (!result) {
return false
}

const serializedDelegationTag = `nostr:${delegation[0]}:${event.pubkey}:${delegation[2]}`

const token = await secp256k1.utils.sha256(Buffer.from(serializedDelegationTag))

return secp256k1.schnorr.verify(delegation[3], token, delegation[1])
}

export const getEventHash = async (event: Event | UnidentifiedEvent | UnsignedEvent): Promise<string> => {
const id = await secp256k1.utils.sha256(Buffer.from(JSON.stringify(serializeEvent(event))))

Expand Down
46 changes: 0 additions & 46 deletions test/unit/factories/delegated-event-strategy-factory.spec.ts

This file was deleted.

Loading

0 comments on commit 6760ab0

Please sign in to comment.