libp2r2p 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -2
- package/base16/index.js +6 -3
- package/base36/index.js +12 -8
- package/base62/index.js +15 -11
- package/base64/index.js +18 -2
- package/base93/index.js +19 -7
- package/content-key/event/index.js +40 -12
- package/double-dh/index.js +21 -6
- package/ecdh/index.js +12 -1
- package/error/index.js +32 -0
- package/event/helpers/serialize.js +31 -0
- package/event/index.js +116 -0
- package/i18n/index.js +15 -13
- package/idb/index.js +5 -3
- package/idb-queue/index.js +21 -20
- package/index.js +10 -0
- package/key/index.js +31 -18
- package/kind/index.js +244 -0
- package/nip04/index.js +47 -0
- package/nip05/index.js +61 -0
- package/nip19/index.js +176 -43
- package/nip44/helpers.js +95 -0
- package/nip44/index.js +14 -0
- package/nip44-v3/index.js +35 -16
- package/nip46/helpers/frame.js +6 -6
- package/nip46/helpers/url.js +8 -6
- package/nip46/services/bunker-signer.js +7 -6
- package/nip46/services/client.js +8 -7
- package/nip46/services/server-session.js +8 -7
- package/nip46/services/transport.js +9 -8
- package/nip96/index.js +285 -0
- package/nip98/index.js +56 -0
- package/nwt/index.js +241 -0
- package/package.json +22 -3
- package/private-channel/helpers/chunks.js +7 -6
- package/private-channel/helpers/event.js +5 -4
- package/private-channel/index.js +63 -61
- package/private-channel/services/received-chunks.js +4 -3
- package/private-message/index.js +32 -31
- package/private-messenger/index.js +23 -22
- package/private-messenger/recovery/index.js +15 -14
- package/private-messenger/services/channel-state.js +2 -1
- package/relay/helpers/hll.js +3 -1
- package/relay/services/query.js +3 -3
- package/relay/services/relay-connection.js +222 -121
- package/relay/services/relay-pool.js +13 -10
- package/temporary-storage/index.js +3 -1
- package/url/index.js +131 -0
- package/web-storage-queue/index.js +8 -6
package/nwt/index.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { base64UrlToBytes, bytesToBase64Url } from '../base64/index.js'
|
|
2
|
+
import { ValidationError } from '../error/index.js'
|
|
3
|
+
import { isValidEvent } from '../event/index.js'
|
|
4
|
+
import { NWT } from '../kind/index.js'
|
|
5
|
+
|
|
6
|
+
const textDecoder = new TextDecoder('utf-8', { fatal: true })
|
|
7
|
+
const textEncoder = new TextEncoder()
|
|
8
|
+
const BASE64URL = /^[A-Za-z0-9_-]+$/
|
|
9
|
+
const REGISTERED_CLAIMS = new Set(['iss', 'sub', 'aud', 'iat', 'exp', 'nbf'])
|
|
10
|
+
const SINGLE_CLAIMS = new Set(['iss', 'sub', 'iat', 'exp', 'nbf'])
|
|
11
|
+
const TIMESTAMP_CLAIMS = new Set(['iat', 'exp', 'nbf'])
|
|
12
|
+
const MAX_CLAIMS = 512
|
|
13
|
+
|
|
14
|
+
function fail (code, { message = code, cause } = {}) {
|
|
15
|
+
throw new ValidationError(code, { message, cause })
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function cloneTags (tags) {
|
|
19
|
+
return tags.map(tag => tag.slice())
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function areTagsEqual (left, right) {
|
|
23
|
+
return left.length === right.length && left.every((tag, index) => {
|
|
24
|
+
const other = right[index]
|
|
25
|
+
return tag.length === other.length && tag.every((value, valueIndex) => value === other[valueIndex])
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function normalizeTimestamp (value, code) {
|
|
30
|
+
if (!Number.isSafeInteger(value) || value < 0) fail(code)
|
|
31
|
+
return value
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function parseTimestamp (value, code) {
|
|
35
|
+
if (!/^(?:0|[1-9][0-9]*)$/.test(value)) fail(code)
|
|
36
|
+
return normalizeTimestamp(Number(value), code)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function normalizeStringClaim (value, code) {
|
|
40
|
+
if (typeof value !== 'string' || value.length === 0) fail(code)
|
|
41
|
+
return value
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function normalizeAudience (audience, { required = false } = {}) {
|
|
45
|
+
if (audience === undefined) {
|
|
46
|
+
if (required) fail('INVALID_AUDIENCE')
|
|
47
|
+
return []
|
|
48
|
+
}
|
|
49
|
+
const values = typeof audience === 'string' ? [audience] : audience
|
|
50
|
+
if (!Array.isArray(values) || (required && values.length === 0)) fail('INVALID_AUDIENCE')
|
|
51
|
+
return values.map(value => normalizeStringClaim(value, 'INVALID_AUDIENCE'))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function normalizeExtraClaims (claims) {
|
|
55
|
+
if (claims === undefined) return []
|
|
56
|
+
if (!Array.isArray(claims)) fail('INVALID_CLAIMS')
|
|
57
|
+
return claims.map(tag => {
|
|
58
|
+
if (!Array.isArray(tag) || tag.length < 2 || tag.some(value => typeof value !== 'string')) fail('INVALID_CLAIM')
|
|
59
|
+
if (tag[0].length === 0 || REGISTERED_CLAIMS.has(tag[0])) fail('INVALID_CUSTOM_CLAIM')
|
|
60
|
+
return tag.slice()
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function parseClaims (event) {
|
|
65
|
+
if (event.tags.length > MAX_CLAIMS) fail('TOO_MANY_CLAIMS')
|
|
66
|
+
|
|
67
|
+
const single = new Map()
|
|
68
|
+
const audience = []
|
|
69
|
+
const claims = []
|
|
70
|
+
|
|
71
|
+
for (const tag of event.tags) {
|
|
72
|
+
if (tag.length < 2 || tag[0].length === 0) fail('INVALID_CLAIM')
|
|
73
|
+
const name = tag[0]
|
|
74
|
+
|
|
75
|
+
if (!REGISTERED_CLAIMS.has(name)) {
|
|
76
|
+
claims.push(tag.slice())
|
|
77
|
+
continue
|
|
78
|
+
}
|
|
79
|
+
if (tag.length !== 2 || tag[1].length === 0) fail('INVALID_REGISTERED_CLAIM')
|
|
80
|
+
if (name === 'aud') {
|
|
81
|
+
audience.push(tag[1])
|
|
82
|
+
continue
|
|
83
|
+
}
|
|
84
|
+
if (SINGLE_CLAIMS.has(name) && single.has(name)) fail('DUPLICATE_SINGLE_CLAIM')
|
|
85
|
+
single.set(name, TIMESTAMP_CLAIMS.has(name)
|
|
86
|
+
? parseTimestamp(tag[1], `INVALID_${name.toUpperCase()}_CLAIM`)
|
|
87
|
+
: tag[1])
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const expiration = single.get('exp') ?? null
|
|
91
|
+
const notBefore = single.get('nbf') ?? null
|
|
92
|
+
if (expiration !== null && notBefore !== null && notBefore > expiration) fail('INVALID_TIME_WINDOW')
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
event,
|
|
96
|
+
id: event.id,
|
|
97
|
+
signer: event.pubkey,
|
|
98
|
+
issuer: single.get('iss') ?? event.pubkey,
|
|
99
|
+
subject: single.get('sub') ?? event.pubkey,
|
|
100
|
+
audience,
|
|
101
|
+
issuedAt: single.get('iat') ?? event.created_at,
|
|
102
|
+
expiration,
|
|
103
|
+
notBefore,
|
|
104
|
+
claims,
|
|
105
|
+
content: event.content
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function parseVerifiedEvent (event) {
|
|
110
|
+
if (!isValidEvent(event)) fail('INVALID_NWT_EVENT')
|
|
111
|
+
if (event.kind !== NWT) fail('INVALID_NWT_KIND')
|
|
112
|
+
return parseClaims(event)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function wireEvent (event) {
|
|
116
|
+
return {
|
|
117
|
+
id: event.id,
|
|
118
|
+
pubkey: event.pubkey,
|
|
119
|
+
created_at: event.created_at,
|
|
120
|
+
kind: event.kind,
|
|
121
|
+
tags: cloneTags(event.tags),
|
|
122
|
+
content: event.content,
|
|
123
|
+
sig: event.sig
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export async function createToken ({
|
|
128
|
+
signEvent,
|
|
129
|
+
issuer,
|
|
130
|
+
subject,
|
|
131
|
+
audience,
|
|
132
|
+
issuedAt,
|
|
133
|
+
expiration,
|
|
134
|
+
notBefore,
|
|
135
|
+
claims,
|
|
136
|
+
content = '',
|
|
137
|
+
createdAt = Math.floor(Date.now() / 1000)
|
|
138
|
+
} = {}) {
|
|
139
|
+
if (typeof signEvent !== 'function') fail('SIGN_EVENT_SHOULD_BE_A_FUNCTION')
|
|
140
|
+
if (typeof content !== 'string') fail('INVALID_CONTENT')
|
|
141
|
+
|
|
142
|
+
const tags = []
|
|
143
|
+
if (issuer !== undefined) tags.push(['iss', normalizeStringClaim(issuer, 'INVALID_ISSUER')])
|
|
144
|
+
if (subject !== undefined) tags.push(['sub', normalizeStringClaim(subject, 'INVALID_SUBJECT')])
|
|
145
|
+
for (const value of normalizeAudience(audience)) tags.push(['aud', value])
|
|
146
|
+
if (issuedAt !== undefined) tags.push(['iat', String(normalizeTimestamp(issuedAt, 'INVALID_ISSUED_AT'))])
|
|
147
|
+
if (expiration !== undefined) tags.push(['exp', String(normalizeTimestamp(expiration, 'INVALID_EXPIRATION'))])
|
|
148
|
+
if (notBefore !== undefined) tags.push(['nbf', String(normalizeTimestamp(notBefore, 'INVALID_NOT_BEFORE'))])
|
|
149
|
+
tags.push(...normalizeExtraClaims(claims))
|
|
150
|
+
if (tags.length > MAX_CLAIMS) fail('TOO_MANY_CLAIMS')
|
|
151
|
+
|
|
152
|
+
const expected = {
|
|
153
|
+
kind: NWT,
|
|
154
|
+
created_at: normalizeTimestamp(createdAt, 'INVALID_CREATED_AT'),
|
|
155
|
+
tags,
|
|
156
|
+
content
|
|
157
|
+
}
|
|
158
|
+
if (expiration !== undefined && notBefore !== undefined && notBefore > expiration) fail('INVALID_TIME_WINDOW')
|
|
159
|
+
|
|
160
|
+
const event = await signEvent({ ...expected, tags: cloneTags(expected.tags) })
|
|
161
|
+
parseVerifiedEvent(event)
|
|
162
|
+
if (
|
|
163
|
+
event.kind !== expected.kind ||
|
|
164
|
+
event.created_at !== expected.created_at ||
|
|
165
|
+
event.content !== expected.content ||
|
|
166
|
+
!areTagsEqual(event.tags, expected.tags)
|
|
167
|
+
) fail('SIGNED_NWT_EVENT_WAS_CHANGED')
|
|
168
|
+
return event
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function encodeToken (event, { includeAuthorizationScheme = false } = {}) {
|
|
172
|
+
parseVerifiedEvent(event)
|
|
173
|
+
const token = bytesToBase64Url(textEncoder.encode(JSON.stringify(wireEvent(event))))
|
|
174
|
+
return includeAuthorizationScheme ? `Nostr ${token}` : token
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function decodeToken (value) {
|
|
178
|
+
if (typeof value !== 'string' || value.length === 0) fail('INVALID_NWT_TOKEN')
|
|
179
|
+
let token = value
|
|
180
|
+
if (value.startsWith('Nostr ')) token = value.slice(6)
|
|
181
|
+
else if (/\s/.test(value)) fail('INVALID_AUTHORIZATION_HEADER')
|
|
182
|
+
if (!BASE64URL.test(token) || token.length % 4 === 1) fail('INVALID_NWT_ENCODING')
|
|
183
|
+
|
|
184
|
+
let bytes
|
|
185
|
+
try {
|
|
186
|
+
bytes = base64UrlToBytes(token)
|
|
187
|
+
if (bytesToBase64Url(bytes) !== token) fail('INVALID_NWT_ENCODING')
|
|
188
|
+
} catch (cause) {
|
|
189
|
+
fail('INVALID_NWT_ENCODING', { cause })
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
const event = JSON.parse(textDecoder.decode(bytes))
|
|
194
|
+
if (!event || typeof event !== 'object' || Array.isArray(event)) fail('INVALID_NWT_EVENT_JSON')
|
|
195
|
+
return event
|
|
196
|
+
} catch (error) {
|
|
197
|
+
if (error instanceof ValidationError && error.code === 'INVALID_NWT_EVENT_JSON') throw error
|
|
198
|
+
fail('INVALID_NWT_EVENT_JSON', { cause: error })
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function validateToken (value, {
|
|
203
|
+
audience,
|
|
204
|
+
signer,
|
|
205
|
+
issuer,
|
|
206
|
+
subject,
|
|
207
|
+
now = Math.floor(Date.now() / 1000),
|
|
208
|
+
clockSkewSeconds = 60,
|
|
209
|
+
requireAudience = false,
|
|
210
|
+
requireExpiration = false
|
|
211
|
+
} = {}) {
|
|
212
|
+
const event = typeof value === 'string' ? decodeToken(value) : value
|
|
213
|
+
const token = parseVerifiedEvent(event)
|
|
214
|
+
normalizeTimestamp(now, 'INVALID_NOW')
|
|
215
|
+
normalizeTimestamp(clockSkewSeconds, 'INVALID_CLOCK_SKEW')
|
|
216
|
+
if (!Number.isSafeInteger(now + clockSkewSeconds) || !Number.isSafeInteger(now - clockSkewSeconds)) {
|
|
217
|
+
fail('INVALID_CLOCK_SKEW')
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (token.notBefore !== null && now + clockSkewSeconds < token.notBefore) fail('NWT_NOT_YET_VALID')
|
|
221
|
+
if (token.expiration !== null && now - clockSkewSeconds >= token.expiration) fail('NWT_EXPIRED')
|
|
222
|
+
if (requireExpiration && token.expiration === null) fail('NWT_EXPIRATION_REQUIRED')
|
|
223
|
+
|
|
224
|
+
if (token.audience.length === 0) {
|
|
225
|
+
if (requireAudience) fail('NWT_AUDIENCE_REQUIRED')
|
|
226
|
+
} else {
|
|
227
|
+
if (audience === undefined) fail('NWT_AUDIENCE_REQUIRED')
|
|
228
|
+
const expectedAudience = normalizeAudience(audience, { required: true })
|
|
229
|
+
if (!expectedAudience.some(value => token.audience.includes(value))) fail('NWT_AUDIENCE_MISMATCH')
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
for (const [expected, actual, code] of [
|
|
233
|
+
[signer, token.signer, 'NWT_SIGNER_MISMATCH'],
|
|
234
|
+
[issuer, token.issuer, 'NWT_ISSUER_MISMATCH'],
|
|
235
|
+
[subject, token.subject, 'NWT_SUBJECT_MISMATCH']
|
|
236
|
+
]) {
|
|
237
|
+
if (expected !== undefined && normalizeStringClaim(expected, code) !== actual) fail(code)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return token
|
|
241
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "libp2r2p",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Peer-to-relay-to-peer",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"p2r2p",
|
|
@@ -20,20 +20,30 @@
|
|
|
20
20
|
"content-key",
|
|
21
21
|
"double-dh",
|
|
22
22
|
"ecdh",
|
|
23
|
+
"error",
|
|
24
|
+
"event",
|
|
23
25
|
"idb",
|
|
24
26
|
"idb-queue",
|
|
25
27
|
"i18n",
|
|
26
28
|
"index.js",
|
|
27
29
|
"key",
|
|
30
|
+
"kind",
|
|
28
31
|
"network",
|
|
32
|
+
"nip04",
|
|
33
|
+
"nip05",
|
|
29
34
|
"nip19",
|
|
35
|
+
"nip44",
|
|
30
36
|
"nip44-v3",
|
|
31
37
|
"nip46",
|
|
38
|
+
"nip96",
|
|
39
|
+
"nip98",
|
|
40
|
+
"nwt",
|
|
32
41
|
"private-channel",
|
|
33
42
|
"private-message",
|
|
34
43
|
"private-messenger",
|
|
35
44
|
"relay",
|
|
36
45
|
"temporary-storage",
|
|
46
|
+
"url",
|
|
37
47
|
"web-storage-queue"
|
|
38
48
|
],
|
|
39
49
|
"scripts": {
|
|
@@ -51,28 +61,37 @@
|
|
|
51
61
|
"./content-key/event": "./content-key/event/index.js",
|
|
52
62
|
"./double-dh": "./double-dh/index.js",
|
|
53
63
|
"./ecdh": "./ecdh/index.js",
|
|
64
|
+
"./error": "./error/index.js",
|
|
65
|
+
"./event": "./event/index.js",
|
|
54
66
|
"./idb": "./idb/index.js",
|
|
55
67
|
"./idb-queue": "./idb-queue/index.js",
|
|
56
68
|
"./i18n": "./i18n/index.js",
|
|
57
69
|
"./key": "./key/index.js",
|
|
70
|
+
"./kind": "./kind/index.js",
|
|
58
71
|
"./network": "./network/index.js",
|
|
72
|
+
"./nip04": "./nip04/index.js",
|
|
73
|
+
"./nip05": "./nip05/index.js",
|
|
59
74
|
"./nip19": "./nip19/index.js",
|
|
75
|
+
"./nip44": "./nip44/index.js",
|
|
60
76
|
"./nip44-v3": "./nip44-v3/index.js",
|
|
61
77
|
"./nip46": "./nip46/index.js",
|
|
78
|
+
"./nip96": "./nip96/index.js",
|
|
79
|
+
"./nip98": "./nip98/index.js",
|
|
80
|
+
"./nwt": "./nwt/index.js",
|
|
62
81
|
"./private-channel": "./private-channel/index.js",
|
|
63
82
|
"./private-message": "./private-message/index.js",
|
|
64
83
|
"./private-messenger": "./private-messenger/index.js",
|
|
65
84
|
"./private-messenger/recovery": "./private-messenger/recovery/index.js",
|
|
66
85
|
"./relay": "./relay/index.js",
|
|
67
86
|
"./temporary-storage": "./temporary-storage/index.js",
|
|
87
|
+
"./url": "./url/index.js",
|
|
68
88
|
"./web-storage-queue": "./web-storage-queue/index.js"
|
|
69
89
|
},
|
|
70
90
|
"dependencies": {
|
|
71
91
|
"@noble/ciphers": "2.2.0",
|
|
72
92
|
"@noble/curves": "2.2.0",
|
|
73
93
|
"@noble/hashes": "2.2.0",
|
|
74
|
-
"@scure/base": "2.0.0"
|
|
75
|
-
"nostr-tools": "2.23.5"
|
|
94
|
+
"@scure/base": "2.0.0"
|
|
76
95
|
},
|
|
77
96
|
"devDependencies": {
|
|
78
97
|
"fake-indexeddb": "6.2.5"
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { generateSecretKey, getPublicKey } from '
|
|
1
|
+
import { generateSecretKey, getPublicKey } from '../../key/index.js'
|
|
2
2
|
import { bytesToBase64, base64ToBytes } from '../../base64/index.js'
|
|
3
3
|
import { bytesToHex } from '../../base16/index.js'
|
|
4
|
-
import {
|
|
4
|
+
import { isValidIykcProof } from '../../content-key/event/index.js'
|
|
5
|
+
import { ValidationError } from '../../error/index.js'
|
|
5
6
|
import * as nip44v3 from '../../nip44-v3/index.js'
|
|
6
7
|
import { JSONL_CHUNK_BYTES } from './chunk-size.js'
|
|
7
8
|
import { ROUTER_KIND } from '../constants/index.js'
|
|
@@ -28,7 +29,7 @@ function rowTempKey (id, index) {
|
|
|
28
29
|
|
|
29
30
|
function normalizeContentKey ({ receiverPubkey, iykcPubkey = '', iykcProof = '' } = {}) {
|
|
30
31
|
if (!iykcPubkey) return { iykcPubkey: '', iykcProof: '' }
|
|
31
|
-
if (!
|
|
32
|
+
if (!isValidIykcProof({ receiverPubkey, iykcPubkey, iykcProof })) throw new ValidationError('INVALID_IYKC_PROOF')
|
|
32
33
|
return { iykcPubkey, iykcProof }
|
|
33
34
|
}
|
|
34
35
|
|
|
@@ -157,7 +158,7 @@ function setPreparedRow (id, index, row, temporaryStorage) {
|
|
|
157
158
|
|
|
158
159
|
function readPreparedRow (preparedRows, index) {
|
|
159
160
|
const row = storageFor(preparedRows.temporaryStorage).getItem(rowTempKey(preparedRows.id, index))
|
|
160
|
-
if (typeof row !== 'string') throw new
|
|
161
|
+
if (typeof row !== 'string') throw new ValidationError('MISSING_PREPARED_ROW')
|
|
161
162
|
return row
|
|
162
163
|
}
|
|
163
164
|
|
|
@@ -187,7 +188,7 @@ async function prepareEnvelopeRowsOnce ({ id, senderSigner, receivers, receiverC
|
|
|
187
188
|
ciphertext = encrypted[0]
|
|
188
189
|
const nextContentPubkey = encrypted[1] || ''
|
|
189
190
|
if (foundOwnContentPubkey && nextContentPubkey !== usedOwnContentPubkey) {
|
|
190
|
-
throw new
|
|
191
|
+
throw new ValidationError('INCONSISTENT_IMKC_PUBKEY')
|
|
191
192
|
}
|
|
192
193
|
foundOwnContentPubkey = true
|
|
193
194
|
if (nextContentPubkey) usedOwnContentPubkey = nextContentPubkey
|
|
@@ -240,7 +241,7 @@ export function preparedRowIndexesForReceivers (preparedRows, receivers) {
|
|
|
240
241
|
for (const receiver of receivers || []) {
|
|
241
242
|
const pubkey = receiverRecord(receiver, {}).receiverPubkey
|
|
242
243
|
const index = preparedRows?.receiverRowIndexesByPubkey?.[pubkey]
|
|
243
|
-
if (!pubkey || index === undefined) throw new
|
|
244
|
+
if (!pubkey || index === undefined) throw new ValidationError('MISSING_PREPARED_RECEIVER')
|
|
244
245
|
if (seen.has(index)) continue
|
|
245
246
|
seen.add(index)
|
|
246
247
|
indexes.push(index)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NYM_CARRIER_KIND, ROUTER_KIND } from '../constants/index.js'
|
|
2
|
+
import { ValidationError } from '../../error/index.js'
|
|
2
3
|
|
|
3
4
|
const encoder = new TextEncoder()
|
|
4
5
|
|
|
@@ -16,7 +17,7 @@ export function readReceiverTag (event) {
|
|
|
16
17
|
|
|
17
18
|
export function readSenderTag (event) {
|
|
18
19
|
const senderPubkey = event.tags?.find(t => t[0] === 'f')?.[1]
|
|
19
|
-
if (!senderPubkey) throw new
|
|
20
|
+
if (!senderPubkey) throw new ValidationError('MISSING_SENDER_TAG')
|
|
20
21
|
return senderPubkey
|
|
21
22
|
}
|
|
22
23
|
|
|
@@ -41,7 +42,7 @@ export function readChunkTag (event) {
|
|
|
41
42
|
const index = Number(tag?.[1])
|
|
42
43
|
const total = Number(tag?.[2])
|
|
43
44
|
if (!Number.isInteger(index) || !Number.isInteger(total) || index < 0 || total < 1) {
|
|
44
|
-
throw new
|
|
45
|
+
throw new ValidationError('INVALID_CHUNK_TAG')
|
|
45
46
|
}
|
|
46
47
|
return { index, total }
|
|
47
48
|
}
|
|
@@ -49,7 +50,7 @@ export function readChunkTag (event) {
|
|
|
49
50
|
export function makeRouterEvent ({ pubkey, senderPubkey, imkcPubkey, imkcProof, receiverPubkey, chunkIndex, chunkTotal, content }) {
|
|
50
51
|
const tags = [['f', senderPubkey]]
|
|
51
52
|
if (imkcPubkey) {
|
|
52
|
-
if (!imkcProof) throw new
|
|
53
|
+
if (!imkcProof) throw new ValidationError('INVALID_IMKC_PROOF')
|
|
53
54
|
tags.push(['imkc', imkcPubkey, imkcProof])
|
|
54
55
|
}
|
|
55
56
|
tags.push(['c', String(chunkIndex), String(chunkTotal)])
|
|
@@ -58,7 +59,7 @@ export function makeRouterEvent ({ pubkey, senderPubkey, imkcPubkey, imkcProof,
|
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
export function makeNymCarrierEvent ({ innerId, chunkIndex, chunkTotal, content, createdAt = nowSeconds() }) {
|
|
61
|
-
if (!innerId) throw new
|
|
62
|
+
if (!innerId) throw new ValidationError('INNER_EVENT_ID_REQUIRED')
|
|
62
63
|
return {
|
|
63
64
|
kind: NYM_CARRIER_KIND,
|
|
64
65
|
created_at: createdAt,
|