feat: support nip-111 (#168)

* feat: support nip-111

* test: update schemas
This commit is contained in:
Ricardo Arturo Cabral Mejía 2023-02-04 13:55:53 -05:00 committed by GitHub
parent 67ad1eb1d1
commit a7169b3569
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 53 additions and 16 deletions

View File

@ -59,6 +59,7 @@ NIPs with a relay-specific implementation are listed here.
- [x] NIP-26: Delegated Event Signing
- [x] NIP-28: Public Chat
- [x] NIP-33: Parameterized Replaceable Events
- [x] NIP-111: Relay Information Document Extensions
## Requirements

View File

@ -16,7 +16,8 @@
26,
28,
33,
40
40,
111
],
"main": "src/index.ts",
"scripts": {

View File

@ -2,6 +2,7 @@ import { NextFunction, Request, Response } from 'express'
import { path } from 'ramda'
import { createSettings } from '../../factories/settings-factory'
import { FeeSchedule } from '../../@types/settings'
import packageJson from '../../../package.json'
export const rootRequestHandler = (request: Request, response: Response, next: NextFunction) => {
@ -9,9 +10,13 @@ export const rootRequestHandler = (request: Request, response: Response, next: N
if (request.header('accept') === 'application/nostr+json') {
const {
info: { name, description, pubkey, contact },
info: { name, description, pubkey, contact, relay_url },
} = settings
const paymentsUrl = new URL(relay_url)
paymentsUrl.protocol = paymentsUrl.protocol === 'wss:' ? 'https:' : 'http:'
paymentsUrl.pathname = '/invoices'
const relayInformationDocument = {
name,
description,
@ -20,6 +25,33 @@ export const rootRequestHandler = (request: Request, response: Response, next: N
supported_nips: packageJson.supportedNips,
software: packageJson.repository.url,
version: packageJson.version,
limitation: {
max_message_length: settings.network.maxPayloadSize,
max_subscriptions: settings.limits.client.subscription.maxSubscriptions,
max_filters: settings.limits.client.subscription.maxFilters,
max_limit: 5000,
max_subid_length: 256,
min_prefix: 4,
max_event_tags: 2500,
max_content_length: 102400,
min_pow_difficulty: settings.limits.event.eventId.minLeadingZeroBits,
auth_required: false,
payment_required: settings.payments.enabled,
},
payments_url: paymentsUrl.toString(),
fees: Object
.getOwnPropertyNames(settings.payments.feeSchedules)
.reduce((prev, feeName) => {
const feeSchedules = settings.payments.feeSchedules[feeName] as FeeSchedule[]
return {
...prev,
[feeName]: feeSchedules.reduce((fees, fee) => (fee.enabled)
? [...fees, { amount: fee.amount, unit: 'msats' }]
: fees, []),
}
}, {} as Record<string, { amount: number, unit: string }>),
}
response

View File

@ -1,6 +1,6 @@
import Schema from 'joi'
export const prefixSchema = Schema.string().case('lower').hex().min(1).max(64).label('prefix')
export const prefixSchema = Schema.string().case('lower').hex().min(4).max(64).label('prefix')
export const idSchema = Schema.string().case('lower').hex().length(64).label('id')

View File

@ -31,9 +31,10 @@ export const eventSchema = Schema.object({
pubkey: pubkeySchema.required(),
created_at: createdAtSchema.required(),
kind: kindSchema.required(),
tags: Schema.array().items(tagSchema).required(),
tags: Schema.array().items(tagSchema).max(2500).required(),
content: Schema.string()
.allow('')
.max(100 * 1024) // 100 kB
.required(),
sig: signatureSchema.required(),
}).unknown(false)

View File

@ -8,5 +8,5 @@ export const filterSchema = Schema.object({
kinds: Schema.array().items(kindSchema).max(20),
since: createdAtSchema,
until: createdAtSchema,
limit: Schema.number().min(0).multiple(1).max(10000),
limit: Schema.number().min(0).multiple(1).max(5000),
}).pattern(/^#[a-z]$/, Schema.array().items(Schema.string().max(1024)).max(256))

View File

@ -12,7 +12,7 @@ export const eventMessageSchema = Schema.array().ordered(
.label('EVENT message')
export const reqMessageSchema = Schema.array()
.ordered(Schema.string().valid('REQ').required(), Schema.string().required().label('subscriptionId'))
.ordered(Schema.string().valid('REQ').required(), Schema.string().max(256).required().label('subscriptionId'))
.items(filterSchema.required().label('filter')).max(12)
.label('REQ message')

View File

@ -10,8 +10,8 @@ describe('NIP-01', () => {
describe('validate filter schema', () => {
beforeEach(() => {
filter = {
ids: ['aa', 'bb', 'cc'],
authors: ['aa', 'bb', 'cc'],
ids: ['aaaa', 'bbbb', 'cccc'],
authors: ['aaaa', 'bbbb', 'cccc'],
kinds: [0, 1, 2, 3],
since: 1000,
until: 1000,
@ -32,7 +32,7 @@ describe('NIP-01', () => {
const cases = {
ids: [
{ message: 'must be an array', transform: assocPath(['ids'], null) },
{ message: 'must contain less than or equal to 1000 items', transform: assocPath(['ids'], range(0, 1001).map(() => 'f')) },
{ message: 'must contain less than or equal to 1000 items', transform: assocPath(['ids'], range(0, 1001).map(() => 'ffff')) },
],
prefixOrId: [
{ message: 'length must be less than or equal to 64 characters long', transform: assocPath(['ids', 0], 'f'.repeat(65)) },
@ -41,7 +41,7 @@ describe('NIP-01', () => {
],
authors: [
{ message: 'must be an array', transform: assocPath(['authors'], null) },
{ message: 'must contain less than or equal to 1000 items', transform: assocPath(['authors'], range(0, 1001).map(() => 'f')) },
{ message: 'must contain less than or equal to 1000 items', transform: assocPath(['authors'], range(0, 1001).map(() => 'ffff')) },
],
prefixOrAuthor: [
{ message: 'length must be less than or equal to 64 characters long', transform: assocPath(['authors', 0], 'f'.repeat(65)) },
@ -73,7 +73,7 @@ describe('NIP-01', () => {
{ message: 'must be a number', transform: assocPath(['limit'], null) },
{ message: 'must be greater than or equal to 0', transform: assocPath(['limit'], -1) },
{ message: 'must be a multiple of 1', transform: assocPath(['limit'], Math.PI) },
{ message: 'must be less than or equal to 10000', transform: assocPath(['limit'], 10001) },
{ message: 'must be less than or equal to 5000', transform: assocPath(['limit'], 5001) },
],
'#e': [
{ message: 'must be an array', transform: assocPath(['#e'], null) },

View File

@ -25,6 +25,8 @@ describe('NIP-01', () => {
const result = validateSchema(messageSchema)(message)
console.log(result)
expect(result).not.to.have.property('error')
expect(result).to.have.deep.property('value', message)
})
@ -56,8 +58,8 @@ describe('NIP-01', () => {
'REQ',
'id',
{
ids: ['aa', 'bb', 'cc'],
authors: ['aa', 'bb', 'cc'],
ids: ['aaaa', 'bbbb', 'cccc'],
authors: ['aaaa', 'bbbb', 'cccc'],
kinds: [0, 1, 2, 3],
since: 1000,
until: 1000,
@ -67,8 +69,8 @@ describe('NIP-01', () => {
'#r': ['00', '11', '22'],
},
{
ids: ['aa', 'bb', 'cc'],
authors: ['aa', 'bb', 'cc'],
ids: ['aaaa', 'bbbb', 'cccc'],
authors: ['aaaa', 'bbbb', 'cccc'],
kinds: [0, 1, 2, 3],
since: 1000,
until: 1000,
@ -82,7 +84,7 @@ describe('NIP-01', () => {
it('returns same message if valid', () => {
const result = validateSchema(messageSchema)(message)
console.log('result', result)
expect(result).not.to.have.property('error')
expect(result).to.have.deep.property('value', message)
})