diff --git a/README.md b/README.md index 02c664d..64782ea 100644 --- a/README.md +++ b/README.md @@ -258,43 +258,6 @@ sub.on('event', async event => { }) ``` -### Performing and checking for delegation - -```js -import { nip26, getPublicKey, generatePrivateKey } from 'nostr-tools' - -// delegator -let sk1 = generatePrivateKey() -let pk1 = getPublicKey(sk1) - -// delegatee -let sk2 = generatePrivateKey() -let pk2 = getPublicKey(sk2) - -// generate delegation -let delegation = nip26.createDelegation(sk1, { - pubkey: pk2, - kind: 1, - since: Math.round(Date.now() / 1000), - until: Math.round(Date.now() / 1000) + 60 * 60 * 24 * 30 /* 30 days */, -}) - -// the delegatee uses the delegation when building an event -let event = { - pubkey: pk2, - kind: 1, - created_at: Math.round(Date.now() / 1000), - content: 'hello from a delegated key', - tags: [['delegation', delegation.from, delegation.cond, delegation.sig]], -} - -// finally any receiver of this event can check for the presence of a valid delegation tag -let delegator = nip26.getDelegator(event) -assert(delegator === pk1) // will be null if there is no delegation tag or if it is invalid -``` - -Please consult the tests or [the source code](https://github.com/fiatjaf/nostr-tools) for more information that isn't available here. - ### Using from the browser (if you don't want to use a bundler) ```html diff --git a/index.ts b/index.ts index 1380332..73c1c40 100644 --- a/index.ts +++ b/index.ts @@ -13,7 +13,6 @@ export * as nip18 from './nip18.ts' export * as nip19 from './nip19.ts' export * as nip21 from './nip21.ts' export * as nip25 from './nip25.ts' -export * as nip26 from './nip26.ts' export * as nip27 from './nip27.ts' export * as nip28 from './nip28.ts' export * as nip39 from './nip39.ts' diff --git a/nip26.test.ts b/nip26.test.ts deleted file mode 100644 index 9bc7020..0000000 --- a/nip26.test.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { getPublicKey, generatePrivateKey } from './keys.ts' -import { getDelegator, createDelegation } from './nip26.ts' -import { buildEvent } from './test-helpers.ts' - -test('parse good delegation from NIP', async () => { - expect( - getDelegator({ - id: 'a080fd288b60ac2225ff2e2d815291bd730911e583e177302cc949a15dc2b2dc', - pubkey: '62903b1ff41559daf9ee98ef1ae67cc52f301bb5ce26d14baba3052f649c3f49', - created_at: 1660896109, - kind: 1, - tags: [ - [ - 'delegation', - '86f0689bd48dcd19c67a19d994f938ee34f251d8c39976290955ff585f2db42e', - 'kind=1&created_at>1640995200', - 'c33c88ba78ec3c760e49db591ac5f7b129e3887c8af7729795e85a0588007e5ac89b46549232d8f918eefd73e726cb450135314bfda419c030d0b6affe401ec1', - ], - ], - content: 'Hello world', - sig: 'cd4a3cd20dc61dcbc98324de561a07fd23b3d9702115920c0814b5fb822cc5b7c5bcdaf3fa326d24ed50c5b9c8214d66c75bae34e3a84c25e4d122afccb66eb6', - }), - ).toEqual('86f0689bd48dcd19c67a19d994f938ee34f251d8c39976290955ff585f2db42e') -}) - -test('parse bad delegations', async () => { - expect( - getDelegator({ - id: 'a080fd288b60ac2225ff2e2d815291bd730911e583e177302cc949a15dc2b2dc', - pubkey: '62903b1ff41559daf9ee98ef1ae67cc52f301bb5ce26d14baba3052f649c3f49', - created_at: 1660896109, - kind: 1, - tags: [ - [ - 'delegation', - '86f0689bd48dcd19c67a19d994f938ee34f251d8c39976290955ff585f2db42f', - 'kind=1&created_at>1640995200', - 'c33c88ba78ec3c760e49db591ac5f7b129e3887c8af7729795e85a0588007e5ac89b46549232d8f918eefd73e726cb450135314bfda419c030d0b6affe401ec1', - ], - ], - content: 'Hello world', - sig: 'cd4a3cd20dc61dcbc98324de561a07fd23b3d9702115920c0814b5fb822cc5b7c5bcdaf3fa326d24ed50c5b9c8214d66c75bae34e3a84c25e4d122afccb66eb6', - }), - ).toEqual(null) - - expect( - getDelegator({ - id: 'a080fd288b60ac2225ff2e2d815291bd730911e583e177302cc949a15dc2b2dc', - pubkey: '62903b1ff41559daf9ee98ef1ae67cc52f301bb5ce26d14baba3052f649c3f49', - created_at: 1660896109, - kind: 1, - tags: [ - [ - 'delegation', - '86f0689bd48dcd19c67a19d994f938ee34f251d8c39976290955ff585f2db42e', - 'kind=1&created_at>1740995200', - 'c33c88ba78ec3c760e49db591ac5f7b129e3887c8af7729795e85a0588007e5ac89b46549232d8f918eefd73e726cb450135314bfda419c030d0b6affe401ec1', - ], - ], - content: 'Hello world', - sig: 'cd4a3cd20dc61dcbc98324de561a07fd23b3d9702115920c0814b5fb822cc5b7c5bcdaf3fa326d24ed50c5b9c8214d66c75bae34e3a84c25e4d122afccb66eb6', - }), - ).toEqual(null) - - expect( - getDelegator({ - id: 'a080fd288b60ac2225ff2e2d815291bd730911e583e177302cc949a15dc2b2dc', - pubkey: '62903b1ff41559daf9ee98ef1ae67c152f301bb5ce26d14baba3052f649c3f49', - created_at: 1660896109, - kind: 1, - tags: [ - [ - 'delegation', - '86f0689bd48dcd19c67a19d994f938ee34f251d8c39976290955ff585f2db42e', - 'kind=1&created_at>1640995200', - 'c33c88ba78ec3c760e49db591ac5f7b129e3887c8af7729795e85a0588007e5ac89b46549232d8f918eefd73e726cb450135314bfda419c030d0b6affe401ec1', - ], - ], - content: 'Hello world', - sig: 'cd4a3cd20dc61dcbc98324de561a07fd23b3d9702115920c0814b5fb822cc5b7c5bcdaf3fa326d24ed50c5b9c8214d66c75bae34e3a84c25e4d122afccb66eb6', - }), - ).toEqual(null) -}) - -test('create and verify delegation', async () => { - let sk1 = generatePrivateKey() - let pk1 = getPublicKey(sk1) - let sk2 = generatePrivateKey() - let pk2 = getPublicKey(sk2) - let delegation = createDelegation(sk1, { pubkey: pk2, kind: 1 }) - expect(delegation).toHaveProperty('from', pk1) - expect(delegation).toHaveProperty('to', pk2) - expect(delegation).toHaveProperty('cond', 'kind=1') - - let event = buildEvent({ - kind: 1, - tags: [['delegation', delegation.from, delegation.cond, delegation.sig]], - pubkey: pk2, - }) - expect(getDelegator(event)).toEqual(pk1) -}) diff --git a/nip26.ts b/nip26.ts deleted file mode 100644 index 4ba46db..0000000 --- a/nip26.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { schnorr } from '@noble/curves/secp256k1' -import { bytesToHex } from '@noble/hashes/utils' -import { sha256 } from '@noble/hashes/sha256' - -import { utf8Encoder } from './utils.ts' -import { getPublicKey } from './keys.ts' - -import type { Event } from './event.ts' - -export type Parameters = { - pubkey: string // the key to whom the delegation will be given - kind?: number - until?: number // delegation will only be valid until this date - since?: number // delegation will be valid from this date on -} - -export type Delegation = { - from: string // the pubkey who signed the delegation - to: string // the pubkey that is allowed to use the delegation - cond: string // the string of conditions as they should be included in the event tag - sig: string -} - -export function createDelegation(privateKey: string, parameters: Parameters): Delegation { - let conditions = [] - if ((parameters.kind || -1) >= 0) conditions.push(`kind=${parameters.kind}`) - if (parameters.until) conditions.push(`created_at<${parameters.until}`) - if (parameters.since) conditions.push(`created_at>${parameters.since}`) - let cond = conditions.join('&') - - if (cond === '') throw new Error('refusing to create a delegation without any conditions') - - let sighash = sha256(utf8Encoder.encode(`nostr:delegation:${parameters.pubkey}:${cond}`)) - - let sig = bytesToHex(schnorr.sign(sighash, privateKey)) - - return { - from: getPublicKey(privateKey), - to: parameters.pubkey, - cond, - sig, - } -} - -export function getDelegator(event: Event): string | null { - // find delegation tag - let tag = event.tags.find(tag => tag[0] === 'delegation' && tag.length >= 4) - if (!tag) return null - - let pubkey = tag[1] - let cond = tag[2] - let sig = tag[3] - - // check conditions - let conditions = cond.split('&') - for (let i = 0; i < conditions.length; i++) { - let [key, operator, value] = conditions[i].split(/\b/) - - // the supported conditions are just 'kind' and 'created_at' for now - if (key === 'kind' && operator === '=' && event.kind === parseInt(value)) continue - else if (key === 'created_at' && operator === '<' && event.created_at < parseInt(value)) continue - else if (key === 'created_at' && operator === '>' && event.created_at > parseInt(value)) continue - else return null // invalid condition - } - - // check signature - let sighash = sha256(utf8Encoder.encode(`nostr:delegation:${event.pubkey}:${cond}`)) - if (!schnorr.verify(sig, sighash, pubkey)) return null - - return pubkey -} diff --git a/package.json b/package.json index f3130ea..1d01a11 100644 --- a/package.json +++ b/package.json @@ -94,11 +94,6 @@ "require": "./lib/cjs/nip25.js", "types": "./lib/types/nip25.d.ts" }, - "./nip26": { - "import": "./lib/esm/nip26.js", - "require": "./lib/cjs/nip26.js", - "types": "./lib/types/nip26.d.ts" - }, "./nip27": { "import": "./lib/esm/nip27.js", "require": "./lib/cjs/nip27.js",